]>
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 <[email protected]> | |
10 | * | |
11 | * Derived from | |
12 | * | |
13 | * saa5249 driver | |
14 | * Copyright (C) 1998 Richard Guenther | |
15 | * <[email protected]> | |
16 | * | |
17 | * with changes by | |
d9b01449 | 18 | * Alan Cox <[email protected]> |
1da177e4 LT |
19 | * |
20 | * and | |
21 | * | |
22 | * vtx.c | |
23 | * Copyright (C) 1994-97 Martin Buck <[email protected]> | |
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> | |
71ef85c7 HV |
46 | #include <linux/mutex.h> |
47 | #include <linux/videotext.h> | |
1b8dac15 HV |
48 | #include <linux/videodev2.h> |
49 | #include <media/v4l2-device.h> | |
50 | #include <media/v4l2-chip-ident.h> | |
35ea11ff | 51 | #include <media/v4l2-ioctl.h> |
1b8dac15 | 52 | #include <media/v4l2-i2c-drv.h> |
1da177e4 LT |
53 | |
54 | MODULE_AUTHOR("Michael Geng <[email protected]>"); | |
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 | { |
1b8dac15 HV |
391 | struct v4l2_subdev sd; |
392 | struct video_device *vdev; | |
71ef85c7 HV |
393 | u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; |
394 | int is_searching[NUM_DAUS]; | |
71ef85c7 HV |
395 | unsigned long in_use; |
396 | struct mutex lock; | |
1da177e4 LT |
397 | }; |
398 | ||
1b8dac15 HV |
399 | static inline struct saa5246a_device *to_dev(struct v4l2_subdev *sd) |
400 | { | |
401 | return container_of(sd, struct saa5246a_device, sd); | |
402 | } | |
403 | ||
71ef85c7 HV |
404 | static struct video_device saa_template; /* Declared near bottom */ |
405 | ||
406 | /* | |
407 | * I2C interfaces | |
408 | */ | |
1da177e4 LT |
409 | |
410 | static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) | |
411 | { | |
1b8dac15 | 412 | struct i2c_client *client = v4l2_get_subdevdata(&t->sd); |
1da177e4 LT |
413 | char buf[64]; |
414 | ||
415 | buf[0] = reg; | |
416 | memcpy(buf+1, data, count); | |
417 | ||
1b8dac15 | 418 | if (i2c_master_send(client, buf, count + 1) == count + 1) |
1da177e4 LT |
419 | return 0; |
420 | return -1; | |
421 | } | |
422 | ||
423 | static int i2c_senddata(struct saa5246a_device *t, ...) | |
424 | { | |
425 | unsigned char buf[64]; | |
426 | int v; | |
b730a81c | 427 | int ct = 0; |
1da177e4 | 428 | va_list argp; |
b730a81c | 429 | va_start(argp, t); |
1da177e4 | 430 | |
b730a81c RK |
431 | while ((v = va_arg(argp, int)) != -1) |
432 | buf[ct++] = v; | |
433 | ||
434 | va_end(argp); | |
1da177e4 LT |
435 | return i2c_sendbuf(t, buf[0], ct-1, buf+1); |
436 | } | |
437 | ||
db955170 | 438 | /* Get count number of bytes from I²C-device at address adr, store them in buf. |
1da177e4 | 439 | * Start & stop handshaking is done by this routine, ack will be sent after the |
e8be02a3 | 440 | * last byte to inhibit further sending of data. If uaccess is 'true', data is |
db955170 | 441 | * written to user-space with put_user. Returns -1 if I²C-device didn't send |
1da177e4 LT |
442 | * acknowledge, 0 otherwise |
443 | */ | |
444 | static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf) | |
445 | { | |
1b8dac15 HV |
446 | struct i2c_client *client = v4l2_get_subdevdata(&t->sd); |
447 | ||
448 | if (i2c_master_recv(client, buf, count) != count) | |
1da177e4 LT |
449 | return -1; |
450 | return 0; | |
451 | } | |
452 | ||
453 | /* When a page is found then the not FOUND bit in one of the status registers | |
454 | * of the SAA5264A chip is cleared. Unfortunately this bit is not set | |
455 | * automatically when a new page is requested. Instead this function must be | |
456 | * called after a page has been requested. | |
457 | * | |
458 | * Return value: 0 if successful | |
459 | */ | |
460 | static int saa5246a_clear_found_bit(struct saa5246a_device *t, | |
461 | unsigned char dau_no) | |
462 | { | |
463 | unsigned char row_25_column_8; | |
464 | ||
465 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
466 | ||
467 | dau_no | | |
468 | R8_DO_NOT_CLEAR_MEMORY, | |
469 | ||
470 | R9_CURSER_ROW_25, | |
471 | ||
472 | R10_CURSER_COLUMN_8, | |
473 | ||
474 | COMMAND_END) || | |
475 | i2c_getdata(t, 1, &row_25_column_8)) | |
476 | { | |
477 | return -EIO; | |
478 | } | |
479 | row_25_column_8 |= ROW25_COLUMN8_PAGE_NOT_FOUND; | |
480 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
481 | ||
482 | dau_no | | |
483 | R8_DO_NOT_CLEAR_MEMORY, | |
484 | ||
485 | R9_CURSER_ROW_25, | |
486 | ||
487 | R10_CURSER_COLUMN_8, | |
488 | ||
489 | row_25_column_8, | |
490 | ||
491 | COMMAND_END)) | |
492 | { | |
493 | return -EIO; | |
494 | } | |
495 | ||
496 | return 0; | |
497 | } | |
498 | ||
499 | /* Requests one videotext page as described in req. The fields of req are | |
500 | * checked and an error is returned if something is invalid. | |
501 | * | |
502 | * Return value: 0 if successful | |
503 | */ | |
504 | static int saa5246a_request_page(struct saa5246a_device *t, | |
505 | vtx_pagereq_t *req) | |
506 | { | |
507 | if (req->pagemask < 0 || req->pagemask >= PGMASK_MAX) | |
508 | return -EINVAL; | |
509 | if (req->pagemask & PGMASK_PAGE) | |
510 | if (req->page < 0 || req->page > PAGE_MAX) | |
511 | return -EINVAL; | |
512 | if (req->pagemask & PGMASK_HOUR) | |
513 | if (req->hour < 0 || req->hour > HOUR_MAX) | |
514 | return -EINVAL; | |
515 | if (req->pagemask & PGMASK_MINUTE) | |
516 | if (req->minute < 0 || req->minute > MINUTE_MAX) | |
517 | return -EINVAL; | |
518 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | |
519 | return -EINVAL; | |
520 | ||
521 | if (i2c_senddata(t, SAA5246A_REGISTER_R2, | |
522 | ||
523 | R2_IN_R3_SELECT_PAGE_HUNDREDS | | |
524 | req->pgbuf << 4 | | |
525 | R2_BANK_0 | | |
526 | R2_HAMMING_CHECK_OFF, | |
527 | ||
528 | HUNDREDS_OF_PAGE(req->page) | | |
529 | R3_HOLD_PAGE | | |
530 | (req->pagemask & PG_HUND ? | |
531 | R3_PAGE_HUNDREDS_DO_CARE : | |
532 | R3_PAGE_HUNDREDS_DO_NOT_CARE), | |
533 | ||
534 | TENS_OF_PAGE(req->page) | | |
535 | (req->pagemask & PG_TEN ? | |
536 | R3_PAGE_TENS_DO_CARE : | |
537 | R3_PAGE_TENS_DO_NOT_CARE), | |
538 | ||
539 | UNITS_OF_PAGE(req->page) | | |
540 | (req->pagemask & PG_UNIT ? | |
541 | R3_PAGE_UNITS_DO_CARE : | |
542 | R3_PAGE_UNITS_DO_NOT_CARE), | |
543 | ||
544 | TENS_OF_HOUR(req->hour) | | |
545 | (req->pagemask & HR_TEN ? | |
546 | R3_HOURS_TENS_DO_CARE : | |
547 | R3_HOURS_TENS_DO_NOT_CARE), | |
548 | ||
549 | UNITS_OF_HOUR(req->hour) | | |
550 | (req->pagemask & HR_UNIT ? | |
551 | R3_HOURS_UNITS_DO_CARE : | |
552 | R3_HOURS_UNITS_DO_NOT_CARE), | |
553 | ||
554 | TENS_OF_MINUTE(req->minute) | | |
555 | (req->pagemask & MIN_TEN ? | |
556 | R3_MINUTES_TENS_DO_CARE : | |
557 | R3_MINUTES_TENS_DO_NOT_CARE), | |
558 | ||
559 | UNITS_OF_MINUTE(req->minute) | | |
560 | (req->pagemask & MIN_UNIT ? | |
561 | R3_MINUTES_UNITS_DO_CARE : | |
562 | R3_MINUTES_UNITS_DO_NOT_CARE), | |
563 | ||
564 | COMMAND_END) || i2c_senddata(t, SAA5246A_REGISTER_R2, | |
565 | ||
566 | R2_IN_R3_SELECT_PAGE_HUNDREDS | | |
567 | req->pgbuf << 4 | | |
568 | R2_BANK_0 | | |
569 | R2_HAMMING_CHECK_OFF, | |
570 | ||
571 | HUNDREDS_OF_PAGE(req->page) | | |
572 | R3_UPDATE_PAGE | | |
573 | (req->pagemask & PG_HUND ? | |
574 | R3_PAGE_HUNDREDS_DO_CARE : | |
575 | R3_PAGE_HUNDREDS_DO_NOT_CARE), | |
576 | ||
577 | COMMAND_END)) | |
578 | { | |
579 | return -EIO; | |
580 | } | |
581 | ||
e8be02a3 | 582 | t->is_searching[req->pgbuf] = true; |
1da177e4 LT |
583 | return 0; |
584 | } | |
585 | ||
586 | /* This routine decodes the page number from the infobits contained in line 25. | |
587 | * | |
588 | * Parameters: | |
589 | * infobits: must be bits 0 to 9 of column 25 | |
590 | * | |
591 | * Return value: page number coded in hexadecimal, i. e. page 123 is coded 0x123 | |
592 | */ | |
593 | static inline int saa5246a_extract_pagenum_from_infobits( | |
594 | unsigned char infobits[10]) | |
595 | { | |
596 | int page_hundreds, page_tens, page_units; | |
597 | ||
598 | page_units = infobits[0] & ROW25_COLUMN0_PAGE_UNITS; | |
599 | page_tens = infobits[1] & ROW25_COLUMN1_PAGE_TENS; | |
600 | page_hundreds = infobits[8] & ROW25_COLUMN8_PAGE_HUNDREDS; | |
601 | ||
602 | /* page 0x.. means page 8.. */ | |
603 | if (page_hundreds == 0) | |
604 | page_hundreds = 8; | |
605 | ||
606 | return((page_hundreds << 8) | (page_tens << 4) | page_units); | |
607 | } | |
608 | ||
609 | /* Decodes the hour from the infobits contained in line 25. | |
610 | * | |
611 | * Parameters: | |
612 | * infobits: must be bits 0 to 9 of column 25 | |
613 | * | |
614 | * Return: hour coded in hexadecimal, i. e. 12h is coded 0x12 | |
615 | */ | |
616 | static inline int saa5246a_extract_hour_from_infobits( | |
617 | unsigned char infobits[10]) | |
618 | { | |
619 | int hour_tens, hour_units; | |
620 | ||
621 | hour_units = infobits[4] & ROW25_COLUMN4_HOUR_UNITS; | |
622 | hour_tens = infobits[5] & ROW25_COLUMN5_HOUR_TENS; | |
623 | ||
624 | return((hour_tens << 4) | hour_units); | |
625 | } | |
626 | ||
627 | /* Decodes the minutes from the infobits contained in line 25. | |
628 | * | |
629 | * Parameters: | |
630 | * infobits: must be bits 0 to 9 of column 25 | |
631 | * | |
632 | * Return: minutes coded in hexadecimal, i. e. 10min is coded 0x10 | |
633 | */ | |
634 | static inline int saa5246a_extract_minutes_from_infobits( | |
635 | unsigned char infobits[10]) | |
636 | { | |
637 | int minutes_tens, minutes_units; | |
638 | ||
639 | minutes_units = infobits[2] & ROW25_COLUMN2_MINUTES_UNITS; | |
640 | minutes_tens = infobits[3] & ROW25_COLUMN3_MINUTES_TENS; | |
641 | ||
642 | return((minutes_tens << 4) | minutes_units); | |
643 | } | |
644 | ||
645 | /* Reads the status bits contained in the first 10 columns of the first line | |
646 | * and extracts the information into info. | |
647 | * | |
648 | * Return value: 0 if successful | |
649 | */ | |
650 | static inline int saa5246a_get_status(struct saa5246a_device *t, | |
651 | vtx_pageinfo_t *info, unsigned char dau_no) | |
652 | { | |
653 | unsigned char infobits[10]; | |
654 | int column; | |
655 | ||
656 | if (dau_no >= NUM_DAUS) | |
657 | return -EINVAL; | |
658 | ||
659 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
660 | ||
661 | dau_no | | |
662 | R8_DO_NOT_CLEAR_MEMORY, | |
663 | ||
664 | R9_CURSER_ROW_25, | |
665 | ||
666 | R10_CURSER_COLUMN_0, | |
667 | ||
668 | COMMAND_END) || | |
669 | i2c_getdata(t, 10, infobits)) | |
670 | { | |
671 | return -EIO; | |
672 | } | |
673 | ||
674 | info->pagenum = saa5246a_extract_pagenum_from_infobits(infobits); | |
675 | info->hour = saa5246a_extract_hour_from_infobits(infobits); | |
676 | info->minute = saa5246a_extract_minutes_from_infobits(infobits); | |
677 | info->charset = ((infobits[7] & ROW25_COLUMN7_CHARACTER_SET) >> 1); | |
678 | info->delete = !!(infobits[3] & ROW25_COLUMN3_DELETE_PAGE); | |
679 | info->headline = !!(infobits[5] & ROW25_COLUMN5_INSERT_HEADLINE); | |
680 | info->subtitle = !!(infobits[5] & ROW25_COLUMN5_INSERT_SUBTITLE); | |
681 | info->supp_header = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_HEADER); | |
682 | info->update = !!(infobits[6] & ROW25_COLUMN6_UPDATE_PAGE); | |
683 | info->inter_seq = !!(infobits[6] & ROW25_COLUMN6_INTERRUPTED_SEQUENCE); | |
684 | info->dis_disp = !!(infobits[6] & ROW25_COLUMN6_SUPPRESS_DISPLAY); | |
685 | info->serial = !!(infobits[7] & ROW25_COLUMN7_SERIAL_MODE); | |
686 | info->notfound = !!(infobits[8] & ROW25_COLUMN8_PAGE_NOT_FOUND); | |
687 | info->pblf = !!(infobits[9] & ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR); | |
688 | info->hamming = 0; | |
689 | for (column = 0; column <= 7; column++) { | |
690 | if (infobits[column] & ROW25_COLUMN0_TO_7_HAMMING_ERROR) { | |
691 | info->hamming = 1; | |
692 | break; | |
693 | } | |
694 | } | |
695 | if (!info->hamming && !info->notfound) | |
e8be02a3 | 696 | t->is_searching[dau_no] = false; |
1da177e4 LT |
697 | return 0; |
698 | } | |
699 | ||
700 | /* Reads 1 videotext page buffer of the SAA5246A. | |
701 | * | |
702 | * req is used both as input and as output. It contains information which part | |
703 | * must be read. The videotext page is copied into req->buffer. | |
704 | * | |
705 | * Return value: 0 if successful | |
706 | */ | |
707 | static inline int saa5246a_get_page(struct saa5246a_device *t, | |
708 | vtx_pagereq_t *req) | |
709 | { | |
710 | int start, end, size; | |
711 | char *buf; | |
712 | int err; | |
713 | ||
714 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || | |
715 | req->start < 0 || req->start > req->end || req->end >= VTX_PAGESIZE) | |
716 | return -EINVAL; | |
717 | ||
718 | buf = kmalloc(VTX_PAGESIZE, GFP_KERNEL); | |
719 | if (!buf) | |
720 | return -ENOMEM; | |
721 | ||
722 | /* Read "normal" part of page */ | |
723 | err = -EIO; | |
724 | ||
725 | end = min(req->end, VTX_PAGESIZE - 1); | |
726 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
727 | req->pgbuf | R8_DO_NOT_CLEAR_MEMORY, | |
728 | ROW(req->start), COLUMN(req->start), COMMAND_END)) | |
729 | goto out; | |
730 | if (i2c_getdata(t, end - req->start + 1, buf)) | |
731 | goto out; | |
732 | err = -EFAULT; | |
733 | if (copy_to_user(req->buffer, buf, end - req->start + 1)) | |
734 | goto out; | |
735 | ||
736 | /* Always get the time from buffer 4, since this stupid SAA5246A only | |
737 | * updates the currently displayed buffer... | |
738 | */ | |
739 | if (REQ_CONTAINS_TIME(req)) { | |
740 | start = max(req->start, POS_TIME_START); | |
741 | end = min(req->end, POS_TIME_END); | |
742 | size = end - start + 1; | |
743 | err = -EINVAL; | |
744 | if (size < 0) | |
745 | goto out; | |
746 | err = -EIO; | |
747 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
748 | R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY, | |
749 | R9_CURSER_ROW_0, start, COMMAND_END)) | |
750 | goto out; | |
751 | if (i2c_getdata(t, size, buf)) | |
752 | goto out; | |
753 | err = -EFAULT; | |
754 | if (copy_to_user(req->buffer + start - req->start, buf, size)) | |
755 | goto out; | |
756 | } | |
757 | /* Insert the header from buffer 4 only, if acquisition circuit is still searching for a page */ | |
758 | if (REQ_CONTAINS_HEADER(req) && t->is_searching[req->pgbuf]) { | |
759 | start = max(req->start, POS_HEADER_START); | |
760 | end = min(req->end, POS_HEADER_END); | |
761 | size = end - start + 1; | |
762 | err = -EINVAL; | |
763 | if (size < 0) | |
764 | goto out; | |
765 | err = -EIO; | |
766 | if (i2c_senddata(t, SAA5246A_REGISTER_R8, | |
767 | R8_ACTIVE_CHAPTER_4 | R8_DO_NOT_CLEAR_MEMORY, | |
768 | R9_CURSER_ROW_0, start, COMMAND_END)) | |
769 | goto out; | |
770 | if (i2c_getdata(t, end - start + 1, buf)) | |
771 | goto out; | |
772 | err = -EFAULT; | |
773 | if (copy_to_user(req->buffer + start - req->start, buf, size)) | |
774 | goto out; | |
775 | } | |
776 | err = 0; | |
777 | out: | |
778 | kfree(buf); | |
779 | return err; | |
780 | } | |
781 | ||
782 | /* Stops the acquisition circuit given in dau_no. The page buffer associated | |
783 | * with this acquisition circuit will no more be updated. The other daus are | |
784 | * not affected. | |
785 | * | |
786 | * Return value: 0 if successful | |
787 | */ | |
788 | static inline int saa5246a_stop_dau(struct saa5246a_device *t, | |
789 | unsigned char dau_no) | |
790 | { | |
791 | if (dau_no >= NUM_DAUS) | |
792 | return -EINVAL; | |
793 | if (i2c_senddata(t, SAA5246A_REGISTER_R2, | |
794 | ||
795 | R2_IN_R3_SELECT_PAGE_HUNDREDS | | |
796 | dau_no << 4 | | |
797 | R2_BANK_0 | | |
798 | R2_HAMMING_CHECK_OFF, | |
799 | ||
800 | R3_PAGE_HUNDREDS_0 | | |
801 | R3_HOLD_PAGE | | |
802 | R3_PAGE_HUNDREDS_DO_NOT_CARE, | |
803 | ||
804 | COMMAND_END)) | |
805 | { | |
806 | return -EIO; | |
807 | } | |
e8be02a3 | 808 | t->is_searching[dau_no] = false; |
1da177e4 LT |
809 | return 0; |
810 | } | |
811 | ||
812 | /* Handles ioctls defined in videotext.h | |
813 | * | |
814 | * Returns 0 if successful | |
815 | */ | |
069b7479 | 816 | static long do_saa5246a_ioctl(struct file *file, unsigned int cmd, void *arg) |
1da177e4 | 817 | { |
c170ecf4 | 818 | struct saa5246a_device *t = video_drvdata(file); |
601e9444 | 819 | |
1da177e4 LT |
820 | switch(cmd) |
821 | { | |
822 | case VTXIOCGETINFO: | |
823 | { | |
824 | vtx_info_t *info = arg; | |
825 | ||
826 | info->version_major = MAJOR_VERSION; | |
827 | info->version_minor = MINOR_VERSION; | |
828 | info->numpages = NUM_DAUS; | |
829 | return 0; | |
830 | } | |
831 | ||
832 | case VTXIOCCLRPAGE: | |
833 | { | |
834 | vtx_pagereq_t *req = arg; | |
835 | ||
836 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | |
837 | return -EINVAL; | |
838 | memset(t->pgbuf[req->pgbuf], ' ', sizeof(t->pgbuf[0])); | |
839 | return 0; | |
840 | } | |
841 | ||
842 | case VTXIOCCLRFOUND: | |
843 | { | |
844 | vtx_pagereq_t *req = arg; | |
845 | ||
846 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | |
847 | return -EINVAL; | |
848 | return(saa5246a_clear_found_bit(t, req->pgbuf)); | |
849 | } | |
850 | ||
851 | case VTXIOCPAGEREQ: | |
852 | { | |
853 | vtx_pagereq_t *req = arg; | |
854 | ||
855 | return(saa5246a_request_page(t, req)); | |
856 | } | |
857 | ||
858 | case VTXIOCGETSTAT: | |
859 | { | |
860 | vtx_pagereq_t *req = arg; | |
861 | vtx_pageinfo_t info; | |
862 | int rval; | |
863 | ||
864 | if ((rval = saa5246a_get_status(t, &info, req->pgbuf))) | |
865 | return rval; | |
866 | if(copy_to_user(req->buffer, &info, | |
867 | sizeof(vtx_pageinfo_t))) | |
868 | return -EFAULT; | |
869 | return 0; | |
870 | } | |
871 | ||
872 | case VTXIOCGETPAGE: | |
873 | { | |
874 | vtx_pagereq_t *req = arg; | |
875 | ||
876 | return(saa5246a_get_page(t, req)); | |
877 | } | |
878 | ||
879 | case VTXIOCSTOPDAU: | |
880 | { | |
881 | vtx_pagereq_t *req = arg; | |
882 | ||
883 | return(saa5246a_stop_dau(t, req->pgbuf)); | |
884 | } | |
885 | ||
886 | case VTXIOCPUTPAGE: | |
887 | case VTXIOCSETDISP: | |
888 | case VTXIOCPUTSTAT: | |
889 | return 0; | |
890 | ||
891 | case VTXIOCCLRCACHE: | |
892 | { | |
893 | return 0; | |
894 | } | |
895 | ||
896 | case VTXIOCSETVIRT: | |
897 | { | |
898 | /* I do not know what "virtual mode" means */ | |
899 | return 0; | |
900 | } | |
901 | } | |
902 | return -EINVAL; | |
903 | } | |
904 | ||
905 | /* | |
906 | * Translates old vtx IOCTLs to new ones | |
907 | * | |
908 | * This keeps new kernel versions compatible with old userspace programs. | |
909 | */ | |
910 | static inline unsigned int vtx_fix_command(unsigned int cmd) | |
911 | { | |
912 | switch (cmd) { | |
913 | case VTXIOCGETINFO_OLD: | |
914 | cmd = VTXIOCGETINFO; | |
915 | break; | |
916 | case VTXIOCCLRPAGE_OLD: | |
917 | cmd = VTXIOCCLRPAGE; | |
918 | break; | |
919 | case VTXIOCCLRFOUND_OLD: | |
920 | cmd = VTXIOCCLRFOUND; | |
921 | break; | |
922 | case VTXIOCPAGEREQ_OLD: | |
923 | cmd = VTXIOCPAGEREQ; | |
924 | break; | |
925 | case VTXIOCGETSTAT_OLD: | |
926 | cmd = VTXIOCGETSTAT; | |
927 | break; | |
928 | case VTXIOCGETPAGE_OLD: | |
929 | cmd = VTXIOCGETPAGE; | |
930 | break; | |
931 | case VTXIOCSTOPDAU_OLD: | |
932 | cmd = VTXIOCSTOPDAU; | |
933 | break; | |
934 | case VTXIOCPUTPAGE_OLD: | |
935 | cmd = VTXIOCPUTPAGE; | |
936 | break; | |
937 | case VTXIOCSETDISP_OLD: | |
938 | cmd = VTXIOCSETDISP; | |
939 | break; | |
940 | case VTXIOCPUTSTAT_OLD: | |
941 | cmd = VTXIOCPUTSTAT; | |
942 | break; | |
943 | case VTXIOCCLRCACHE_OLD: | |
944 | cmd = VTXIOCCLRCACHE; | |
945 | break; | |
946 | case VTXIOCSETVIRT_OLD: | |
947 | cmd = VTXIOCSETVIRT; | |
948 | break; | |
949 | } | |
950 | return cmd; | |
951 | } | |
952 | ||
953 | /* | |
954 | * Handle the locking | |
955 | */ | |
069b7479 | 956 | static long saa5246a_ioctl(struct file *file, |
1da177e4 LT |
957 | unsigned int cmd, unsigned long arg) |
958 | { | |
c170ecf4 | 959 | struct saa5246a_device *t = video_drvdata(file); |
069b7479 | 960 | long err; |
1da177e4 LT |
961 | |
962 | cmd = vtx_fix_command(cmd); | |
3593cab5 | 963 | mutex_lock(&t->lock); |
f473bf76 | 964 | err = video_usercopy(file, cmd, arg, do_saa5246a_ioctl); |
3593cab5 | 965 | mutex_unlock(&t->lock); |
1da177e4 LT |
966 | return err; |
967 | } | |
968 | ||
bec43661 | 969 | static int saa5246a_open(struct file *file) |
1da177e4 | 970 | { |
c170ecf4 | 971 | struct saa5246a_device *t = video_drvdata(file); |
1da177e4 | 972 | |
7d43cd53 HV |
973 | if (test_and_set_bit(0, &t->in_use)) |
974 | return -EBUSY; | |
1da177e4 LT |
975 | |
976 | if (i2c_senddata(t, SAA5246A_REGISTER_R0, | |
1da177e4 LT |
977 | R0_SELECT_R11 | |
978 | R0_PLL_TIME_CONSTANT_LONG | | |
979 | R0_ENABLE_nODD_EVEN_OUTPUT | | |
980 | R0_ENABLE_HDR_POLL | | |
981 | R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED | | |
982 | R0_NO_FREE_RUN_PLL | | |
983 | R0_NO_AUTOMATIC_FASTEXT_PROMPT, | |
984 | ||
985 | R1_NON_INTERLACED_312_312_LINES | | |
986 | R1_DEW | | |
987 | R1_EXTENDED_PACKET_DISABLE | | |
988 | R1_DAUS_ALL_ON | | |
989 | R1_8_BITS_NO_PARITY | | |
990 | R1_VCS_TO_SCS, | |
991 | ||
992 | COMMAND_END) || | |
993 | i2c_senddata(t, SAA5246A_REGISTER_R4, | |
994 | ||
995 | /* We do not care much for the TV display but nevertheless we | |
996 | * need the currently displayed page later because only on that | |
997 | * page the time is updated. */ | |
998 | R4_DISPLAY_PAGE_4, | |
999 | ||
1000 | COMMAND_END)) | |
1001 | { | |
7d43cd53 HV |
1002 | clear_bit(0, &t->in_use); |
1003 | return -EIO; | |
1da177e4 | 1004 | } |
1da177e4 | 1005 | return 0; |
1da177e4 LT |
1006 | } |
1007 | ||
bec43661 | 1008 | static int saa5246a_release(struct file *file) |
1da177e4 | 1009 | { |
c170ecf4 | 1010 | struct saa5246a_device *t = video_drvdata(file); |
1da177e4 LT |
1011 | |
1012 | /* Stop all acquisition circuits. */ | |
1013 | i2c_senddata(t, SAA5246A_REGISTER_R1, | |
1014 | ||
1015 | R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES | | |
1016 | R1_DEW | | |
1017 | R1_EXTENDED_PACKET_DISABLE | | |
1018 | R1_DAUS_ALL_OFF | | |
1019 | R1_8_BITS_NO_PARITY | | |
1020 | R1_VCS_TO_SCS, | |
1021 | ||
1022 | COMMAND_END); | |
7d43cd53 | 1023 | clear_bit(0, &t->in_use); |
1da177e4 LT |
1024 | return 0; |
1025 | } | |
1026 | ||
bec43661 | 1027 | static const struct v4l2_file_operations saa_fops = { |
1da177e4 LT |
1028 | .owner = THIS_MODULE, |
1029 | .open = saa5246a_open, | |
1030 | .release = saa5246a_release, | |
1031 | .ioctl = saa5246a_ioctl, | |
1da177e4 LT |
1032 | }; |
1033 | ||
1034 | static struct video_device saa_template = | |
1035 | { | |
71ef85c7 | 1036 | .name = "saa5246a", |
1da177e4 LT |
1037 | .fops = &saa_fops, |
1038 | .release = video_device_release, | |
1039 | .minor = -1, | |
1040 | }; | |
71ef85c7 | 1041 | |
1b8dac15 HV |
1042 | static int saa5246a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) |
1043 | { | |
1044 | struct i2c_client *client = v4l2_get_subdevdata(sd); | |
1045 | ||
1046 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5246A, 0); | |
1047 | } | |
1048 | ||
1049 | static const struct v4l2_subdev_core_ops saa5246a_core_ops = { | |
1050 | .g_chip_ident = saa5246a_g_chip_ident, | |
1051 | }; | |
1052 | ||
1053 | static const struct v4l2_subdev_ops saa5246a_ops = { | |
1054 | .core = &saa5246a_core_ops, | |
1055 | }; | |
71ef85c7 | 1056 | |
71ef85c7 HV |
1057 | |
1058 | static int saa5246a_probe(struct i2c_client *client, | |
1059 | const struct i2c_device_id *id) | |
1060 | { | |
1061 | int pgbuf; | |
1062 | int err; | |
71ef85c7 | 1063 | struct saa5246a_device *t; |
1b8dac15 | 1064 | struct v4l2_subdev *sd; |
71ef85c7 HV |
1065 | |
1066 | v4l_info(client, "chip found @ 0x%x (%s)\n", | |
1067 | client->addr << 1, client->adapter->name); | |
1068 | v4l_info(client, "VideoText version %d.%d\n", | |
1069 | MAJOR_VERSION, MINOR_VERSION); | |
1070 | t = kzalloc(sizeof(*t), GFP_KERNEL); | |
1071 | if (t == NULL) | |
1072 | return -ENOMEM; | |
1b8dac15 HV |
1073 | sd = &t->sd; |
1074 | v4l2_i2c_subdev_init(sd, client, &saa5246a_ops); | |
71ef85c7 HV |
1075 | mutex_init(&t->lock); |
1076 | ||
1077 | /* Now create a video4linux device */ | |
1b8dac15 HV |
1078 | t->vdev = video_device_alloc(); |
1079 | if (t->vdev == NULL) { | |
71ef85c7 HV |
1080 | kfree(t); |
1081 | return -ENOMEM; | |
1082 | } | |
1b8dac15 | 1083 | memcpy(t->vdev, &saa_template, sizeof(*t->vdev)); |
71ef85c7 HV |
1084 | |
1085 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { | |
1086 | memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); | |
1087 | t->is_searching[pgbuf] = false; | |
1088 | } | |
1b8dac15 | 1089 | video_set_drvdata(t->vdev, t); |
71ef85c7 HV |
1090 | |
1091 | /* Register it */ | |
1b8dac15 | 1092 | err = video_register_device(t->vdev, VFL_TYPE_VTX, -1); |
71ef85c7 | 1093 | if (err < 0) { |
1b8dac15 | 1094 | video_device_release(t->vdev); |
9401608b | 1095 | kfree(t); |
71ef85c7 HV |
1096 | return err; |
1097 | } | |
71ef85c7 HV |
1098 | return 0; |
1099 | } | |
1100 | ||
1101 | static int saa5246a_remove(struct i2c_client *client) | |
1102 | { | |
1b8dac15 HV |
1103 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
1104 | struct saa5246a_device *t = to_dev(sd); | |
71ef85c7 | 1105 | |
1b8dac15 HV |
1106 | video_unregister_device(t->vdev); |
1107 | v4l2_device_unregister_subdev(sd); | |
1108 | kfree(t); | |
71ef85c7 HV |
1109 | return 0; |
1110 | } | |
1111 | ||
1112 | static const struct i2c_device_id saa5246a_id[] = { | |
1113 | { "saa5246a", 0 }, | |
1114 | { } | |
1115 | }; | |
1116 | MODULE_DEVICE_TABLE(i2c, saa5246a_id); | |
1117 | ||
1118 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { | |
1119 | .name = "saa5246a", | |
71ef85c7 HV |
1120 | .probe = saa5246a_probe, |
1121 | .remove = saa5246a_remove, | |
1122 | .id_table = saa5246a_id, | |
1123 | }; |