Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from | |
3 | * Philips. | |
4 | * | |
5 | * Only capturing of Teletext pages is tested. The videotext chips also have a | |
6 | * TV output but my hardware doesn't use it. For this reason this driver does | |
7 | * not support changing any TV display settings. | |
8 | * | |
9 | * Copyright (C) 2004 Michael Geng <linux@MichaelGeng.de> | |
10 | * | |
11 | * Derived from | |
12 | * | |
13 | * saa5249 driver | |
14 | * Copyright (C) 1998 Richard Guenther | |
15 | * <richard.guenther@student.uni-tuebingen.de> | |
16 | * | |
17 | * with changes by | |
d9b01449 | 18 | * Alan Cox <alan@lxorguk.ukuu.org.uk> |
1da177e4 LT |
19 | * |
20 | * and | |
21 | * | |
22 | * vtx.c | |
23 | * Copyright (C) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de> | |
24 | * | |
25 | * This program is free software; you can redistribute it and/or modify | |
26 | * it under the terms of the GNU General Public License as published by | |
27 | * the Free Software Foundation; either version 2 of the License, or | |
28 | * (at your option) any later version. | |
29 | * | |
30 | * This program is distributed in the hope that it will be useful, | |
31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
33 | * GNU General Public License for more details. | |
34 | * | |
35 | * You should have received a copy of the GNU General Public License | |
36 | * along with this program; if not, write to the Free Software | |
37 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
38 | * USA. | |
39 | */ | |
40 | ||
41 | #include <linux/module.h> | |
42 | #include <linux/kernel.h> | |
1da177e4 LT |
43 | #include <linux/mm.h> |
44 | #include <linux/init.h> | |
45 | #include <linux/i2c.h> | |
d56dc612 | 46 | #include <linux/smp_lock.h> |
71ef85c7 HV |
47 | #include <linux/mutex.h> |
48 | #include <linux/videotext.h> | |
1da177e4 | 49 | #include <linux/videodev.h> |
5e87efa3 | 50 | #include <media/v4l2-common.h> |
35ea11ff | 51 | #include <media/v4l2-ioctl.h> |
71ef85c7 | 52 | #include <media/v4l2-i2c-drv-legacy.h> |
1da177e4 LT |
53 | |
54 | MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); | |
55 | MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); | |
56 | MODULE_LICENSE("GPL"); | |
57 | ||
71ef85c7 HV |
58 | #define MAJOR_VERSION 1 /* driver major version number */ |
59 | #define MINOR_VERSION 8 /* driver minor version number */ | |
60 | ||
61 | /* Number of DAUs = number of pages that can be searched at the same time. */ | |
62 | #define NUM_DAUS 4 | |
63 | ||
64 | #define NUM_ROWS_PER_PAGE 40 | |
65 | ||
66 | /* first column is 0 (not 1) */ | |
67 | #define POS_TIME_START 32 | |
68 | #define POS_TIME_END 39 | |
69 | ||
70 | #define POS_HEADER_START 7 | |
71 | #define POS_HEADER_END 31 | |
72 | ||
73 | /* Returns 'true' if the part of the videotext page described with req contains | |
74 | (at least parts of) the time field */ | |
75 | #define REQ_CONTAINS_TIME(p_req) \ | |
76 | ((p_req)->start <= POS_TIME_END && \ | |
77 | (p_req)->end >= POS_TIME_START) | |
78 | ||
79 | /* Returns 'true' if the part of the videotext page described with req contains | |
80 | (at least parts of) the page header */ | |
81 | #define REQ_CONTAINS_HEADER(p_req) \ | |
82 | ((p_req)->start <= POS_HEADER_END && \ | |
83 | (p_req)->end >= POS_HEADER_START) | |
84 | ||
85 | /*****************************************************************************/ | |
86 | /* Mode register numbers of the SAA5246A */ | |
87 | /*****************************************************************************/ | |
88 | #define SAA5246A_REGISTER_R0 0 | |
89 | #define SAA5246A_REGISTER_R1 1 | |
90 | #define SAA5246A_REGISTER_R2 2 | |
91 | #define SAA5246A_REGISTER_R3 3 | |
92 | #define SAA5246A_REGISTER_R4 4 | |
93 | #define SAA5246A_REGISTER_R5 5 | |
94 | #define SAA5246A_REGISTER_R6 6 | |
95 | #define SAA5246A_REGISTER_R7 7 | |
96 | #define SAA5246A_REGISTER_R8 8 | |
97 | #define SAA5246A_REGISTER_R9 9 | |
98 | #define SAA5246A_REGISTER_R10 10 | |
99 | #define SAA5246A_REGISTER_R11 11 | |
100 | #define SAA5246A_REGISTER_R11B 11 | |
101 | ||
102 | /* SAA5246A mode registers often autoincrement to the next register. | |
103 | Therefore we use variable argument lists. The following macro indicates | |
104 | the end of a command list. */ | |
105 | #define COMMAND_END (-1) | |
106 | ||
107 | /*****************************************************************************/ | |
108 | /* Contents of the mode registers of the SAA5246A */ | |
109 | /*****************************************************************************/ | |
110 | /* Register R0 (Advanced Control) */ | |
111 | #define R0_SELECT_R11 0x00 | |
112 | #define R0_SELECT_R11B 0x01 | |
113 | ||
114 | #define R0_PLL_TIME_CONSTANT_LONG 0x00 | |
115 | #define R0_PLL_TIME_CONSTANT_SHORT 0x02 | |
116 | ||
117 | #define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 | |
118 | #define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 | |
119 | ||
120 | #define R0_ENABLE_HDR_POLL 0x00 | |
121 | #define R0_DISABLE_HDR_POLL 0x10 | |
122 | ||
123 | #define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 | |
124 | #define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 | |
125 | ||
126 | #define R0_NO_FREE_RUN_PLL 0x00 | |
127 | #define R0_FREE_RUN_PLL 0x40 | |
128 | ||
129 | #define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 | |
130 | #define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 | |
131 | ||
132 | /* Register R1 (Mode) */ | |
133 | #define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 | |
134 | #define R1_NON_INTERLACED_312_313_LINES 0x01 | |
135 | #define R1_NON_INTERLACED_312_312_LINES 0x02 | |
136 | #define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 | |
137 | #define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 | |
138 | ||
139 | #define R1_DEW 0x00 | |
140 | #define R1_FULL_FIELD 0x08 | |
141 | ||
142 | #define R1_EXTENDED_PACKET_DISABLE 0x00 | |
143 | #define R1_EXTENDED_PACKET_ENABLE 0x10 | |
144 | ||
145 | #define R1_DAUS_ALL_ON 0x00 | |
146 | #define R1_DAUS_ALL_OFF 0x20 | |
147 | ||
148 | #define R1_7_BITS_PLUS_PARITY 0x00 | |
149 | #define R1_8_BITS_NO_PARITY 0x40 | |
150 | ||
151 | #define R1_VCS_TO_SCS 0x00 | |
152 | #define R1_NO_VCS_TO_SCS 0x80 | |
153 | ||
154 | /* Register R2 (Page request address) */ | |
155 | #define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 | |
156 | #define R2_IN_R3_SELECT_PAGE_TENS 0x01 | |
157 | #define R2_IN_R3_SELECT_PAGE_UNITS 0x02 | |
158 | #define R2_IN_R3_SELECT_HOURS_TENS 0x03 | |
159 | #define R2_IN_R3_SELECT_HOURS_UNITS 0x04 | |
160 | #define R2_IN_R3_SELECT_MINUTES_TENS 0x05 | |
161 | #define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 | |
162 | ||
163 | #define R2_DAU_0 0x00 | |
164 | #define R2_DAU_1 0x10 | |
165 | #define R2_DAU_2 0x20 | |
166 | #define R2_DAU_3 0x30 | |
167 | ||
168 | #define R2_BANK_0 0x00 | |
169 | #define R2_BANK 1 0x40 | |
170 | ||
171 | #define R2_HAMMING_CHECK_ON 0x80 | |
172 | #define R2_HAMMING_CHECK_OFF 0x00 | |
173 | ||
174 | /* Register R3 (Page request data) */ | |
175 | #define R3_PAGE_HUNDREDS_0 0x00 | |
176 | #define R3_PAGE_HUNDREDS_1 0x01 | |
177 | #define R3_PAGE_HUNDREDS_2 0x02 | |
178 | #define R3_PAGE_HUNDREDS_3 0x03 | |
179 | #define R3_PAGE_HUNDREDS_4 0x04 | |
180 | #define R3_PAGE_HUNDREDS_5 0x05 | |
181 | #define R3_PAGE_HUNDREDS_6 0x06 | |
182 | #define R3_PAGE_HUNDREDS_7 0x07 | |
1da177e4 | 183 | |
71ef85c7 HV |
184 | #define R3_HOLD_PAGE 0x00 |
185 | #define R3_UPDATE_PAGE 0x08 | |
1da177e4 | 186 | |
71ef85c7 HV |
187 | #define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 |
188 | #define R3_PAGE_HUNDREDS_DO_CARE 0x10 | |
f87086e3 | 189 | |
71ef85c7 HV |
190 | #define R3_PAGE_TENS_DO_NOT_CARE 0x00 |
191 | #define R3_PAGE_TENS_DO_CARE 0x10 | |
1da177e4 | 192 | |
71ef85c7 HV |
193 | #define R3_PAGE_UNITS_DO_NOT_CARE 0x00 |
194 | #define R3_PAGE_UNITS_DO_CARE 0x10 | |
1da177e4 | 195 | |
71ef85c7 HV |
196 | #define R3_HOURS_TENS_DO_NOT_CARE 0x00 |
197 | #define R3_HOURS_TENS_DO_CARE 0x10 | |
1da177e4 | 198 | |
71ef85c7 HV |
199 | #define R3_HOURS_UNITS_DO_NOT_CARE 0x00 |
200 | #define R3_HOURS_UNITS_DO_CARE 0x10 | |
1da177e4 | 201 | |
71ef85c7 HV |
202 | #define R3_MINUTES_TENS_DO_NOT_CARE 0x00 |
203 | #define R3_MINUTES_TENS_DO_CARE 0x10 | |
1da177e4 | 204 | |
71ef85c7 HV |
205 | #define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 |
206 | #define R3_MINUTES_UNITS_DO_CARE 0x10 | |
1da177e4 | 207 | |
71ef85c7 HV |
208 | /* Register R4 (Display chapter) */ |
209 | #define R4_DISPLAY_PAGE_0 0x00 | |
210 | #define R4_DISPLAY_PAGE_1 0x01 | |
211 | #define R4_DISPLAY_PAGE_2 0x02 | |
212 | #define R4_DISPLAY_PAGE_3 0x03 | |
213 | #define R4_DISPLAY_PAGE_4 0x04 | |
214 | #define R4_DISPLAY_PAGE_5 0x05 | |
215 | #define R4_DISPLAY_PAGE_6 0x06 | |
216 | #define R4_DISPLAY_PAGE_7 0x07 | |
1da177e4 | 217 | |
71ef85c7 HV |
218 | /* Register R5 (Normal display control) */ |
219 | #define R5_PICTURE_INSIDE_BOXING_OFF 0x00 | |
220 | #define R5_PICTURE_INSIDE_BOXING_ON 0x01 | |
1da177e4 | 221 | |
71ef85c7 HV |
222 | #define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 |
223 | #define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 | |
1da177e4 | 224 | |
71ef85c7 HV |
225 | #define R5_TEXT_INSIDE_BOXING_OFF 0x00 |
226 | #define R5_TEXT_INSIDE_BOXING_ON 0x04 | |
1da177e4 | 227 | |
71ef85c7 HV |
228 | #define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 |
229 | #define R5_TEXT_OUTSIDE_BOXING_ON 0x08 | |
1da177e4 | 230 | |
71ef85c7 HV |
231 | #define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 |
232 | #define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 | |
c170ecf4 | 233 | |
71ef85c7 HV |
234 | #define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 |
235 | #define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 | |
1da177e4 | 236 | |
71ef85c7 HV |
237 | #define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 |
238 | #define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 | |
239 | ||
240 | #define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 | |
241 | #define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 | |
242 | ||
243 | /* Register R6 (Newsflash display) */ | |
244 | #define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 | |
245 | #define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 | |
246 | ||
247 | #define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 | |
248 | #define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 | |
249 | ||
250 | #define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 | |
251 | #define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 | |
252 | ||
253 | #define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 | |
254 | #define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 | |
255 | ||
256 | #define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 | |
257 | #define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 | |
258 | ||
259 | #define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 | |
260 | #define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 | |
261 | ||
262 | #define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 | |
263 | #define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 | |
264 | ||
265 | #define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 | |
266 | #define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 | |
267 | ||
268 | /* Register R7 (Display mode) */ | |
269 | #define R7_BOX_OFF_ROW_0 0x00 | |
270 | #define R7_BOX_ON_ROW_0 0x01 | |
271 | ||
272 | #define R7_BOX_OFF_ROW_1_TO_23 0x00 | |
273 | #define R7_BOX_ON_ROW_1_TO_23 0x02 | |
274 | ||
275 | #define R7_BOX_OFF_ROW_24 0x00 | |
276 | #define R7_BOX_ON_ROW_24 0x04 | |
277 | ||
278 | #define R7_SINGLE_HEIGHT 0x00 | |
279 | #define R7_DOUBLE_HEIGHT 0x08 | |
280 | ||
281 | #define R7_TOP_HALF 0x00 | |
282 | #define R7_BOTTOM_HALF 0x10 | |
283 | ||
284 | #define R7_REVEAL_OFF 0x00 | |
285 | #define R7_REVEAL_ON 0x20 | |
286 | ||
287 | #define R7_CURSER_OFF 0x00 | |
288 | #define R7_CURSER_ON 0x40 | |
289 | ||
290 | #define R7_STATUS_BOTTOM 0x00 | |
291 | #define R7_STATUS_TOP 0x80 | |
292 | ||
293 | /* Register R8 (Active chapter) */ | |
294 | #define R8_ACTIVE_CHAPTER_0 0x00 | |
295 | #define R8_ACTIVE_CHAPTER_1 0x01 | |
296 | #define R8_ACTIVE_CHAPTER_2 0x02 | |
297 | #define R8_ACTIVE_CHAPTER_3 0x03 | |
298 | #define R8_ACTIVE_CHAPTER_4 0x04 | |
299 | #define R8_ACTIVE_CHAPTER_5 0x05 | |
300 | #define R8_ACTIVE_CHAPTER_6 0x06 | |
301 | #define R8_ACTIVE_CHAPTER_7 0x07 | |
1da177e4 | 302 | |
71ef85c7 HV |
303 | #define R8_CLEAR_MEMORY 0x08 |
304 | #define R8_DO_NOT_CLEAR_MEMORY 0x00 | |
305 | ||
306 | /* Register R9 (Curser row) */ | |
307 | #define R9_CURSER_ROW_0 0x00 | |
308 | #define R9_CURSER_ROW_1 0x01 | |
309 | #define R9_CURSER_ROW_2 0x02 | |
310 | #define R9_CURSER_ROW_25 0x19 | |
311 | ||
312 | /* Register R10 (Curser column) */ | |
313 | #define R10_CURSER_COLUMN_0 0x00 | |
314 | #define R10_CURSER_COLUMN_6 0x06 | |
315 | #define R10_CURSER_COLUMN_8 0x08 | |
316 | ||
317 | /*****************************************************************************/ | |
318 | /* Row 25 control data in column 0 to 9 */ | |
319 | /*****************************************************************************/ | |
320 | #define ROW25_COLUMN0_PAGE_UNITS 0x0F | |
321 | ||
322 | #define ROW25_COLUMN1_PAGE_TENS 0x0F | |
323 | ||
324 | #define ROW25_COLUMN2_MINUTES_UNITS 0x0F | |
325 | ||
326 | #define ROW25_COLUMN3_MINUTES_TENS 0x07 | |
327 | #define ROW25_COLUMN3_DELETE_PAGE 0x08 | |
328 | ||
329 | #define ROW25_COLUMN4_HOUR_UNITS 0x0F | |
330 | ||
331 | #define ROW25_COLUMN5_HOUR_TENS 0x03 | |
332 | #define ROW25_COLUMN5_INSERT_HEADLINE 0x04 | |
333 | #define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 | |
334 | ||
335 | #define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 | |
336 | #define ROW25_COLUMN6_UPDATE_PAGE 0x02 | |
337 | #define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 | |
338 | #define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 | |
339 | ||
340 | #define ROW25_COLUMN7_SERIAL_MODE 0x01 | |
341 | #define ROW25_COLUMN7_CHARACTER_SET 0x0E | |
342 | ||
343 | #define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 | |
344 | #define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 | |
345 | ||
346 | #define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 | |
347 | ||
348 | #define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 | |
349 | ||
350 | /*****************************************************************************/ | |
351 | /* Helper macros for extracting page, hour and minute digits */ | |
352 | /*****************************************************************************/ | |
353 | /* BYTE_POS 0 is at row 0, column 0, | |
354 | BYTE_POS 1 is at row 0, column 1, | |
355 | BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) | |
356 | BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), | |
357 | ... */ | |
358 | #define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) | |
359 | #define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) | |
360 | ||
361 | /*****************************************************************************/ | |
362 | /* Helper macros for extracting page, hour and minute digits */ | |
363 | /*****************************************************************************/ | |
364 | /* Macros for extracting hundreds, tens and units of a page number which | |
365 | must be in the range 0 ... 0x799. | |
366 | Note that page is coded in hexadecimal, i.e. 0x123 means page 123. | |
367 | page 0x.. means page 8.. */ | |
368 | #define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) | |
369 | #define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) | |
370 | #define UNITS_OF_PAGE(page) ((page) & 0xF) | |
371 | ||
372 | /* Macros for extracting tens and units of a hour information which | |
373 | must be in the range 0 ... 0x24. | |
374 | Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ | |
375 | #define TENS_OF_HOUR(hour) ((hour) / 0x10) | |
376 | #define UNITS_OF_HOUR(hour) ((hour) & 0xF) | |
377 | ||
378 | /* Macros for extracting tens and units of a minute information which | |
379 | must be in the range 0 ... 0x59. | |
380 | Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ | |
381 | #define TENS_OF_MINUTE(minute) ((minute) / 0x10) | |
382 | #define UNITS_OF_MINUTE(minute) ((minute) & 0xF) | |
383 | ||
384 | #define HOUR_MAX 0x23 | |
385 | #define MINUTE_MAX 0x59 | |
386 | #define PAGE_MAX 0x8FF | |
387 | ||
388 | ||
389 | struct saa5246a_device | |
1da177e4 | 390 | { |
71ef85c7 HV |
391 | u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; |
392 | int is_searching[NUM_DAUS]; | |
393 | struct i2c_client *client; | |
394 | unsigned long in_use; | |
395 | struct mutex lock; | |
1da177e4 LT |
396 | }; |
397 | ||
71ef85c7 HV |
398 | static struct video_device saa_template; /* Declared near bottom */ |
399 | ||
400 | /* | |
401 | * I2C interfaces | |
402 | */ | |
1da177e4 LT |
403 | |
404 | static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) | |
405 | { | |
406 | char buf[64]; | |
407 | ||
408 | buf[0] = reg; | |
409 | memcpy(buf+1, data, count); | |
410 | ||
411 | if(i2c_master_send(t->client, buf, count+1)==count+1) | |
412 | return 0; | |
413 | return -1; | |
414 | } | |
415 | ||
416 | static int i2c_senddata(struct saa5246a_device *t, ...) | |
417 | { | |
418 | unsigned char buf[64]; | |
419 | int v; | |
b730a81c | 420 | int ct = 0; |
1da177e4 | 421 | va_list argp; |
b730a81c | 422 | va_start(argp, t); |
1da177e4 | 423 | |
b730a81c RK |
424 | while ((v = va_arg(argp, int)) != -1) |
425 | buf[ct++] = v; | |
426 | ||
427 | va_end(argp); | |
1da177e4 LT |
428 | return i2c_sendbuf(t, buf[0], ct-1, buf+1); |
429 | } | |
430 | ||
db955170 | 431 | /* Get count number of bytes from I²C-device at address adr, store them in buf. |
1da177e4 | 432 | * Start & stop handshaking is done by this routine, ack will be sent after the |
e8be02a3 | 433 | * last byte to inhibit further sending of data. If uaccess is 'true', data is |
db955170 | 434 | * written to user-space with put_user. Returns -1 if I²C-device didn't send |
1da177e4 LT |
435 | * acknowledge, 0 otherwise |
436 | */ | |
437 | static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf) | |
438 | { | |
439 | if(i2c_master_recv(t->client, buf, count)!=count) | |
440 | return -1; | |
441 | return 0; | |
442 | } | |
443 | ||
444 | /* When a page is found then the not FOUND bit in one of the status registers | |
445 | * of the SAA5264A chip is cleared. Unfortunately this bit is not set | |
446 | * automatically when a new page is requested. Instead this function must be | |
447 | * called after a page has been requested. | |
448 | * | |
449 | * Return value: 0 if successful | |
450 | */ | |
451 | static int saa5246a_clear_found_bit(struct saa5246a_device *t, | |
452 | unsigned char dau_no) | |
453 | { | |
454 | unsigned char row_25_column_8; | |
455 | ||
456 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
457 | ||
458 | dau_no | | |
459 | R8_DO_NOT_CLEAR_MEMORY, | |
460 | ||
461 | R9_CURSER_ROW_25, | |
462 | ||
463 | R10_CURSER_COLUMN_8, | |
464 | ||
465 | COMMAND_END) || | |
466 | i2c_getdata(t, 1, &row_25_column_8)) | |
467 | { | |
468 | return -EIO; | |
469 | } | |
470 | row_25_column_8 |= ROW25_COLUMN8_PAGE_NOT_FOUND; | |
471 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
472 | ||
473 | dau_no | | |
474 | R8_DO_NOT_CLEAR_MEMORY, | |
475 | ||
476 | R9_CURSER_ROW_25, | |
477 | ||
478 | R10_CURSER_COLUMN_8, | |
479 | ||
480 | row_25_column_8, | |
481 | ||
482 | COMMAND_END)) | |
483 | { | |
484 | return -EIO; | |
485 | } | |
486 | ||
487 | return 0; | |
488 | } | |
489 | ||
490 | /* Requests one videotext page as described in req. The fields of req are | |
491 | * checked and an error is returned if something is invalid. | |
492 | * | |
493 | * Return value: 0 if successful | |
494 | */ | |
495 | static int saa5246a_request_page(struct saa5246a_device *t, | |
496 | vtx_pagereq_t *req) | |
497 | { | |
498 | if (req->pagemask < 0 || req->pagemask >= PGMASK_MAX) | |
499 | return -EINVAL; | |
500 | if (req->pagemask & PGMASK_PAGE) | |
501 | if (req->page < 0 || req->page > PAGE_MAX) | |
502 | return -EINVAL; | |
503 | if (req->pagemask & PGMASK_HOUR) | |
504 | if (req->hour < 0 || req->hour > HOUR_MAX) | |
505 | return -EINVAL; | |
506 | if (req->pagemask & PGMASK_MINUTE) | |
507 | if (req->minute < 0 || req->minute > MINUTE_MAX) | |
508 | return -EINVAL; | |
509 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | |
510 | return -EINVAL; | |
511 | ||
512 | if (i2c_senddata(t, SAA5246A_REGISTER_R2, | |
513 | ||
514 | R2_IN_R3_SELECT_PAGE_HUNDREDS | | |
515 | req->pgbuf << 4 | | |
516 | R2_BANK_0 | | |
517 | R2_HAMMING_CHECK_OFF, | |
518 | ||
519 | HUNDREDS_OF_PAGE(req->page) | | |
520 | R3_HOLD_PAGE | | |
521 | (req->pagemask & PG_HUND ? | |
522 | R3_PAGE_HUNDREDS_DO_CARE : | |
523 | R3_PAGE_HUNDREDS_DO_NOT_CARE), | |
524 | ||
525 | TENS_OF_PAGE(req->page) | | |
526 | (req->pagemask & PG_TEN ? | |
527 | R3_PAGE_TENS_DO_CARE : | |
528 | R3_PAGE_TENS_DO_NOT_CARE), | |
529 | ||
530 | UNITS_OF_PAGE(req->page) | | |
531 | (req->pagemask & PG_UNIT ? | |
532 | R3_PAGE_UNITS_DO_CARE : | |
533 | R3_PAGE_UNITS_DO_NOT_CARE), | |
534 | ||
535 | TENS_OF_HOUR(req->hour) | | |
536 | (req->pagemask & HR_TEN ? | |
537 | R3_HOURS_TENS_DO_CARE : | |
538 | R3_HOURS_TENS_DO_NOT_CARE), | |
539 | ||
540 | UNITS_OF_HOUR(req->hour) | | |
541 | (req->pagemask & HR_UNIT ? | |
542 | R3_HOURS_UNITS_DO_CARE : | |
543 | R3_HOURS_UNITS_DO_NOT_CARE), | |
544 | ||
545 | TENS_OF_MINUTE(req->minute) | | |
546 | (req->pagemask & MIN_TEN ? | |
547 | R3_MINUTES_TENS_DO_CARE : | |
548 | R3_MINUTES_TENS_DO_NOT_CARE), | |
549 | ||
550 | UNITS_OF_MINUTE(req->minute) | | |
551 | (req->pagemask & MIN_UNIT ? | |
552 | R3_MINUTES_UNITS_DO_CARE : | |
553 | R3_MINUTES_UNITS_DO_NOT_CARE), | |
554 | ||
555 | COMMAND_END) || i2c_senddata(t, SAA5246A_REGISTER_R2, | |
556 | ||
557 | R2_IN_R3_SELECT_PAGE_HUNDREDS | | |
558 | req->pgbuf << 4 | | |
559 | R2_BANK_0 | | |
560 | R2_HAMMING_CHECK_OFF, | |
561 | ||
562 | HUNDREDS_OF_PAGE(req->page) | | |
563 | R3_UPDATE_PAGE | | |
564 | (req->pagemask & PG_HUND ? | |
565 | R3_PAGE_HUNDREDS_DO_CARE : | |
566 | R3_PAGE_HUNDREDS_DO_NOT_CARE), | |
567 | ||
568 | COMMAND_END)) | |
569 | { | |
570 | return -EIO; | |
571 | } | |
572 | ||
e8be02a3 | 573 | t->is_searching[req->pgbuf] = true; |
1da177e4 LT |
574 | return 0; |
575 | } | |
576 | ||
577 | /* This routine decodes the page number from the infobits contained in line 25. | |
578 | * | |
579 | * Parameters: | |
580 | * infobits: must be bits 0 to 9 of column 25 | |
581 | * | |
582 | * Return value: page number coded in hexadecimal, i. e. page 123 is coded 0x123 | |
583 | */ | |
584 | static inline int saa5246a_extract_pagenum_from_infobits( | |
585 | unsigned char infobits[10]) | |
586 | { | |
587 | int page_hundreds, page_tens, page_units; | |
588 | ||
589 | page_units = infobits[0] & ROW25_COLUMN0_PAGE_UNITS; | |
590 | page_tens = infobits[1] & ROW25_COLUMN1_PAGE_TENS; | |
591 | page_hundreds = infobits[8] & ROW25_COLUMN8_PAGE_HUNDREDS; | |
592 | ||
593 | /* page 0x.. means page 8.. */ | |
594 | if (page_hundreds == 0) | |
595 | page_hundreds = 8; | |
596 | ||
597 | return((page_hundreds << 8) | (page_tens << 4) | page_units); | |
598 | } | |
599 | ||
600 | /* Decodes the hour from the infobits contained in line 25. | |
601 | * | |
602 | * Parameters: | |
603 | * infobits: must be bits 0 to 9 of column 25 | |
604 | * | |
605 | * Return: hour coded in hexadecimal, i. e. 12h is coded 0x12 | |
606 | */ | |
607 | static inline int saa5246a_extract_hour_from_infobits( | |
608 | unsigned char infobits[10]) | |
609 | { | |
610 | int hour_tens, hour_units; | |
611 | ||
612 | hour_units = infobits[4] & ROW25_COLUMN4_HOUR_UNITS; | |
613 | hour_tens = infobits[5] & ROW25_COLUMN5_HOUR_TENS; | |
614 | ||
615 | return((hour_tens << 4) | hour_units); | |
616 | } | |
617 | ||
618 | /* Decodes the minutes from the infobits contained in line 25. | |
619 | * | |
620 | * Parameters: | |
621 | * infobits: must be bits 0 to 9 of column 25 | |
622 | * | |
623 | * Return: minutes coded in hexadecimal, i. e. 10min is coded 0x10 | |
624 | */ | |
625 | static inline int saa5246a_extract_minutes_from_infobits( | |
626 | unsigned char infobits[10]) | |
627 | { | |
628 | int minutes_tens, minutes_units; | |
629 | ||
630 | minutes_units = infobits[2] & ROW25_COLUMN2_MINUTES_UNITS; | |
631 | minutes_tens = infobits[3] & ROW25_COLUMN3_MINUTES_TENS; | |
632 | ||
633 | return((minutes_tens << 4) | minutes_units); | |
634 | } | |
635 | ||
636 | /* Reads the status bits contained in the first 10 columns of the first line | |
637 | * and extracts the information into info. | |
638 | * | |
639 | * Return value: 0 if successful | |
640 | */ | |
641 | static inline int saa5246a_get_status(struct saa5246a_device *t, | |
642 | vtx_pageinfo_t *info, unsigned char dau_no) | |
643 | { | |
644 | unsigned char infobits[10]; | |
645 | int column; | |
646 | ||
647 | if (dau_no >= NUM_DAUS) | |
648 | return -EINVAL; | |
649 | ||
650 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
651 | ||
652 | dau_no | | |
653 | R8_DO_NOT_CLEAR_MEMORY, | |
654 | ||
655 | R9_CURSER_ROW_25, | |
656 | ||
657 | R10_CURSER_COLUMN_0, | |
658 | ||
659 | COMMAND_END) || | |
660 | i2c_getdata(t, 10, infobits)) | |
661 | { | |
662 | return -EIO; | |
663 | } | |
664 | ||
665 | info->pagenum = saa5246a_extract_pagenum_from_infobits(infobits); | |
666 | info->hour = saa5246a_extract_hour_from_infobits(infobits); | |
667 | info->minute = saa5246a_extract_minutes_from_infobits(infobits); | |
668 | info->charset = ((infobits[7] & ROW25_COLUMN7_CHARACTER_SET) >> 1); | |
669 | info->delete = !!(infobits[3] & ROW25_COLUMN3_DELETE_PAGE); | |
670 | info->headline = !!(infobits[5] & ROW25_COLUMN5_INSERT_HEADLINE); | |
671 | info->subtitle = !!(infobits[5] & ROW25_COLUMN5_INSERT_SUBTITLE); | |
672 | info->supp_header = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_HEADER); | |
673 | info->update = !!(infobits[6] & ROW25_COLUMN6_UPDATE_PAGE); | |
674 | info->inter_seq = !!(infobits[6] & ROW25_COLUMN6_INTERRUPTED_SEQUENCE); | |
675 | info->dis_disp = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_DISPLAY); | |
676 | info->serial = !!(infobits[7] & ROW25_COLUMN7_SERIAL_MODE); | |
677 | info->notfound = !!(infobits[8] & ROW25_COLUMN8_PAGE_NOT_FOUND); | |
678 | info->pblf = !!(infobits[9] & ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR); | |
679 | info->hamming = 0; | |
680 | for (column = 0; column <= 7; column++) { | |
681 | if (infobits[column] & ROW25_COLUMN0_TO_7_HAMMING_ERROR) { | |
682 | info->hamming = 1; | |
683 | break; | |
684 | } | |
685 | } | |
686 | if (!info->hamming && !info->notfound) | |
e8be02a3 | 687 | t->is_searching[dau_no] = false; |
1da177e4 LT |
688 | return 0; |
689 | } | |
690 | ||
691 | /* Reads 1 videotext page buffer of the SAA5246A. | |
692 | * | |
693 | * req is used both as input and as output. It contains information which part | |
694 | * must be read. The videotext page is copied into req->buffer. | |
695 | * | |
696 | * Return value: 0 if successful | |
697 | */ | |
698 | static inline int saa5246a_get_page(struct saa5246a_device *t, | |
699 | vtx_pagereq_t *req) | |
700 | { | |
701 | int start, end, size; | |
702 | char *buf; | |
703 | int err; | |
704 | ||
705 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || | |
706 | req->start < 0 || req->start > req->end || req->end >= VTX_PAGESIZE) | |
707 | return -EINVAL; | |
708 | ||
709 | buf = kmalloc(VTX_PAGESIZE, GFP_KERNEL); | |
710 | if (!buf) | |
711 | return -ENOMEM; | |
712 | ||
713 | /* Read "normal" part of page */ | |
714 | err = -EIO; | |
715 | ||
716 | end = min(req->end, VTX_PAGESIZE - 1); | |
717 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
718 | req->pgbuf | R8_DO_NOT_CLEAR_MEMORY, | |
719 | ROW(req->start), COLUMN(req->start), COMMAND_END)) | |
720 | goto out; | |
721 | if (i2c_getdata(t, end - req->start + 1, buf)) | |
722 | goto out; | |
723 | err = -EFAULT; | |
724 | if (copy_to_user(req->buffer, buf, end - req->start + 1)) | |
725 | goto out; | |
726 | ||
727 | /* Always get the time from buffer 4, since this stupid SAA5246A only | |
728 | * updates the currently displayed buffer... | |
729 | */ | |
730 | if (REQ_CONTAINS_TIME(req)) { | |
731 | start = max(req->start, POS_TIME_START); | |
732 | end = min(req->end, POS_TIME_END); | |
733 | size = end - start + 1; | |
734 | err = -EINVAL; | |
735 | if (size < 0) | |
736 | goto out; | |
737 | err = -EIO; | |
738 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
739 | R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY, | |
740 | R9_CURSER_ROW_0, start, COMMAND_END)) | |
741 | goto out; | |
742 | if (i2c_getdata(t, size, buf)) | |
743 | goto out; | |
744 | err = -EFAULT; | |
745 | if (copy_to_user(req->buffer + start - req->start, buf, size)) | |
746 | goto out; | |
747 | } | |
748 | /* Insert the header from buffer 4 only, if acquisition circuit is still searching for a page */ | |
749 | if (REQ_CONTAINS_HEADER(req) && t->is_searching[req->pgbuf]) { | |
750 | start = max(req->start, POS_HEADER_START); | |
751 | end = min(req->end, POS_HEADER_END); | |
752 | size = end - start + 1; | |
753 | err = -EINVAL; | |
754 | if (size < 0) | |
755 | goto out; | |
756 | err = -EIO; | |
757 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
758 | R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY, | |
759 | R9_CURSER_ROW_0, start, COMMAND_END)) | |
760 | goto out; | |
761 | if (i2c_getdata(t, end - start + 1, buf)) | |
762 | goto out; | |
763 | err = -EFAULT; | |
764 | if (copy_to_user(req->buffer + start - req->start, buf, size)) | |
765 | goto out; | |
766 | } | |
767 | err = 0; | |
768 | out: | |
769 | kfree(buf); | |
770 | return err; | |
771 | } | |
772 | ||
773 | /* Stops the acquisition circuit given in dau_no. The page buffer associated | |
774 | * with this acquisition circuit will no more be updated. The other daus are | |
775 | * not affected. | |
776 | * | |
777 | * Return value: 0 if successful | |
778 | */ | |
779 | static inline int saa5246a_stop_dau(struct saa5246a_device *t, | |
780 | unsigned char dau_no) | |
781 | { | |
782 | if (dau_no >= NUM_DAUS) | |
783 | return -EINVAL; | |
784 | if (i2c_senddata(t, SAA5246A_REGISTER_R2, | |
785 | ||
786 | R2_IN_R3_SELECT_PAGE_HUNDREDS | | |
787 | dau_no << 4 | | |
788 | R2_BANK_0 | | |
789 | R2_HAMMING_CHECK_OFF, | |
790 | ||
791 | R3_PAGE_HUNDREDS_0 | | |
792 | R3_HOLD_PAGE | | |
793 | R3_PAGE_HUNDREDS_DO_NOT_CARE, | |
794 | ||
795 | COMMAND_END)) | |
796 | { | |
797 | return -EIO; | |
798 | } | |
e8be02a3 | 799 | t->is_searching[dau_no] = false; |
1da177e4 LT |
800 | return 0; |
801 | } | |
802 | ||
803 | /* Handles ioctls defined in videotext.h | |
804 | * | |
805 | * Returns 0 if successful | |
806 | */ | |
f473bf76 | 807 | static int do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg) |
1da177e4 | 808 | { |
c170ecf4 | 809 | struct saa5246a_device *t = video_drvdata(file); |
601e9444 | 810 | |
1da177e4 LT |
811 | switch(cmd) |
812 | { | |
813 | case VTXIOCGETINFO: | |
814 | { | |
815 | vtx_info_t *info = arg; | |
816 | ||
817 | info->version_major = MAJOR_VERSION; | |
818 | info->version_minor = MINOR_VERSION; | |
819 | info->numpages = NUM_DAUS; | |
820 | return 0; | |
821 | } | |
822 | ||
823 | case VTXIOCCLRPAGE: | |
824 | { | |
825 | vtx_pagereq_t *req = arg; | |
826 | ||
827 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | |
828 | return -EINVAL; | |
829 | memset(t->pgbuf[req->pgbuf], ' ', sizeof(t->pgbuf[0])); | |
830 | return 0; | |
831 | } | |
832 | ||
833 | case VTXIOCCLRFOUND: | |
834 | { | |
835 | vtx_pagereq_t *req = arg; | |
836 | ||
837 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | |
838 | return -EINVAL; | |
839 | return(saa5246a_clear_found_bit(t, req->pgbuf)); | |
840 | } | |
841 | ||
842 | case VTXIOCPAGEREQ: | |
843 | { | |
844 | vtx_pagereq_t *req = arg; | |
845 | ||
846 | return(saa5246a_request_page(t, req)); | |
847 | } | |
848 | ||
849 | case VTXIOCGETSTAT: | |
850 | { | |
851 | vtx_pagereq_t *req = arg; | |
852 | vtx_pageinfo_t info; | |
853 | int rval; | |
854 | ||
855 | if ((rval = saa5246a_get_status(t, &info, req->pgbuf))) | |
856 | return rval; | |
857 | if(copy_to_user(req->buffer, &info, | |
858 | sizeof(vtx_pageinfo_t))) | |
859 | return -EFAULT; | |
860 | return 0; | |
861 | } | |
862 | ||
863 | case VTXIOCGETPAGE: | |
864 | { | |
865 | vtx_pagereq_t *req = arg; | |
866 | ||
867 | return(saa5246a_get_page(t, req)); | |
868 | } | |
869 | ||
870 | case VTXIOCSTOPDAU: | |
871 | { | |
872 | vtx_pagereq_t *req = arg; | |
873 | ||
874 | return(saa5246a_stop_dau(t, req->pgbuf)); | |
875 | } | |
876 | ||
877 | case VTXIOCPUTPAGE: | |
878 | case VTXIOCSETDISP: | |
879 | case VTXIOCPUTSTAT: | |
880 | return 0; | |
881 | ||
882 | case VTXIOCCLRCACHE: | |
883 | { | |
884 | return 0; | |
885 | } | |
886 | ||
887 | case VTXIOCSETVIRT: | |
888 | { | |
889 | /* I do not know what "virtual mode" means */ | |
890 | return 0; | |
891 | } | |
892 | } | |
893 | return -EINVAL; | |
894 | } | |
895 | ||
896 | /* | |
897 | * Translates old vtx IOCTLs to new ones | |
898 | * | |
899 | * This keeps new kernel versions compatible with old userspace programs. | |
900 | */ | |
901 | static inline unsigned int vtx_fix_command(unsigned int cmd) | |
902 | { | |
903 | switch (cmd) { | |
904 | case VTXIOCGETINFO_OLD: | |
905 | cmd = VTXIOCGETINFO; | |
906 | break; | |
907 | case VTXIOCCLRPAGE_OLD: | |
908 | cmd = VTXIOCCLRPAGE; | |
909 | break; | |
910 | case VTXIOCCLRFOUND_OLD: | |
911 | cmd = VTXIOCCLRFOUND; | |
912 | break; | |
913 | case VTXIOCPAGEREQ_OLD: | |
914 | cmd = VTXIOCPAGEREQ; | |
915 | break; | |
916 | case VTXIOCGETSTAT_OLD: | |
917 | cmd = VTXIOCGETSTAT; | |
918 | break; | |
919 | case VTXIOCGETPAGE_OLD: | |
920 | cmd = VTXIOCGETPAGE; | |
921 | break; | |
922 | case VTXIOCSTOPDAU_OLD: | |
923 | cmd = VTXIOCSTOPDAU; | |
924 | break; | |
925 | case VTXIOCPUTPAGE_OLD: | |
926 | cmd = VTXIOCPUTPAGE; | |
927 | break; | |
928 | case VTXIOCSETDISP_OLD: | |
929 | cmd = VTXIOCSETDISP; | |
930 | break; | |
931 | case VTXIOCPUTSTAT_OLD: | |
932 | cmd = VTXIOCPUTSTAT; | |
933 | break; | |
934 | case VTXIOCCLRCACHE_OLD: | |
935 | cmd = VTXIOCCLRCACHE; | |
936 | break; | |
937 | case VTXIOCSETVIRT_OLD: | |
938 | cmd = VTXIOCSETVIRT; | |
939 | break; | |
940 | } | |
941 | return cmd; | |
942 | } | |
943 | ||
944 | /* | |
945 | * Handle the locking | |
946 | */ | |
bec43661 | 947 | static int saa5246a_ioctl(struct file *file, |
1da177e4 LT |
948 | unsigned int cmd, unsigned long arg) |
949 | { | |
c170ecf4 | 950 | struct saa5246a_device *t = video_drvdata(file); |
1da177e4 LT |
951 | int err; |
952 | ||
953 | cmd = vtx_fix_command(cmd); | |
3593cab5 | 954 | mutex_lock(&t->lock); |
f473bf76 | 955 | err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl); |
3593cab5 | 956 | mutex_unlock(&t->lock); |
1da177e4 LT |
957 | return err; |
958 | } | |
959 | ||
bec43661 | 960 | static int saa5246a_open(struct file *file) |
1da177e4 | 961 | { |
c170ecf4 | 962 | struct saa5246a_device *t = video_drvdata(file); |
1da177e4 | 963 | |
7d43cd53 HV |
964 | if (t->client == NULL) |
965 | return -ENODEV; | |
1da177e4 | 966 | |
7d43cd53 HV |
967 | if (test_and_set_bit(0, &t->in_use)) |
968 | return -EBUSY; | |
1da177e4 LT |
969 | |
970 | if (i2c_senddata(t, SAA5246A_REGISTER_R0, | |
1da177e4 LT |
971 | R0_SELECT_R11 | |
972 | R0_PLL_TIME_CONSTANT_LONG | | |
973 | R0_ENABLE_nODD_EVEN_OUTPUT | | |
974 | R0_ENABLE_HDR_POLL | | |
975 | R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED | | |
976 | R0_NO_FREE_RUN_PLL | | |
977 | R0_NO_AUTOMATIC_FASTEXT_PROMPT, | |
978 | ||
979 | R1_NON_INTERLACED_312_312_LINES | | |
980 | R1_DEW | | |
981 | R1_EXTENDED_PACKET_DISABLE | | |
982 | R1_DAUS_ALL_ON | | |
983 | R1_8_BITS_NO_PARITY | | |
984 | R1_VCS_TO_SCS, | |
985 | ||
986 | COMMAND_END) || | |
987 | i2c_senddata(t, SAA5246A_REGISTER_R4, | |
988 | ||
989 | /* We do not care much for the TV display but nevertheless we | |
990 | * need the currently displayed page later because only on that | |
991 | * page the time is updated. */ | |
992 | R4_DISPLAY_PAGE_4, | |
993 | ||
994 | COMMAND_END)) | |
995 | { | |
7d43cd53 HV |
996 | clear_bit(0, &t->in_use); |
997 | return -EIO; | |
1da177e4 | 998 | } |
1da177e4 | 999 | return 0; |
1da177e4 LT |
1000 | } |
1001 | ||
bec43661 | 1002 | static int saa5246a_release(struct file *file) |
1da177e4 | 1003 | { |
c170ecf4 | 1004 | struct saa5246a_device *t = video_drvdata(file); |
1da177e4 LT |
1005 | |
1006 | /* Stop all acquisition circuits. */ | |
1007 | i2c_senddata(t, SAA5246A_REGISTER_R1, | |
1008 | ||
1009 | R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES | | |
1010 | R1_DEW | | |
1011 | R1_EXTENDED_PACKET_DISABLE | | |
1012 | R1_DAUS_ALL_OFF | | |
1013 | R1_8_BITS_NO_PARITY | | |
1014 | R1_VCS_TO_SCS, | |
1015 | ||
1016 | COMMAND_END); | |
7d43cd53 | 1017 | clear_bit(0, &t->in_use); |
1da177e4 LT |
1018 | return 0; |
1019 | } | |
1020 | ||
bec43661 | 1021 | static const struct v4l2_file_operations saa_fops = { |
1da177e4 LT |
1022 | .owner = THIS_MODULE, |
1023 | .open = saa5246a_open, | |
1024 | .release = saa5246a_release, | |
1025 | .ioctl = saa5246a_ioctl, | |
1da177e4 LT |
1026 | }; |
1027 | ||
1028 | static struct video_device saa_template = | |
1029 | { | |
71ef85c7 | 1030 | .name = "saa5246a", |
1da177e4 LT |
1031 | .fops = &saa_fops, |
1032 | .release = video_device_release, | |
1033 | .minor = -1, | |
1034 | }; | |
71ef85c7 HV |
1035 | |
1036 | /* Addresses to scan */ | |
1037 | static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; | |
1038 | ||
1039 | I2C_CLIENT_INSMOD; | |
1040 | ||
1041 | static int saa5246a_probe(struct i2c_client *client, | |
1042 | const struct i2c_device_id *id) | |
1043 | { | |
1044 | int pgbuf; | |
1045 | int err; | |
1046 | struct video_device *vd; | |
1047 | struct saa5246a_device *t; | |
1048 | ||
1049 | v4l_info(client, "chip found @ 0x%x (%s)\n", | |
1050 | client->addr << 1, client->adapter->name); | |
1051 | v4l_info(client, "VideoText version %d.%d\n", | |
1052 | MAJOR_VERSION, MINOR_VERSION); | |
1053 | t = kzalloc(sizeof(*t), GFP_KERNEL); | |
1054 | if (t == NULL) | |
1055 | return -ENOMEM; | |
1056 | mutex_init(&t->lock); | |
1057 | ||
1058 | /* Now create a video4linux device */ | |
1059 | vd = video_device_alloc(); | |
1060 | if (vd == NULL) { | |
1061 | kfree(t); | |
1062 | return -ENOMEM; | |
1063 | } | |
1064 | i2c_set_clientdata(client, vd); | |
1065 | memcpy(vd, &saa_template, sizeof(*vd)); | |
1066 | ||
1067 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { | |
1068 | memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); | |
1069 | t->is_searching[pgbuf] = false; | |
1070 | } | |
1071 | video_set_drvdata(vd, t); | |
1072 | ||
1073 | /* Register it */ | |
1074 | err = video_register_device(vd, VFL_TYPE_VTX, -1); | |
1075 | if (err < 0) { | |
1076 | kfree(t); | |
1077 | video_device_release(vd); | |
1078 | return err; | |
1079 | } | |
1080 | t->client = client; | |
1081 | return 0; | |
1082 | } | |
1083 | ||
1084 | static int saa5246a_remove(struct i2c_client *client) | |
1085 | { | |
1086 | struct video_device *vd = i2c_get_clientdata(client); | |
1087 | ||
1088 | video_unregister_device(vd); | |
1089 | kfree(video_get_drvdata(vd)); | |
1090 | return 0; | |
1091 | } | |
1092 | ||
1093 | static const struct i2c_device_id saa5246a_id[] = { | |
1094 | { "saa5246a", 0 }, | |
1095 | { } | |
1096 | }; | |
1097 | MODULE_DEVICE_TABLE(i2c, saa5246a_id); | |
1098 | ||
1099 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { | |
1100 | .name = "saa5246a", | |
1101 | .driverid = I2C_DRIVERID_SAA5249, | |
1102 | .probe = saa5246a_probe, | |
1103 | .remove = saa5246a_remove, | |
1104 | .id_table = saa5246a_id, | |
1105 | }; |