diff options
Diffstat (limited to 'drivers/media/video/saa5246a.c')
-rw-r--r-- | drivers/media/video/saa5246a.c | 556 |
1 files changed, 413 insertions, 143 deletions
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 6ee63e69b36c..4a21b8a6a709 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c | |||
@@ -43,135 +43,363 @@ | |||
43 | #include <linux/mm.h> | 43 | #include <linux/mm.h> |
44 | #include <linux/init.h> | 44 | #include <linux/init.h> |
45 | #include <linux/i2c.h> | 45 | #include <linux/i2c.h> |
46 | #include <linux/smp_lock.h> | ||
47 | #include <linux/mutex.h> | ||
46 | #include <linux/videotext.h> | 48 | #include <linux/videotext.h> |
47 | #include <linux/videodev.h> | 49 | #include <linux/videodev.h> |
48 | #include <media/v4l2-common.h> | 50 | #include <media/v4l2-common.h> |
49 | #include <media/v4l2-ioctl.h> | 51 | #include <media/v4l2-ioctl.h> |
50 | #include <linux/mutex.h> | 52 | #include <media/v4l2-i2c-drv-legacy.h> |
51 | |||
52 | #include "saa5246a.h" | ||
53 | 53 | ||
54 | MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); | 54 | MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); |
55 | MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); | 55 | MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); |
56 | MODULE_LICENSE("GPL"); | 56 | MODULE_LICENSE("GPL"); |
57 | 57 | ||
58 | struct saa5246a_device | 58 | #define MAJOR_VERSION 1 /* driver major version number */ |
59 | { | 59 | #define MINOR_VERSION 8 /* driver minor version number */ |
60 | u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; | 60 | |
61 | int is_searching[NUM_DAUS]; | 61 | /* Number of DAUs = number of pages that can be searched at the same time. */ |
62 | struct i2c_client *client; | 62 | #define NUM_DAUS 4 |
63 | struct mutex lock; | 63 | |
64 | }; | 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 | ||
65 | 183 | ||
66 | static struct video_device saa_template; /* Declared near bottom */ | 184 | #define R3_HOLD_PAGE 0x00 |
185 | #define R3_UPDATE_PAGE 0x08 | ||
67 | 186 | ||
68 | /* Addresses to scan */ | 187 | #define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 |
69 | static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; | 188 | #define R3_PAGE_HUNDREDS_DO_CARE 0x10 |
70 | 189 | ||
71 | I2C_CLIENT_INSMOD; | 190 | #define R3_PAGE_TENS_DO_NOT_CARE 0x00 |
191 | #define R3_PAGE_TENS_DO_CARE 0x10 | ||
72 | 192 | ||
73 | static struct i2c_client client_template; | 193 | #define R3_PAGE_UNITS_DO_NOT_CARE 0x00 |
194 | #define R3_PAGE_UNITS_DO_CARE 0x10 | ||
74 | 195 | ||
75 | static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) | 196 | #define R3_HOURS_TENS_DO_NOT_CARE 0x00 |
76 | { | 197 | #define R3_HOURS_TENS_DO_CARE 0x10 |
77 | int pgbuf; | ||
78 | int err; | ||
79 | struct i2c_client *client; | ||
80 | struct video_device *vd; | ||
81 | struct saa5246a_device *t; | ||
82 | 198 | ||
83 | printk(KERN_INFO "saa5246a: teletext chip found.\n"); | 199 | #define R3_HOURS_UNITS_DO_NOT_CARE 0x00 |
84 | client=kmalloc(sizeof(*client), GFP_KERNEL); | 200 | #define R3_HOURS_UNITS_DO_CARE 0x10 |
85 | if(client==NULL) | ||
86 | return -ENOMEM; | ||
87 | client_template.adapter = adap; | ||
88 | client_template.addr = addr; | ||
89 | memcpy(client, &client_template, sizeof(*client)); | ||
90 | t = kzalloc(sizeof(*t), GFP_KERNEL); | ||
91 | if(t==NULL) | ||
92 | { | ||
93 | kfree(client); | ||
94 | return -ENOMEM; | ||
95 | } | ||
96 | strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); | ||
97 | mutex_init(&t->lock); | ||
98 | 201 | ||
99 | /* | 202 | #define R3_MINUTES_TENS_DO_NOT_CARE 0x00 |
100 | * Now create a video4linux device | 203 | #define R3_MINUTES_TENS_DO_CARE 0x10 |
101 | */ | ||
102 | 204 | ||
103 | vd = video_device_alloc(); | 205 | #define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 |
104 | if(vd==NULL) | 206 | #define R3_MINUTES_UNITS_DO_CARE 0x10 |
105 | { | ||
106 | kfree(t); | ||
107 | kfree(client); | ||
108 | return -ENOMEM; | ||
109 | } | ||
110 | i2c_set_clientdata(client, vd); | ||
111 | memcpy(vd, &saa_template, sizeof(*vd)); | ||
112 | 207 | ||
113 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) | 208 | /* Register R4 (Display chapter) */ |
114 | { | 209 | #define R4_DISPLAY_PAGE_0 0x00 |
115 | memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); | 210 | #define R4_DISPLAY_PAGE_1 0x01 |
116 | t->is_searching[pgbuf] = false; | 211 | #define R4_DISPLAY_PAGE_2 0x02 |
117 | } | 212 | #define R4_DISPLAY_PAGE_3 0x03 |
118 | vd->priv=t; | 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 | ||
119 | 217 | ||
218 | /* Register R5 (Normal display control) */ | ||
219 | #define R5_PICTURE_INSIDE_BOXING_OFF 0x00 | ||
220 | #define R5_PICTURE_INSIDE_BOXING_ON 0x01 | ||
120 | 221 | ||
121 | /* | 222 | #define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 |
122 | * Register it | 223 | #define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 |
123 | */ | ||
124 | 224 | ||
125 | if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) | 225 | #define R5_TEXT_INSIDE_BOXING_OFF 0x00 |
126 | { | 226 | #define R5_TEXT_INSIDE_BOXING_ON 0x04 |
127 | kfree(t); | ||
128 | kfree(client); | ||
129 | video_device_release(vd); | ||
130 | return err; | ||
131 | } | ||
132 | t->client = client; | ||
133 | i2c_attach_client(client); | ||
134 | return 0; | ||
135 | } | ||
136 | 227 | ||
137 | /* | 228 | #define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 |
138 | * We do most of the hard work when we become a device on the i2c. | 229 | #define R5_TEXT_OUTSIDE_BOXING_ON 0x08 |
139 | */ | ||
140 | static int saa5246a_probe(struct i2c_adapter *adap) | ||
141 | { | ||
142 | if (adap->class & I2C_CLASS_TV_ANALOG) | ||
143 | return i2c_probe(adap, &addr_data, saa5246a_attach); | ||
144 | return 0; | ||
145 | } | ||
146 | 230 | ||
147 | static int saa5246a_detach(struct i2c_client *client) | 231 | #define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 |
148 | { | 232 | #define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 |
149 | struct video_device *vd = i2c_get_clientdata(client); | ||
150 | i2c_detach_client(client); | ||
151 | video_unregister_device(vd); | ||
152 | kfree(vd->priv); | ||
153 | kfree(client); | ||
154 | return 0; | ||
155 | } | ||
156 | 233 | ||
157 | /* | 234 | #define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 |
158 | * I2C interfaces | 235 | #define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 |
159 | */ | 236 | |
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 | ||
302 | |||
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 | ||
160 | 325 | ||
161 | static struct i2c_driver i2c_driver_videotext = | 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 | ||
162 | { | 390 | { |
163 | .driver = { | 391 | u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; |
164 | .name = IF_NAME, /* name */ | 392 | int is_searching[NUM_DAUS]; |
165 | }, | 393 | struct i2c_client *client; |
166 | .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ | 394 | unsigned long in_use; |
167 | .attach_adapter = saa5246a_probe, | 395 | struct mutex lock; |
168 | .detach_client = saa5246a_detach, | ||
169 | }; | 396 | }; |
170 | 397 | ||
171 | static struct i2c_client client_template = { | 398 | static struct video_device saa_template; /* Declared near bottom */ |
172 | .driver = &i2c_driver_videotext, | 399 | |
173 | .name = "(unset)", | 400 | /* |
174 | }; | 401 | * I2C interfaces |
402 | */ | ||
175 | 403 | ||
176 | static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) | 404 | static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) |
177 | { | 405 | { |
@@ -579,8 +807,8 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t, | |||
579 | static int do_saa5246a_ioctl(struct inode *inode, struct file *file, | 807 | static int do_saa5246a_ioctl(struct inode *inode, struct file *file, |
580 | unsigned int cmd, void *arg) | 808 | unsigned int cmd, void *arg) |
581 | { | 809 | { |
582 | struct video_device *vd = video_devdata(file); | 810 | struct saa5246a_device *t = video_drvdata(file); |
583 | struct saa5246a_device *t=vd->priv; | 811 | |
584 | switch(cmd) | 812 | switch(cmd) |
585 | { | 813 | { |
586 | case VTXIOCGETINFO: | 814 | case VTXIOCGETINFO: |
@@ -720,8 +948,7 @@ static inline unsigned int vtx_fix_command(unsigned int cmd) | |||
720 | static int saa5246a_ioctl(struct inode *inode, struct file *file, | 948 | static int saa5246a_ioctl(struct inode *inode, struct file *file, |
721 | unsigned int cmd, unsigned long arg) | 949 | unsigned int cmd, unsigned long arg) |
722 | { | 950 | { |
723 | struct video_device *vd = video_devdata(file); | 951 | struct saa5246a_device *t = video_drvdata(file); |
724 | struct saa5246a_device *t = vd->priv; | ||
725 | int err; | 952 | int err; |
726 | 953 | ||
727 | cmd = vtx_fix_command(cmd); | 954 | cmd = vtx_fix_command(cmd); |
@@ -733,21 +960,15 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file, | |||
733 | 960 | ||
734 | static int saa5246a_open(struct inode *inode, struct file *file) | 961 | static int saa5246a_open(struct inode *inode, struct file *file) |
735 | { | 962 | { |
736 | struct video_device *vd = video_devdata(file); | 963 | struct saa5246a_device *t = video_drvdata(file); |
737 | struct saa5246a_device *t = vd->priv; | ||
738 | int err; | ||
739 | 964 | ||
740 | err = video_exclusive_open(inode,file); | 965 | if (t->client == NULL) |
741 | if (err < 0) | 966 | return -ENODEV; |
742 | return err; | ||
743 | 967 | ||
744 | if (t->client==NULL) { | 968 | if (test_and_set_bit(0, &t->in_use)) |
745 | err = -ENODEV; | 969 | return -EBUSY; |
746 | goto fail; | ||
747 | } | ||
748 | 970 | ||
749 | if (i2c_senddata(t, SAA5246A_REGISTER_R0, | 971 | if (i2c_senddata(t, SAA5246A_REGISTER_R0, |
750 | |||
751 | R0_SELECT_R11 | | 972 | R0_SELECT_R11 | |
752 | R0_PLL_TIME_CONSTANT_LONG | | 973 | R0_PLL_TIME_CONSTANT_LONG | |
753 | R0_ENABLE_nODD_EVEN_OUTPUT | | 974 | R0_ENABLE_nODD_EVEN_OUTPUT | |
@@ -773,21 +994,15 @@ static int saa5246a_open(struct inode *inode, struct file *file) | |||
773 | 994 | ||
774 | COMMAND_END)) | 995 | COMMAND_END)) |
775 | { | 996 | { |
776 | err = -EIO; | 997 | clear_bit(0, &t->in_use); |
777 | goto fail; | 998 | return -EIO; |
778 | } | 999 | } |
779 | |||
780 | return 0; | 1000 | return 0; |
781 | |||
782 | fail: | ||
783 | video_exclusive_release(inode,file); | ||
784 | return err; | ||
785 | } | 1001 | } |
786 | 1002 | ||
787 | static int saa5246a_release(struct inode *inode, struct file *file) | 1003 | static int saa5246a_release(struct inode *inode, struct file *file) |
788 | { | 1004 | { |
789 | struct video_device *vd = video_devdata(file); | 1005 | struct saa5246a_device *t = video_drvdata(file); |
790 | struct saa5246a_device *t = vd->priv; | ||
791 | 1006 | ||
792 | /* Stop all acquisition circuits. */ | 1007 | /* Stop all acquisition circuits. */ |
793 | i2c_senddata(t, SAA5246A_REGISTER_R1, | 1008 | i2c_senddata(t, SAA5246A_REGISTER_R1, |
@@ -800,26 +1015,10 @@ static int saa5246a_release(struct inode *inode, struct file *file) | |||
800 | R1_VCS_TO_SCS, | 1015 | R1_VCS_TO_SCS, |
801 | 1016 | ||
802 | COMMAND_END); | 1017 | COMMAND_END); |
803 | video_exclusive_release(inode,file); | 1018 | clear_bit(0, &t->in_use); |
804 | return 0; | 1019 | return 0; |
805 | } | 1020 | } |
806 | 1021 | ||
807 | static int __init init_saa_5246a (void) | ||
808 | { | ||
809 | printk(KERN_INFO | ||
810 | "SAA5246A (or compatible) Teletext decoder driver version %d.%d\n", | ||
811 | MAJOR_VERSION, MINOR_VERSION); | ||
812 | return i2c_add_driver(&i2c_driver_videotext); | ||
813 | } | ||
814 | |||
815 | static void __exit cleanup_saa_5246a (void) | ||
816 | { | ||
817 | i2c_del_driver(&i2c_driver_videotext); | ||
818 | } | ||
819 | |||
820 | module_init(init_saa_5246a); | ||
821 | module_exit(cleanup_saa_5246a); | ||
822 | |||
823 | static const struct file_operations saa_fops = { | 1022 | static const struct file_operations saa_fops = { |
824 | .owner = THIS_MODULE, | 1023 | .owner = THIS_MODULE, |
825 | .open = saa5246a_open, | 1024 | .open = saa5246a_open, |
@@ -830,8 +1029,79 @@ static const struct file_operations saa_fops = { | |||
830 | 1029 | ||
831 | static struct video_device saa_template = | 1030 | static struct video_device saa_template = |
832 | { | 1031 | { |
833 | .name = IF_NAME, | 1032 | .name = "saa5246a", |
834 | .fops = &saa_fops, | 1033 | .fops = &saa_fops, |
835 | .release = video_device_release, | 1034 | .release = video_device_release, |
836 | .minor = -1, | 1035 | .minor = -1, |
837 | }; | 1036 | }; |
1037 | |||
1038 | /* Addresses to scan */ | ||
1039 | static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; | ||
1040 | |||
1041 | I2C_CLIENT_INSMOD; | ||
1042 | |||
1043 | static int saa5246a_probe(struct i2c_client *client, | ||
1044 | const struct i2c_device_id *id) | ||
1045 | { | ||
1046 | int pgbuf; | ||
1047 | int err; | ||
1048 | struct video_device *vd; | ||
1049 | struct saa5246a_device *t; | ||
1050 | |||
1051 | v4l_info(client, "chip found @ 0x%x (%s)\n", | ||
1052 | client->addr << 1, client->adapter->name); | ||
1053 | v4l_info(client, "VideoText version %d.%d\n", | ||
1054 | MAJOR_VERSION, MINOR_VERSION); | ||
1055 | t = kzalloc(sizeof(*t), GFP_KERNEL); | ||
1056 | if (t == NULL) | ||
1057 | return -ENOMEM; | ||
1058 | mutex_init(&t->lock); | ||
1059 | |||
1060 | /* Now create a video4linux device */ | ||
1061 | vd = video_device_alloc(); | ||
1062 | if (vd == NULL) { | ||
1063 | kfree(t); | ||
1064 | return -ENOMEM; | ||
1065 | } | ||
1066 | i2c_set_clientdata(client, vd); | ||
1067 | memcpy(vd, &saa_template, sizeof(*vd)); | ||
1068 | |||
1069 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { | ||
1070 | memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); | ||
1071 | t->is_searching[pgbuf] = false; | ||
1072 | } | ||
1073 | video_set_drvdata(vd, t); | ||
1074 | |||
1075 | /* Register it */ | ||
1076 | err = video_register_device(vd, VFL_TYPE_VTX, -1); | ||
1077 | if (err < 0) { | ||
1078 | kfree(t); | ||
1079 | video_device_release(vd); | ||
1080 | return err; | ||
1081 | } | ||
1082 | t->client = client; | ||
1083 | return 0; | ||
1084 | } | ||
1085 | |||
1086 | static int saa5246a_remove(struct i2c_client *client) | ||
1087 | { | ||
1088 | struct video_device *vd = i2c_get_clientdata(client); | ||
1089 | |||
1090 | video_unregister_device(vd); | ||
1091 | kfree(video_get_drvdata(vd)); | ||
1092 | return 0; | ||
1093 | } | ||
1094 | |||
1095 | static const struct i2c_device_id saa5246a_id[] = { | ||
1096 | { "saa5246a", 0 }, | ||
1097 | { } | ||
1098 | }; | ||
1099 | MODULE_DEVICE_TABLE(i2c, saa5246a_id); | ||
1100 | |||
1101 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { | ||
1102 | .name = "saa5246a", | ||
1103 | .driverid = I2C_DRIVERID_SAA5249, | ||
1104 | .probe = saa5246a_probe, | ||
1105 | .remove = saa5246a_remove, | ||
1106 | .id_table = saa5246a_id, | ||
1107 | }; | ||