diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/common |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/common')
-rw-r--r-- | drivers/media/common/Kconfig | 12 | ||||
-rw-r--r-- | drivers/media/common/Makefile | 6 | ||||
-rw-r--r-- | drivers/media/common/ir-common.c | 381 | ||||
-rw-r--r-- | drivers/media/common/saa7146_core.c | 547 | ||||
-rw-r--r-- | drivers/media/common/saa7146_fops.c | 564 | ||||
-rw-r--r-- | drivers/media/common/saa7146_hlp.c | 1036 | ||||
-rw-r--r-- | drivers/media/common/saa7146_i2c.c | 421 | ||||
-rw-r--r-- | drivers/media/common/saa7146_vbi.c | 508 | ||||
-rw-r--r-- | drivers/media/common/saa7146_video.c | 1509 | ||||
-rw-r--r-- | drivers/media/common/saa7146_vv_ksyms.c | 12 |
10 files changed, 4996 insertions, 0 deletions
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig new file mode 100644 index 000000000000..caebd0a1c021 --- /dev/null +++ b/drivers/media/common/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | config VIDEO_SAA7146 | ||
2 | tristate | ||
3 | select I2C | ||
4 | |||
5 | config VIDEO_SAA7146_VV | ||
6 | tristate | ||
7 | select VIDEO_BUF | ||
8 | select VIDEO_VIDEOBUF | ||
9 | select VIDEO_SAA7146 | ||
10 | |||
11 | config VIDEO_VIDEOBUF | ||
12 | tristate | ||
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile new file mode 100644 index 000000000000..97b4341255ea --- /dev/null +++ b/drivers/media/common/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | saa7146-objs := saa7146_i2c.o saa7146_core.o | ||
2 | saa7146_vv-objs := saa7146_vv_ksyms.o saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o | ||
3 | |||
4 | obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o | ||
5 | obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o | ||
6 | obj-$(CONFIG_VIDEO_IR) += ir-common.o | ||
diff --git a/drivers/media/common/ir-common.c b/drivers/media/common/ir-common.c new file mode 100644 index 000000000000..8c842e2f59a2 --- /dev/null +++ b/drivers/media/common/ir-common.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * $Id: ir-common.c,v 1.8 2005/02/22 12:28:40 kraxel Exp $ | ||
3 | * | ||
4 | * some common structs and functions to handle infrared remotes via | ||
5 | * input layer ... | ||
6 | * | ||
7 | * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/moduleparam.h> | ||
26 | #include <media/ir-common.h> | ||
27 | |||
28 | /* -------------------------------------------------------------------------- */ | ||
29 | |||
30 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
31 | MODULE_LICENSE("GPL"); | ||
32 | |||
33 | static int repeat = 1; | ||
34 | module_param(repeat, int, 0444); | ||
35 | MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)"); | ||
36 | |||
37 | static int debug = 0; /* debug level (0,1,2) */ | ||
38 | module_param(debug, int, 0644); | ||
39 | |||
40 | #define dprintk(level, fmt, arg...) if (debug >= level) \ | ||
41 | printk(KERN_DEBUG fmt , ## arg) | ||
42 | |||
43 | /* -------------------------------------------------------------------------- */ | ||
44 | |||
45 | /* generic RC5 keytable */ | ||
46 | /* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */ | ||
47 | /* used by old (black) Hauppauge remotes */ | ||
48 | IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = { | ||
49 | [ 0x00 ] = KEY_KP0, // 0 | ||
50 | [ 0x01 ] = KEY_KP1, // 1 | ||
51 | [ 0x02 ] = KEY_KP2, // 2 | ||
52 | [ 0x03 ] = KEY_KP3, // 3 | ||
53 | [ 0x04 ] = KEY_KP4, // 4 | ||
54 | [ 0x05 ] = KEY_KP5, // 5 | ||
55 | [ 0x06 ] = KEY_KP6, // 6 | ||
56 | [ 0x07 ] = KEY_KP7, // 7 | ||
57 | [ 0x08 ] = KEY_KP8, // 8 | ||
58 | [ 0x09 ] = KEY_KP9, // 9 | ||
59 | |||
60 | [ 0x0b ] = KEY_CHANNEL, // channel / program (japan: 11) | ||
61 | [ 0x0c ] = KEY_POWER, // standby | ||
62 | [ 0x0d ] = KEY_MUTE, // mute / demute | ||
63 | [ 0x0f ] = KEY_TV, // display | ||
64 | [ 0x10 ] = KEY_VOLUMEUP, // volume + | ||
65 | [ 0x11 ] = KEY_VOLUMEDOWN, // volume - | ||
66 | [ 0x12 ] = KEY_BRIGHTNESSUP, // brightness + | ||
67 | [ 0x13 ] = KEY_BRIGHTNESSDOWN, // brightness - | ||
68 | [ 0x1e ] = KEY_SEARCH, // search + | ||
69 | [ 0x20 ] = KEY_CHANNELUP, // channel / program + | ||
70 | [ 0x21 ] = KEY_CHANNELDOWN, // channel / program - | ||
71 | [ 0x22 ] = KEY_CHANNEL, // alt / channel | ||
72 | [ 0x23 ] = KEY_LANGUAGE, // 1st / 2nd language | ||
73 | [ 0x26 ] = KEY_SLEEP, // sleeptimer | ||
74 | [ 0x2e ] = KEY_MENU, // 2nd controls (USA: menu) | ||
75 | [ 0x30 ] = KEY_PAUSE, // pause | ||
76 | [ 0x32 ] = KEY_REWIND, // rewind | ||
77 | [ 0x33 ] = KEY_GOTO, // go to | ||
78 | [ 0x35 ] = KEY_PLAY, // play | ||
79 | [ 0x36 ] = KEY_STOP, // stop | ||
80 | [ 0x37 ] = KEY_RECORD, // recording | ||
81 | [ 0x3c ] = KEY_TEXT, // teletext submode (Japan: 12) | ||
82 | [ 0x3d ] = KEY_SUSPEND, // system standby | ||
83 | |||
84 | #if 0 /* FIXME */ | ||
85 | [ 0x0a ] = KEY_RESERVED, // 1/2/3 digits (japan: 10) | ||
86 | [ 0x0e ] = KEY_RESERVED, // P.P. (personal preference) | ||
87 | [ 0x14 ] = KEY_RESERVED, // colour saturation + | ||
88 | [ 0x15 ] = KEY_RESERVED, // colour saturation - | ||
89 | [ 0x16 ] = KEY_RESERVED, // bass + | ||
90 | [ 0x17 ] = KEY_RESERVED, // bass - | ||
91 | [ 0x18 ] = KEY_RESERVED, // treble + | ||
92 | [ 0x19 ] = KEY_RESERVED, // treble - | ||
93 | [ 0x1a ] = KEY_RESERVED, // balance right | ||
94 | [ 0x1b ] = KEY_RESERVED, // balance left | ||
95 | [ 0x1c ] = KEY_RESERVED, // contrast + | ||
96 | [ 0x1d ] = KEY_RESERVED, // contrast - | ||
97 | [ 0x1f ] = KEY_RESERVED, // tint/hue + | ||
98 | [ 0x24 ] = KEY_RESERVED, // spacial stereo on/off | ||
99 | [ 0x25 ] = KEY_RESERVED, // mono / stereo (USA) | ||
100 | [ 0x27 ] = KEY_RESERVED, // tint / hue - | ||
101 | [ 0x28 ] = KEY_RESERVED, // RF switch/PIP select | ||
102 | [ 0x29 ] = KEY_RESERVED, // vote | ||
103 | [ 0x2a ] = KEY_RESERVED, // timed page/channel clck | ||
104 | [ 0x2b ] = KEY_RESERVED, // increment (USA) | ||
105 | [ 0x2c ] = KEY_RESERVED, // decrement (USA) | ||
106 | [ 0x2d ] = KEY_RESERVED, // | ||
107 | [ 0x2f ] = KEY_RESERVED, // PIP shift | ||
108 | [ 0x31 ] = KEY_RESERVED, // erase | ||
109 | [ 0x34 ] = KEY_RESERVED, // wind | ||
110 | [ 0x38 ] = KEY_RESERVED, // external 1 | ||
111 | [ 0x39 ] = KEY_RESERVED, // external 2 | ||
112 | [ 0x3a ] = KEY_RESERVED, // PIP display mode | ||
113 | [ 0x3b ] = KEY_RESERVED, // view data mode / advance | ||
114 | [ 0x3e ] = KEY_RESERVED, // crispener on/off | ||
115 | [ 0x3f ] = KEY_RESERVED, // system select | ||
116 | #endif | ||
117 | }; | ||
118 | EXPORT_SYMBOL_GPL(ir_codes_rc5_tv); | ||
119 | |||
120 | /* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */ | ||
121 | IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = { | ||
122 | [ 5 ] = KEY_KP1, | ||
123 | [ 6 ] = KEY_KP2, | ||
124 | [ 7 ] = KEY_KP3, | ||
125 | [ 9 ] = KEY_KP4, | ||
126 | [ 10 ] = KEY_KP5, | ||
127 | [ 11 ] = KEY_KP6, | ||
128 | [ 13 ] = KEY_KP7, | ||
129 | [ 14 ] = KEY_KP8, | ||
130 | [ 15 ] = KEY_KP9, | ||
131 | [ 18 ] = KEY_KP0, | ||
132 | |||
133 | [ 0 ] = KEY_POWER, | ||
134 | // [ 27 ] = MTS button | ||
135 | [ 2 ] = KEY_TUNER, // TV/FM | ||
136 | [ 30 ] = KEY_VIDEO, | ||
137 | // [ 22 ] = display button | ||
138 | [ 4 ] = KEY_VOLUMEUP, | ||
139 | [ 8 ] = KEY_VOLUMEDOWN, | ||
140 | [ 12 ] = KEY_CHANNELUP, | ||
141 | [ 16 ] = KEY_CHANNELDOWN, | ||
142 | [ 3 ] = KEY_ZOOM, // fullscreen | ||
143 | [ 31 ] = KEY_SUBTITLE, // closed caption/teletext | ||
144 | [ 32 ] = KEY_SLEEP, | ||
145 | // [ 41 ] = boss key | ||
146 | [ 20 ] = KEY_MUTE, | ||
147 | [ 43 ] = KEY_RED, | ||
148 | [ 44 ] = KEY_GREEN, | ||
149 | [ 45 ] = KEY_YELLOW, | ||
150 | [ 46 ] = KEY_BLUE, | ||
151 | [ 24 ] = KEY_KPPLUS, //fine tune + | ||
152 | [ 25 ] = KEY_KPMINUS, //fine tune - | ||
153 | // [ 42 ] = picture in picture | ||
154 | [ 33 ] = KEY_KPDOT, | ||
155 | [ 19 ] = KEY_KPENTER, | ||
156 | // [ 17 ] = recall | ||
157 | [ 34 ] = KEY_BACK, | ||
158 | [ 35 ] = KEY_PLAYPAUSE, | ||
159 | [ 36 ] = KEY_NEXT, | ||
160 | // [ 37 ] = time shifting | ||
161 | [ 38 ] = KEY_STOP, | ||
162 | [ 39 ] = KEY_RECORD | ||
163 | // [ 40 ] = snapshot | ||
164 | }; | ||
165 | EXPORT_SYMBOL_GPL(ir_codes_winfast); | ||
166 | |||
167 | /* empty keytable, can be used as placeholder for not-yet created keytables */ | ||
168 | IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = { | ||
169 | [ 42 ] = KEY_COFFEE, | ||
170 | }; | ||
171 | EXPORT_SYMBOL_GPL(ir_codes_empty); | ||
172 | |||
173 | /* Hauppauge: the newer, gray remotes (seems there are multiple | ||
174 | * slightly different versions), shipped with cx88+ivtv cards. | ||
175 | * almost rc5 coding, but some non-standard keys */ | ||
176 | IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = { | ||
177 | [ 0x00 ] = KEY_KP0, // 0 | ||
178 | [ 0x01 ] = KEY_KP1, // 1 | ||
179 | [ 0x02 ] = KEY_KP2, // 2 | ||
180 | [ 0x03 ] = KEY_KP3, // 3 | ||
181 | [ 0x04 ] = KEY_KP4, // 4 | ||
182 | [ 0x05 ] = KEY_KP5, // 5 | ||
183 | [ 0x06 ] = KEY_KP6, // 6 | ||
184 | [ 0x07 ] = KEY_KP7, // 7 | ||
185 | [ 0x08 ] = KEY_KP8, // 8 | ||
186 | [ 0x09 ] = KEY_KP9, // 9 | ||
187 | [ 0x0b ] = KEY_RED, // red button | ||
188 | [ 0x0c ] = KEY_OPTION, // black key without text | ||
189 | [ 0x0d ] = KEY_MENU, // menu | ||
190 | [ 0x0f ] = KEY_MUTE, // mute | ||
191 | [ 0x10 ] = KEY_VOLUMEUP, // volume + | ||
192 | [ 0x11 ] = KEY_VOLUMEDOWN, // volume - | ||
193 | [ 0x1e ] = KEY_NEXT, // skip >| | ||
194 | [ 0x1f ] = KEY_EXIT, // back/exit | ||
195 | [ 0x20 ] = KEY_CHANNELUP, // channel / program + | ||
196 | [ 0x21 ] = KEY_CHANNELDOWN, // channel / program - | ||
197 | [ 0x22 ] = KEY_CHANNEL, // source (old black remote) | ||
198 | [ 0x24 ] = KEY_PREVIOUS, // replay |< | ||
199 | [ 0x25 ] = KEY_ENTER, // OK | ||
200 | [ 0x26 ] = KEY_SLEEP, // minimize (old black remote) | ||
201 | [ 0x29 ] = KEY_BLUE, // blue key | ||
202 | [ 0x2e ] = KEY_GREEN, // green button | ||
203 | [ 0x30 ] = KEY_PAUSE, // pause | ||
204 | [ 0x32 ] = KEY_REWIND, // backward << | ||
205 | [ 0x34 ] = KEY_FASTFORWARD, // forward >> | ||
206 | [ 0x35 ] = KEY_PLAY, // play | ||
207 | [ 0x36 ] = KEY_STOP, // stop | ||
208 | [ 0x37 ] = KEY_RECORD, // recording | ||
209 | [ 0x38 ] = KEY_YELLOW, // yellow key | ||
210 | [ 0x3b ] = KEY_SELECT, // top right button | ||
211 | [ 0x3c ] = KEY_ZOOM, // full | ||
212 | [ 0x3d ] = KEY_POWER, // system power (green button) | ||
213 | }; | ||
214 | EXPORT_SYMBOL(ir_codes_hauppauge_new); | ||
215 | |||
216 | /* -------------------------------------------------------------------------- */ | ||
217 | |||
218 | static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) | ||
219 | { | ||
220 | if (KEY_RESERVED == ir->keycode) { | ||
221 | printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n", | ||
222 | dev->name,ir->ir_key,ir->ir_raw,ir->keypressed); | ||
223 | return; | ||
224 | } | ||
225 | dprintk(1,"%s: key event code=%d down=%d\n", | ||
226 | dev->name,ir->keycode,ir->keypressed); | ||
227 | input_report_key(dev,ir->keycode,ir->keypressed); | ||
228 | input_sync(dev); | ||
229 | } | ||
230 | |||
231 | /* -------------------------------------------------------------------------- */ | ||
232 | |||
233 | void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, | ||
234 | int ir_type, IR_KEYTAB_TYPE *ir_codes) | ||
235 | { | ||
236 | int i; | ||
237 | |||
238 | ir->ir_type = ir_type; | ||
239 | if (ir_codes) | ||
240 | memcpy(ir->ir_codes, ir_codes, sizeof(ir->ir_codes)); | ||
241 | |||
242 | init_input_dev(dev); | ||
243 | dev->keycode = ir->ir_codes; | ||
244 | dev->keycodesize = sizeof(IR_KEYTAB_TYPE); | ||
245 | dev->keycodemax = IR_KEYTAB_SIZE; | ||
246 | for (i = 0; i < IR_KEYTAB_SIZE; i++) | ||
247 | set_bit(ir->ir_codes[i], dev->keybit); | ||
248 | clear_bit(0, dev->keybit); | ||
249 | |||
250 | set_bit(EV_KEY, dev->evbit); | ||
251 | if (repeat) | ||
252 | set_bit(EV_REP, dev->evbit); | ||
253 | } | ||
254 | |||
255 | void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) | ||
256 | { | ||
257 | if (ir->keypressed) { | ||
258 | ir->keypressed = 0; | ||
259 | ir_input_key_event(dev,ir); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, | ||
264 | u32 ir_key, u32 ir_raw) | ||
265 | { | ||
266 | u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key); | ||
267 | |||
268 | if (ir->keypressed && ir->keycode != keycode) { | ||
269 | ir->keypressed = 0; | ||
270 | ir_input_key_event(dev,ir); | ||
271 | } | ||
272 | if (!ir->keypressed) { | ||
273 | ir->ir_key = ir_key; | ||
274 | ir->ir_raw = ir_raw; | ||
275 | ir->keycode = keycode; | ||
276 | ir->keypressed = 1; | ||
277 | ir_input_key_event(dev,ir); | ||
278 | } | ||
279 | #if 0 | ||
280 | /* maybe do something like this ??? */ | ||
281 | input_event(a, EV_IR, ir->ir_type, ir->ir_raw); | ||
282 | #endif | ||
283 | } | ||
284 | |||
285 | /* -------------------------------------------------------------------------- */ | ||
286 | |||
287 | u32 ir_extract_bits(u32 data, u32 mask) | ||
288 | { | ||
289 | int mbit, vbit; | ||
290 | u32 value; | ||
291 | |||
292 | value = 0; | ||
293 | vbit = 0; | ||
294 | for (mbit = 0; mbit < 32; mbit++) { | ||
295 | if (!(mask & ((u32)1 << mbit))) | ||
296 | continue; | ||
297 | if (data & ((u32)1 << mbit)) | ||
298 | value |= (1 << vbit); | ||
299 | vbit++; | ||
300 | } | ||
301 | return value; | ||
302 | } | ||
303 | |||
304 | static int inline getbit(u32 *samples, int bit) | ||
305 | { | ||
306 | return (samples[bit/32] & (1 << (31-(bit%32)))) ? 1 : 0; | ||
307 | } | ||
308 | |||
309 | /* sump raw samples for visual debugging ;) */ | ||
310 | int ir_dump_samples(u32 *samples, int count) | ||
311 | { | ||
312 | int i, bit, start; | ||
313 | |||
314 | printk(KERN_DEBUG "ir samples: "); | ||
315 | start = 0; | ||
316 | for (i = 0; i < count * 32; i++) { | ||
317 | bit = getbit(samples,i); | ||
318 | if (bit) | ||
319 | start = 1; | ||
320 | if (0 == start) | ||
321 | continue; | ||
322 | printk("%s", bit ? "#" : "_"); | ||
323 | } | ||
324 | printk("\n"); | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /* decode raw samples, biphase coding, used by rc5 for example */ | ||
329 | int ir_decode_biphase(u32 *samples, int count, int low, int high) | ||
330 | { | ||
331 | int i,last,bit,len,flips; | ||
332 | u32 value; | ||
333 | |||
334 | /* find start bit (1) */ | ||
335 | for (i = 0; i < 32; i++) { | ||
336 | bit = getbit(samples,i); | ||
337 | if (bit) | ||
338 | break; | ||
339 | } | ||
340 | |||
341 | /* go decoding */ | ||
342 | len = 0; | ||
343 | flips = 0; | ||
344 | value = 1; | ||
345 | for (; i < count * 32; i++) { | ||
346 | if (len > high) | ||
347 | break; | ||
348 | if (flips > 1) | ||
349 | break; | ||
350 | last = bit; | ||
351 | bit = getbit(samples,i); | ||
352 | if (last == bit) { | ||
353 | len++; | ||
354 | continue; | ||
355 | } | ||
356 | if (len < low) { | ||
357 | len++; | ||
358 | flips++; | ||
359 | continue; | ||
360 | } | ||
361 | value <<= 1; | ||
362 | value |= bit; | ||
363 | flips = 0; | ||
364 | len = 1; | ||
365 | } | ||
366 | return value; | ||
367 | } | ||
368 | |||
369 | EXPORT_SYMBOL_GPL(ir_input_init); | ||
370 | EXPORT_SYMBOL_GPL(ir_input_nokey); | ||
371 | EXPORT_SYMBOL_GPL(ir_input_keydown); | ||
372 | |||
373 | EXPORT_SYMBOL_GPL(ir_extract_bits); | ||
374 | EXPORT_SYMBOL_GPL(ir_dump_samples); | ||
375 | EXPORT_SYMBOL_GPL(ir_decode_biphase); | ||
376 | |||
377 | /* | ||
378 | * Local variables: | ||
379 | * c-basic-offset: 8 | ||
380 | * End: | ||
381 | */ | ||
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c new file mode 100644 index 000000000000..9f6c19ac1285 --- /dev/null +++ b/drivers/media/common/saa7146_core.c | |||
@@ -0,0 +1,547 @@ | |||
1 | /* | ||
2 | saa7146.o - driver for generic saa7146-based hardware | ||
3 | |||
4 | Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> | ||
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 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | |||
21 | #include <media/saa7146.h> | ||
22 | |||
23 | LIST_HEAD(saa7146_devices); | ||
24 | DECLARE_MUTEX(saa7146_devices_lock); | ||
25 | |||
26 | static int saa7146_num = 0; | ||
27 | |||
28 | unsigned int saa7146_debug = 0; | ||
29 | |||
30 | module_param(saa7146_debug, int, 0644); | ||
31 | MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); | ||
32 | |||
33 | #if 0 | ||
34 | static void dump_registers(struct saa7146_dev* dev) | ||
35 | { | ||
36 | int i = 0; | ||
37 | |||
38 | INFO((" @ %li jiffies:\n",jiffies)); | ||
39 | for(i = 0; i <= 0x148; i+=4) { | ||
40 | printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i)); | ||
41 | } | ||
42 | } | ||
43 | #endif | ||
44 | |||
45 | /**************************************************************************** | ||
46 | * gpio and debi helper functions | ||
47 | ****************************************************************************/ | ||
48 | |||
49 | void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data) | ||
50 | { | ||
51 | u32 value = 0; | ||
52 | |||
53 | BUG_ON(port > 3); | ||
54 | |||
55 | value = saa7146_read(dev, GPIO_CTRL); | ||
56 | value &= ~(0xff << (8*port)); | ||
57 | value |= (data << (8*port)); | ||
58 | saa7146_write(dev, GPIO_CTRL, value); | ||
59 | } | ||
60 | |||
61 | /* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */ | ||
62 | int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop) | ||
63 | { | ||
64 | unsigned long start; | ||
65 | |||
66 | /* wait for registers to be programmed */ | ||
67 | start = jiffies; | ||
68 | while (1) { | ||
69 | if (saa7146_read(dev, MC2) & 2) | ||
70 | break; | ||
71 | if (time_after(jiffies, start + HZ/20)) { | ||
72 | DEB_S(("timed out while waiting for registers getting programmed\n")); | ||
73 | return -ETIMEDOUT; | ||
74 | } | ||
75 | if (nobusyloop) | ||
76 | msleep(1); | ||
77 | } | ||
78 | |||
79 | /* wait for transfer to complete */ | ||
80 | start = jiffies; | ||
81 | while (1) { | ||
82 | if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S)) | ||
83 | break; | ||
84 | saa7146_read(dev, MC2); | ||
85 | if (time_after(jiffies, start + HZ/4)) { | ||
86 | DEB_S(("timed out while waiting for transfer completion\n")); | ||
87 | return -ETIMEDOUT; | ||
88 | } | ||
89 | if (nobusyloop) | ||
90 | msleep(1); | ||
91 | } | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | /**************************************************************************** | ||
97 | * general helper functions | ||
98 | ****************************************************************************/ | ||
99 | |||
100 | /* this is videobuf_vmalloc_to_sg() from video-buf.c | ||
101 | make sure virt has been allocated with vmalloc_32(), otherwise the BUG() | ||
102 | may be triggered on highmem machines */ | ||
103 | static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) | ||
104 | { | ||
105 | struct scatterlist *sglist; | ||
106 | struct page *pg; | ||
107 | int i; | ||
108 | |||
109 | sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL); | ||
110 | if (NULL == sglist) | ||
111 | return NULL; | ||
112 | memset(sglist,0,sizeof(struct scatterlist)*nr_pages); | ||
113 | for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { | ||
114 | pg = vmalloc_to_page(virt); | ||
115 | if (NULL == pg) | ||
116 | goto err; | ||
117 | if (PageHighMem(pg)) | ||
118 | BUG(); | ||
119 | sglist[i].page = pg; | ||
120 | sglist[i].length = PAGE_SIZE; | ||
121 | } | ||
122 | return sglist; | ||
123 | |||
124 | err: | ||
125 | kfree(sglist); | ||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | /********************************************************************************/ | ||
130 | /* common page table functions */ | ||
131 | |||
132 | char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) | ||
133 | { | ||
134 | int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; | ||
135 | char *mem = vmalloc_32(length); | ||
136 | int slen = 0; | ||
137 | |||
138 | if (NULL == mem) { | ||
139 | return NULL; | ||
140 | } | ||
141 | |||
142 | if (!(pt->slist = vmalloc_to_sg(mem, pages))) { | ||
143 | vfree(mem); | ||
144 | return NULL; | ||
145 | } | ||
146 | |||
147 | if (saa7146_pgtable_alloc(pci, pt)) { | ||
148 | kfree(pt->slist); | ||
149 | pt->slist = NULL; | ||
150 | vfree(mem); | ||
151 | return NULL; | ||
152 | } | ||
153 | |||
154 | slen = pci_map_sg(pci,pt->slist,pages,PCI_DMA_FROMDEVICE); | ||
155 | if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) { | ||
156 | return NULL; | ||
157 | } | ||
158 | |||
159 | return mem; | ||
160 | } | ||
161 | |||
162 | void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) | ||
163 | { | ||
164 | if (NULL == pt->cpu) | ||
165 | return; | ||
166 | pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); | ||
167 | pt->cpu = NULL; | ||
168 | if (NULL != pt->slist) { | ||
169 | kfree(pt->slist); | ||
170 | pt->slist = NULL; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) | ||
175 | { | ||
176 | u32 *cpu; | ||
177 | dma_addr_t dma_addr; | ||
178 | |||
179 | cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr); | ||
180 | if (NULL == cpu) { | ||
181 | return -ENOMEM; | ||
182 | } | ||
183 | pt->size = PAGE_SIZE; | ||
184 | pt->cpu = cpu; | ||
185 | pt->dma = dma_addr; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, | ||
191 | struct scatterlist *list, int sglen ) | ||
192 | { | ||
193 | u32 *ptr, fill; | ||
194 | int nr_pages = 0; | ||
195 | int i,p; | ||
196 | |||
197 | BUG_ON(0 == sglen); | ||
198 | BUG_ON(list->offset > PAGE_SIZE); | ||
199 | |||
200 | /* if we have a user buffer, the first page may not be | ||
201 | aligned to a page boundary. */ | ||
202 | pt->offset = list->offset; | ||
203 | |||
204 | ptr = pt->cpu; | ||
205 | for (i = 0; i < sglen; i++, list++) { | ||
206 | /* | ||
207 | printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset); | ||
208 | */ | ||
209 | for (p = 0; p * 4096 < list->length; p++, ptr++) { | ||
210 | *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096); | ||
211 | nr_pages++; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | |||
216 | /* safety; fill the page table up with the last valid page */ | ||
217 | fill = *(ptr-1); | ||
218 | for(i=nr_pages;i<1024;i++) { | ||
219 | *ptr++ = fill; | ||
220 | } | ||
221 | |||
222 | /* | ||
223 | ptr = pt->cpu; | ||
224 | printk("offset: %d\n",pt->offset); | ||
225 | for(i=0;i<5;i++) { | ||
226 | printk("ptr1 %d: 0x%08x\n",i,ptr[i]); | ||
227 | } | ||
228 | */ | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | /********************************************************************************/ | ||
233 | /* interrupt handler */ | ||
234 | static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs) | ||
235 | { | ||
236 | struct saa7146_dev *dev = dev_id; | ||
237 | u32 isr = 0; | ||
238 | |||
239 | /* read out the interrupt status register */ | ||
240 | isr = saa7146_read(dev, ISR); | ||
241 | |||
242 | /* is this our interrupt? */ | ||
243 | if ( 0 == isr ) { | ||
244 | /* nope, some other device */ | ||
245 | return IRQ_NONE; | ||
246 | } | ||
247 | |||
248 | saa7146_write(dev, ISR, isr); | ||
249 | |||
250 | if( 0 != (dev->ext)) { | ||
251 | if( 0 != (dev->ext->irq_mask & isr )) { | ||
252 | if( 0 != dev->ext->irq_func ) { | ||
253 | dev->ext->irq_func(dev, &isr); | ||
254 | } | ||
255 | isr &= ~dev->ext->irq_mask; | ||
256 | } | ||
257 | } | ||
258 | if (0 != (isr & (MASK_27))) { | ||
259 | DEB_INT(("irq: RPS0 (0x%08x).\n",isr)); | ||
260 | if( 0 != dev->vv_data && 0 != dev->vv_callback) { | ||
261 | dev->vv_callback(dev,isr); | ||
262 | } | ||
263 | isr &= ~MASK_27; | ||
264 | } | ||
265 | if (0 != (isr & (MASK_28))) { | ||
266 | if( 0 != dev->vv_data && 0 != dev->vv_callback) { | ||
267 | dev->vv_callback(dev,isr); | ||
268 | } | ||
269 | isr &= ~MASK_28; | ||
270 | } | ||
271 | if (0 != (isr & (MASK_16|MASK_17))) { | ||
272 | u32 status = saa7146_read(dev, I2C_STATUS); | ||
273 | if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) { | ||
274 | SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); | ||
275 | /* only wake up if we expect something */ | ||
276 | if( 0 != dev->i2c_op ) { | ||
277 | u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2; | ||
278 | u32 ssr = (saa7146_read(dev, SSR) >> 17) & 0x1f; | ||
279 | DEB_I2C(("irq: i2c, status: 0x%08x, psr:0x%02x, ssr:0x%02x).\n",status,psr,ssr)); | ||
280 | dev->i2c_op = 0; | ||
281 | wake_up(&dev->i2c_wq); | ||
282 | } else { | ||
283 | DEB_I2C(("unexpected irq: i2c, status: 0x%08x, isr %#x\n",status, isr)); | ||
284 | } | ||
285 | } else { | ||
286 | DEB_I2C(("unhandled irq: i2c, status: 0x%08x, isr %#x\n",status, isr)); | ||
287 | } | ||
288 | isr &= ~(MASK_16|MASK_17); | ||
289 | } | ||
290 | if( 0 != isr ) { | ||
291 | ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr)); | ||
292 | ERR(("disabling interrupt source(s)!\n")); | ||
293 | SAA7146_IER_DISABLE(dev,isr); | ||
294 | } | ||
295 | return IRQ_HANDLED; | ||
296 | } | ||
297 | |||
298 | /*********************************************************************************/ | ||
299 | /* configuration-functions */ | ||
300 | |||
301 | static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent) | ||
302 | { | ||
303 | struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data; | ||
304 | struct saa7146_extension *ext = pci_ext->ext; | ||
305 | struct saa7146_dev *dev; | ||
306 | int err = -ENOMEM; | ||
307 | |||
308 | dev = kmalloc(sizeof(struct saa7146_dev), GFP_KERNEL); | ||
309 | if (!dev) { | ||
310 | ERR(("out of memory.\n")); | ||
311 | goto out; | ||
312 | } | ||
313 | |||
314 | /* clear out mem for sure */ | ||
315 | memset(dev, 0x0, sizeof(struct saa7146_dev)); | ||
316 | |||
317 | DEB_EE(("pci:%p\n",pci)); | ||
318 | |||
319 | err = pci_enable_device(pci); | ||
320 | if (err < 0) { | ||
321 | ERR(("pci_enable_device() failed.\n")); | ||
322 | goto err_free; | ||
323 | } | ||
324 | |||
325 | /* enable bus-mastering */ | ||
326 | pci_set_master(pci); | ||
327 | |||
328 | dev->pci = pci; | ||
329 | |||
330 | /* get chip-revision; this is needed to enable bug-fixes */ | ||
331 | err = pci_read_config_dword(pci, PCI_CLASS_REVISION, &dev->revision); | ||
332 | if (err < 0) { | ||
333 | ERR(("pci_read_config_dword() failed.\n")); | ||
334 | goto err_disable; | ||
335 | } | ||
336 | dev->revision &= 0xf; | ||
337 | |||
338 | /* remap the memory from virtual to physical adress */ | ||
339 | |||
340 | err = pci_request_region(pci, 0, "saa7146"); | ||
341 | if (err < 0) | ||
342 | goto err_disable; | ||
343 | |||
344 | dev->mem = ioremap(pci_resource_start(pci, 0), | ||
345 | pci_resource_len(pci, 0)); | ||
346 | if (!dev->mem) { | ||
347 | ERR(("ioremap() failed.\n")); | ||
348 | err = -ENODEV; | ||
349 | goto err_release; | ||
350 | } | ||
351 | |||
352 | /* we don't do a master reset here anymore, it screws up | ||
353 | some boards that don't have an i2c-eeprom for configuration | ||
354 | values */ | ||
355 | /* | ||
356 | saa7146_write(dev, MC1, MASK_31); | ||
357 | */ | ||
358 | |||
359 | /* disable all irqs */ | ||
360 | saa7146_write(dev, IER, 0); | ||
361 | |||
362 | /* shut down all dma transfers and rps tasks */ | ||
363 | saa7146_write(dev, MC1, 0x30ff0000); | ||
364 | |||
365 | /* clear out any rps-signals pending */ | ||
366 | saa7146_write(dev, MC2, 0xf8000000); | ||
367 | |||
368 | /* request an interrupt for the saa7146 */ | ||
369 | err = request_irq(pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT, | ||
370 | dev->name, dev); | ||
371 | if (err < 0) { | ||
372 | ERR(("request_irq() failed.\n")); | ||
373 | goto err_unmap; | ||
374 | } | ||
375 | |||
376 | err = -ENOMEM; | ||
377 | |||
378 | /* get memory for various stuff */ | ||
379 | dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, | ||
380 | &dev->d_rps0.dma_handle); | ||
381 | if (!dev->d_rps0.cpu_addr) | ||
382 | goto err_free_irq; | ||
383 | memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM); | ||
384 | |||
385 | dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, | ||
386 | &dev->d_rps1.dma_handle); | ||
387 | if (!dev->d_rps1.cpu_addr) | ||
388 | goto err_free_rps0; | ||
389 | memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM); | ||
390 | |||
391 | dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, | ||
392 | &dev->d_i2c.dma_handle); | ||
393 | if (!dev->d_i2c.cpu_addr) | ||
394 | goto err_free_rps1; | ||
395 | memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM); | ||
396 | |||
397 | /* the rest + print status message */ | ||
398 | |||
399 | /* create a nice device name */ | ||
400 | sprintf(dev->name, "saa7146 (%d)", saa7146_num); | ||
401 | |||
402 | INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device)); | ||
403 | dev->ext = ext; | ||
404 | |||
405 | pci_set_drvdata(pci, dev); | ||
406 | |||
407 | init_MUTEX(&dev->lock); | ||
408 | spin_lock_init(&dev->int_slock); | ||
409 | spin_lock_init(&dev->slock); | ||
410 | |||
411 | init_MUTEX(&dev->i2c_lock); | ||
412 | |||
413 | dev->module = THIS_MODULE; | ||
414 | init_waitqueue_head(&dev->i2c_wq); | ||
415 | |||
416 | /* set some sane pci arbitrition values */ | ||
417 | saa7146_write(dev, PCI_BT_V1, 0x1c00101f); | ||
418 | |||
419 | /* TODO: use the status code of the callback */ | ||
420 | |||
421 | err = -ENODEV; | ||
422 | |||
423 | if (ext->probe && ext->probe(dev)) { | ||
424 | DEB_D(("ext->probe() failed for %p. skipping device.\n",dev)); | ||
425 | goto err_free_i2c; | ||
426 | } | ||
427 | |||
428 | if (ext->attach(dev, pci_ext)) { | ||
429 | DEB_D(("ext->attach() failed for %p. skipping device.\n",dev)); | ||
430 | goto err_unprobe; | ||
431 | } | ||
432 | |||
433 | INIT_LIST_HEAD(&dev->item); | ||
434 | list_add_tail(&dev->item,&saa7146_devices); | ||
435 | saa7146_num++; | ||
436 | |||
437 | err = 0; | ||
438 | out: | ||
439 | return err; | ||
440 | |||
441 | err_unprobe: | ||
442 | pci_set_drvdata(pci, NULL); | ||
443 | err_free_i2c: | ||
444 | pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr, | ||
445 | dev->d_i2c.dma_handle); | ||
446 | err_free_rps1: | ||
447 | pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr, | ||
448 | dev->d_rps1.dma_handle); | ||
449 | err_free_rps0: | ||
450 | pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr, | ||
451 | dev->d_rps0.dma_handle); | ||
452 | err_free_irq: | ||
453 | free_irq(pci->irq, (void *)dev); | ||
454 | err_unmap: | ||
455 | iounmap(dev->mem); | ||
456 | err_release: | ||
457 | pci_release_region(pci, 0); | ||
458 | err_disable: | ||
459 | pci_disable_device(pci); | ||
460 | err_free: | ||
461 | kfree(dev); | ||
462 | goto out; | ||
463 | } | ||
464 | |||
465 | static void saa7146_remove_one(struct pci_dev *pdev) | ||
466 | { | ||
467 | struct saa7146_dev* dev = pci_get_drvdata(pdev); | ||
468 | struct { | ||
469 | void *addr; | ||
470 | dma_addr_t dma; | ||
471 | } dev_map[] = { | ||
472 | { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle }, | ||
473 | { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle }, | ||
474 | { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle }, | ||
475 | { NULL, 0 } | ||
476 | }, *p; | ||
477 | |||
478 | DEB_EE(("dev:%p\n",dev)); | ||
479 | |||
480 | dev->ext->detach(dev); | ||
481 | |||
482 | /* shut down all video dma transfers */ | ||
483 | saa7146_write(dev, MC1, 0x00ff0000); | ||
484 | |||
485 | /* disable all irqs, release irq-routine */ | ||
486 | saa7146_write(dev, IER, 0); | ||
487 | |||
488 | free_irq(pdev->irq, dev); | ||
489 | |||
490 | for (p = dev_map; p->addr; p++) | ||
491 | pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma); | ||
492 | |||
493 | iounmap(dev->mem); | ||
494 | pci_release_region(pdev, 0); | ||
495 | list_del(&dev->item); | ||
496 | pci_disable_device(pdev); | ||
497 | kfree(dev); | ||
498 | |||
499 | saa7146_num--; | ||
500 | } | ||
501 | |||
502 | /*********************************************************************************/ | ||
503 | /* extension handling functions */ | ||
504 | |||
505 | int saa7146_register_extension(struct saa7146_extension* ext) | ||
506 | { | ||
507 | DEB_EE(("ext:%p\n",ext)); | ||
508 | |||
509 | ext->driver.name = ext->name; | ||
510 | ext->driver.id_table = ext->pci_tbl; | ||
511 | ext->driver.probe = saa7146_init_one; | ||
512 | ext->driver.remove = saa7146_remove_one; | ||
513 | |||
514 | printk("saa7146: register extension '%s'.\n",ext->name); | ||
515 | return pci_module_init(&ext->driver); | ||
516 | } | ||
517 | |||
518 | int saa7146_unregister_extension(struct saa7146_extension* ext) | ||
519 | { | ||
520 | DEB_EE(("ext:%p\n",ext)); | ||
521 | printk("saa7146: unregister extension '%s'.\n",ext->name); | ||
522 | pci_unregister_driver(&ext->driver); | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | EXPORT_SYMBOL_GPL(saa7146_register_extension); | ||
527 | EXPORT_SYMBOL_GPL(saa7146_unregister_extension); | ||
528 | |||
529 | /* misc functions used by extension modules */ | ||
530 | EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc); | ||
531 | EXPORT_SYMBOL_GPL(saa7146_pgtable_free); | ||
532 | EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single); | ||
533 | EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable); | ||
534 | EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done); | ||
535 | |||
536 | EXPORT_SYMBOL_GPL(saa7146_setgpio); | ||
537 | |||
538 | EXPORT_SYMBOL_GPL(saa7146_i2c_transfer); | ||
539 | EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare); | ||
540 | |||
541 | EXPORT_SYMBOL_GPL(saa7146_debug); | ||
542 | EXPORT_SYMBOL_GPL(saa7146_devices); | ||
543 | EXPORT_SYMBOL_GPL(saa7146_devices_lock); | ||
544 | |||
545 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); | ||
546 | MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); | ||
547 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c new file mode 100644 index 000000000000..cb826c9adfe7 --- /dev/null +++ b/drivers/media/common/saa7146_fops.c | |||
@@ -0,0 +1,564 @@ | |||
1 | #include <media/saa7146_vv.h> | ||
2 | #include <linux/version.h> | ||
3 | |||
4 | #define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1) | ||
5 | |||
6 | /****************************************************************************/ | ||
7 | /* resource management functions, shamelessly stolen from saa7134 driver */ | ||
8 | |||
9 | int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) | ||
10 | { | ||
11 | struct saa7146_dev *dev = fh->dev; | ||
12 | struct saa7146_vv *vv = dev->vv_data; | ||
13 | |||
14 | if (fh->resources & bit) { | ||
15 | DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources)); | ||
16 | /* have it already allocated */ | ||
17 | return 1; | ||
18 | } | ||
19 | |||
20 | /* is it free? */ | ||
21 | down(&dev->lock); | ||
22 | if (vv->resources & bit) { | ||
23 | DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit)); | ||
24 | /* no, someone else uses it */ | ||
25 | up(&dev->lock); | ||
26 | return 0; | ||
27 | } | ||
28 | /* it's free, grab it */ | ||
29 | fh->resources |= bit; | ||
30 | vv->resources |= bit; | ||
31 | DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources)); | ||
32 | up(&dev->lock); | ||
33 | return 1; | ||
34 | } | ||
35 | |||
36 | void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) | ||
37 | { | ||
38 | struct saa7146_dev *dev = fh->dev; | ||
39 | struct saa7146_vv *vv = dev->vv_data; | ||
40 | |||
41 | if ((fh->resources & bits) != bits) | ||
42 | BUG(); | ||
43 | |||
44 | down(&dev->lock); | ||
45 | fh->resources &= ~bits; | ||
46 | vv->resources &= ~bits; | ||
47 | DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources)); | ||
48 | up(&dev->lock); | ||
49 | } | ||
50 | |||
51 | |||
52 | /********************************************************************************/ | ||
53 | /* common dma functions */ | ||
54 | |||
55 | void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf) | ||
56 | { | ||
57 | DEB_EE(("dev:%p, buf:%p\n",dev,buf)); | ||
58 | |||
59 | if (in_interrupt()) | ||
60 | BUG(); | ||
61 | |||
62 | videobuf_waiton(&buf->vb,0,0); | ||
63 | videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma); | ||
64 | videobuf_dma_free(&buf->vb.dma); | ||
65 | buf->vb.state = STATE_NEEDS_INIT; | ||
66 | } | ||
67 | |||
68 | |||
69 | /********************************************************************************/ | ||
70 | /* common buffer functions */ | ||
71 | |||
72 | int saa7146_buffer_queue(struct saa7146_dev *dev, | ||
73 | struct saa7146_dmaqueue *q, | ||
74 | struct saa7146_buf *buf) | ||
75 | { | ||
76 | assert_spin_locked(&dev->slock); | ||
77 | DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf)); | ||
78 | |||
79 | BUG_ON(!q); | ||
80 | |||
81 | if (NULL == q->curr) { | ||
82 | q->curr = buf; | ||
83 | DEB_D(("immediately activating buffer %p\n", buf)); | ||
84 | buf->activate(dev,buf,NULL); | ||
85 | } else { | ||
86 | list_add_tail(&buf->vb.queue,&q->queue); | ||
87 | buf->vb.state = STATE_QUEUED; | ||
88 | DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf)); | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | void saa7146_buffer_finish(struct saa7146_dev *dev, | ||
94 | struct saa7146_dmaqueue *q, | ||
95 | int state) | ||
96 | { | ||
97 | assert_spin_locked(&dev->slock); | ||
98 | DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state)); | ||
99 | DEB_EE(("q->curr:%p\n",q->curr)); | ||
100 | |||
101 | BUG_ON(!q->curr); | ||
102 | |||
103 | /* finish current buffer */ | ||
104 | if (NULL == q->curr) { | ||
105 | DEB_D(("aiii. no current buffer\n")); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | q->curr->vb.state = state; | ||
110 | do_gettimeofday(&q->curr->vb.ts); | ||
111 | wake_up(&q->curr->vb.done); | ||
112 | |||
113 | q->curr = NULL; | ||
114 | } | ||
115 | |||
116 | void saa7146_buffer_next(struct saa7146_dev *dev, | ||
117 | struct saa7146_dmaqueue *q, int vbi) | ||
118 | { | ||
119 | struct saa7146_buf *buf,*next = NULL; | ||
120 | |||
121 | BUG_ON(!q); | ||
122 | |||
123 | DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi)); | ||
124 | |||
125 | assert_spin_locked(&dev->slock); | ||
126 | if (!list_empty(&q->queue)) { | ||
127 | /* activate next one from queue */ | ||
128 | buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue); | ||
129 | list_del(&buf->vb.queue); | ||
130 | if (!list_empty(&q->queue)) | ||
131 | next = list_entry(q->queue.next,struct saa7146_buf, vb.queue); | ||
132 | q->curr = buf; | ||
133 | DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next)); | ||
134 | buf->activate(dev,buf,next); | ||
135 | } else { | ||
136 | DEB_INT(("no next buffer. stopping.\n")); | ||
137 | if( 0 != vbi ) { | ||
138 | /* turn off video-dma3 */ | ||
139 | saa7146_write(dev,MC1, MASK_20); | ||
140 | } else { | ||
141 | /* nothing to do -- just prevent next video-dma1 transfer | ||
142 | by lowering the protection address */ | ||
143 | |||
144 | // fixme: fix this for vflip != 0 | ||
145 | |||
146 | saa7146_write(dev, PROT_ADDR1, 0); | ||
147 | saa7146_write(dev, MC2, (MASK_02|MASK_18)); | ||
148 | |||
149 | /* write the address of the rps-program */ | ||
150 | saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); | ||
151 | /* turn on rps */ | ||
152 | saa7146_write(dev, MC1, (MASK_12 | MASK_28)); | ||
153 | |||
154 | /* | ||
155 | printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); | ||
156 | printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); | ||
157 | printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); | ||
158 | printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); | ||
159 | printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); | ||
160 | printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); | ||
161 | */ | ||
162 | } | ||
163 | del_timer(&q->timeout); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | void saa7146_buffer_timeout(unsigned long data) | ||
168 | { | ||
169 | struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data; | ||
170 | struct saa7146_dev *dev = q->dev; | ||
171 | unsigned long flags; | ||
172 | |||
173 | DEB_EE(("dev:%p, dmaq:%p\n", dev, q)); | ||
174 | |||
175 | spin_lock_irqsave(&dev->slock,flags); | ||
176 | if (q->curr) { | ||
177 | DEB_D(("timeout on %p\n", q->curr)); | ||
178 | saa7146_buffer_finish(dev,q,STATE_ERROR); | ||
179 | } | ||
180 | |||
181 | /* we don't restart the transfer here like other drivers do. when | ||
182 | a streaming capture is disabled, the timeout function will be | ||
183 | called for the current buffer. if we activate the next buffer now, | ||
184 | we mess up our capture logic. if a timeout occurs on another buffer, | ||
185 | then something is seriously broken before, so no need to buffer the | ||
186 | next capture IMHO... */ | ||
187 | /* | ||
188 | saa7146_buffer_next(dev,q); | ||
189 | */ | ||
190 | spin_unlock_irqrestore(&dev->slock,flags); | ||
191 | } | ||
192 | |||
193 | /********************************************************************************/ | ||
194 | /* file operations */ | ||
195 | |||
196 | static int fops_open(struct inode *inode, struct file *file) | ||
197 | { | ||
198 | unsigned int minor = iminor(inode); | ||
199 | struct saa7146_dev *h = NULL, *dev = NULL; | ||
200 | struct list_head *list; | ||
201 | struct saa7146_fh *fh = NULL; | ||
202 | int result = 0; | ||
203 | |||
204 | enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
205 | |||
206 | DEB_EE(("inode:%p, file:%p, minor:%d\n",inode,file,minor)); | ||
207 | |||
208 | if (down_interruptible(&saa7146_devices_lock)) | ||
209 | return -ERESTARTSYS; | ||
210 | |||
211 | list_for_each(list,&saa7146_devices) { | ||
212 | h = list_entry(list, struct saa7146_dev, item); | ||
213 | if( NULL == h->vv_data ) { | ||
214 | DEB_D(("device %p has not registered video devices.\n",h)); | ||
215 | continue; | ||
216 | } | ||
217 | DEB_D(("trying: %p @ major %d,%d\n",h,h->vv_data->video_minor,h->vv_data->vbi_minor)); | ||
218 | |||
219 | if (h->vv_data->video_minor == minor) { | ||
220 | dev = h; | ||
221 | } | ||
222 | if (h->vv_data->vbi_minor == minor) { | ||
223 | type = V4L2_BUF_TYPE_VBI_CAPTURE; | ||
224 | dev = h; | ||
225 | } | ||
226 | } | ||
227 | if (NULL == dev) { | ||
228 | DEB_S(("no such video device.\n")); | ||
229 | result = -ENODEV; | ||
230 | goto out; | ||
231 | } | ||
232 | |||
233 | DEB_D(("using: %p\n",dev)); | ||
234 | |||
235 | /* check if an extension is registered */ | ||
236 | if( NULL == dev->ext ) { | ||
237 | DEB_S(("no extension registered for this device.\n")); | ||
238 | result = -ENODEV; | ||
239 | goto out; | ||
240 | } | ||
241 | |||
242 | /* allocate per open data */ | ||
243 | fh = kmalloc(sizeof(*fh),GFP_KERNEL); | ||
244 | if (NULL == fh) { | ||
245 | DEB_S(("cannot allocate memory for per open data.\n")); | ||
246 | result = -ENOMEM; | ||
247 | goto out; | ||
248 | } | ||
249 | memset(fh,0,sizeof(*fh)); | ||
250 | |||
251 | file->private_data = fh; | ||
252 | fh->dev = dev; | ||
253 | fh->type = type; | ||
254 | |||
255 | if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { | ||
256 | DEB_S(("initializing vbi...\n")); | ||
257 | result = saa7146_vbi_uops.open(dev,file); | ||
258 | } else { | ||
259 | DEB_S(("initializing video...\n")); | ||
260 | result = saa7146_video_uops.open(dev,file); | ||
261 | } | ||
262 | |||
263 | if (0 != result) { | ||
264 | goto out; | ||
265 | } | ||
266 | |||
267 | if( 0 == try_module_get(dev->ext->module)) { | ||
268 | result = -EINVAL; | ||
269 | goto out; | ||
270 | } | ||
271 | |||
272 | result = 0; | ||
273 | out: | ||
274 | if( fh != 0 && result != 0 ) { | ||
275 | kfree(fh); | ||
276 | file->private_data = NULL; | ||
277 | } | ||
278 | up(&saa7146_devices_lock); | ||
279 | return result; | ||
280 | } | ||
281 | |||
282 | static int fops_release(struct inode *inode, struct file *file) | ||
283 | { | ||
284 | struct saa7146_fh *fh = file->private_data; | ||
285 | struct saa7146_dev *dev = fh->dev; | ||
286 | |||
287 | DEB_EE(("inode:%p, file:%p\n",inode,file)); | ||
288 | |||
289 | if (down_interruptible(&saa7146_devices_lock)) | ||
290 | return -ERESTARTSYS; | ||
291 | |||
292 | if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { | ||
293 | saa7146_vbi_uops.release(dev,file); | ||
294 | } else { | ||
295 | saa7146_video_uops.release(dev,file); | ||
296 | } | ||
297 | |||
298 | module_put(dev->ext->module); | ||
299 | file->private_data = NULL; | ||
300 | kfree(fh); | ||
301 | |||
302 | up(&saa7146_devices_lock); | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg); | ||
308 | static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | ||
309 | { | ||
310 | /* | ||
311 | DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg)); | ||
312 | */ | ||
313 | return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl); | ||
314 | } | ||
315 | |||
316 | static int fops_mmap(struct file *file, struct vm_area_struct * vma) | ||
317 | { | ||
318 | struct saa7146_fh *fh = file->private_data; | ||
319 | struct videobuf_queue *q; | ||
320 | |||
321 | switch (fh->type) { | ||
322 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: { | ||
323 | DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma)); | ||
324 | q = &fh->video_q; | ||
325 | break; | ||
326 | } | ||
327 | case V4L2_BUF_TYPE_VBI_CAPTURE: { | ||
328 | DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma)); | ||
329 | q = &fh->vbi_q; | ||
330 | break; | ||
331 | } | ||
332 | default: | ||
333 | BUG(); | ||
334 | return 0; | ||
335 | } | ||
336 | return videobuf_mmap_mapper(q,vma); | ||
337 | } | ||
338 | |||
339 | static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) | ||
340 | { | ||
341 | struct saa7146_fh *fh = file->private_data; | ||
342 | struct videobuf_buffer *buf = NULL; | ||
343 | struct videobuf_queue *q; | ||
344 | |||
345 | DEB_EE(("file:%p, poll:%p\n",file, wait)); | ||
346 | |||
347 | if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { | ||
348 | if( 0 == fh->vbi_q.streaming ) | ||
349 | return videobuf_poll_stream(file, &fh->vbi_q, wait); | ||
350 | q = &fh->vbi_q; | ||
351 | } else { | ||
352 | DEB_D(("using video queue.\n")); | ||
353 | q = &fh->video_q; | ||
354 | } | ||
355 | |||
356 | if (!list_empty(&q->stream)) | ||
357 | buf = list_entry(q->stream.next, struct videobuf_buffer, stream); | ||
358 | |||
359 | if (!buf) { | ||
360 | DEB_D(("buf == NULL!\n")); | ||
361 | return POLLERR; | ||
362 | } | ||
363 | |||
364 | poll_wait(file, &buf->done, wait); | ||
365 | if (buf->state == STATE_DONE || buf->state == STATE_ERROR) { | ||
366 | DEB_D(("poll succeeded!\n")); | ||
367 | return POLLIN|POLLRDNORM; | ||
368 | } | ||
369 | |||
370 | DEB_D(("nothing to poll for, buf->state:%d\n",buf->state)); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
375 | { | ||
376 | struct saa7146_fh *fh = file->private_data; | ||
377 | |||
378 | switch (fh->type) { | ||
379 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: { | ||
380 | // DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count)); | ||
381 | return saa7146_video_uops.read(file,data,count,ppos); | ||
382 | } | ||
383 | case V4L2_BUF_TYPE_VBI_CAPTURE: { | ||
384 | // DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count)); | ||
385 | return saa7146_vbi_uops.read(file,data,count,ppos); | ||
386 | } | ||
387 | break; | ||
388 | default: | ||
389 | BUG(); | ||
390 | return 0; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | static struct file_operations video_fops = | ||
395 | { | ||
396 | .owner = THIS_MODULE, | ||
397 | .open = fops_open, | ||
398 | .release = fops_release, | ||
399 | .read = fops_read, | ||
400 | .poll = fops_poll, | ||
401 | .mmap = fops_mmap, | ||
402 | .ioctl = fops_ioctl, | ||
403 | .llseek = no_llseek, | ||
404 | }; | ||
405 | |||
406 | void vv_callback(struct saa7146_dev *dev, unsigned long status) | ||
407 | { | ||
408 | u32 isr = status; | ||
409 | |||
410 | DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status)); | ||
411 | |||
412 | if (0 != (isr & (MASK_27))) { | ||
413 | DEB_INT(("irq: RPS0 (0x%08x).\n",isr)); | ||
414 | saa7146_video_uops.irq_done(dev,isr); | ||
415 | } | ||
416 | |||
417 | if (0 != (isr & (MASK_28))) { | ||
418 | u32 mc2 = saa7146_read(dev, MC2); | ||
419 | if( 0 != (mc2 & MASK_15)) { | ||
420 | DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr)); | ||
421 | wake_up(&dev->vv_data->vbi_wq); | ||
422 | saa7146_write(dev,MC2, MASK_31); | ||
423 | return; | ||
424 | } | ||
425 | DEB_INT(("irq: RPS1 (0x%08x).\n",isr)); | ||
426 | saa7146_vbi_uops.irq_done(dev,isr); | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static struct video_device device_template = | ||
431 | { | ||
432 | .hardware = VID_HARDWARE_SAA7146, | ||
433 | .fops = &video_fops, | ||
434 | .minor = -1, | ||
435 | }; | ||
436 | |||
437 | int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) | ||
438 | { | ||
439 | struct saa7146_vv *vv = kmalloc (sizeof(struct saa7146_vv),GFP_KERNEL); | ||
440 | if( NULL == vv ) { | ||
441 | ERR(("out of memory. aborting.\n")); | ||
442 | return -1; | ||
443 | } | ||
444 | memset(vv, 0x0, sizeof(*vv)); | ||
445 | |||
446 | DEB_EE(("dev:%p\n",dev)); | ||
447 | |||
448 | /* set default values for video parts of the saa7146 */ | ||
449 | saa7146_write(dev, BCS_CTRL, 0x80400040); | ||
450 | |||
451 | /* enable video-port pins */ | ||
452 | saa7146_write(dev, MC1, (MASK_10 | MASK_26)); | ||
453 | |||
454 | /* save per-device extension data (one extension can | ||
455 | handle different devices that might need different | ||
456 | configuration data) */ | ||
457 | dev->ext_vv_data = ext_vv; | ||
458 | |||
459 | vv->video_minor = -1; | ||
460 | vv->vbi_minor = -1; | ||
461 | |||
462 | vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle); | ||
463 | if( NULL == vv->d_clipping.cpu_addr ) { | ||
464 | ERR(("out of memory. aborting.\n")); | ||
465 | kfree(vv); | ||
466 | return -1; | ||
467 | } | ||
468 | memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM); | ||
469 | |||
470 | saa7146_video_uops.init(dev,vv); | ||
471 | saa7146_vbi_uops.init(dev,vv); | ||
472 | |||
473 | dev->vv_data = vv; | ||
474 | dev->vv_callback = &vv_callback; | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | int saa7146_vv_release(struct saa7146_dev* dev) | ||
480 | { | ||
481 | struct saa7146_vv *vv = dev->vv_data; | ||
482 | |||
483 | DEB_EE(("dev:%p\n",dev)); | ||
484 | |||
485 | pci_free_consistent(dev->pci, SAA7146_RPS_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); | ||
486 | kfree(vv); | ||
487 | dev->vv_data = NULL; | ||
488 | dev->vv_callback = NULL; | ||
489 | |||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, | ||
494 | char *name, int type) | ||
495 | { | ||
496 | struct saa7146_vv *vv = dev->vv_data; | ||
497 | struct video_device *vfd; | ||
498 | |||
499 | DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type)); | ||
500 | |||
501 | // released by vfd->release | ||
502 | vfd = video_device_alloc(); | ||
503 | if (vfd == NULL) | ||
504 | return -ENOMEM; | ||
505 | |||
506 | memcpy(vfd, &device_template, sizeof(struct video_device)); | ||
507 | strlcpy(vfd->name, name, sizeof(vfd->name)); | ||
508 | vfd->release = video_device_release; | ||
509 | vfd->priv = dev; | ||
510 | |||
511 | // fixme: -1 should be an insmod parameter *for the extension* (like "video_nr"); | ||
512 | if (video_register_device(vfd, type, -1) < 0) { | ||
513 | ERR(("cannot register v4l2 device. skipping.\n")); | ||
514 | return -1; | ||
515 | } | ||
516 | |||
517 | if( VFL_TYPE_GRABBER == type ) { | ||
518 | vv->video_minor = vfd->minor; | ||
519 | INFO(("%s: registered device video%d [v4l2]\n", | ||
520 | dev->name, vfd->minor & 0x1f)); | ||
521 | } else { | ||
522 | vv->vbi_minor = vfd->minor; | ||
523 | INFO(("%s: registered device vbi%d [v4l2]\n", | ||
524 | dev->name, vfd->minor & 0x1f)); | ||
525 | } | ||
526 | |||
527 | *vid = vfd; | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev) | ||
532 | { | ||
533 | struct saa7146_vv *vv = dev->vv_data; | ||
534 | |||
535 | DEB_EE(("dev:%p\n",dev)); | ||
536 | |||
537 | if( VFL_TYPE_GRABBER == (*vid)->type ) { | ||
538 | vv->video_minor = -1; | ||
539 | } else { | ||
540 | vv->vbi_minor = -1; | ||
541 | } | ||
542 | |||
543 | video_unregister_device(*vid); | ||
544 | *vid = NULL; | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static int __init saa7146_vv_init_module(void) | ||
550 | { | ||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | |||
555 | static void __exit saa7146_vv_cleanup_module(void) | ||
556 | { | ||
557 | } | ||
558 | |||
559 | module_init(saa7146_vv_init_module); | ||
560 | module_exit(saa7146_vv_cleanup_module); | ||
561 | |||
562 | MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); | ||
563 | MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware"); | ||
564 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c new file mode 100644 index 000000000000..ec52dff8cb69 --- /dev/null +++ b/drivers/media/common/saa7146_hlp.c | |||
@@ -0,0 +1,1036 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <media/saa7146_vv.h> | ||
3 | |||
4 | static void calculate_output_format_register(struct saa7146_dev* saa, u32 palette, u32* clip_format) | ||
5 | { | ||
6 | /* clear out the necessary bits */ | ||
7 | *clip_format &= 0x0000ffff; | ||
8 | /* set these bits new */ | ||
9 | *clip_format |= (( ((palette&0xf00)>>8) << 30) | ((palette&0x00f) << 24) | (((palette&0x0f0)>>4) << 16)); | ||
10 | } | ||
11 | |||
12 | static void calculate_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync, u32* hps_ctrl) | ||
13 | { | ||
14 | *hps_ctrl &= ~(MASK_30 | MASK_31 | MASK_28); | ||
15 | *hps_ctrl |= (source << 30) | (sync << 28); | ||
16 | } | ||
17 | |||
18 | static void calculate_hxo_and_hyo(struct saa7146_vv *vv, u32* hps_h_scale, u32* hps_ctrl) | ||
19 | { | ||
20 | int hyo = 0, hxo = 0; | ||
21 | |||
22 | hyo = vv->standard->v_offset; | ||
23 | hxo = vv->standard->h_offset; | ||
24 | |||
25 | *hps_h_scale &= ~(MASK_B0 | 0xf00); | ||
26 | *hps_h_scale |= (hxo << 0); | ||
27 | |||
28 | *hps_ctrl &= ~(MASK_W0 | MASK_B2); | ||
29 | *hps_ctrl |= (hyo << 12); | ||
30 | } | ||
31 | |||
32 | /* helper functions for the calculation of the horizontal- and vertical | ||
33 | scaling registers, clip-format-register etc ... | ||
34 | these functions take pointers to the (most-likely read-out | ||
35 | original-values) and manipulate them according to the requested | ||
36 | changes. | ||
37 | */ | ||
38 | |||
39 | /* hps_coeff used for CXY and CXUV; scale 1/1 -> scale 1/64 */ | ||
40 | static struct { | ||
41 | u16 hps_coeff; | ||
42 | u16 weight_sum; | ||
43 | } hps_h_coeff_tab [] = { | ||
44 | {0x00, 2}, {0x02, 4}, {0x00, 4}, {0x06, 8}, {0x02, 8}, | ||
45 | {0x08, 8}, {0x00, 8}, {0x1E, 16}, {0x0E, 8}, {0x26, 8}, | ||
46 | {0x06, 8}, {0x42, 8}, {0x02, 8}, {0x80, 8}, {0x00, 8}, | ||
47 | {0xFE, 16}, {0xFE, 8}, {0x7E, 8}, {0x7E, 8}, {0x3E, 8}, | ||
48 | {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, {0x0E, 8}, {0x0E, 8}, | ||
49 | {0x06, 8}, {0x06, 8}, {0x02, 8}, {0x02, 8}, {0x00, 8}, | ||
50 | {0x00, 8}, {0xFE, 16}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, | ||
51 | {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, | ||
52 | {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, | ||
53 | {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0xFE, 8}, {0x7E, 8}, | ||
54 | {0x7E, 8}, {0x3E, 8}, {0x3E, 8}, {0x1E, 8}, {0x1E, 8}, | ||
55 | {0x0E, 8}, {0x0E, 8}, {0x06, 8}, {0x06, 8}, {0x02, 8}, | ||
56 | {0x02, 8}, {0x00, 8}, {0x00, 8}, {0xFE, 16} | ||
57 | }; | ||
58 | |||
59 | /* table of attenuation values for horizontal scaling */ | ||
60 | static u8 h_attenuation[] = { 1, 2, 4, 8, 2, 4, 8, 16, 0}; | ||
61 | |||
62 | /* calculate horizontal scale registers */ | ||
63 | static int calculate_h_scale_registers(struct saa7146_dev *dev, | ||
64 | int in_x, int out_x, int flip_lr, | ||
65 | u32* hps_ctrl, u32* hps_v_gain, u32* hps_h_prescale, u32* hps_h_scale) | ||
66 | { | ||
67 | /* horizontal prescaler */ | ||
68 | u32 dcgx = 0, xpsc = 0, xacm = 0, cxy = 0, cxuv = 0; | ||
69 | /* horizontal scaler */ | ||
70 | u32 xim = 0, xp = 0, xsci =0; | ||
71 | /* vertical scale & gain */ | ||
72 | u32 pfuv = 0; | ||
73 | |||
74 | /* helper variables */ | ||
75 | u32 h_atten = 0, i = 0; | ||
76 | |||
77 | if ( 0 == out_x ) { | ||
78 | return -EINVAL; | ||
79 | } | ||
80 | |||
81 | /* mask out vanity-bit */ | ||
82 | *hps_ctrl &= ~MASK_29; | ||
83 | |||
84 | /* calculate prescale-(xspc)-value: [n .. 1/2) : 1 | ||
85 | [1/2 .. 1/3) : 2 | ||
86 | [1/3 .. 1/4) : 3 | ||
87 | ... */ | ||
88 | if (in_x > out_x) { | ||
89 | xpsc = in_x / out_x; | ||
90 | } | ||
91 | else { | ||
92 | /* zooming */ | ||
93 | xpsc = 1; | ||
94 | } | ||
95 | |||
96 | /* if flip_lr-bit is set, number of pixels after | ||
97 | horizontal prescaling must be < 384 */ | ||
98 | if ( 0 != flip_lr ) { | ||
99 | |||
100 | /* set vanity bit */ | ||
101 | *hps_ctrl |= MASK_29; | ||
102 | |||
103 | while (in_x / xpsc >= 384 ) | ||
104 | xpsc++; | ||
105 | } | ||
106 | /* if zooming is wanted, number of pixels after | ||
107 | horizontal prescaling must be < 768 */ | ||
108 | else { | ||
109 | while ( in_x / xpsc >= 768 ) | ||
110 | xpsc++; | ||
111 | } | ||
112 | |||
113 | /* maximum prescale is 64 (p.69) */ | ||
114 | if ( xpsc > 64 ) | ||
115 | xpsc = 64; | ||
116 | |||
117 | /* keep xacm clear*/ | ||
118 | xacm = 0; | ||
119 | |||
120 | /* set horizontal filter parameters (CXY = CXUV) */ | ||
121 | cxy = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].hps_coeff; | ||
122 | cxuv = cxy; | ||
123 | |||
124 | /* calculate and set horizontal fine scale (xsci) */ | ||
125 | |||
126 | /* bypass the horizontal scaler ? */ | ||
127 | if ( (in_x == out_x) && ( 1 == xpsc ) ) | ||
128 | xsci = 0x400; | ||
129 | else | ||
130 | xsci = ( (1024 * in_x) / (out_x * xpsc) ) + xpsc; | ||
131 | |||
132 | /* set start phase for horizontal fine scale (xp) to 0 */ | ||
133 | xp = 0; | ||
134 | |||
135 | /* set xim, if we bypass the horizontal scaler */ | ||
136 | if ( 0x400 == xsci ) | ||
137 | xim = 1; | ||
138 | else | ||
139 | xim = 0; | ||
140 | |||
141 | /* if the prescaler is bypassed, enable horizontal | ||
142 | accumulation mode (xacm) and clear dcgx */ | ||
143 | if( 1 == xpsc ) { | ||
144 | xacm = 1; | ||
145 | dcgx = 0; | ||
146 | } else { | ||
147 | xacm = 0; | ||
148 | /* get best match in the table of attenuations | ||
149 | for horizontal scaling */ | ||
150 | h_atten = hps_h_coeff_tab[( (xpsc - 1) < 63 ? (xpsc - 1) : 63 )].weight_sum; | ||
151 | |||
152 | for (i = 0; h_attenuation[i] != 0; i++) { | ||
153 | if (h_attenuation[i] >= h_atten) | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | dcgx = i; | ||
158 | } | ||
159 | |||
160 | /* the horizontal scaling increment controls the UV filter | ||
161 | to reduce the bandwith to improve the display quality, | ||
162 | so set it ... */ | ||
163 | if ( xsci == 0x400) | ||
164 | pfuv = 0x00; | ||
165 | else if ( xsci < 0x600) | ||
166 | pfuv = 0x01; | ||
167 | else if ( xsci < 0x680) | ||
168 | pfuv = 0x11; | ||
169 | else if ( xsci < 0x700) | ||
170 | pfuv = 0x22; | ||
171 | else | ||
172 | pfuv = 0x33; | ||
173 | |||
174 | |||
175 | *hps_v_gain &= MASK_W0|MASK_B2; | ||
176 | *hps_v_gain |= (pfuv << 24); | ||
177 | |||
178 | *hps_h_scale &= ~(MASK_W1 | 0xf000); | ||
179 | *hps_h_scale |= (xim << 31) | (xp << 24) | (xsci << 12); | ||
180 | |||
181 | *hps_h_prescale |= (dcgx << 27) | ((xpsc-1) << 18) | (xacm << 17) | (cxy << 8) | (cxuv << 0); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static struct { | ||
187 | u16 hps_coeff; | ||
188 | u16 weight_sum; | ||
189 | } hps_v_coeff_tab [] = { | ||
190 | {0x0100, 2}, {0x0102, 4}, {0x0300, 4}, {0x0106, 8}, {0x0502, 8}, | ||
191 | {0x0708, 8}, {0x0F00, 8}, {0x011E, 16}, {0x110E, 16}, {0x1926, 16}, | ||
192 | {0x3906, 16}, {0x3D42, 16}, {0x7D02, 16}, {0x7F80, 16}, {0xFF00, 16}, | ||
193 | {0x01FE, 32}, {0x01FE, 32}, {0x817E, 32}, {0x817E, 32}, {0xC13E, 32}, | ||
194 | {0xC13E, 32}, {0xE11E, 32}, {0xE11E, 32}, {0xF10E, 32}, {0xF10E, 32}, | ||
195 | {0xF906, 32}, {0xF906, 32}, {0xFD02, 32}, {0xFD02, 32}, {0xFF00, 32}, | ||
196 | {0xFF00, 32}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, | ||
197 | {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, | ||
198 | {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, | ||
199 | {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x01FE, 64}, {0x817E, 64}, | ||
200 | {0x817E, 64}, {0xC13E, 64}, {0xC13E, 64}, {0xE11E, 64}, {0xE11E, 64}, | ||
201 | {0xF10E, 64}, {0xF10E, 64}, {0xF906, 64}, {0xF906, 64}, {0xFD02, 64}, | ||
202 | {0xFD02, 64}, {0xFF00, 64}, {0xFF00, 64}, {0x01FE, 128} | ||
203 | }; | ||
204 | |||
205 | /* table of attenuation values for vertical scaling */ | ||
206 | static u16 v_attenuation[] = { 2, 4, 8, 16, 32, 64, 128, 256, 0}; | ||
207 | |||
208 | /* calculate vertical scale registers */ | ||
209 | static int calculate_v_scale_registers(struct saa7146_dev *dev, enum v4l2_field field, | ||
210 | int in_y, int out_y, u32* hps_v_scale, u32* hps_v_gain) | ||
211 | { | ||
212 | int lpi = 0; | ||
213 | |||
214 | /* vertical scaling */ | ||
215 | u32 yacm = 0, ysci = 0, yacl = 0, ypo = 0, ype = 0; | ||
216 | /* vertical scale & gain */ | ||
217 | u32 dcgy = 0, cya_cyb = 0; | ||
218 | |||
219 | /* helper variables */ | ||
220 | u32 v_atten = 0, i = 0; | ||
221 | |||
222 | /* error, if vertical zooming */ | ||
223 | if ( in_y < out_y ) { | ||
224 | return -EINVAL; | ||
225 | } | ||
226 | |||
227 | /* linear phase interpolation may be used | ||
228 | if scaling is between 1 and 1/2 (both fields used) | ||
229 | or scaling is between 1/2 and 1/4 (if only one field is used) */ | ||
230 | |||
231 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
232 | if( 2*out_y >= in_y) { | ||
233 | lpi = 1; | ||
234 | } | ||
235 | } else if (field == V4L2_FIELD_TOP | ||
236 | || field == V4L2_FIELD_ALTERNATE | ||
237 | || field == V4L2_FIELD_BOTTOM) { | ||
238 | if( 4*out_y >= in_y ) { | ||
239 | lpi = 1; | ||
240 | } | ||
241 | out_y *= 2; | ||
242 | } | ||
243 | if( 0 != lpi ) { | ||
244 | |||
245 | yacm = 0; | ||
246 | yacl = 0; | ||
247 | cya_cyb = 0x00ff; | ||
248 | |||
249 | /* calculate scaling increment */ | ||
250 | if ( in_y > out_y ) | ||
251 | ysci = ((1024 * in_y) / (out_y + 1)) - 1024; | ||
252 | else | ||
253 | ysci = 0; | ||
254 | |||
255 | dcgy = 0; | ||
256 | |||
257 | /* calculate ype and ypo */ | ||
258 | ype = ysci / 16; | ||
259 | ypo = ype + (ysci / 64); | ||
260 | |||
261 | } else { | ||
262 | yacm = 1; | ||
263 | |||
264 | /* calculate scaling increment */ | ||
265 | ysci = (((10 * 1024 * (in_y - out_y - 1)) / in_y) + 9) / 10; | ||
266 | |||
267 | /* calculate ype and ypo */ | ||
268 | ypo = ype = ((ysci + 15) / 16); | ||
269 | |||
270 | /* the sequence length interval (yacl) has to be set according | ||
271 | to the prescale value, e.g. [n .. 1/2) : 0 | ||
272 | [1/2 .. 1/3) : 1 | ||
273 | [1/3 .. 1/4) : 2 | ||
274 | ... */ | ||
275 | if ( ysci < 512) { | ||
276 | yacl = 0; | ||
277 | } else { | ||
278 | yacl = ( ysci / (1024 - ysci) ); | ||
279 | } | ||
280 | |||
281 | /* get filter coefficients for cya, cyb from table hps_v_coeff_tab */ | ||
282 | cya_cyb = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].hps_coeff; | ||
283 | |||
284 | /* get best match in the table of attenuations for vertical scaling */ | ||
285 | v_atten = hps_v_coeff_tab[ (yacl < 63 ? yacl : 63 ) ].weight_sum; | ||
286 | |||
287 | for (i = 0; v_attenuation[i] != 0; i++) { | ||
288 | if (v_attenuation[i] >= v_atten) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | dcgy = i; | ||
293 | } | ||
294 | |||
295 | /* ypo and ype swapped in spec ? */ | ||
296 | *hps_v_scale |= (yacm << 31) | (ysci << 21) | (yacl << 15) | (ypo << 8 ) | (ype << 1); | ||
297 | |||
298 | *hps_v_gain &= ~(MASK_W0|MASK_B2); | ||
299 | *hps_v_gain |= (dcgy << 16) | (cya_cyb << 0); | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | /* simple bubble-sort algorithm with duplicate elimination */ | ||
305 | static int sort_and_eliminate(u32* values, int* count) | ||
306 | { | ||
307 | int low = 0, high = 0, top = 0, temp = 0; | ||
308 | int cur = 0, next = 0; | ||
309 | |||
310 | /* sanity checks */ | ||
311 | if( (0 > *count) || (NULL == values) ) { | ||
312 | return -EINVAL; | ||
313 | } | ||
314 | |||
315 | /* bubble sort the first ´count´ items of the array ´values´ */ | ||
316 | for( top = *count; top > 0; top--) { | ||
317 | for( low = 0, high = 1; high < top; low++, high++) { | ||
318 | if( values[low] > values[high] ) { | ||
319 | temp = values[low]; | ||
320 | values[low] = values[high]; | ||
321 | values[high] = temp; | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | |||
326 | /* remove duplicate items */ | ||
327 | for( cur = 0, next = 1; next < *count; next++) { | ||
328 | if( values[cur] != values[next]) | ||
329 | values[++cur] = values[next]; | ||
330 | } | ||
331 | |||
332 | *count = cur + 1; | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct saa7146_fh *fh, | ||
338 | struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field) | ||
339 | { | ||
340 | struct saa7146_vv *vv = dev->vv_data; | ||
341 | u32 *clipping = vv->d_clipping.cpu_addr; | ||
342 | |||
343 | int width = fh->ov.win.w.width; | ||
344 | int height = fh->ov.win.w.height; | ||
345 | int clipcount = fh->ov.nclips; | ||
346 | |||
347 | u32 line_list[32]; | ||
348 | u32 pixel_list[32]; | ||
349 | int numdwords = 0; | ||
350 | |||
351 | int i = 0, j = 0; | ||
352 | int cnt_line = 0, cnt_pixel = 0; | ||
353 | |||
354 | int x[32], y[32], w[32], h[32]; | ||
355 | |||
356 | /* clear out memory */ | ||
357 | memset(&line_list[0], 0x00, sizeof(u32)*32); | ||
358 | memset(&pixel_list[0], 0x00, sizeof(u32)*32); | ||
359 | memset(clipping, 0x00, SAA7146_CLIPPING_MEM); | ||
360 | |||
361 | /* fill the line and pixel-lists */ | ||
362 | for(i = 0; i < clipcount; i++) { | ||
363 | int l = 0, r = 0, t = 0, b = 0; | ||
364 | |||
365 | x[i] = fh->ov.clips[i].c.left; | ||
366 | y[i] = fh->ov.clips[i].c.top; | ||
367 | w[i] = fh->ov.clips[i].c.width; | ||
368 | h[i] = fh->ov.clips[i].c.height; | ||
369 | |||
370 | if( w[i] < 0) { | ||
371 | x[i] += w[i]; w[i] = -w[i]; | ||
372 | } | ||
373 | if( h[i] < 0) { | ||
374 | y[i] += h[i]; h[i] = -h[i]; | ||
375 | } | ||
376 | if( x[i] < 0) { | ||
377 | w[i] += x[i]; x[i] = 0; | ||
378 | } | ||
379 | if( y[i] < 0) { | ||
380 | h[i] += y[i]; y[i] = 0; | ||
381 | } | ||
382 | if( 0 != vv->vflip ) { | ||
383 | y[i] = height - y[i] - h[i]; | ||
384 | } | ||
385 | |||
386 | l = x[i]; | ||
387 | r = x[i]+w[i]; | ||
388 | t = y[i]; | ||
389 | b = y[i]+h[i]; | ||
390 | |||
391 | /* insert left/right coordinates */ | ||
392 | pixel_list[ 2*i ] = min_t(int, l, width); | ||
393 | pixel_list[(2*i)+1] = min_t(int, r, width); | ||
394 | /* insert top/bottom coordinates */ | ||
395 | line_list[ 2*i ] = min_t(int, t, height); | ||
396 | line_list[(2*i)+1] = min_t(int, b, height); | ||
397 | } | ||
398 | |||
399 | /* sort and eliminate lists */ | ||
400 | cnt_line = cnt_pixel = 2*clipcount; | ||
401 | sort_and_eliminate( &pixel_list[0], &cnt_pixel ); | ||
402 | sort_and_eliminate( &line_list[0], &cnt_line ); | ||
403 | |||
404 | /* calculate the number of used u32s */ | ||
405 | numdwords = max_t(int, (cnt_line+1), (cnt_pixel+1))*2; | ||
406 | numdwords = max_t(int, 4, numdwords); | ||
407 | numdwords = min_t(int, 64, numdwords); | ||
408 | |||
409 | /* fill up cliptable */ | ||
410 | for(i = 0; i < cnt_pixel; i++) { | ||
411 | clipping[2*i] |= cpu_to_le32(pixel_list[i] << 16); | ||
412 | } | ||
413 | for(i = 0; i < cnt_line; i++) { | ||
414 | clipping[(2*i)+1] |= cpu_to_le32(line_list[i] << 16); | ||
415 | } | ||
416 | |||
417 | /* fill up cliptable with the display infos */ | ||
418 | for(j = 0; j < clipcount; j++) { | ||
419 | |||
420 | for(i = 0; i < cnt_pixel; i++) { | ||
421 | |||
422 | if( x[j] < 0) | ||
423 | x[j] = 0; | ||
424 | |||
425 | if( pixel_list[i] < (x[j] + w[j])) { | ||
426 | |||
427 | if ( pixel_list[i] >= x[j] ) { | ||
428 | clipping[2*i] |= cpu_to_le32(1 << j); | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | for(i = 0; i < cnt_line; i++) { | ||
433 | |||
434 | if( y[j] < 0) | ||
435 | y[j] = 0; | ||
436 | |||
437 | if( line_list[i] < (y[j] + h[j]) ) { | ||
438 | |||
439 | if( line_list[i] >= y[j] ) { | ||
440 | clipping[(2*i)+1] |= cpu_to_le32(1 << j); | ||
441 | } | ||
442 | } | ||
443 | } | ||
444 | } | ||
445 | |||
446 | /* adjust arbitration control register */ | ||
447 | *arbtr_ctrl &= 0xffff00ff; | ||
448 | *arbtr_ctrl |= 0x00001c00; | ||
449 | |||
450 | vdma2->base_even = vv->d_clipping.dma_handle; | ||
451 | vdma2->base_odd = vv->d_clipping.dma_handle; | ||
452 | vdma2->prot_addr = vv->d_clipping.dma_handle+((sizeof(u32))*(numdwords)); | ||
453 | vdma2->base_page = 0x04; | ||
454 | vdma2->pitch = 0x00; | ||
455 | vdma2->num_line_byte = (0 << 16 | (sizeof(u32))*(numdwords-1) ); | ||
456 | |||
457 | /* set clipping-mode. this depends on the field(s) used */ | ||
458 | *clip_format &= 0xfffffff7; | ||
459 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
460 | *clip_format |= 0x00000008; | ||
461 | } else { | ||
462 | *clip_format |= 0x00000000; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | /* disable clipping */ | ||
467 | static void saa7146_disable_clipping(struct saa7146_dev *dev) | ||
468 | { | ||
469 | u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | ||
470 | |||
471 | /* mask out relevant bits (=lower word)*/ | ||
472 | clip_format &= MASK_W1; | ||
473 | |||
474 | /* upload clipping-registers*/ | ||
475 | saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); | ||
476 | saa7146_write(dev, MC2, (MASK_05 | MASK_21)); | ||
477 | |||
478 | /* disable video dma2 */ | ||
479 | saa7146_write(dev, MC1, MASK_21); | ||
480 | } | ||
481 | |||
482 | static void saa7146_set_clipping_rect(struct saa7146_fh *fh) | ||
483 | { | ||
484 | struct saa7146_dev *dev = fh->dev; | ||
485 | enum v4l2_field field = fh->ov.win.field; | ||
486 | struct saa7146_video_dma vdma2; | ||
487 | u32 clip_format; | ||
488 | u32 arbtr_ctrl; | ||
489 | |||
490 | /* check clipcount, disable clipping if clipcount == 0*/ | ||
491 | if( fh->ov.nclips == 0 ) { | ||
492 | saa7146_disable_clipping(dev); | ||
493 | return; | ||
494 | } | ||
495 | |||
496 | clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | ||
497 | arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); | ||
498 | |||
499 | calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field); | ||
500 | |||
501 | /* set clipping format */ | ||
502 | clip_format &= 0xffff0008; | ||
503 | clip_format |= (SAA7146_CLIPPING_RECT << 4); | ||
504 | |||
505 | /* prepare video dma2 */ | ||
506 | saa7146_write(dev, BASE_EVEN2, vdma2.base_even); | ||
507 | saa7146_write(dev, BASE_ODD2, vdma2.base_odd); | ||
508 | saa7146_write(dev, PROT_ADDR2, vdma2.prot_addr); | ||
509 | saa7146_write(dev, BASE_PAGE2, vdma2.base_page); | ||
510 | saa7146_write(dev, PITCH2, vdma2.pitch); | ||
511 | saa7146_write(dev, NUM_LINE_BYTE2, vdma2.num_line_byte); | ||
512 | |||
513 | /* prepare the rest */ | ||
514 | saa7146_write(dev, CLIP_FORMAT_CTRL,clip_format); | ||
515 | saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); | ||
516 | |||
517 | /* upload clip_control-register, clipping-registers, enable video dma2 */ | ||
518 | saa7146_write(dev, MC2, (MASK_05 | MASK_21 | MASK_03 | MASK_19)); | ||
519 | saa7146_write(dev, MC1, (MASK_05 | MASK_21)); | ||
520 | } | ||
521 | |||
522 | static void saa7146_set_window(struct saa7146_dev *dev, int width, int height, enum v4l2_field field) | ||
523 | { | ||
524 | struct saa7146_vv *vv = dev->vv_data; | ||
525 | |||
526 | int source = vv->current_hps_source; | ||
527 | int sync = vv->current_hps_sync; | ||
528 | |||
529 | u32 hps_v_scale = 0, hps_v_gain = 0, hps_ctrl = 0, hps_h_prescale = 0, hps_h_scale = 0; | ||
530 | |||
531 | /* set vertical scale */ | ||
532 | hps_v_scale = 0; /* all bits get set by the function-call */ | ||
533 | hps_v_gain = 0; /* fixme: saa7146_read(dev, HPS_V_GAIN);*/ | ||
534 | calculate_v_scale_registers(dev, field, vv->standard->v_field*2, height, &hps_v_scale, &hps_v_gain); | ||
535 | |||
536 | /* set horizontal scale */ | ||
537 | hps_ctrl = 0; | ||
538 | hps_h_prescale = 0; /* all bits get set in the function */ | ||
539 | hps_h_scale = 0; | ||
540 | calculate_h_scale_registers(dev, vv->standard->h_pixels, width, vv->hflip, &hps_ctrl, &hps_v_gain, &hps_h_prescale, &hps_h_scale); | ||
541 | |||
542 | /* set hyo and hxo */ | ||
543 | calculate_hxo_and_hyo(vv, &hps_h_scale, &hps_ctrl); | ||
544 | calculate_hps_source_and_sync(dev, source, sync, &hps_ctrl); | ||
545 | |||
546 | /* write out new register contents */ | ||
547 | saa7146_write(dev, HPS_V_SCALE, hps_v_scale); | ||
548 | saa7146_write(dev, HPS_V_GAIN, hps_v_gain); | ||
549 | saa7146_write(dev, HPS_CTRL, hps_ctrl); | ||
550 | saa7146_write(dev, HPS_H_PRESCALE,hps_h_prescale); | ||
551 | saa7146_write(dev, HPS_H_SCALE, hps_h_scale); | ||
552 | |||
553 | /* upload shadow-ram registers */ | ||
554 | saa7146_write(dev, MC2, (MASK_05 | MASK_06 | MASK_21 | MASK_22) ); | ||
555 | } | ||
556 | |||
557 | /* calculate the new memory offsets for a desired position */ | ||
558 | static void saa7146_set_position(struct saa7146_dev *dev, int w_x, int w_y, int w_height, enum v4l2_field field, u32 pixelformat) | ||
559 | { | ||
560 | struct saa7146_vv *vv = dev->vv_data; | ||
561 | struct saa7146_format *sfmt = format_by_fourcc(dev, pixelformat); | ||
562 | |||
563 | int b_depth = vv->ov_fmt->depth; | ||
564 | int b_bpl = vv->ov_fb.fmt.bytesperline; | ||
565 | u32 base = (u32)vv->ov_fb.base; | ||
566 | |||
567 | struct saa7146_video_dma vdma1; | ||
568 | |||
569 | /* calculate memory offsets for picture, look if we shall top-down-flip */ | ||
570 | vdma1.pitch = 2*b_bpl; | ||
571 | if ( 0 == vv->vflip ) { | ||
572 | vdma1.base_even = (u32)base + (w_y * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); | ||
573 | vdma1.base_odd = vdma1.base_even + (vdma1.pitch / 2); | ||
574 | vdma1.prot_addr = vdma1.base_even + (w_height * (vdma1.pitch / 2)); | ||
575 | } | ||
576 | else { | ||
577 | vdma1.base_even = (u32)base + ((w_y+w_height) * (vdma1.pitch/2)) + (w_x * (b_depth / 8)); | ||
578 | vdma1.base_odd = vdma1.base_even - (vdma1.pitch / 2); | ||
579 | vdma1.prot_addr = vdma1.base_odd - (w_height * (vdma1.pitch / 2)); | ||
580 | } | ||
581 | |||
582 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
583 | } else if (field == V4L2_FIELD_ALTERNATE) { | ||
584 | /* fixme */ | ||
585 | vdma1.base_odd = vdma1.prot_addr; | ||
586 | vdma1.pitch /= 2; | ||
587 | } else if (field == V4L2_FIELD_TOP) { | ||
588 | vdma1.base_odd = vdma1.prot_addr; | ||
589 | vdma1.pitch /= 2; | ||
590 | } else if (field == V4L2_FIELD_BOTTOM) { | ||
591 | vdma1.base_odd = vdma1.base_even; | ||
592 | vdma1.base_even = vdma1.prot_addr; | ||
593 | vdma1.pitch /= 2; | ||
594 | } | ||
595 | |||
596 | if ( 0 != vv->vflip ) { | ||
597 | vdma1.pitch *= -1; | ||
598 | } | ||
599 | |||
600 | vdma1.base_page = sfmt->swap; | ||
601 | vdma1.num_line_byte = (vv->standard->v_field<<16)+vv->standard->h_pixels; | ||
602 | |||
603 | saa7146_write_out_dma(dev, 1, &vdma1); | ||
604 | } | ||
605 | |||
606 | static void saa7146_set_output_format(struct saa7146_dev *dev, unsigned long palette) | ||
607 | { | ||
608 | u32 clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL); | ||
609 | |||
610 | /* call helper function */ | ||
611 | calculate_output_format_register(dev,palette,&clip_format); | ||
612 | |||
613 | /* update the hps registers */ | ||
614 | saa7146_write(dev, CLIP_FORMAT_CTRL, clip_format); | ||
615 | saa7146_write(dev, MC2, (MASK_05 | MASK_21)); | ||
616 | } | ||
617 | |||
618 | /* select input-source */ | ||
619 | void saa7146_set_hps_source_and_sync(struct saa7146_dev *dev, int source, int sync) | ||
620 | { | ||
621 | struct saa7146_vv *vv = dev->vv_data; | ||
622 | u32 hps_ctrl = 0; | ||
623 | |||
624 | /* read old state */ | ||
625 | hps_ctrl = saa7146_read(dev, HPS_CTRL); | ||
626 | |||
627 | hps_ctrl &= ~( MASK_31 | MASK_30 | MASK_28 ); | ||
628 | hps_ctrl |= (source << 30) | (sync << 28); | ||
629 | |||
630 | /* write back & upload register */ | ||
631 | saa7146_write(dev, HPS_CTRL, hps_ctrl); | ||
632 | saa7146_write(dev, MC2, (MASK_05 | MASK_21)); | ||
633 | |||
634 | vv->current_hps_source = source; | ||
635 | vv->current_hps_sync = sync; | ||
636 | } | ||
637 | |||
638 | int saa7146_enable_overlay(struct saa7146_fh *fh) | ||
639 | { | ||
640 | struct saa7146_dev *dev = fh->dev; | ||
641 | struct saa7146_vv *vv = dev->vv_data; | ||
642 | |||
643 | saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field); | ||
644 | saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat); | ||
645 | saa7146_set_output_format(dev, vv->ov_fmt->trans); | ||
646 | saa7146_set_clipping_rect(fh); | ||
647 | |||
648 | /* enable video dma1 */ | ||
649 | saa7146_write(dev, MC1, (MASK_06 | MASK_22)); | ||
650 | return 0; | ||
651 | } | ||
652 | |||
653 | void saa7146_disable_overlay(struct saa7146_fh *fh) | ||
654 | { | ||
655 | struct saa7146_dev *dev = fh->dev; | ||
656 | |||
657 | /* disable clipping + video dma1 */ | ||
658 | saa7146_disable_clipping(dev); | ||
659 | saa7146_write(dev, MC1, MASK_22); | ||
660 | } | ||
661 | |||
662 | void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) | ||
663 | { | ||
664 | int where = 0; | ||
665 | |||
666 | if( which < 1 || which > 3) { | ||
667 | return; | ||
668 | } | ||
669 | |||
670 | /* calculate starting address */ | ||
671 | where = (which-1)*0x18; | ||
672 | |||
673 | saa7146_write(dev, where, vdma->base_odd); | ||
674 | saa7146_write(dev, where+0x04, vdma->base_even); | ||
675 | saa7146_write(dev, where+0x08, vdma->prot_addr); | ||
676 | saa7146_write(dev, where+0x0c, vdma->pitch); | ||
677 | saa7146_write(dev, where+0x10, vdma->base_page); | ||
678 | saa7146_write(dev, where+0x14, vdma->num_line_byte); | ||
679 | |||
680 | /* upload */ | ||
681 | saa7146_write(dev, MC2, (MASK_02<<(which-1))|(MASK_18<<(which-1))); | ||
682 | /* | ||
683 | printk("vdma%d.base_even: 0x%08x\n", which,vdma->base_even); | ||
684 | printk("vdma%d.base_odd: 0x%08x\n", which,vdma->base_odd); | ||
685 | printk("vdma%d.prot_addr: 0x%08x\n", which,vdma->prot_addr); | ||
686 | printk("vdma%d.base_page: 0x%08x\n", which,vdma->base_page); | ||
687 | printk("vdma%d.pitch: 0x%08x\n", which,vdma->pitch); | ||
688 | printk("vdma%d.num_line_byte: 0x%08x\n", which,vdma->num_line_byte); | ||
689 | */ | ||
690 | } | ||
691 | |||
692 | static int calculate_video_dma_grab_packed(struct saa7146_dev* dev, struct saa7146_buf *buf) | ||
693 | { | ||
694 | struct saa7146_vv *vv = dev->vv_data; | ||
695 | struct saa7146_video_dma vdma1; | ||
696 | |||
697 | struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); | ||
698 | |||
699 | int width = buf->fmt->width; | ||
700 | int height = buf->fmt->height; | ||
701 | int bytesperline = buf->fmt->bytesperline; | ||
702 | enum v4l2_field field = buf->fmt->field; | ||
703 | |||
704 | int depth = sfmt->depth; | ||
705 | |||
706 | DEB_CAP(("[size=%dx%d,fields=%s]\n", | ||
707 | width,height,v4l2_field_names[field])); | ||
708 | |||
709 | if( bytesperline != 0) { | ||
710 | vdma1.pitch = bytesperline*2; | ||
711 | } else { | ||
712 | vdma1.pitch = (width*depth*2)/8; | ||
713 | } | ||
714 | vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); | ||
715 | vdma1.base_page = buf->pt[0].dma | ME1 | sfmt->swap; | ||
716 | |||
717 | if( 0 != vv->vflip ) { | ||
718 | vdma1.prot_addr = buf->pt[0].offset; | ||
719 | vdma1.base_even = buf->pt[0].offset+(vdma1.pitch/2)*height; | ||
720 | vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); | ||
721 | } else { | ||
722 | vdma1.base_even = buf->pt[0].offset; | ||
723 | vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); | ||
724 | vdma1.prot_addr = buf->pt[0].offset+(vdma1.pitch/2)*height; | ||
725 | } | ||
726 | |||
727 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
728 | } else if (field == V4L2_FIELD_ALTERNATE) { | ||
729 | /* fixme */ | ||
730 | if ( vv->last_field == V4L2_FIELD_TOP ) { | ||
731 | vdma1.base_odd = vdma1.prot_addr; | ||
732 | vdma1.pitch /= 2; | ||
733 | } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { | ||
734 | vdma1.base_odd = vdma1.base_even; | ||
735 | vdma1.base_even = vdma1.prot_addr; | ||
736 | vdma1.pitch /= 2; | ||
737 | } | ||
738 | } else if (field == V4L2_FIELD_TOP) { | ||
739 | vdma1.base_odd = vdma1.prot_addr; | ||
740 | vdma1.pitch /= 2; | ||
741 | } else if (field == V4L2_FIELD_BOTTOM) { | ||
742 | vdma1.base_odd = vdma1.base_even; | ||
743 | vdma1.base_even = vdma1.prot_addr; | ||
744 | vdma1.pitch /= 2; | ||
745 | } | ||
746 | |||
747 | if( 0 != vv->vflip ) { | ||
748 | vdma1.pitch *= -1; | ||
749 | } | ||
750 | |||
751 | saa7146_write_out_dma(dev, 1, &vdma1); | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | static int calc_planar_422(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) | ||
756 | { | ||
757 | int height = buf->fmt->height; | ||
758 | int width = buf->fmt->width; | ||
759 | |||
760 | vdma2->pitch = width; | ||
761 | vdma3->pitch = width; | ||
762 | |||
763 | /* fixme: look at bytesperline! */ | ||
764 | |||
765 | if( 0 != vv->vflip ) { | ||
766 | vdma2->prot_addr = buf->pt[1].offset; | ||
767 | vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[1].offset; | ||
768 | vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); | ||
769 | |||
770 | vdma3->prot_addr = buf->pt[2].offset; | ||
771 | vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[2].offset; | ||
772 | vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); | ||
773 | } else { | ||
774 | vdma3->base_even = buf->pt[2].offset; | ||
775 | vdma3->base_odd = vdma3->base_even + (vdma3->pitch/2); | ||
776 | vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; | ||
777 | |||
778 | vdma2->base_even = buf->pt[1].offset; | ||
779 | vdma2->base_odd = vdma2->base_even + (vdma2->pitch/2); | ||
780 | vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; | ||
781 | } | ||
782 | |||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | static int calc_planar_420(struct saa7146_vv *vv, struct saa7146_buf *buf, struct saa7146_video_dma *vdma2, struct saa7146_video_dma *vdma3) | ||
787 | { | ||
788 | int height = buf->fmt->height; | ||
789 | int width = buf->fmt->width; | ||
790 | |||
791 | vdma2->pitch = width/2; | ||
792 | vdma3->pitch = width/2; | ||
793 | |||
794 | if( 0 != vv->vflip ) { | ||
795 | vdma2->prot_addr = buf->pt[2].offset; | ||
796 | vdma2->base_even = ((vdma2->pitch/2)*height)+buf->pt[2].offset; | ||
797 | vdma2->base_odd = vdma2->base_even - (vdma2->pitch/2); | ||
798 | |||
799 | vdma3->prot_addr = buf->pt[1].offset; | ||
800 | vdma3->base_even = ((vdma3->pitch/2)*height)+buf->pt[1].offset; | ||
801 | vdma3->base_odd = vdma3->base_even - (vdma3->pitch/2); | ||
802 | |||
803 | } else { | ||
804 | vdma3->base_even = buf->pt[2].offset; | ||
805 | vdma3->base_odd = vdma3->base_even + (vdma3->pitch); | ||
806 | vdma3->prot_addr = (vdma3->pitch/2)*height+buf->pt[2].offset; | ||
807 | |||
808 | vdma2->base_even = buf->pt[1].offset; | ||
809 | vdma2->base_odd = vdma2->base_even + (vdma2->pitch); | ||
810 | vdma2->prot_addr = (vdma2->pitch/2)*height+buf->pt[1].offset; | ||
811 | } | ||
812 | return 0; | ||
813 | } | ||
814 | |||
815 | static int calculate_video_dma_grab_planar(struct saa7146_dev* dev, struct saa7146_buf *buf) | ||
816 | { | ||
817 | struct saa7146_vv *vv = dev->vv_data; | ||
818 | struct saa7146_video_dma vdma1; | ||
819 | struct saa7146_video_dma vdma2; | ||
820 | struct saa7146_video_dma vdma3; | ||
821 | |||
822 | struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); | ||
823 | |||
824 | int width = buf->fmt->width; | ||
825 | int height = buf->fmt->height; | ||
826 | enum v4l2_field field = buf->fmt->field; | ||
827 | |||
828 | BUG_ON(0 == buf->pt[0].dma); | ||
829 | BUG_ON(0 == buf->pt[1].dma); | ||
830 | BUG_ON(0 == buf->pt[2].dma); | ||
831 | |||
832 | DEB_CAP(("[size=%dx%d,fields=%s]\n", | ||
833 | width,height,v4l2_field_names[field])); | ||
834 | |||
835 | /* fixme: look at bytesperline! */ | ||
836 | |||
837 | /* fixme: what happens for user space buffers here?. The offsets are | ||
838 | most likely wrong, this version here only works for page-aligned | ||
839 | buffers, modifications to the pagetable-functions are necessary...*/ | ||
840 | |||
841 | vdma1.pitch = width*2; | ||
842 | vdma1.num_line_byte = ((vv->standard->v_field<<16) + vv->standard->h_pixels); | ||
843 | vdma1.base_page = buf->pt[0].dma | ME1; | ||
844 | |||
845 | if( 0 != vv->vflip ) { | ||
846 | vdma1.prot_addr = buf->pt[0].offset; | ||
847 | vdma1.base_even = ((vdma1.pitch/2)*height)+buf->pt[0].offset; | ||
848 | vdma1.base_odd = vdma1.base_even - (vdma1.pitch/2); | ||
849 | } else { | ||
850 | vdma1.base_even = buf->pt[0].offset; | ||
851 | vdma1.base_odd = vdma1.base_even + (vdma1.pitch/2); | ||
852 | vdma1.prot_addr = (vdma1.pitch/2)*height+buf->pt[0].offset; | ||
853 | } | ||
854 | |||
855 | vdma2.num_line_byte = 0; /* unused */ | ||
856 | vdma2.base_page = buf->pt[1].dma | ME1; | ||
857 | |||
858 | vdma3.num_line_byte = 0; /* unused */ | ||
859 | vdma3.base_page = buf->pt[2].dma | ME1; | ||
860 | |||
861 | switch( sfmt->depth ) { | ||
862 | case 12: { | ||
863 | calc_planar_420(vv,buf,&vdma2,&vdma3); | ||
864 | break; | ||
865 | } | ||
866 | case 16: { | ||
867 | calc_planar_422(vv,buf,&vdma2,&vdma3); | ||
868 | break; | ||
869 | } | ||
870 | default: { | ||
871 | return -1; | ||
872 | } | ||
873 | } | ||
874 | |||
875 | if (V4L2_FIELD_HAS_BOTH(field)) { | ||
876 | } else if (field == V4L2_FIELD_ALTERNATE) { | ||
877 | /* fixme */ | ||
878 | vdma1.base_odd = vdma1.prot_addr; | ||
879 | vdma1.pitch /= 2; | ||
880 | vdma2.base_odd = vdma2.prot_addr; | ||
881 | vdma2.pitch /= 2; | ||
882 | vdma3.base_odd = vdma3.prot_addr; | ||
883 | vdma3.pitch /= 2; | ||
884 | } else if (field == V4L2_FIELD_TOP) { | ||
885 | vdma1.base_odd = vdma1.prot_addr; | ||
886 | vdma1.pitch /= 2; | ||
887 | vdma2.base_odd = vdma2.prot_addr; | ||
888 | vdma2.pitch /= 2; | ||
889 | vdma3.base_odd = vdma3.prot_addr; | ||
890 | vdma3.pitch /= 2; | ||
891 | } else if (field == V4L2_FIELD_BOTTOM) { | ||
892 | vdma1.base_odd = vdma1.base_even; | ||
893 | vdma1.base_even = vdma1.prot_addr; | ||
894 | vdma1.pitch /= 2; | ||
895 | vdma2.base_odd = vdma2.base_even; | ||
896 | vdma2.base_even = vdma2.prot_addr; | ||
897 | vdma2.pitch /= 2; | ||
898 | vdma3.base_odd = vdma3.base_even; | ||
899 | vdma3.base_even = vdma3.prot_addr; | ||
900 | vdma3.pitch /= 2; | ||
901 | } | ||
902 | |||
903 | if( 0 != vv->vflip ) { | ||
904 | vdma1.pitch *= -1; | ||
905 | vdma2.pitch *= -1; | ||
906 | vdma3.pitch *= -1; | ||
907 | } | ||
908 | |||
909 | saa7146_write_out_dma(dev, 1, &vdma1); | ||
910 | if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) { | ||
911 | saa7146_write_out_dma(dev, 3, &vdma2); | ||
912 | saa7146_write_out_dma(dev, 2, &vdma3); | ||
913 | } else { | ||
914 | saa7146_write_out_dma(dev, 2, &vdma2); | ||
915 | saa7146_write_out_dma(dev, 3, &vdma3); | ||
916 | } | ||
917 | return 0; | ||
918 | } | ||
919 | |||
920 | static void program_capture_engine(struct saa7146_dev *dev, int planar) | ||
921 | { | ||
922 | struct saa7146_vv *vv = dev->vv_data; | ||
923 | int count = 0; | ||
924 | |||
925 | unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; | ||
926 | unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; | ||
927 | |||
928 | /* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/ | ||
929 | WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait); | ||
930 | WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait); | ||
931 | |||
932 | /* set rps register 0 */ | ||
933 | WRITE_RPS0(CMD_WR_REG | (1 << 8) | (MC2/4)); | ||
934 | WRITE_RPS0(MASK_27 | MASK_11); | ||
935 | |||
936 | /* turn on video-dma1 */ | ||
937 | WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); | ||
938 | WRITE_RPS0(MASK_06 | MASK_22); /* => mask */ | ||
939 | WRITE_RPS0(MASK_06 | MASK_22); /* => values */ | ||
940 | if( 0 != planar ) { | ||
941 | /* turn on video-dma2 */ | ||
942 | WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); | ||
943 | WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ | ||
944 | WRITE_RPS0(MASK_05 | MASK_21); /* => values */ | ||
945 | |||
946 | /* turn on video-dma3 */ | ||
947 | WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); | ||
948 | WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ | ||
949 | WRITE_RPS0(MASK_04 | MASK_20); /* => values */ | ||
950 | } | ||
951 | |||
952 | /* wait for o_fid_a/b / e_fid_a/b toggle */ | ||
953 | if ( vv->last_field == V4L2_FIELD_INTERLACED ) { | ||
954 | WRITE_RPS0(CMD_PAUSE | o_wait); | ||
955 | WRITE_RPS0(CMD_PAUSE | e_wait); | ||
956 | } else if ( vv->last_field == V4L2_FIELD_TOP ) { | ||
957 | WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); | ||
958 | WRITE_RPS0(CMD_PAUSE | o_wait); | ||
959 | } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { | ||
960 | WRITE_RPS0(CMD_PAUSE | (vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? MASK_10 : MASK_09)); | ||
961 | WRITE_RPS0(CMD_PAUSE | e_wait); | ||
962 | } | ||
963 | |||
964 | /* turn off video-dma1 */ | ||
965 | WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); | ||
966 | WRITE_RPS0(MASK_22 | MASK_06); /* => mask */ | ||
967 | WRITE_RPS0(MASK_22); /* => values */ | ||
968 | if( 0 != planar ) { | ||
969 | /* turn off video-dma2 */ | ||
970 | WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); | ||
971 | WRITE_RPS0(MASK_05 | MASK_21); /* => mask */ | ||
972 | WRITE_RPS0(MASK_21); /* => values */ | ||
973 | |||
974 | /* turn off video-dma3 */ | ||
975 | WRITE_RPS0(CMD_WR_REG_MASK | (MC1/4)); | ||
976 | WRITE_RPS0(MASK_04 | MASK_20); /* => mask */ | ||
977 | WRITE_RPS0(MASK_20); /* => values */ | ||
978 | } | ||
979 | |||
980 | /* generate interrupt */ | ||
981 | WRITE_RPS0(CMD_INTERRUPT); | ||
982 | |||
983 | /* stop */ | ||
984 | WRITE_RPS0(CMD_STOP); | ||
985 | } | ||
986 | |||
987 | void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) | ||
988 | { | ||
989 | struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); | ||
990 | struct saa7146_vv *vv = dev->vv_data; | ||
991 | u32 vdma1_prot_addr; | ||
992 | |||
993 | DEB_CAP(("buf:%p, next:%p\n",buf,next)); | ||
994 | |||
995 | vdma1_prot_addr = saa7146_read(dev, PROT_ADDR1); | ||
996 | if( 0 == vdma1_prot_addr ) { | ||
997 | /* clear out beginning of streaming bit (rps register 0)*/ | ||
998 | DEB_CAP(("forcing sync to new frame\n")); | ||
999 | saa7146_write(dev, MC2, MASK_27 ); | ||
1000 | } | ||
1001 | |||
1002 | saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field); | ||
1003 | saa7146_set_output_format(dev, sfmt->trans); | ||
1004 | saa7146_disable_clipping(dev); | ||
1005 | |||
1006 | if ( vv->last_field == V4L2_FIELD_INTERLACED ) { | ||
1007 | } else if ( vv->last_field == V4L2_FIELD_TOP ) { | ||
1008 | vv->last_field = V4L2_FIELD_BOTTOM; | ||
1009 | } else if ( vv->last_field == V4L2_FIELD_BOTTOM ) { | ||
1010 | vv->last_field = V4L2_FIELD_TOP; | ||
1011 | } | ||
1012 | |||
1013 | if( 0 != IS_PLANAR(sfmt->trans)) { | ||
1014 | calculate_video_dma_grab_planar(dev, buf); | ||
1015 | program_capture_engine(dev,1); | ||
1016 | } else { | ||
1017 | calculate_video_dma_grab_packed(dev, buf); | ||
1018 | program_capture_engine(dev,0); | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1)); | ||
1023 | printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1)); | ||
1024 | printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1)); | ||
1025 | printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1)); | ||
1026 | printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1)); | ||
1027 | printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1)); | ||
1028 | printk("vdma%d => vptr : 0x%08x\n", 1,saa7146_read(dev,PCI_VDP1)); | ||
1029 | */ | ||
1030 | |||
1031 | /* write the address of the rps-program */ | ||
1032 | saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle); | ||
1033 | |||
1034 | /* turn on rps */ | ||
1035 | saa7146_write(dev, MC1, (MASK_12 | MASK_28)); | ||
1036 | } | ||
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c new file mode 100644 index 000000000000..781f23f0cbcc --- /dev/null +++ b/drivers/media/common/saa7146_i2c.c | |||
@@ -0,0 +1,421 @@ | |||
1 | #include <linux/version.h> | ||
2 | #include <media/saa7146_vv.h> | ||
3 | |||
4 | static u32 saa7146_i2c_func(struct i2c_adapter *adapter) | ||
5 | { | ||
6 | //fm DEB_I2C(("'%s'.\n", adapter->name)); | ||
7 | |||
8 | return I2C_FUNC_I2C | ||
9 | | I2C_FUNC_SMBUS_QUICK | ||
10 | | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE | ||
11 | | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; | ||
12 | } | ||
13 | |||
14 | /* this function returns the status-register of our i2c-device */ | ||
15 | static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) | ||
16 | { | ||
17 | u32 iicsta = saa7146_read(dev, I2C_STATUS); | ||
18 | /* | ||
19 | DEB_I2C(("status: 0x%08x\n",iicsta)); | ||
20 | */ | ||
21 | return iicsta; | ||
22 | } | ||
23 | |||
24 | /* this function runs through the i2c-messages and prepares the data to be | ||
25 | sent through the saa7146. have a look at the specifications p. 122 ff | ||
26 | to understand this. it returns the number of u32s to send, or -1 | ||
27 | in case of an error. */ | ||
28 | static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op) | ||
29 | { | ||
30 | int h1, h2; | ||
31 | int i, j, addr; | ||
32 | int mem = 0, op_count = 0; | ||
33 | |||
34 | /* first determine size of needed memory */ | ||
35 | for(i = 0; i < num; i++) { | ||
36 | mem += m[i].len + 1; | ||
37 | } | ||
38 | |||
39 | /* worst case: we need one u32 for three bytes to be send | ||
40 | plus one extra byte to address the device */ | ||
41 | mem = 1 + ((mem-1) / 3); | ||
42 | |||
43 | /* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes | ||
44 | size. if we exceed this limit... */ | ||
45 | if ( (4*mem) > SAA7146_I2C_MEM ) { | ||
46 | //fm DEB_I2C(("cannot prepare i2c-message.\n")); | ||
47 | return -ENOMEM; | ||
48 | } | ||
49 | |||
50 | /* be careful: clear out the i2c-mem first */ | ||
51 | memset(op,0,sizeof(u32)*mem); | ||
52 | |||
53 | /* loop through all messages */ | ||
54 | for(i = 0; i < num; i++) { | ||
55 | |||
56 | /* insert the address of the i2c-slave. | ||
57 | note: we get 7 bit i2c-addresses, | ||
58 | so we have to perform a translation */ | ||
59 | addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0); | ||
60 | h1 = op_count/3; h2 = op_count%3; | ||
61 | op[h1] |= ( (u8)addr << ((3-h2)*8)); | ||
62 | op[h1] |= (SAA7146_I2C_START << ((3-h2)*2)); | ||
63 | op_count++; | ||
64 | |||
65 | /* loop through all bytes of message i */ | ||
66 | for(j = 0; j < m[i].len; j++) { | ||
67 | /* insert the data bytes */ | ||
68 | h1 = op_count/3; h2 = op_count%3; | ||
69 | op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); | ||
70 | op[h1] |= ( SAA7146_I2C_CONT << ((3-h2)*2)); | ||
71 | op_count++; | ||
72 | } | ||
73 | |||
74 | } | ||
75 | |||
76 | /* have a look at the last byte inserted: | ||
77 | if it was: ...CONT change it to ...STOP */ | ||
78 | h1 = (op_count-1)/3; h2 = (op_count-1)%3; | ||
79 | if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) { | ||
80 | op[h1] &= ~(0x2 << ((3-h2)*2)); | ||
81 | op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2)); | ||
82 | } | ||
83 | |||
84 | /* return the number of u32s to send */ | ||
85 | return mem; | ||
86 | } | ||
87 | |||
88 | /* this functions loops through all i2c-messages. normally, it should determine | ||
89 | which bytes were read through the adapter and write them back to the corresponding | ||
90 | i2c-message. but instead, we simply write back all bytes. | ||
91 | fixme: this could be improved. */ | ||
92 | static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op) | ||
93 | { | ||
94 | int i, j; | ||
95 | int op_count = 0; | ||
96 | |||
97 | /* loop through all messages */ | ||
98 | for(i = 0; i < num; i++) { | ||
99 | |||
100 | op_count++; | ||
101 | |||
102 | /* loop throgh all bytes of message i */ | ||
103 | for(j = 0; j < m[i].len; j++) { | ||
104 | /* write back all bytes that could have been read */ | ||
105 | m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8)); | ||
106 | op_count++; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */ | ||
114 | static int saa7146_i2c_reset(struct saa7146_dev *dev) | ||
115 | { | ||
116 | /* get current status */ | ||
117 | u32 status = saa7146_i2c_status(dev); | ||
118 | |||
119 | /* clear registers for sure */ | ||
120 | saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); | ||
121 | saa7146_write(dev, I2C_TRANSFER, 0); | ||
122 | |||
123 | /* check if any operation is still in progress */ | ||
124 | if ( 0 != ( status & SAA7146_I2C_BUSY) ) { | ||
125 | |||
126 | /* yes, kill ongoing operation */ | ||
127 | DEB_I2C(("busy_state detected.\n")); | ||
128 | |||
129 | /* set "ABORT-OPERATION"-bit (bit 7)*/ | ||
130 | saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); | ||
131 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
132 | msleep(SAA7146_I2C_DELAY); | ||
133 | |||
134 | /* clear all error-bits pending; this is needed because p.123, note 1 */ | ||
135 | saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); | ||
136 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
137 | msleep(SAA7146_I2C_DELAY); | ||
138 | } | ||
139 | |||
140 | /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ | ||
141 | status = saa7146_i2c_status(dev); | ||
142 | |||
143 | if ( dev->i2c_bitrate != status ) { | ||
144 | |||
145 | DEB_I2C(("error_state detected. status:0x%08x\n",status)); | ||
146 | |||
147 | /* Repeat the abort operation. This seems to be necessary | ||
148 | after serious protocol errors caused by e.g. the SAA7740 */ | ||
149 | saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); | ||
150 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
151 | msleep(SAA7146_I2C_DELAY); | ||
152 | |||
153 | /* clear all error-bits pending */ | ||
154 | saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); | ||
155 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
156 | msleep(SAA7146_I2C_DELAY); | ||
157 | |||
158 | /* the data sheet says it might be necessary to clear the status | ||
159 | twice after an abort */ | ||
160 | saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); | ||
161 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
162 | msleep(SAA7146_I2C_DELAY); | ||
163 | } | ||
164 | |||
165 | /* if any error is still present, a fatal error has occured ... */ | ||
166 | status = saa7146_i2c_status(dev); | ||
167 | if ( dev->i2c_bitrate != status ) { | ||
168 | DEB_I2C(("fatal error. status:0x%08x\n",status)); | ||
169 | return -1; | ||
170 | } | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | /* this functions writes out the data-byte 'dword' to the i2c-device. | ||
176 | it returns 0 if ok, -1 if the transfer failed, -2 if the transfer | ||
177 | failed badly (e.g. address error) */ | ||
178 | static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_delay) | ||
179 | { | ||
180 | u32 status = 0, mc2 = 0; | ||
181 | int trial = 0; | ||
182 | unsigned long timeout; | ||
183 | |||
184 | /* write out i2c-command */ | ||
185 | DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op)); | ||
186 | |||
187 | if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { | ||
188 | |||
189 | saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); | ||
190 | saa7146_write(dev, I2C_TRANSFER, *dword); | ||
191 | |||
192 | dev->i2c_op = 1; | ||
193 | SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); | ||
194 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
195 | |||
196 | wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0); | ||
197 | if (signal_pending (current)) { | ||
198 | /* a signal arrived */ | ||
199 | return -ERESTARTSYS; | ||
200 | } | ||
201 | status = saa7146_read(dev, I2C_STATUS); | ||
202 | } else { | ||
203 | saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); | ||
204 | saa7146_write(dev, I2C_TRANSFER, *dword); | ||
205 | saa7146_write(dev, MC2, (MASK_00 | MASK_16)); | ||
206 | |||
207 | /* do not poll for i2c-status before upload is complete */ | ||
208 | timeout = jiffies + HZ/100 + 1; /* 10ms */ | ||
209 | while(1) { | ||
210 | mc2 = (saa7146_read(dev, MC2) & 0x1); | ||
211 | if( 0 != mc2 ) { | ||
212 | break; | ||
213 | } | ||
214 | if (time_after(jiffies,timeout)) { | ||
215 | printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for MC2\n"); | ||
216 | return -EIO; | ||
217 | } | ||
218 | } | ||
219 | /* wait until we get a transfer done or error */ | ||
220 | timeout = jiffies + HZ/100 + 1; /* 10ms */ | ||
221 | while(1) { | ||
222 | /** | ||
223 | * first read usually delivers bogus results... | ||
224 | */ | ||
225 | saa7146_i2c_status(dev); | ||
226 | status = saa7146_i2c_status(dev); | ||
227 | if ((status & 0x3) != 1) | ||
228 | break; | ||
229 | if (time_after(jiffies,timeout)) { | ||
230 | /* this is normal when probing the bus | ||
231 | * (no answer from nonexisistant device...) | ||
232 | */ | ||
233 | DEB_I2C(("saa7146_i2c_writeout: timed out waiting for end of xfer\n")); | ||
234 | return -EIO; | ||
235 | } | ||
236 | if ((++trial < 20) && short_delay) | ||
237 | udelay(10); | ||
238 | else | ||
239 | msleep(1); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | /* give a detailed status report */ | ||
244 | if ( 0 != (status & SAA7146_I2C_ERR)) { | ||
245 | |||
246 | if( 0 != (status & SAA7146_I2C_SPERR) ) { | ||
247 | DEB_I2C(("error due to invalid start/stop condition.\n")); | ||
248 | } | ||
249 | if( 0 != (status & SAA7146_I2C_DTERR) ) { | ||
250 | DEB_I2C(("error in data transmission.\n")); | ||
251 | } | ||
252 | if( 0 != (status & SAA7146_I2C_DRERR) ) { | ||
253 | DEB_I2C(("error when receiving data.\n")); | ||
254 | } | ||
255 | if( 0 != (status & SAA7146_I2C_AL) ) { | ||
256 | DEB_I2C(("error because arbitration lost.\n")); | ||
257 | } | ||
258 | |||
259 | /* we handle address-errors here */ | ||
260 | if( 0 != (status & SAA7146_I2C_APERR) ) { | ||
261 | DEB_I2C(("error in address phase.\n")); | ||
262 | return -EREMOTEIO; | ||
263 | } | ||
264 | |||
265 | return -EIO; | ||
266 | } | ||
267 | |||
268 | /* read back data, just in case we were reading ... */ | ||
269 | *dword = saa7146_read(dev, I2C_TRANSFER); | ||
270 | |||
271 | DEB_I2C(("after: 0x%08x\n",*dword)); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) | ||
276 | { | ||
277 | int i = 0, count = 0; | ||
278 | u32* buffer = dev->d_i2c.cpu_addr; | ||
279 | int err = 0; | ||
280 | int address_err = 0; | ||
281 | int short_delay = 0; | ||
282 | |||
283 | if (down_interruptible (&dev->i2c_lock)) | ||
284 | return -ERESTARTSYS; | ||
285 | |||
286 | for(i=0;i<num;i++) { | ||
287 | DEB_I2C(("msg:%d/%d\n",i+1,num)); | ||
288 | } | ||
289 | |||
290 | /* prepare the message(s), get number of u32s to transfer */ | ||
291 | count = saa7146_i2c_msg_prepare(msgs, num, buffer); | ||
292 | if ( 0 > count ) { | ||
293 | err = -1; | ||
294 | goto out; | ||
295 | } | ||
296 | |||
297 | if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) ) | ||
298 | short_delay = 1; | ||
299 | |||
300 | do { | ||
301 | /* reset the i2c-device if necessary */ | ||
302 | err = saa7146_i2c_reset(dev); | ||
303 | if ( 0 > err ) { | ||
304 | DEB_I2C(("could not reset i2c-device.\n")); | ||
305 | goto out; | ||
306 | } | ||
307 | |||
308 | /* write out the u32s one after another */ | ||
309 | for(i = 0; i < count; i++) { | ||
310 | err = saa7146_i2c_writeout(dev, &buffer[i], short_delay); | ||
311 | if ( 0 != err) { | ||
312 | /* this one is unsatisfying: some i2c slaves on some | ||
313 | dvb cards don't acknowledge correctly, so the saa7146 | ||
314 | thinks that an address error occured. in that case, the | ||
315 | transaction should be retrying, even if an address error | ||
316 | occured. analog saa7146 based cards extensively rely on | ||
317 | i2c address probing, however, and address errors indicate that a | ||
318 | device is really *not* there. retrying in that case | ||
319 | increases the time the device needs to probe greatly, so | ||
320 | it should be avoided. because of the fact, that only | ||
321 | analog based cards use irq based i2c transactions (for dvb | ||
322 | cards, this screwes up other interrupt sources), we bail out | ||
323 | completely for analog cards after an address error and trust | ||
324 | the saa7146 address error detection. */ | ||
325 | if ( -EREMOTEIO == err ) { | ||
326 | if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { | ||
327 | goto out; | ||
328 | } | ||
329 | address_err++; | ||
330 | } | ||
331 | DEB_I2C(("error while sending message(s). starting again.\n")); | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | if( 0 == err ) { | ||
336 | err = num; | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | /* delay a bit before retrying */ | ||
341 | msleep(10); | ||
342 | |||
343 | } while (err != num && retries--); | ||
344 | |||
345 | /* if every retry had an address error, exit right away */ | ||
346 | if (address_err == retries) { | ||
347 | goto out; | ||
348 | } | ||
349 | |||
350 | /* if any things had to be read, get the results */ | ||
351 | if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) { | ||
352 | DEB_I2C(("could not cleanup i2c-message.\n")); | ||
353 | err = -1; | ||
354 | goto out; | ||
355 | } | ||
356 | |||
357 | /* return the number of delivered messages */ | ||
358 | DEB_I2C(("transmission successful. (msg:%d).\n",err)); | ||
359 | out: | ||
360 | /* another bug in revision 0: the i2c-registers get uploaded randomly by other | ||
361 | uploads, so we better clear them out before continueing */ | ||
362 | if( 0 == dev->revision ) { | ||
363 | u32 zero = 0; | ||
364 | saa7146_i2c_reset(dev); | ||
365 | if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { | ||
366 | INFO(("revision 0 error. this should never happen.\n")); | ||
367 | } | ||
368 | } | ||
369 | |||
370 | up(&dev->i2c_lock); | ||
371 | return err; | ||
372 | } | ||
373 | |||
374 | /* utility functions */ | ||
375 | static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) | ||
376 | { | ||
377 | struct saa7146_dev* dev = i2c_get_adapdata(adapter); | ||
378 | |||
379 | /* use helper function to transfer data */ | ||
380 | return saa7146_i2c_transfer(dev, msg, num, adapter->retries); | ||
381 | } | ||
382 | |||
383 | |||
384 | /*****************************************************************************/ | ||
385 | /* i2c-adapter helper functions */ | ||
386 | #include <linux/i2c-id.h> | ||
387 | |||
388 | /* exported algorithm data */ | ||
389 | static struct i2c_algorithm saa7146_algo = { | ||
390 | .name = "saa7146 i2c algorithm", | ||
391 | .id = I2C_ALGO_SAA7146, | ||
392 | .master_xfer = saa7146_i2c_xfer, | ||
393 | .functionality = saa7146_i2c_func, | ||
394 | }; | ||
395 | |||
396 | int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate) | ||
397 | { | ||
398 | DEB_EE(("bitrate: 0x%08x\n",bitrate)); | ||
399 | |||
400 | /* enable i2c-port pins */ | ||
401 | saa7146_write(dev, MC1, (MASK_08 | MASK_24)); | ||
402 | |||
403 | dev->i2c_bitrate = bitrate; | ||
404 | saa7146_i2c_reset(dev); | ||
405 | |||
406 | if( NULL != i2c_adapter ) { | ||
407 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) | ||
408 | i2c_adapter->data = dev; | ||
409 | #else | ||
410 | BUG_ON(!i2c_adapter->class); | ||
411 | i2c_set_adapdata(i2c_adapter,dev); | ||
412 | #endif | ||
413 | i2c_adapter->algo = &saa7146_algo; | ||
414 | i2c_adapter->algo_data = NULL; | ||
415 | i2c_adapter->id = I2C_ALGO_SAA7146; | ||
416 | i2c_adapter->timeout = SAA7146_I2C_TIMEOUT; | ||
417 | i2c_adapter->retries = SAA7146_I2C_RETRIES; | ||
418 | } | ||
419 | |||
420 | return 0; | ||
421 | } | ||
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c new file mode 100644 index 000000000000..cb86a97fda1f --- /dev/null +++ b/drivers/media/common/saa7146_vbi.c | |||
@@ -0,0 +1,508 @@ | |||
1 | #include <media/saa7146_vv.h> | ||
2 | |||
3 | static int vbi_pixel_to_capture = 720 * 2; | ||
4 | |||
5 | static int vbi_workaround(struct saa7146_dev *dev) | ||
6 | { | ||
7 | struct saa7146_vv *vv = dev->vv_data; | ||
8 | |||
9 | u32 *cpu; | ||
10 | dma_addr_t dma_addr; | ||
11 | |||
12 | int count = 0; | ||
13 | int i; | ||
14 | |||
15 | DECLARE_WAITQUEUE(wait, current); | ||
16 | |||
17 | DEB_VBI(("dev:%p\n",dev)); | ||
18 | |||
19 | /* once again, a bug in the saa7146: the brs acquisition | ||
20 | is buggy and especially the BXO-counter does not work | ||
21 | as specified. there is this workaround, but please | ||
22 | don't let me explain it. ;-) */ | ||
23 | |||
24 | cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr); | ||
25 | if (NULL == cpu) | ||
26 | return -ENOMEM; | ||
27 | |||
28 | /* setup some basic programming, just for the workaround */ | ||
29 | saa7146_write(dev, BASE_EVEN3, dma_addr); | ||
30 | saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture); | ||
31 | saa7146_write(dev, PROT_ADDR3, dma_addr+4096); | ||
32 | saa7146_write(dev, PITCH3, vbi_pixel_to_capture); | ||
33 | saa7146_write(dev, BASE_PAGE3, 0x0); | ||
34 | saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0)); | ||
35 | saa7146_write(dev, MC2, MASK_04|MASK_20); | ||
36 | |||
37 | /* load brs-control register */ | ||
38 | WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); | ||
39 | /* BXO = 1h, BRS to outbound */ | ||
40 | WRITE_RPS1(0xc000008c); | ||
41 | /* wait for vbi_a or vbi_b*/ | ||
42 | if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { | ||
43 | DEB_D(("...using port b\n")); | ||
44 | WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B); | ||
45 | WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B); | ||
46 | /* | ||
47 | WRITE_RPS1(CMD_PAUSE | MASK_09); | ||
48 | */ | ||
49 | } else { | ||
50 | DEB_D(("...using port a\n")); | ||
51 | WRITE_RPS1(CMD_PAUSE | MASK_10); | ||
52 | } | ||
53 | /* upload brs */ | ||
54 | WRITE_RPS1(CMD_UPLOAD | MASK_08); | ||
55 | /* load brs-control register */ | ||
56 | WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); | ||
57 | /* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */ | ||
58 | WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19); | ||
59 | /* wait for brs_done */ | ||
60 | WRITE_RPS1(CMD_PAUSE | MASK_08); | ||
61 | /* upload brs */ | ||
62 | WRITE_RPS1(CMD_UPLOAD | MASK_08); | ||
63 | /* load video-dma3 NumLines3 and NumBytes3 */ | ||
64 | WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4)); | ||
65 | /* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */ | ||
66 | WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture)); | ||
67 | /* load brs-control register */ | ||
68 | WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4)); | ||
69 | /* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */ | ||
70 | WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start | ||
71 | /* wait for brs_done */ | ||
72 | WRITE_RPS1(CMD_PAUSE | MASK_08); | ||
73 | /* upload brs and video-dma3*/ | ||
74 | WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04); | ||
75 | /* load mc2 register: enable dma3 */ | ||
76 | WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4)); | ||
77 | WRITE_RPS1(MASK_20 | MASK_04); | ||
78 | /* generate interrupt */ | ||
79 | WRITE_RPS1(CMD_INTERRUPT); | ||
80 | /* stop rps1 */ | ||
81 | WRITE_RPS1(CMD_STOP); | ||
82 | |||
83 | /* we have to do the workaround twice to be sure that | ||
84 | everything is ok */ | ||
85 | for(i = 0; i < 2; i++) { | ||
86 | |||
87 | /* indicate to the irq handler that we do the workaround */ | ||
88 | saa7146_write(dev, MC2, MASK_31|MASK_15); | ||
89 | |||
90 | saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0)); | ||
91 | saa7146_write(dev, MC2, MASK_04|MASK_20); | ||
92 | |||
93 | /* enable rps1 irqs */ | ||
94 | SAA7146_IER_ENABLE(dev,MASK_28); | ||
95 | |||
96 | /* prepare to wait to be woken up by the irq-handler */ | ||
97 | add_wait_queue(&vv->vbi_wq, &wait); | ||
98 | current->state = TASK_INTERRUPTIBLE; | ||
99 | |||
100 | /* start rps1 to enable workaround */ | ||
101 | saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); | ||
102 | saa7146_write(dev, MC1, (MASK_13 | MASK_29)); | ||
103 | |||
104 | schedule(); | ||
105 | |||
106 | DEB_VBI(("brs bug workaround %d/1.\n",i)); | ||
107 | |||
108 | remove_wait_queue(&vv->vbi_wq, &wait); | ||
109 | current->state = TASK_RUNNING; | ||
110 | |||
111 | /* disable rps1 irqs */ | ||
112 | SAA7146_IER_DISABLE(dev,MASK_28); | ||
113 | |||
114 | /* stop video-dma3 */ | ||
115 | saa7146_write(dev, MC1, MASK_20); | ||
116 | |||
117 | if(signal_pending(current)) { | ||
118 | |||
119 | DEB_VBI(("aborted (rps:0x%08x).\n",saa7146_read(dev,RPS_ADDR1))); | ||
120 | |||
121 | /* stop rps1 for sure */ | ||
122 | saa7146_write(dev, MC1, MASK_29); | ||
123 | |||
124 | pci_free_consistent(dev->pci, 4096, cpu, dma_addr); | ||
125 | return -EINTR; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | pci_free_consistent(dev->pci, 4096, cpu, dma_addr); | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next) | ||
134 | { | ||
135 | struct saa7146_vv *vv = dev->vv_data; | ||
136 | |||
137 | struct saa7146_video_dma vdma3; | ||
138 | |||
139 | int count = 0; | ||
140 | unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B; | ||
141 | unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B; | ||
142 | |||
143 | /* | ||
144 | vdma3.base_even = 0xc8000000+2560*70; | ||
145 | vdma3.base_odd = 0xc8000000; | ||
146 | vdma3.prot_addr = 0xc8000000+2560*164; | ||
147 | vdma3.pitch = 2560; | ||
148 | vdma3.base_page = 0; | ||
149 | vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above! | ||
150 | */ | ||
151 | vdma3.base_even = buf->pt[2].offset; | ||
152 | vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture; | ||
153 | vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture; | ||
154 | vdma3.pitch = vbi_pixel_to_capture; | ||
155 | vdma3.base_page = buf->pt[2].dma | ME1; | ||
156 | vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture; | ||
157 | |||
158 | saa7146_write_out_dma(dev, 3, &vdma3); | ||
159 | |||
160 | /* write beginning of rps-program */ | ||
161 | count = 0; | ||
162 | |||
163 | /* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */ | ||
164 | |||
165 | /* we don't wait here for the first field anymore. this is different from the video | ||
166 | capture and might cause that the first buffer is only half filled (with only | ||
167 | one field). but since this is some sort of streaming data, this is not that negative. | ||
168 | but by doing this, we can use the whole engine from video-buf.c... */ | ||
169 | |||
170 | /* | ||
171 | WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait); | ||
172 | WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait); | ||
173 | */ | ||
174 | /* set bit 1 */ | ||
175 | WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4)); | ||
176 | WRITE_RPS1(MASK_28 | MASK_12); | ||
177 | |||
178 | /* turn on video-dma3 */ | ||
179 | WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4)); | ||
180 | WRITE_RPS1(MASK_04 | MASK_20); /* => mask */ | ||
181 | WRITE_RPS1(MASK_04 | MASK_20); /* => values */ | ||
182 | |||
183 | /* wait for o_fid_a/b / e_fid_a/b toggle */ | ||
184 | WRITE_RPS1(CMD_PAUSE | o_wait); | ||
185 | WRITE_RPS1(CMD_PAUSE | e_wait); | ||
186 | |||
187 | /* generate interrupt */ | ||
188 | WRITE_RPS1(CMD_INTERRUPT); | ||
189 | |||
190 | /* stop */ | ||
191 | WRITE_RPS1(CMD_STOP); | ||
192 | |||
193 | /* enable rps1 irqs */ | ||
194 | SAA7146_IER_ENABLE(dev, MASK_28); | ||
195 | |||
196 | /* write the address of the rps-program */ | ||
197 | saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); | ||
198 | |||
199 | /* turn on rps */ | ||
200 | saa7146_write(dev, MC1, (MASK_13 | MASK_29)); | ||
201 | } | ||
202 | |||
203 | static int buffer_activate(struct saa7146_dev *dev, | ||
204 | struct saa7146_buf *buf, | ||
205 | struct saa7146_buf *next) | ||
206 | { | ||
207 | struct saa7146_vv *vv = dev->vv_data; | ||
208 | buf->vb.state = STATE_ACTIVE; | ||
209 | |||
210 | DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next)); | ||
211 | saa7146_set_vbi_capture(dev,buf,next); | ||
212 | |||
213 | mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field) | ||
218 | { | ||
219 | struct file *file = q->priv_data; | ||
220 | struct saa7146_fh *fh = file->private_data; | ||
221 | struct saa7146_dev *dev = fh->dev; | ||
222 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
223 | |||
224 | int err = 0; | ||
225 | int lines, llength, size; | ||
226 | |||
227 | lines = 16 * 2 ; /* 2 fields */ | ||
228 | llength = vbi_pixel_to_capture; | ||
229 | size = lines * llength; | ||
230 | |||
231 | DEB_VBI(("vb:%p\n",vb)); | ||
232 | |||
233 | if (0 != buf->vb.baddr && buf->vb.bsize < size) { | ||
234 | DEB_VBI(("size mismatch.\n")); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | |||
238 | if (buf->vb.size != size) | ||
239 | saa7146_dma_free(dev,buf); | ||
240 | |||
241 | if (STATE_NEEDS_INIT == buf->vb.state) { | ||
242 | buf->vb.width = llength; | ||
243 | buf->vb.height = lines; | ||
244 | buf->vb.size = size; | ||
245 | buf->vb.field = field; // FIXME: check this | ||
246 | |||
247 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | ||
248 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); | ||
249 | |||
250 | err = videobuf_iolock(dev->pci,&buf->vb, NULL); | ||
251 | if (err) | ||
252 | goto oops; | ||
253 | err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], buf->vb.dma.sglist, buf->vb.dma.sglen); | ||
254 | if (0 != err) | ||
255 | return err; | ||
256 | } | ||
257 | buf->vb.state = STATE_PREPARED; | ||
258 | buf->activate = buffer_activate; | ||
259 | |||
260 | return 0; | ||
261 | |||
262 | oops: | ||
263 | DEB_VBI(("error out.\n")); | ||
264 | saa7146_dma_free(dev,buf); | ||
265 | |||
266 | return err; | ||
267 | } | ||
268 | |||
269 | static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | ||
270 | { | ||
271 | int llength,lines; | ||
272 | |||
273 | lines = 16 * 2 ; /* 2 fields */ | ||
274 | llength = vbi_pixel_to_capture; | ||
275 | |||
276 | *size = lines * llength; | ||
277 | *count = 2; | ||
278 | |||
279 | DEB_VBI(("count:%d, size:%d\n",*count,*size)); | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
285 | { | ||
286 | struct file *file = q->priv_data; | ||
287 | struct saa7146_fh *fh = file->private_data; | ||
288 | struct saa7146_dev *dev = fh->dev; | ||
289 | struct saa7146_vv *vv = dev->vv_data; | ||
290 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
291 | |||
292 | DEB_VBI(("vb:%p\n",vb)); | ||
293 | saa7146_buffer_queue(dev,&vv->vbi_q,buf); | ||
294 | } | ||
295 | |||
296 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
297 | { | ||
298 | struct file *file = q->priv_data; | ||
299 | struct saa7146_fh *fh = file->private_data; | ||
300 | struct saa7146_dev *dev = fh->dev; | ||
301 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
302 | |||
303 | DEB_VBI(("vb:%p\n",vb)); | ||
304 | saa7146_dma_free(dev,buf); | ||
305 | } | ||
306 | |||
307 | static struct videobuf_queue_ops vbi_qops = { | ||
308 | .buf_setup = buffer_setup, | ||
309 | .buf_prepare = buffer_prepare, | ||
310 | .buf_queue = buffer_queue, | ||
311 | .buf_release = buffer_release, | ||
312 | }; | ||
313 | |||
314 | /* ------------------------------------------------------------------ */ | ||
315 | |||
316 | static void vbi_stop(struct saa7146_fh *fh, struct file *file) | ||
317 | { | ||
318 | struct saa7146_dev *dev = fh->dev; | ||
319 | struct saa7146_vv *vv = dev->vv_data; | ||
320 | unsigned long flags; | ||
321 | DEB_VBI(("dev:%p, fh:%p\n",dev, fh)); | ||
322 | |||
323 | spin_lock_irqsave(&dev->slock,flags); | ||
324 | |||
325 | /* disable rps1 */ | ||
326 | saa7146_write(dev, MC1, MASK_29); | ||
327 | |||
328 | /* disable rps1 irqs */ | ||
329 | SAA7146_IER_DISABLE(dev, MASK_28); | ||
330 | |||
331 | /* shut down dma 3 transfers */ | ||
332 | saa7146_write(dev, MC1, MASK_20); | ||
333 | |||
334 | if (vv->vbi_q.curr) { | ||
335 | saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE); | ||
336 | } | ||
337 | |||
338 | videobuf_queue_cancel(&fh->vbi_q); | ||
339 | |||
340 | vv->vbi_streaming = NULL; | ||
341 | |||
342 | del_timer(&vv->vbi_q.timeout); | ||
343 | del_timer(&fh->vbi_read_timeout); | ||
344 | |||
345 | spin_unlock_irqrestore(&dev->slock, flags); | ||
346 | } | ||
347 | |||
348 | static void vbi_read_timeout(unsigned long data) | ||
349 | { | ||
350 | struct file *file = (struct file*)data; | ||
351 | struct saa7146_fh *fh = file->private_data; | ||
352 | struct saa7146_dev *dev = fh->dev; | ||
353 | |||
354 | DEB_VBI(("dev:%p, fh:%p\n",dev, fh)); | ||
355 | |||
356 | vbi_stop(fh, file); | ||
357 | } | ||
358 | |||
359 | static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) | ||
360 | { | ||
361 | DEB_VBI(("dev:%p\n",dev)); | ||
362 | |||
363 | INIT_LIST_HEAD(&vv->vbi_q.queue); | ||
364 | |||
365 | init_timer(&vv->vbi_q.timeout); | ||
366 | vv->vbi_q.timeout.function = saa7146_buffer_timeout; | ||
367 | vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q); | ||
368 | vv->vbi_q.dev = dev; | ||
369 | |||
370 | init_waitqueue_head(&vv->vbi_wq); | ||
371 | } | ||
372 | |||
373 | static int vbi_open(struct saa7146_dev *dev, struct file *file) | ||
374 | { | ||
375 | struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; | ||
376 | |||
377 | u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); | ||
378 | int ret = 0; | ||
379 | |||
380 | DEB_VBI(("dev:%p, fh:%p\n",dev,fh)); | ||
381 | |||
382 | ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS); | ||
383 | if (0 == ret) { | ||
384 | DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n")); | ||
385 | return -EBUSY; | ||
386 | } | ||
387 | |||
388 | /* adjust arbitrition control for video dma 3 */ | ||
389 | arbtr_ctrl &= ~0x1f0000; | ||
390 | arbtr_ctrl |= 0x1d0000; | ||
391 | saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); | ||
392 | saa7146_write(dev, MC2, (MASK_04|MASK_20)); | ||
393 | |||
394 | memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt)); | ||
395 | |||
396 | fh->vbi_fmt.sampling_rate = 27000000; | ||
397 | fh->vbi_fmt.offset = 248; /* todo */ | ||
398 | fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture; | ||
399 | fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY; | ||
400 | |||
401 | /* fixme: this only works for PAL */ | ||
402 | fh->vbi_fmt.start[0] = 5; | ||
403 | fh->vbi_fmt.count[0] = 16; | ||
404 | fh->vbi_fmt.start[1] = 312; | ||
405 | fh->vbi_fmt.count[1] = 16; | ||
406 | |||
407 | videobuf_queue_init(&fh->vbi_q, &vbi_qops, | ||
408 | dev->pci, &dev->slock, | ||
409 | V4L2_BUF_TYPE_VBI_CAPTURE, | ||
410 | V4L2_FIELD_SEQ_TB, // FIXME: does this really work? | ||
411 | sizeof(struct saa7146_buf), | ||
412 | file); | ||
413 | init_MUTEX(&fh->vbi_q.lock); | ||
414 | |||
415 | init_timer(&fh->vbi_read_timeout); | ||
416 | fh->vbi_read_timeout.function = vbi_read_timeout; | ||
417 | fh->vbi_read_timeout.data = (unsigned long)file; | ||
418 | |||
419 | /* initialize the brs */ | ||
420 | if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { | ||
421 | saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19)); | ||
422 | } else { | ||
423 | saa7146_write(dev, BRS_CTRL, 0x00000001); | ||
424 | |||
425 | if (0 != (ret = vbi_workaround(dev))) { | ||
426 | DEB_VBI(("vbi workaround failed!\n")); | ||
427 | /* return ret;*/ | ||
428 | } | ||
429 | } | ||
430 | |||
431 | /* upload brs register */ | ||
432 | saa7146_write(dev, MC2, (MASK_08|MASK_24)); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static void vbi_close(struct saa7146_dev *dev, struct file *file) | ||
437 | { | ||
438 | struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; | ||
439 | struct saa7146_vv *vv = dev->vv_data; | ||
440 | DEB_VBI(("dev:%p, fh:%p\n",dev,fh)); | ||
441 | |||
442 | if( fh == vv->vbi_streaming ) { | ||
443 | vbi_stop(fh, file); | ||
444 | } | ||
445 | saa7146_res_free(fh, RESOURCE_DMA3_BRS); | ||
446 | } | ||
447 | |||
448 | static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) | ||
449 | { | ||
450 | struct saa7146_vv *vv = dev->vv_data; | ||
451 | spin_lock(&dev->slock); | ||
452 | |||
453 | if (vv->vbi_q.curr) { | ||
454 | DEB_VBI(("dev:%p, curr:%p\n",dev,vv->vbi_q.curr)); | ||
455 | /* this must be += 2, one count for each field */ | ||
456 | vv->vbi_fieldcount+=2; | ||
457 | vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount; | ||
458 | saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE); | ||
459 | } else { | ||
460 | DEB_VBI(("dev:%p\n",dev)); | ||
461 | } | ||
462 | saa7146_buffer_next(dev,&vv->vbi_q,1); | ||
463 | |||
464 | spin_unlock(&dev->slock); | ||
465 | } | ||
466 | |||
467 | static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
468 | { | ||
469 | struct saa7146_fh *fh = file->private_data; | ||
470 | struct saa7146_dev *dev = fh->dev; | ||
471 | struct saa7146_vv *vv = dev->vv_data; | ||
472 | ssize_t ret = 0; | ||
473 | |||
474 | DEB_VBI(("dev:%p, fh:%p\n",dev,fh)); | ||
475 | |||
476 | if( NULL == vv->vbi_streaming ) { | ||
477 | // fixme: check if dma3 is available | ||
478 | // fixme: activate vbi engine here if necessary. (really?) | ||
479 | vv->vbi_streaming = fh; | ||
480 | } | ||
481 | |||
482 | if( fh != vv->vbi_streaming ) { | ||
483 | DEB_VBI(("open %p is already using vbi capture.",vv->vbi_streaming)); | ||
484 | return -EBUSY; | ||
485 | } | ||
486 | |||
487 | mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); | ||
488 | ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, | ||
489 | file->f_flags & O_NONBLOCK); | ||
490 | /* | ||
491 | printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3)); | ||
492 | printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3)); | ||
493 | printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3)); | ||
494 | printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3)); | ||
495 | printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3)); | ||
496 | printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3)); | ||
497 | printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL)); | ||
498 | */ | ||
499 | return ret; | ||
500 | } | ||
501 | |||
502 | struct saa7146_use_ops saa7146_vbi_uops = { | ||
503 | .init = vbi_init, | ||
504 | .open = vbi_open, | ||
505 | .release = vbi_close, | ||
506 | .irq_done = vbi_irq_done, | ||
507 | .read = vbi_read, | ||
508 | }; | ||
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c new file mode 100644 index 000000000000..8dd4d15ca36d --- /dev/null +++ b/drivers/media/common/saa7146_video.c | |||
@@ -0,0 +1,1509 @@ | |||
1 | #include <media/saa7146_vv.h> | ||
2 | |||
3 | static int max_memory = 32; | ||
4 | |||
5 | module_param(max_memory, int, 0644); | ||
6 | MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)"); | ||
7 | |||
8 | #define IS_CAPTURE_ACTIVE(fh) \ | ||
9 | (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh)) | ||
10 | |||
11 | #define IS_OVERLAY_ACTIVE(fh) \ | ||
12 | (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh)) | ||
13 | |||
14 | /* format descriptions for capture and preview */ | ||
15 | static struct saa7146_format formats[] = { | ||
16 | { | ||
17 | .name = "RGB-8 (3-3-2)", | ||
18 | .pixelformat = V4L2_PIX_FMT_RGB332, | ||
19 | .trans = RGB08_COMPOSED, | ||
20 | .depth = 8, | ||
21 | .flags = 0, | ||
22 | }, { | ||
23 | .name = "RGB-16 (5/B-6/G-5/R)", | ||
24 | .pixelformat = V4L2_PIX_FMT_RGB565, | ||
25 | .trans = RGB16_COMPOSED, | ||
26 | .depth = 16, | ||
27 | .flags = 0, | ||
28 | }, { | ||
29 | .name = "RGB-24 (B-G-R)", | ||
30 | .pixelformat = V4L2_PIX_FMT_BGR24, | ||
31 | .trans = RGB24_COMPOSED, | ||
32 | .depth = 24, | ||
33 | .flags = 0, | ||
34 | }, { | ||
35 | .name = "RGB-32 (B-G-R)", | ||
36 | .pixelformat = V4L2_PIX_FMT_BGR32, | ||
37 | .trans = RGB32_COMPOSED, | ||
38 | .depth = 32, | ||
39 | .flags = 0, | ||
40 | }, { | ||
41 | .name = "RGB-32 (R-G-B)", | ||
42 | .pixelformat = V4L2_PIX_FMT_RGB32, | ||
43 | .trans = RGB32_COMPOSED, | ||
44 | .depth = 32, | ||
45 | .flags = 0, | ||
46 | .swap = 0x2, | ||
47 | }, { | ||
48 | .name = "Greyscale-8", | ||
49 | .pixelformat = V4L2_PIX_FMT_GREY, | ||
50 | .trans = Y8, | ||
51 | .depth = 8, | ||
52 | .flags = 0, | ||
53 | }, { | ||
54 | .name = "YUV 4:2:2 planar (Y-Cb-Cr)", | ||
55 | .pixelformat = V4L2_PIX_FMT_YUV422P, | ||
56 | .trans = YUV422_DECOMPOSED, | ||
57 | .depth = 16, | ||
58 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | ||
59 | }, { | ||
60 | .name = "YVU 4:2:0 planar (Y-Cb-Cr)", | ||
61 | .pixelformat = V4L2_PIX_FMT_YVU420, | ||
62 | .trans = YUV420_DECOMPOSED, | ||
63 | .depth = 12, | ||
64 | .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR, | ||
65 | }, { | ||
66 | .name = "YUV 4:2:0 planar (Y-Cb-Cr)", | ||
67 | .pixelformat = V4L2_PIX_FMT_YUV420, | ||
68 | .trans = YUV420_DECOMPOSED, | ||
69 | .depth = 12, | ||
70 | .flags = FORMAT_IS_PLANAR, | ||
71 | }, { | ||
72 | .name = "YUV 4:2:2 (U-Y-V-Y)", | ||
73 | .pixelformat = V4L2_PIX_FMT_UYVY, | ||
74 | .trans = YUV422_COMPOSED, | ||
75 | .depth = 16, | ||
76 | .flags = 0, | ||
77 | } | ||
78 | }; | ||
79 | |||
80 | /* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps. | ||
81 | due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped | ||
82 | (like V4L2_PIX_FMT_YUYV) ... 8-( */ | ||
83 | |||
84 | static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format); | ||
85 | |||
86 | struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc) | ||
87 | { | ||
88 | int i, j = NUM_FORMATS; | ||
89 | |||
90 | for (i = 0; i < j; i++) { | ||
91 | if (formats[i].pixelformat == fourcc) { | ||
92 | return formats+i; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | DEB_D(("unknown pixelformat:'%4.4s'\n",(char *)&fourcc)); | ||
97 | return NULL; | ||
98 | } | ||
99 | |||
100 | static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f) | ||
101 | { | ||
102 | struct saa7146_dev *dev = fh->dev; | ||
103 | DEB_EE(("dev:%p, fh:%p\n",dev,fh)); | ||
104 | |||
105 | switch (f->type) { | ||
106 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
107 | f->fmt.pix = fh->video_fmt; | ||
108 | return 0; | ||
109 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: | ||
110 | f->fmt.win = fh->ov.win; | ||
111 | return 0; | ||
112 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
113 | { | ||
114 | f->fmt.vbi = fh->vbi_fmt; | ||
115 | return 0; | ||
116 | } | ||
117 | default: | ||
118 | DEB_D(("invalid format type '%d'.\n",f->type)); | ||
119 | return -EINVAL; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static int try_win(struct saa7146_dev *dev, struct v4l2_window *win) | ||
124 | { | ||
125 | struct saa7146_vv *vv = dev->vv_data; | ||
126 | enum v4l2_field field; | ||
127 | int maxw, maxh; | ||
128 | |||
129 | DEB_EE(("dev:%p\n",dev)); | ||
130 | |||
131 | if (NULL == vv->ov_fb.base) { | ||
132 | DEB_D(("no fb base set.\n")); | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | if (NULL == vv->ov_fmt) { | ||
136 | DEB_D(("no fb fmt set.\n")); | ||
137 | return -EINVAL; | ||
138 | } | ||
139 | if (win->w.width < 48 || win->w.height < 32) { | ||
140 | DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height)); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | if (win->clipcount > 16) { | ||
144 | DEB_D(("clipcount too big.\n")); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | field = win->field; | ||
149 | maxw = vv->standard->h_max_out; | ||
150 | maxh = vv->standard->v_max_out; | ||
151 | |||
152 | if (V4L2_FIELD_ANY == field) { | ||
153 | field = (win->w.height > maxh/2) | ||
154 | ? V4L2_FIELD_INTERLACED | ||
155 | : V4L2_FIELD_TOP; | ||
156 | } | ||
157 | switch (field) { | ||
158 | case V4L2_FIELD_TOP: | ||
159 | case V4L2_FIELD_BOTTOM: | ||
160 | case V4L2_FIELD_ALTERNATE: | ||
161 | maxh = maxh / 2; | ||
162 | break; | ||
163 | case V4L2_FIELD_INTERLACED: | ||
164 | break; | ||
165 | default: { | ||
166 | DEB_D(("no known field mode '%d'.\n",field)); | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | win->field = field; | ||
172 | if (win->w.width > maxw) | ||
173 | win->w.width = maxw; | ||
174 | if (win->w.height > maxh) | ||
175 | win->w.height = maxh; | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int try_fmt(struct saa7146_fh *fh, struct v4l2_format *f) | ||
181 | { | ||
182 | struct saa7146_dev *dev = fh->dev; | ||
183 | struct saa7146_vv *vv = dev->vv_data; | ||
184 | int err; | ||
185 | |||
186 | switch (f->type) { | ||
187 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
188 | { | ||
189 | struct saa7146_format *fmt; | ||
190 | enum v4l2_field field; | ||
191 | int maxw, maxh; | ||
192 | int calc_bpl; | ||
193 | |||
194 | DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh)); | ||
195 | |||
196 | fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat); | ||
197 | if (NULL == fmt) { | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | |||
201 | field = f->fmt.pix.field; | ||
202 | maxw = vv->standard->h_max_out; | ||
203 | maxh = vv->standard->v_max_out; | ||
204 | |||
205 | if (V4L2_FIELD_ANY == field) { | ||
206 | field = (f->fmt.pix.height > maxh/2) | ||
207 | ? V4L2_FIELD_INTERLACED | ||
208 | : V4L2_FIELD_BOTTOM; | ||
209 | } | ||
210 | switch (field) { | ||
211 | case V4L2_FIELD_ALTERNATE: { | ||
212 | vv->last_field = V4L2_FIELD_TOP; | ||
213 | maxh = maxh / 2; | ||
214 | break; | ||
215 | } | ||
216 | case V4L2_FIELD_TOP: | ||
217 | case V4L2_FIELD_BOTTOM: | ||
218 | vv->last_field = V4L2_FIELD_INTERLACED; | ||
219 | maxh = maxh / 2; | ||
220 | break; | ||
221 | case V4L2_FIELD_INTERLACED: | ||
222 | vv->last_field = V4L2_FIELD_INTERLACED; | ||
223 | break; | ||
224 | default: { | ||
225 | DEB_D(("no known field mode '%d'.\n",field)); | ||
226 | return -EINVAL; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | f->fmt.pix.field = field; | ||
231 | if (f->fmt.pix.width > maxw) | ||
232 | f->fmt.pix.width = maxw; | ||
233 | if (f->fmt.pix.height > maxh) | ||
234 | f->fmt.pix.height = maxh; | ||
235 | |||
236 | calc_bpl = (f->fmt.pix.width * fmt->depth)/8; | ||
237 | |||
238 | if (f->fmt.pix.bytesperline < calc_bpl) | ||
239 | f->fmt.pix.bytesperline = calc_bpl; | ||
240 | |||
241 | if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */ | ||
242 | f->fmt.pix.bytesperline = calc_bpl; | ||
243 | |||
244 | f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height; | ||
245 | DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage)); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: | ||
250 | DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh)); | ||
251 | err = try_win(dev,&f->fmt.win); | ||
252 | if (0 != err) { | ||
253 | return err; | ||
254 | } | ||
255 | return 0; | ||
256 | default: | ||
257 | DEB_EE(("unknown format type '%d'\n",f->type)); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | int saa7146_start_preview(struct saa7146_fh *fh) | ||
263 | { | ||
264 | struct saa7146_dev *dev = fh->dev; | ||
265 | struct saa7146_vv *vv = dev->vv_data; | ||
266 | int ret = 0, err = 0; | ||
267 | |||
268 | DEB_EE(("dev:%p, fh:%p\n",dev,fh)); | ||
269 | |||
270 | /* check if we have overlay informations */ | ||
271 | if( NULL == fh->ov.fh ) { | ||
272 | DEB_D(("no overlay data available. try S_FMT first.\n")); | ||
273 | return -EAGAIN; | ||
274 | } | ||
275 | |||
276 | /* check if streaming capture is running */ | ||
277 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
278 | DEB_D(("streaming capture is active.\n")); | ||
279 | return -EBUSY; | ||
280 | } | ||
281 | |||
282 | /* check if overlay is running */ | ||
283 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
284 | if (vv->video_fh == fh) { | ||
285 | DEB_D(("overlay is already active.\n")); | ||
286 | return 0; | ||
287 | } | ||
288 | DEB_D(("overlay is already active in another open.\n")); | ||
289 | return -EBUSY; | ||
290 | } | ||
291 | |||
292 | if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) { | ||
293 | DEB_D(("cannot get necessary overlay resources\n")); | ||
294 | return -EBUSY; | ||
295 | } | ||
296 | |||
297 | err = try_win(dev,&fh->ov.win); | ||
298 | if (0 != err) { | ||
299 | saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | ||
300 | return -EBUSY; | ||
301 | } | ||
302 | |||
303 | vv->ov_data = &fh->ov; | ||
304 | |||
305 | DEB_D(("%dx%d+%d+%d %s field=%s\n", | ||
306 | fh->ov.win.w.width,fh->ov.win.w.height, | ||
307 | fh->ov.win.w.left,fh->ov.win.w.top, | ||
308 | vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field])); | ||
309 | |||
310 | if (0 != (ret = saa7146_enable_overlay(fh))) { | ||
311 | DEB_D(("enabling overlay failed: %d\n",ret)); | ||
312 | saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | ||
313 | return ret; | ||
314 | } | ||
315 | |||
316 | vv->video_status = STATUS_OVERLAY; | ||
317 | vv->video_fh = fh; | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | int saa7146_stop_preview(struct saa7146_fh *fh) | ||
323 | { | ||
324 | struct saa7146_dev *dev = fh->dev; | ||
325 | struct saa7146_vv *vv = dev->vv_data; | ||
326 | |||
327 | DEB_EE(("dev:%p, fh:%p\n",dev,fh)); | ||
328 | |||
329 | /* check if streaming capture is running */ | ||
330 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
331 | DEB_D(("streaming capture is active.\n")); | ||
332 | return -EBUSY; | ||
333 | } | ||
334 | |||
335 | /* check if overlay is running at all */ | ||
336 | if ((vv->video_status & STATUS_OVERLAY) == 0) { | ||
337 | DEB_D(("no active overlay.\n")); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | if (vv->video_fh != fh) { | ||
342 | DEB_D(("overlay is active, but in another open.\n")); | ||
343 | return -EBUSY; | ||
344 | } | ||
345 | |||
346 | vv->video_status = 0; | ||
347 | vv->video_fh = NULL; | ||
348 | |||
349 | saa7146_disable_overlay(fh); | ||
350 | |||
351 | saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f) | ||
357 | { | ||
358 | struct saa7146_dev *dev = fh->dev; | ||
359 | struct saa7146_vv *vv = dev->vv_data; | ||
360 | |||
361 | int err; | ||
362 | |||
363 | switch (f->type) { | ||
364 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
365 | DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh)); | ||
366 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
367 | DEB_EE(("streaming capture is active\n")); | ||
368 | return -EBUSY; | ||
369 | } | ||
370 | err = try_fmt(fh,f); | ||
371 | if (0 != err) | ||
372 | return err; | ||
373 | fh->video_fmt = f->fmt.pix; | ||
374 | DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat)); | ||
375 | return 0; | ||
376 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: | ||
377 | DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh)); | ||
378 | err = try_win(dev,&f->fmt.win); | ||
379 | if (0 != err) | ||
380 | return err; | ||
381 | down(&dev->lock); | ||
382 | fh->ov.win = f->fmt.win; | ||
383 | fh->ov.nclips = f->fmt.win.clipcount; | ||
384 | if (fh->ov.nclips > 16) | ||
385 | fh->ov.nclips = 16; | ||
386 | if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) { | ||
387 | up(&dev->lock); | ||
388 | return -EFAULT; | ||
389 | } | ||
390 | |||
391 | /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ | ||
392 | fh->ov.fh = fh; | ||
393 | |||
394 | up(&dev->lock); | ||
395 | |||
396 | /* check if our current overlay is active */ | ||
397 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
398 | saa7146_stop_preview(fh); | ||
399 | saa7146_start_preview(fh); | ||
400 | } | ||
401 | return 0; | ||
402 | default: | ||
403 | DEB_D(("unknown format type '%d'\n",f->type)); | ||
404 | return -EINVAL; | ||
405 | } | ||
406 | } | ||
407 | |||
408 | /********************************************************************************/ | ||
409 | /* device controls */ | ||
410 | |||
411 | static struct v4l2_queryctrl controls[] = { | ||
412 | { | ||
413 | .id = V4L2_CID_BRIGHTNESS, | ||
414 | .name = "Brightness", | ||
415 | .minimum = 0, | ||
416 | .maximum = 255, | ||
417 | .step = 1, | ||
418 | .default_value = 128, | ||
419 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
420 | },{ | ||
421 | .id = V4L2_CID_CONTRAST, | ||
422 | .name = "Contrast", | ||
423 | .minimum = 0, | ||
424 | .maximum = 127, | ||
425 | .step = 1, | ||
426 | .default_value = 64, | ||
427 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
428 | },{ | ||
429 | .id = V4L2_CID_SATURATION, | ||
430 | .name = "Saturation", | ||
431 | .minimum = 0, | ||
432 | .maximum = 127, | ||
433 | .step = 1, | ||
434 | .default_value = 64, | ||
435 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
436 | },{ | ||
437 | .id = V4L2_CID_VFLIP, | ||
438 | .name = "Vertical flip", | ||
439 | .minimum = 0, | ||
440 | .maximum = 1, | ||
441 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
442 | },{ | ||
443 | .id = V4L2_CID_HFLIP, | ||
444 | .name = "Horizontal flip", | ||
445 | .minimum = 0, | ||
446 | .maximum = 1, | ||
447 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
448 | }, | ||
449 | }; | ||
450 | static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl); | ||
451 | |||
452 | #define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0) | ||
453 | |||
454 | static struct v4l2_queryctrl* ctrl_by_id(int id) | ||
455 | { | ||
456 | int i; | ||
457 | |||
458 | for (i = 0; i < NUM_CONTROLS; i++) | ||
459 | if (controls[i].id == id) | ||
460 | return controls+i; | ||
461 | return NULL; | ||
462 | } | ||
463 | |||
464 | static int get_control(struct saa7146_fh *fh, struct v4l2_control *c) | ||
465 | { | ||
466 | struct saa7146_dev *dev = fh->dev; | ||
467 | struct saa7146_vv *vv = dev->vv_data; | ||
468 | |||
469 | const struct v4l2_queryctrl* ctrl; | ||
470 | u32 value = 0; | ||
471 | |||
472 | ctrl = ctrl_by_id(c->id); | ||
473 | if (NULL == ctrl) | ||
474 | return -EINVAL; | ||
475 | switch (c->id) { | ||
476 | case V4L2_CID_BRIGHTNESS: | ||
477 | value = saa7146_read(dev, BCS_CTRL); | ||
478 | c->value = 0xff & (value >> 24); | ||
479 | DEB_D(("V4L2_CID_BRIGHTNESS: %d\n",c->value)); | ||
480 | break; | ||
481 | case V4L2_CID_CONTRAST: | ||
482 | value = saa7146_read(dev, BCS_CTRL); | ||
483 | c->value = 0x7f & (value >> 16); | ||
484 | DEB_D(("V4L2_CID_CONTRAST: %d\n",c->value)); | ||
485 | break; | ||
486 | case V4L2_CID_SATURATION: | ||
487 | value = saa7146_read(dev, BCS_CTRL); | ||
488 | c->value = 0x7f & (value >> 0); | ||
489 | DEB_D(("V4L2_CID_SATURATION: %d\n",c->value)); | ||
490 | break; | ||
491 | case V4L2_CID_VFLIP: | ||
492 | c->value = vv->vflip; | ||
493 | DEB_D(("V4L2_CID_VFLIP: %d\n",c->value)); | ||
494 | break; | ||
495 | case V4L2_CID_HFLIP: | ||
496 | c->value = vv->hflip; | ||
497 | DEB_D(("V4L2_CID_HFLIP: %d\n",c->value)); | ||
498 | break; | ||
499 | default: | ||
500 | return -EINVAL; | ||
501 | } | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static int set_control(struct saa7146_fh *fh, struct v4l2_control *c) | ||
507 | { | ||
508 | struct saa7146_dev *dev = fh->dev; | ||
509 | struct saa7146_vv *vv = dev->vv_data; | ||
510 | |||
511 | const struct v4l2_queryctrl* ctrl; | ||
512 | |||
513 | ctrl = ctrl_by_id(c->id); | ||
514 | if (NULL == ctrl) { | ||
515 | DEB_D(("unknown control %d\n",c->id)); | ||
516 | return -EINVAL; | ||
517 | } | ||
518 | |||
519 | down(&dev->lock); | ||
520 | |||
521 | switch (ctrl->type) { | ||
522 | case V4L2_CTRL_TYPE_BOOLEAN: | ||
523 | case V4L2_CTRL_TYPE_MENU: | ||
524 | case V4L2_CTRL_TYPE_INTEGER: | ||
525 | if (c->value < ctrl->minimum) | ||
526 | c->value = ctrl->minimum; | ||
527 | if (c->value > ctrl->maximum) | ||
528 | c->value = ctrl->maximum; | ||
529 | break; | ||
530 | default: | ||
531 | /* nothing */; | ||
532 | }; | ||
533 | |||
534 | switch (c->id) { | ||
535 | case V4L2_CID_BRIGHTNESS: { | ||
536 | u32 value = saa7146_read(dev, BCS_CTRL); | ||
537 | value &= 0x00ffffff; | ||
538 | value |= (c->value << 24); | ||
539 | saa7146_write(dev, BCS_CTRL, value); | ||
540 | saa7146_write(dev, MC2, MASK_22 | MASK_06 ); | ||
541 | break; | ||
542 | } | ||
543 | case V4L2_CID_CONTRAST: { | ||
544 | u32 value = saa7146_read(dev, BCS_CTRL); | ||
545 | value &= 0xff00ffff; | ||
546 | value |= (c->value << 16); | ||
547 | saa7146_write(dev, BCS_CTRL, value); | ||
548 | saa7146_write(dev, MC2, MASK_22 | MASK_06 ); | ||
549 | break; | ||
550 | } | ||
551 | case V4L2_CID_SATURATION: { | ||
552 | u32 value = saa7146_read(dev, BCS_CTRL); | ||
553 | value &= 0xffffff00; | ||
554 | value |= (c->value << 0); | ||
555 | saa7146_write(dev, BCS_CTRL, value); | ||
556 | saa7146_write(dev, MC2, MASK_22 | MASK_06 ); | ||
557 | break; | ||
558 | } | ||
559 | case V4L2_CID_HFLIP: | ||
560 | /* fixme: we can support changing VFLIP and HFLIP here... */ | ||
561 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
562 | DEB_D(("V4L2_CID_HFLIP while active capture.\n")); | ||
563 | up(&dev->lock); | ||
564 | return -EINVAL; | ||
565 | } | ||
566 | vv->hflip = c->value; | ||
567 | break; | ||
568 | case V4L2_CID_VFLIP: | ||
569 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
570 | DEB_D(("V4L2_CID_VFLIP while active capture.\n")); | ||
571 | up(&dev->lock); | ||
572 | return -EINVAL; | ||
573 | } | ||
574 | vv->vflip = c->value; | ||
575 | break; | ||
576 | default: { | ||
577 | return -EINVAL; | ||
578 | } | ||
579 | } | ||
580 | up(&dev->lock); | ||
581 | |||
582 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
583 | saa7146_stop_preview(fh); | ||
584 | saa7146_start_preview(fh); | ||
585 | } | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | /********************************************************************************/ | ||
590 | /* common pagetable functions */ | ||
591 | |||
592 | static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf) | ||
593 | { | ||
594 | struct pci_dev *pci = dev->pci; | ||
595 | struct scatterlist *list = buf->vb.dma.sglist; | ||
596 | int length = buf->vb.dma.sglen; | ||
597 | struct saa7146_format *sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); | ||
598 | |||
599 | DEB_EE(("dev:%p, buf:%p, sg_len:%d\n",dev,buf,length)); | ||
600 | |||
601 | if( 0 != IS_PLANAR(sfmt->trans)) { | ||
602 | struct saa7146_pgtable *pt1 = &buf->pt[0]; | ||
603 | struct saa7146_pgtable *pt2 = &buf->pt[1]; | ||
604 | struct saa7146_pgtable *pt3 = &buf->pt[2]; | ||
605 | u32 *ptr1, *ptr2, *ptr3; | ||
606 | u32 fill; | ||
607 | |||
608 | int size = buf->fmt->width*buf->fmt->height; | ||
609 | int i,p,m1,m2,m3,o1,o2; | ||
610 | |||
611 | switch( sfmt->depth ) { | ||
612 | case 12: { | ||
613 | /* create some offsets inside the page table */ | ||
614 | m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; | ||
615 | m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1; | ||
616 | m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; | ||
617 | o1 = size%PAGE_SIZE; | ||
618 | o2 = (size+(size/4))%PAGE_SIZE; | ||
619 | DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2)); | ||
620 | break; | ||
621 | } | ||
622 | case 16: { | ||
623 | /* create some offsets inside the page table */ | ||
624 | m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1; | ||
625 | m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1; | ||
626 | m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1; | ||
627 | o1 = size%PAGE_SIZE; | ||
628 | o2 = (size+(size/2))%PAGE_SIZE; | ||
629 | DEB_CAP(("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",size,m1,m2,m3,o1,o2)); | ||
630 | break; | ||
631 | } | ||
632 | default: { | ||
633 | return -1; | ||
634 | } | ||
635 | } | ||
636 | |||
637 | ptr1 = pt1->cpu; | ||
638 | ptr2 = pt2->cpu; | ||
639 | ptr3 = pt3->cpu; | ||
640 | |||
641 | /* walk all pages, copy all page addresses to ptr1 */ | ||
642 | for (i = 0; i < length; i++, list++) { | ||
643 | for (p = 0; p * 4096 < list->length; p++, ptr1++) { | ||
644 | *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset); | ||
645 | } | ||
646 | } | ||
647 | /* | ||
648 | ptr1 = pt1->cpu; | ||
649 | for(j=0;j<40;j++) { | ||
650 | printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); | ||
651 | } | ||
652 | */ | ||
653 | |||
654 | /* if we have a user buffer, the first page may not be | ||
655 | aligned to a page boundary. */ | ||
656 | pt1->offset = buf->vb.dma.sglist->offset; | ||
657 | pt2->offset = pt1->offset+o1; | ||
658 | pt3->offset = pt1->offset+o2; | ||
659 | |||
660 | /* create video-dma2 page table */ | ||
661 | ptr1 = pt1->cpu; | ||
662 | for(i = m1; i <= m2 ; i++, ptr2++) { | ||
663 | *ptr2 = ptr1[i]; | ||
664 | } | ||
665 | fill = *(ptr2-1); | ||
666 | for(;i<1024;i++,ptr2++) { | ||
667 | *ptr2 = fill; | ||
668 | } | ||
669 | /* create video-dma3 page table */ | ||
670 | ptr1 = pt1->cpu; | ||
671 | for(i = m2; i <= m3; i++,ptr3++) { | ||
672 | *ptr3 = ptr1[i]; | ||
673 | } | ||
674 | fill = *(ptr3-1); | ||
675 | for(;i<1024;i++,ptr3++) { | ||
676 | *ptr3 = fill; | ||
677 | } | ||
678 | /* finally: finish up video-dma1 page table */ | ||
679 | ptr1 = pt1->cpu+m1; | ||
680 | fill = pt1->cpu[m1]; | ||
681 | for(i=m1;i<1024;i++,ptr1++) { | ||
682 | *ptr1 = fill; | ||
683 | } | ||
684 | /* | ||
685 | ptr1 = pt1->cpu; | ||
686 | ptr2 = pt2->cpu; | ||
687 | ptr3 = pt3->cpu; | ||
688 | for(j=0;j<40;j++) { | ||
689 | printk("ptr1 %d: 0x%08x\n",j,ptr1[j]); | ||
690 | } | ||
691 | for(j=0;j<40;j++) { | ||
692 | printk("ptr2 %d: 0x%08x\n",j,ptr2[j]); | ||
693 | } | ||
694 | for(j=0;j<40;j++) { | ||
695 | printk("ptr3 %d: 0x%08x\n",j,ptr3[j]); | ||
696 | } | ||
697 | */ | ||
698 | } else { | ||
699 | struct saa7146_pgtable *pt = &buf->pt[0]; | ||
700 | return saa7146_pgtable_build_single(pci, pt, list, length); | ||
701 | } | ||
702 | |||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | |||
707 | /********************************************************************************/ | ||
708 | /* file operations */ | ||
709 | |||
710 | static int video_begin(struct saa7146_fh *fh) | ||
711 | { | ||
712 | struct saa7146_dev *dev = fh->dev; | ||
713 | struct saa7146_vv *vv = dev->vv_data; | ||
714 | struct saa7146_format *fmt = NULL; | ||
715 | unsigned int resource; | ||
716 | int ret = 0, err = 0; | ||
717 | |||
718 | DEB_EE(("dev:%p, fh:%p\n",dev,fh)); | ||
719 | |||
720 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | ||
721 | if (vv->video_fh == fh) { | ||
722 | DEB_S(("already capturing.\n")); | ||
723 | return 0; | ||
724 | } | ||
725 | DEB_S(("already capturing in another open.\n")); | ||
726 | return -EBUSY; | ||
727 | } | ||
728 | |||
729 | if ((vv->video_status & STATUS_OVERLAY) != 0) { | ||
730 | DEB_S(("warning: suspending overlay video for streaming capture.\n")); | ||
731 | vv->ov_suspend = vv->video_fh; | ||
732 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | ||
733 | if (0 != err) { | ||
734 | DEB_D(("suspending video failed. aborting\n")); | ||
735 | return err; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat); | ||
740 | /* we need to have a valid format set here */ | ||
741 | BUG_ON(NULL == fmt); | ||
742 | |||
743 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | ||
744 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; | ||
745 | } else { | ||
746 | resource = RESOURCE_DMA1_HPS; | ||
747 | } | ||
748 | |||
749 | ret = saa7146_res_get(fh, resource); | ||
750 | if (0 == ret) { | ||
751 | DEB_S(("cannot get capture resource %d\n",resource)); | ||
752 | if (vv->ov_suspend != NULL) { | ||
753 | saa7146_start_preview(vv->ov_suspend); | ||
754 | vv->ov_suspend = NULL; | ||
755 | } | ||
756 | return -EBUSY; | ||
757 | } | ||
758 | |||
759 | /* clear out beginning of streaming bit (rps register 0)*/ | ||
760 | saa7146_write(dev, MC2, MASK_27 ); | ||
761 | |||
762 | /* enable rps0 irqs */ | ||
763 | SAA7146_IER_ENABLE(dev, MASK_27); | ||
764 | |||
765 | vv->video_fh = fh; | ||
766 | vv->video_status = STATUS_CAPTURE; | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int video_end(struct saa7146_fh *fh, struct file *file) | ||
772 | { | ||
773 | struct saa7146_dev *dev = fh->dev; | ||
774 | struct saa7146_vv *vv = dev->vv_data; | ||
775 | struct saa7146_format *fmt = NULL; | ||
776 | unsigned long flags; | ||
777 | unsigned int resource; | ||
778 | u32 dmas = 0; | ||
779 | DEB_EE(("dev:%p, fh:%p\n",dev,fh)); | ||
780 | |||
781 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | ||
782 | DEB_S(("not capturing.\n")); | ||
783 | return 0; | ||
784 | } | ||
785 | |||
786 | if (vv->video_fh != fh) { | ||
787 | DEB_S(("capturing, but in another open.\n")); | ||
788 | return -EBUSY; | ||
789 | } | ||
790 | |||
791 | fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat); | ||
792 | /* we need to have a valid format set here */ | ||
793 | BUG_ON(NULL == fmt); | ||
794 | |||
795 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | ||
796 | resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS; | ||
797 | dmas = MASK_22 | MASK_21 | MASK_20; | ||
798 | } else { | ||
799 | resource = RESOURCE_DMA1_HPS; | ||
800 | dmas = MASK_22; | ||
801 | } | ||
802 | spin_lock_irqsave(&dev->slock,flags); | ||
803 | |||
804 | /* disable rps0 */ | ||
805 | saa7146_write(dev, MC1, MASK_28); | ||
806 | |||
807 | /* disable rps0 irqs */ | ||
808 | SAA7146_IER_DISABLE(dev, MASK_27); | ||
809 | |||
810 | /* shut down all used video dma transfers */ | ||
811 | saa7146_write(dev, MC1, dmas); | ||
812 | |||
813 | spin_unlock_irqrestore(&dev->slock, flags); | ||
814 | |||
815 | vv->video_fh = NULL; | ||
816 | vv->video_status = 0; | ||
817 | |||
818 | saa7146_res_free(fh, resource); | ||
819 | |||
820 | if (vv->ov_suspend != NULL) { | ||
821 | saa7146_start_preview(vv->ov_suspend); | ||
822 | vv->ov_suspend = NULL; | ||
823 | } | ||
824 | |||
825 | return 0; | ||
826 | } | ||
827 | |||
828 | /* | ||
829 | * This function is _not_ called directly, but from | ||
830 | * video_generic_ioctl (and maybe others). userspace | ||
831 | * copying is done already, arg is a kernel pointer. | ||
832 | */ | ||
833 | |||
834 | int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) | ||
835 | { | ||
836 | struct saa7146_fh *fh = file->private_data; | ||
837 | struct saa7146_dev *dev = fh->dev; | ||
838 | struct saa7146_vv *vv = dev->vv_data; | ||
839 | |||
840 | int err = 0, result = 0, ee = 0; | ||
841 | |||
842 | struct saa7146_use_ops *ops; | ||
843 | struct videobuf_queue *q; | ||
844 | |||
845 | /* check if extension handles the command */ | ||
846 | for(ee = 0; dev->ext_vv_data->ioctls[ee].flags != 0; ee++) { | ||
847 | if( cmd == dev->ext_vv_data->ioctls[ee].cmd ) | ||
848 | break; | ||
849 | } | ||
850 | |||
851 | if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) { | ||
852 | DEB_D(("extension handles ioctl exclusive.\n")); | ||
853 | result = dev->ext_vv_data->ioctl(fh, cmd, arg); | ||
854 | return result; | ||
855 | } | ||
856 | if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_BEFORE) ) { | ||
857 | DEB_D(("extension handles ioctl before.\n")); | ||
858 | result = dev->ext_vv_data->ioctl(fh, cmd, arg); | ||
859 | if( -EAGAIN != result ) { | ||
860 | return result; | ||
861 | } | ||
862 | } | ||
863 | |||
864 | /* fixme: add handle "after" case (is it still needed?) */ | ||
865 | |||
866 | switch (fh->type) { | ||
867 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: { | ||
868 | ops = &saa7146_video_uops; | ||
869 | q = &fh->video_q; | ||
870 | break; | ||
871 | } | ||
872 | case V4L2_BUF_TYPE_VBI_CAPTURE: { | ||
873 | ops = &saa7146_vbi_uops; | ||
874 | q = &fh->vbi_q; | ||
875 | break; | ||
876 | } | ||
877 | default: | ||
878 | BUG(); | ||
879 | return 0; | ||
880 | } | ||
881 | |||
882 | switch (cmd) { | ||
883 | case VIDIOC_QUERYCAP: | ||
884 | { | ||
885 | struct v4l2_capability *cap = arg; | ||
886 | memset(cap,0,sizeof(*cap)); | ||
887 | |||
888 | DEB_EE(("VIDIOC_QUERYCAP\n")); | ||
889 | |||
890 | strcpy(cap->driver, "saa7146 v4l2"); | ||
891 | strlcpy(cap->card, dev->ext->name, sizeof(cap->card)); | ||
892 | sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); | ||
893 | cap->version = SAA7146_VERSION_CODE; | ||
894 | cap->capabilities = | ||
895 | V4L2_CAP_VIDEO_CAPTURE | | ||
896 | V4L2_CAP_VIDEO_OVERLAY | | ||
897 | V4L2_CAP_READWRITE | | ||
898 | V4L2_CAP_STREAMING; | ||
899 | cap->capabilities |= dev->ext_vv_data->capabilities; | ||
900 | return 0; | ||
901 | } | ||
902 | case VIDIOC_G_FBUF: | ||
903 | { | ||
904 | struct v4l2_framebuffer *fb = arg; | ||
905 | |||
906 | DEB_EE(("VIDIOC_G_FBUF\n")); | ||
907 | |||
908 | *fb = vv->ov_fb; | ||
909 | fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; | ||
910 | return 0; | ||
911 | } | ||
912 | case VIDIOC_S_FBUF: | ||
913 | { | ||
914 | struct v4l2_framebuffer *fb = arg; | ||
915 | struct saa7146_format *fmt; | ||
916 | |||
917 | DEB_EE(("VIDIOC_S_FBUF\n")); | ||
918 | |||
919 | if(!capable(CAP_SYS_ADMIN) && | ||
920 | !capable(CAP_SYS_RAWIO)) | ||
921 | return -EPERM; | ||
922 | |||
923 | /* check args */ | ||
924 | fmt = format_by_fourcc(dev,fb->fmt.pixelformat); | ||
925 | if (NULL == fmt) { | ||
926 | return -EINVAL; | ||
927 | } | ||
928 | |||
929 | /* planar formats are not allowed for overlay video, clipping and video dma would clash */ | ||
930 | if (0 != (fmt->flags & FORMAT_IS_PLANAR)) { | ||
931 | DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat)); | ||
932 | } | ||
933 | |||
934 | /* check if overlay is running */ | ||
935 | if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
936 | if (vv->video_fh != fh) { | ||
937 | DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n")); | ||
938 | return -EBUSY; | ||
939 | } | ||
940 | } | ||
941 | |||
942 | down(&dev->lock); | ||
943 | |||
944 | /* ok, accept it */ | ||
945 | vv->ov_fb = *fb; | ||
946 | vv->ov_fmt = fmt; | ||
947 | if (0 == vv->ov_fb.fmt.bytesperline) | ||
948 | vv->ov_fb.fmt.bytesperline = | ||
949 | vv->ov_fb.fmt.width*fmt->depth/8; | ||
950 | |||
951 | up(&dev->lock); | ||
952 | |||
953 | return 0; | ||
954 | } | ||
955 | case VIDIOC_ENUM_FMT: | ||
956 | { | ||
957 | struct v4l2_fmtdesc *f = arg; | ||
958 | int index; | ||
959 | |||
960 | switch (f->type) { | ||
961 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
962 | case V4L2_BUF_TYPE_VIDEO_OVERLAY: { | ||
963 | index = f->index; | ||
964 | if (index < 0 || index >= NUM_FORMATS) { | ||
965 | return -EINVAL; | ||
966 | } | ||
967 | memset(f,0,sizeof(*f)); | ||
968 | f->index = index; | ||
969 | strlcpy(f->description,formats[index].name,sizeof(f->description)); | ||
970 | f->pixelformat = formats[index].pixelformat; | ||
971 | break; | ||
972 | } | ||
973 | default: | ||
974 | return -EINVAL; | ||
975 | } | ||
976 | |||
977 | DEB_EE(("VIDIOC_ENUM_FMT: type:%d, index:%d\n",f->type,f->index)); | ||
978 | return 0; | ||
979 | } | ||
980 | case VIDIOC_QUERYCTRL: | ||
981 | { | ||
982 | const struct v4l2_queryctrl *ctrl; | ||
983 | struct v4l2_queryctrl *c = arg; | ||
984 | |||
985 | if ((c->id < V4L2_CID_BASE || | ||
986 | c->id >= V4L2_CID_LASTP1) && | ||
987 | (c->id < V4L2_CID_PRIVATE_BASE || | ||
988 | c->id >= V4L2_CID_PRIVATE_LASTP1)) | ||
989 | return -EINVAL; | ||
990 | |||
991 | ctrl = ctrl_by_id(c->id); | ||
992 | if( NULL == ctrl ) { | ||
993 | return -EINVAL; | ||
994 | /* | ||
995 | c->flags = V4L2_CTRL_FLAG_DISABLED; | ||
996 | return 0; | ||
997 | */ | ||
998 | } | ||
999 | |||
1000 | DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id)); | ||
1001 | *c = *ctrl; | ||
1002 | return 0; | ||
1003 | } | ||
1004 | case VIDIOC_G_CTRL: { | ||
1005 | DEB_EE(("VIDIOC_G_CTRL\n")); | ||
1006 | return get_control(fh,arg); | ||
1007 | } | ||
1008 | case VIDIOC_S_CTRL: | ||
1009 | { | ||
1010 | DEB_EE(("VIDIOC_S_CTRL\n")); | ||
1011 | err = set_control(fh,arg); | ||
1012 | return err; | ||
1013 | } | ||
1014 | case VIDIOC_G_PARM: | ||
1015 | { | ||
1016 | struct v4l2_streamparm *parm = arg; | ||
1017 | if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) { | ||
1018 | return -EINVAL; | ||
1019 | } | ||
1020 | memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm)); | ||
1021 | parm->parm.capture.readbuffers = 1; | ||
1022 | // fixme: only for PAL! | ||
1023 | parm->parm.capture.timeperframe.numerator = 1; | ||
1024 | parm->parm.capture.timeperframe.denominator = 25; | ||
1025 | return 0; | ||
1026 | } | ||
1027 | case VIDIOC_G_FMT: | ||
1028 | { | ||
1029 | struct v4l2_format *f = arg; | ||
1030 | DEB_EE(("VIDIOC_G_FMT\n")); | ||
1031 | return g_fmt(fh,f); | ||
1032 | } | ||
1033 | case VIDIOC_S_FMT: | ||
1034 | { | ||
1035 | struct v4l2_format *f = arg; | ||
1036 | DEB_EE(("VIDIOC_S_FMT\n")); | ||
1037 | return s_fmt(fh,f); | ||
1038 | } | ||
1039 | case VIDIOC_TRY_FMT: | ||
1040 | { | ||
1041 | struct v4l2_format *f = arg; | ||
1042 | DEB_EE(("VIDIOC_TRY_FMT\n")); | ||
1043 | return try_fmt(fh,f); | ||
1044 | } | ||
1045 | case VIDIOC_G_STD: | ||
1046 | { | ||
1047 | v4l2_std_id *id = arg; | ||
1048 | DEB_EE(("VIDIOC_G_STD\n")); | ||
1049 | *id = vv->standard->id; | ||
1050 | return 0; | ||
1051 | } | ||
1052 | /* the saa7146 supfhrts (used in conjunction with the saa7111a for example) | ||
1053 | PAL / NTSC / SECAM. if your hardware does not (or does more) | ||
1054 | -- override this function in your extension */ | ||
1055 | case VIDIOC_ENUMSTD: | ||
1056 | { | ||
1057 | struct v4l2_standard *e = arg; | ||
1058 | if (e->index < 0 ) | ||
1059 | return -EINVAL; | ||
1060 | if( e->index < dev->ext_vv_data->num_stds ) { | ||
1061 | DEB_EE(("VIDIOC_ENUMSTD: index:%d\n",e->index)); | ||
1062 | v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name); | ||
1063 | return 0; | ||
1064 | } | ||
1065 | return -EINVAL; | ||
1066 | } | ||
1067 | case VIDIOC_S_STD: | ||
1068 | { | ||
1069 | v4l2_std_id *id = arg; | ||
1070 | int found = 0; | ||
1071 | int i, err; | ||
1072 | |||
1073 | DEB_EE(("VIDIOC_S_STD\n")); | ||
1074 | |||
1075 | if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) { | ||
1076 | DEB_D(("cannot change video standard while streaming capture is active\n")); | ||
1077 | return -EBUSY; | ||
1078 | } | ||
1079 | |||
1080 | if ((vv->video_status & STATUS_OVERLAY) != 0) { | ||
1081 | vv->ov_suspend = vv->video_fh; | ||
1082 | err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */ | ||
1083 | if (0 != err) { | ||
1084 | DEB_D(("suspending video failed. aborting\n")); | ||
1085 | return err; | ||
1086 | } | ||
1087 | } | ||
1088 | |||
1089 | down(&dev->lock); | ||
1090 | |||
1091 | for(i = 0; i < dev->ext_vv_data->num_stds; i++) | ||
1092 | if (*id & dev->ext_vv_data->stds[i].id) | ||
1093 | break; | ||
1094 | if (i != dev->ext_vv_data->num_stds) { | ||
1095 | vv->standard = &dev->ext_vv_data->stds[i]; | ||
1096 | if( NULL != dev->ext_vv_data->std_callback ) | ||
1097 | dev->ext_vv_data->std_callback(dev, vv->standard); | ||
1098 | found = 1; | ||
1099 | } | ||
1100 | |||
1101 | up(&dev->lock); | ||
1102 | |||
1103 | if (vv->ov_suspend != NULL) { | ||
1104 | saa7146_start_preview(vv->ov_suspend); | ||
1105 | vv->ov_suspend = NULL; | ||
1106 | } | ||
1107 | |||
1108 | if( 0 == found ) { | ||
1109 | DEB_EE(("VIDIOC_S_STD: standard not found.\n")); | ||
1110 | return -EINVAL; | ||
1111 | } | ||
1112 | |||
1113 | DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",vv->standard->name)); | ||
1114 | return 0; | ||
1115 | } | ||
1116 | case VIDIOC_OVERLAY: | ||
1117 | |||
1118 | |||
1119 | |||
1120 | |||
1121 | { | ||
1122 | int on = *(int *)arg; | ||
1123 | int err = 0; | ||
1124 | |||
1125 | DEB_D(("VIDIOC_OVERLAY on:%d\n",on)); | ||
1126 | if (on != 0) { | ||
1127 | err = saa7146_start_preview(fh); | ||
1128 | } else { | ||
1129 | err = saa7146_stop_preview(fh); | ||
1130 | } | ||
1131 | return err; | ||
1132 | } | ||
1133 | case VIDIOC_REQBUFS: { | ||
1134 | struct v4l2_requestbuffers *req = arg; | ||
1135 | DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type)); | ||
1136 | return videobuf_reqbufs(q,req); | ||
1137 | } | ||
1138 | case VIDIOC_QUERYBUF: { | ||
1139 | struct v4l2_buffer *buf = arg; | ||
1140 | DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset)); | ||
1141 | return videobuf_querybuf(q,buf); | ||
1142 | } | ||
1143 | case VIDIOC_QBUF: { | ||
1144 | struct v4l2_buffer *buf = arg; | ||
1145 | int ret = 0; | ||
1146 | ret = videobuf_qbuf(q,buf); | ||
1147 | DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index)); | ||
1148 | return ret; | ||
1149 | } | ||
1150 | case VIDIOC_DQBUF: { | ||
1151 | struct v4l2_buffer *buf = arg; | ||
1152 | int ret = 0; | ||
1153 | ret = videobuf_dqbuf(q,buf,file->f_flags & O_NONBLOCK); | ||
1154 | DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index)); | ||
1155 | return ret; | ||
1156 | } | ||
1157 | case VIDIOC_STREAMON: { | ||
1158 | int *type = arg; | ||
1159 | DEB_D(("VIDIOC_STREAMON, type:%d\n",*type)); | ||
1160 | |||
1161 | err = video_begin(fh); | ||
1162 | if( 0 != err) { | ||
1163 | return err; | ||
1164 | } | ||
1165 | err = videobuf_streamon(q); | ||
1166 | return err; | ||
1167 | } | ||
1168 | case VIDIOC_STREAMOFF: { | ||
1169 | int *type = arg; | ||
1170 | |||
1171 | DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type)); | ||
1172 | |||
1173 | /* ugly: we need to copy some checks from video_end(), | ||
1174 | because videobuf_streamoff() relies on the capture running. | ||
1175 | check and fix this */ | ||
1176 | if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) { | ||
1177 | DEB_S(("not capturing.\n")); | ||
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | if (vv->video_fh != fh) { | ||
1182 | DEB_S(("capturing, but in another open.\n")); | ||
1183 | return -EBUSY; | ||
1184 | } | ||
1185 | |||
1186 | err = videobuf_streamoff(q); | ||
1187 | if (0 != err) { | ||
1188 | DEB_D(("warning: videobuf_streamoff() failed.\n")); | ||
1189 | video_end(fh, file); | ||
1190 | } else { | ||
1191 | err = video_end(fh, file); | ||
1192 | } | ||
1193 | return err; | ||
1194 | } | ||
1195 | case VIDIOCGMBUF: | ||
1196 | { | ||
1197 | struct video_mbuf *mbuf = arg; | ||
1198 | struct videobuf_queue *q; | ||
1199 | int i; | ||
1200 | |||
1201 | /* fixme: number of capture buffers and sizes for v4l apps */ | ||
1202 | int gbuffers = 2; | ||
1203 | int gbufsize = 768*576*4; | ||
1204 | |||
1205 | DEB_D(("VIDIOCGMBUF \n")); | ||
1206 | |||
1207 | q = &fh->video_q; | ||
1208 | down(&q->lock); | ||
1209 | err = videobuf_mmap_setup(q,gbuffers,gbufsize, | ||
1210 | V4L2_MEMORY_MMAP); | ||
1211 | if (err < 0) { | ||
1212 | up(&q->lock); | ||
1213 | return err; | ||
1214 | } | ||
1215 | memset(mbuf,0,sizeof(*mbuf)); | ||
1216 | mbuf->frames = gbuffers; | ||
1217 | mbuf->size = gbuffers * gbufsize; | ||
1218 | for (i = 0; i < gbuffers; i++) | ||
1219 | mbuf->offsets[i] = i * gbufsize; | ||
1220 | up(&q->lock); | ||
1221 | return 0; | ||
1222 | } | ||
1223 | default: | ||
1224 | return v4l_compat_translate_ioctl(inode,file,cmd,arg, | ||
1225 | saa7146_video_do_ioctl); | ||
1226 | } | ||
1227 | return 0; | ||
1228 | } | ||
1229 | |||
1230 | /*********************************************************************************/ | ||
1231 | /* buffer handling functions */ | ||
1232 | |||
1233 | static int buffer_activate (struct saa7146_dev *dev, | ||
1234 | struct saa7146_buf *buf, | ||
1235 | struct saa7146_buf *next) | ||
1236 | { | ||
1237 | struct saa7146_vv *vv = dev->vv_data; | ||
1238 | |||
1239 | buf->vb.state = STATE_ACTIVE; | ||
1240 | saa7146_set_capture(dev,buf,next); | ||
1241 | |||
1242 | mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT); | ||
1243 | return 0; | ||
1244 | } | ||
1245 | |||
1246 | static int buffer_prepare(struct videobuf_queue *q, | ||
1247 | struct videobuf_buffer *vb, enum v4l2_field field) | ||
1248 | { | ||
1249 | struct file *file = q->priv_data; | ||
1250 | struct saa7146_fh *fh = file->private_data; | ||
1251 | struct saa7146_dev *dev = fh->dev; | ||
1252 | struct saa7146_vv *vv = dev->vv_data; | ||
1253 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
1254 | int size,err = 0; | ||
1255 | |||
1256 | DEB_CAP(("vbuf:%p\n",vb)); | ||
1257 | |||
1258 | /* sanity checks */ | ||
1259 | if (fh->video_fmt.width < 48 || | ||
1260 | fh->video_fmt.height < 32 || | ||
1261 | fh->video_fmt.width > vv->standard->h_max_out || | ||
1262 | fh->video_fmt.height > vv->standard->v_max_out) { | ||
1263 | DEB_D(("w (%d) / h (%d) out of bounds.\n",fh->video_fmt.width,fh->video_fmt.height)); | ||
1264 | return -EINVAL; | ||
1265 | } | ||
1266 | |||
1267 | size = fh->video_fmt.sizeimage; | ||
1268 | if (0 != buf->vb.baddr && buf->vb.bsize < size) { | ||
1269 | DEB_D(("size mismatch.\n")); | ||
1270 | return -EINVAL; | ||
1271 | } | ||
1272 | |||
1273 | DEB_CAP(("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", | ||
1274 | fh->video_fmt.width,fh->video_fmt.height,size,v4l2_field_names[fh->video_fmt.field])); | ||
1275 | if (buf->vb.width != fh->video_fmt.width || | ||
1276 | buf->vb.bytesperline != fh->video_fmt.bytesperline || | ||
1277 | buf->vb.height != fh->video_fmt.height || | ||
1278 | buf->vb.size != size || | ||
1279 | buf->vb.field != field || | ||
1280 | buf->vb.field != fh->video_fmt.field || | ||
1281 | buf->fmt != &fh->video_fmt) { | ||
1282 | saa7146_dma_free(dev,buf); | ||
1283 | } | ||
1284 | |||
1285 | if (STATE_NEEDS_INIT == buf->vb.state) { | ||
1286 | struct saa7146_format *sfmt; | ||
1287 | |||
1288 | buf->vb.bytesperline = fh->video_fmt.bytesperline; | ||
1289 | buf->vb.width = fh->video_fmt.width; | ||
1290 | buf->vb.height = fh->video_fmt.height; | ||
1291 | buf->vb.size = size; | ||
1292 | buf->vb.field = field; | ||
1293 | buf->fmt = &fh->video_fmt; | ||
1294 | buf->vb.field = fh->video_fmt.field; | ||
1295 | |||
1296 | sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); | ||
1297 | |||
1298 | if( 0 != IS_PLANAR(sfmt->trans)) { | ||
1299 | saa7146_pgtable_free(dev->pci, &buf->pt[0]); | ||
1300 | saa7146_pgtable_free(dev->pci, &buf->pt[1]); | ||
1301 | saa7146_pgtable_free(dev->pci, &buf->pt[2]); | ||
1302 | |||
1303 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); | ||
1304 | saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); | ||
1305 | saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); | ||
1306 | } else { | ||
1307 | saa7146_pgtable_free(dev->pci, &buf->pt[0]); | ||
1308 | saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); | ||
1309 | } | ||
1310 | |||
1311 | err = videobuf_iolock(dev->pci,&buf->vb, &vv->ov_fb); | ||
1312 | if (err) | ||
1313 | goto oops; | ||
1314 | err = saa7146_pgtable_build(dev,buf); | ||
1315 | if (err) | ||
1316 | goto oops; | ||
1317 | } | ||
1318 | buf->vb.state = STATE_PREPARED; | ||
1319 | buf->activate = buffer_activate; | ||
1320 | |||
1321 | return 0; | ||
1322 | |||
1323 | oops: | ||
1324 | DEB_D(("error out.\n")); | ||
1325 | saa7146_dma_free(dev,buf); | ||
1326 | |||
1327 | return err; | ||
1328 | } | ||
1329 | |||
1330 | static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | ||
1331 | { | ||
1332 | struct file *file = q->priv_data; | ||
1333 | struct saa7146_fh *fh = file->private_data; | ||
1334 | |||
1335 | if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) | ||
1336 | *count = MAX_SAA7146_CAPTURE_BUFFERS; | ||
1337 | |||
1338 | *size = fh->video_fmt.sizeimage; | ||
1339 | |||
1340 | /* check if we exceed the "max_memory" parameter */ | ||
1341 | if( (*count * *size) > (max_memory*1048576) ) { | ||
1342 | *count = (max_memory*1048576) / *size; | ||
1343 | } | ||
1344 | |||
1345 | DEB_CAP(("%d buffers, %d bytes each.\n",*count,*size)); | ||
1346 | |||
1347 | return 0; | ||
1348 | } | ||
1349 | |||
1350 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
1351 | { | ||
1352 | struct file *file = q->priv_data; | ||
1353 | struct saa7146_fh *fh = file->private_data; | ||
1354 | struct saa7146_dev *dev = fh->dev; | ||
1355 | struct saa7146_vv *vv = dev->vv_data; | ||
1356 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
1357 | |||
1358 | DEB_CAP(("vbuf:%p\n",vb)); | ||
1359 | saa7146_buffer_queue(fh->dev,&vv->video_q,buf); | ||
1360 | } | ||
1361 | |||
1362 | |||
1363 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
1364 | { | ||
1365 | struct file *file = q->priv_data; | ||
1366 | struct saa7146_fh *fh = file->private_data; | ||
1367 | struct saa7146_dev *dev = fh->dev; | ||
1368 | struct saa7146_buf *buf = (struct saa7146_buf *)vb; | ||
1369 | |||
1370 | DEB_CAP(("vbuf:%p\n",vb)); | ||
1371 | saa7146_dma_free(dev,buf); | ||
1372 | } | ||
1373 | |||
1374 | static struct videobuf_queue_ops video_qops = { | ||
1375 | .buf_setup = buffer_setup, | ||
1376 | .buf_prepare = buffer_prepare, | ||
1377 | .buf_queue = buffer_queue, | ||
1378 | .buf_release = buffer_release, | ||
1379 | }; | ||
1380 | |||
1381 | /********************************************************************************/ | ||
1382 | /* file operations */ | ||
1383 | |||
1384 | static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) | ||
1385 | { | ||
1386 | INIT_LIST_HEAD(&vv->video_q.queue); | ||
1387 | |||
1388 | init_timer(&vv->video_q.timeout); | ||
1389 | vv->video_q.timeout.function = saa7146_buffer_timeout; | ||
1390 | vv->video_q.timeout.data = (unsigned long)(&vv->video_q); | ||
1391 | vv->video_q.dev = dev; | ||
1392 | |||
1393 | /* set some default values */ | ||
1394 | vv->standard = &dev->ext_vv_data->stds[0]; | ||
1395 | |||
1396 | /* FIXME: what's this? */ | ||
1397 | vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A; | ||
1398 | vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A; | ||
1399 | } | ||
1400 | |||
1401 | |||
1402 | static int video_open(struct saa7146_dev *dev, struct file *file) | ||
1403 | { | ||
1404 | struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; | ||
1405 | struct saa7146_format *sfmt; | ||
1406 | |||
1407 | fh->video_fmt.width = 384; | ||
1408 | fh->video_fmt.height = 288; | ||
1409 | fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24; | ||
1410 | fh->video_fmt.bytesperline = 0; | ||
1411 | fh->video_fmt.field = V4L2_FIELD_ANY; | ||
1412 | sfmt = format_by_fourcc(dev,fh->video_fmt.pixelformat); | ||
1413 | fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8; | ||
1414 | |||
1415 | videobuf_queue_init(&fh->video_q, &video_qops, | ||
1416 | dev->pci, &dev->slock, | ||
1417 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
1418 | V4L2_FIELD_INTERLACED, | ||
1419 | sizeof(struct saa7146_buf), | ||
1420 | file); | ||
1421 | |||
1422 | init_MUTEX(&fh->video_q.lock); | ||
1423 | |||
1424 | return 0; | ||
1425 | } | ||
1426 | |||
1427 | |||
1428 | static void video_close(struct saa7146_dev *dev, struct file *file) | ||
1429 | { | ||
1430 | struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; | ||
1431 | struct saa7146_vv *vv = dev->vv_data; | ||
1432 | int err; | ||
1433 | |||
1434 | if (IS_CAPTURE_ACTIVE(fh) != 0) { | ||
1435 | err = video_end(fh, file); | ||
1436 | } else if (IS_OVERLAY_ACTIVE(fh) != 0) { | ||
1437 | err = saa7146_stop_preview(fh); | ||
1438 | } | ||
1439 | |||
1440 | /* hmm, why is this function declared void? */ | ||
1441 | /* return err */ | ||
1442 | } | ||
1443 | |||
1444 | |||
1445 | static void video_irq_done(struct saa7146_dev *dev, unsigned long st) | ||
1446 | { | ||
1447 | struct saa7146_vv *vv = dev->vv_data; | ||
1448 | struct saa7146_dmaqueue *q = &vv->video_q; | ||
1449 | |||
1450 | spin_lock(&dev->slock); | ||
1451 | DEB_CAP(("called.\n")); | ||
1452 | |||
1453 | /* only finish the buffer if we have one... */ | ||
1454 | if( NULL != q->curr ) { | ||
1455 | saa7146_buffer_finish(dev,q,STATE_DONE); | ||
1456 | } | ||
1457 | saa7146_buffer_next(dev,q,0); | ||
1458 | |||
1459 | spin_unlock(&dev->slock); | ||
1460 | } | ||
1461 | |||
1462 | static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
1463 | { | ||
1464 | struct saa7146_fh *fh = file->private_data; | ||
1465 | struct saa7146_dev *dev = fh->dev; | ||
1466 | struct saa7146_vv *vv = dev->vv_data; | ||
1467 | ssize_t ret = 0; | ||
1468 | |||
1469 | DEB_EE(("called.\n")); | ||
1470 | |||
1471 | if ((vv->video_status & STATUS_CAPTURE) != 0) { | ||
1472 | /* fixme: should we allow read() captures while streaming capture? */ | ||
1473 | if (vv->video_fh == fh) { | ||
1474 | DEB_S(("already capturing.\n")); | ||
1475 | return -EBUSY; | ||
1476 | } | ||
1477 | DEB_S(("already capturing in another open.\n")); | ||
1478 | return -EBUSY; | ||
1479 | } | ||
1480 | |||
1481 | ret = video_begin(fh); | ||
1482 | if( 0 != ret) { | ||
1483 | goto out; | ||
1484 | } | ||
1485 | |||
1486 | ret = videobuf_read_one(&fh->video_q , data, count, ppos, | ||
1487 | file->f_flags & O_NONBLOCK); | ||
1488 | if (ret != 0) { | ||
1489 | video_end(fh, file); | ||
1490 | } else { | ||
1491 | ret = video_end(fh, file); | ||
1492 | } | ||
1493 | out: | ||
1494 | /* restart overlay if it was active before */ | ||
1495 | if (vv->ov_suspend != NULL) { | ||
1496 | saa7146_start_preview(vv->ov_suspend); | ||
1497 | vv->ov_suspend = NULL; | ||
1498 | } | ||
1499 | |||
1500 | return ret; | ||
1501 | } | ||
1502 | |||
1503 | struct saa7146_use_ops saa7146_video_uops = { | ||
1504 | .init = video_init, | ||
1505 | .open = video_open, | ||
1506 | .release = video_close, | ||
1507 | .irq_done = video_irq_done, | ||
1508 | .read = video_read, | ||
1509 | }; | ||
diff --git a/drivers/media/common/saa7146_vv_ksyms.c b/drivers/media/common/saa7146_vv_ksyms.c new file mode 100644 index 000000000000..62226eb4753b --- /dev/null +++ b/drivers/media/common/saa7146_vv_ksyms.c | |||
@@ -0,0 +1,12 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <media/saa7146_vv.h> | ||
3 | |||
4 | EXPORT_SYMBOL_GPL(saa7146_start_preview); | ||
5 | EXPORT_SYMBOL_GPL(saa7146_stop_preview); | ||
6 | |||
7 | EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync); | ||
8 | EXPORT_SYMBOL_GPL(saa7146_register_device); | ||
9 | EXPORT_SYMBOL_GPL(saa7146_unregister_device); | ||
10 | |||
11 | EXPORT_SYMBOL_GPL(saa7146_vv_init); | ||
12 | EXPORT_SYMBOL_GPL(saa7146_vv_release); | ||