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