1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * saa7127 - Philips SAA7127/SAA7129 video encoder driver |
4 | * |
5 | * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl> |
6 | * |
7 | * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter |
8 | * |
9 | * Copyright (C) 2000-2001 Gillem <htoa@gmx.net> |
10 | * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de> |
11 | * |
12 | * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo |
13 | * |
14 | * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org> |
15 | * |
16 | * This driver is designed for the Hauppauge 250/350 Linux driver |
17 | * from the ivtv Project |
18 | * |
19 | * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com> |
20 | * |
21 | * Dual output support: |
22 | * Copyright (C) 2004 Eric Varsanyi |
23 | * |
24 | * NTSC Tuning and 7.5 IRE Setup |
25 | * Copyright (C) 2004 Chris Kennedy <c@groovy.org> |
26 | * |
27 | * VBI additions & cleanup: |
28 | * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl> |
29 | * |
30 | * Note: the saa7126 is identical to the saa7127, and the saa7128 is |
31 | * identical to the saa7129, except that the saa7126 and saa7128 have |
32 | * macrovision anti-taping support. This driver will almost certainly |
33 | * work fine for those chips, except of course for the missing anti-taping |
34 | * support. |
35 | */ |
36 | |
37 | |
38 | #include <linux/kernel.h> |
39 | #include <linux/module.h> |
40 | #include <linux/slab.h> |
41 | #include <linux/i2c.h> |
42 | #include <linux/videodev2.h> |
43 | #include <media/v4l2-device.h> |
44 | #include <media/i2c/saa7127.h> |
45 | |
46 | static int debug; |
47 | static int test_image; |
48 | |
49 | MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver" ); |
50 | MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil" ); |
51 | MODULE_LICENSE("GPL" ); |
52 | module_param(debug, int, 0644); |
53 | module_param(test_image, int, 0644); |
54 | MODULE_PARM_DESC(debug, "debug level (0-2)" ); |
55 | MODULE_PARM_DESC(test_image, "test_image (0-1)" ); |
56 | |
57 | |
58 | /* |
59 | * SAA7127 registers |
60 | */ |
61 | |
62 | #define SAA7127_REG_STATUS 0x00 |
63 | #define SAA7127_REG_WIDESCREEN_CONFIG 0x26 |
64 | #define SAA7127_REG_WIDESCREEN_ENABLE 0x27 |
65 | #define SAA7127_REG_BURST_START 0x28 |
66 | #define SAA7127_REG_BURST_END 0x29 |
67 | #define SAA7127_REG_COPYGEN_0 0x2a |
68 | #define SAA7127_REG_COPYGEN_1 0x2b |
69 | #define SAA7127_REG_COPYGEN_2 0x2c |
70 | #define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d |
71 | #define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 |
72 | #define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 |
73 | #define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a |
74 | #define SAA7129_REG_FADE_KEY_COL2 0x4f |
75 | #define SAA7127_REG_CHROMA_PHASE 0x5a |
76 | #define SAA7127_REG_GAINU 0x5b |
77 | #define SAA7127_REG_GAINV 0x5c |
78 | #define SAA7127_REG_BLACK_LEVEL 0x5d |
79 | #define SAA7127_REG_BLANKING_LEVEL 0x5e |
80 | #define SAA7127_REG_VBI_BLANKING 0x5f |
81 | #define SAA7127_REG_DAC_CONTROL 0x61 |
82 | #define SAA7127_REG_BURST_AMP 0x62 |
83 | #define SAA7127_REG_SUBC3 0x63 |
84 | #define SAA7127_REG_SUBC2 0x64 |
85 | #define SAA7127_REG_SUBC1 0x65 |
86 | #define SAA7127_REG_SUBC0 0x66 |
87 | #define SAA7127_REG_LINE_21_ODD_0 0x67 |
88 | #define SAA7127_REG_LINE_21_ODD_1 0x68 |
89 | #define SAA7127_REG_LINE_21_EVEN_0 0x69 |
90 | #define SAA7127_REG_LINE_21_EVEN_1 0x6a |
91 | #define SAA7127_REG_RCV_PORT_CONTROL 0x6b |
92 | #define SAA7127_REG_VTRIG 0x6c |
93 | #define SAA7127_REG_HTRIG_HI 0x6d |
94 | #define SAA7127_REG_MULTI 0x6e |
95 | #define SAA7127_REG_CLOSED_CAPTION 0x6f |
96 | #define SAA7127_REG_RCV2_OUTPUT_START 0x70 |
97 | #define SAA7127_REG_RCV2_OUTPUT_END 0x71 |
98 | #define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 |
99 | #define SAA7127_REG_TTX_REQUEST_H_START 0x73 |
100 | #define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 |
101 | #define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 |
102 | #define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 |
103 | #define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 |
104 | #define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 |
105 | #define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 |
106 | #define SAA7127_REG_FIRST_ACTIVE 0x7a |
107 | #define SAA7127_REG_LAST_ACTIVE 0x7b |
108 | #define SAA7127_REG_MSB_VERTICAL 0x7c |
109 | #define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e |
110 | #define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f |
111 | |
112 | /* |
113 | ********************************************************************** |
114 | * |
115 | * Arrays with configuration parameters for the SAA7127 |
116 | * |
117 | ********************************************************************** |
118 | */ |
119 | |
120 | struct i2c_reg_value { |
121 | unsigned char reg; |
122 | unsigned char value; |
123 | }; |
124 | |
125 | static const struct i2c_reg_value [] = { |
126 | { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, |
127 | { SAA7127_REG_VTRIG, 0xfa }, |
128 | { 0, 0 } |
129 | }; |
130 | |
131 | static const struct i2c_reg_value saa7127_init_config_common[] = { |
132 | { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, |
133 | { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, |
134 | { SAA7127_REG_COPYGEN_0, 0x77 }, |
135 | { SAA7127_REG_COPYGEN_1, 0x41 }, |
136 | { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ |
137 | { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf }, |
138 | { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, |
139 | { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, |
140 | { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ |
141 | { SAA7127_REG_LINE_21_ODD_0, 0x77 }, |
142 | { SAA7127_REG_LINE_21_ODD_1, 0x41 }, |
143 | { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, |
144 | { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, |
145 | { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, |
146 | { SAA7127_REG_VTRIG, 0xf9 }, |
147 | { SAA7127_REG_HTRIG_HI, 0x00 }, |
148 | { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, |
149 | { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, |
150 | { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, |
151 | { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, |
152 | { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, |
153 | { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, |
154 | { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, |
155 | { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, |
156 | { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, |
157 | { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, |
158 | { SAA7127_REG_FIRST_ACTIVE, 0x1a }, |
159 | { SAA7127_REG_LAST_ACTIVE, 0x01 }, |
160 | { SAA7127_REG_MSB_VERTICAL, 0xc0 }, |
161 | { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, |
162 | { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, |
163 | { 0, 0 } |
164 | }; |
165 | |
166 | #define SAA7127_60HZ_DAC_CONTROL 0x15 |
167 | static const struct i2c_reg_value saa7127_init_config_60hz[] = { |
168 | { SAA7127_REG_BURST_START, 0x19 }, |
169 | /* BURST_END is also used as a chip ID in saa7127_probe */ |
170 | { SAA7127_REG_BURST_END, 0x1d }, |
171 | { SAA7127_REG_CHROMA_PHASE, 0xa3 }, |
172 | { SAA7127_REG_GAINU, 0x98 }, |
173 | { SAA7127_REG_GAINV, 0xd3 }, |
174 | { SAA7127_REG_BLACK_LEVEL, 0x39 }, |
175 | { SAA7127_REG_BLANKING_LEVEL, 0x2e }, |
176 | { SAA7127_REG_VBI_BLANKING, 0x2e }, |
177 | { SAA7127_REG_DAC_CONTROL, 0x15 }, |
178 | { SAA7127_REG_BURST_AMP, 0x4d }, |
179 | { SAA7127_REG_SUBC3, 0x1f }, |
180 | { SAA7127_REG_SUBC2, 0x7c }, |
181 | { SAA7127_REG_SUBC1, 0xf0 }, |
182 | { SAA7127_REG_SUBC0, 0x21 }, |
183 | { SAA7127_REG_MULTI, 0x90 }, |
184 | { SAA7127_REG_CLOSED_CAPTION, 0x11 }, |
185 | { 0, 0 } |
186 | }; |
187 | |
188 | #define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 |
189 | static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { |
190 | { SAA7127_REG_BURST_START, 0x21 }, |
191 | /* BURST_END is also used as a chip ID in saa7127_probe */ |
192 | { SAA7127_REG_BURST_END, 0x1d }, |
193 | { SAA7127_REG_CHROMA_PHASE, 0x3f }, |
194 | { SAA7127_REG_GAINU, 0x7d }, |
195 | { SAA7127_REG_GAINV, 0xaf }, |
196 | { SAA7127_REG_BLACK_LEVEL, 0x33 }, |
197 | { SAA7127_REG_BLANKING_LEVEL, 0x35 }, |
198 | { SAA7127_REG_VBI_BLANKING, 0x35 }, |
199 | { SAA7127_REG_DAC_CONTROL, 0x02 }, |
200 | { SAA7127_REG_BURST_AMP, 0x2f }, |
201 | { SAA7127_REG_SUBC3, 0xcb }, |
202 | { SAA7127_REG_SUBC2, 0x8a }, |
203 | { SAA7127_REG_SUBC1, 0x09 }, |
204 | { SAA7127_REG_SUBC0, 0x2a }, |
205 | { SAA7127_REG_MULTI, 0xa0 }, |
206 | { SAA7127_REG_CLOSED_CAPTION, 0x00 }, |
207 | { 0, 0 } |
208 | }; |
209 | |
210 | #define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 |
211 | static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { |
212 | { SAA7127_REG_BURST_START, 0x21 }, |
213 | /* BURST_END is also used as a chip ID in saa7127_probe */ |
214 | { SAA7127_REG_BURST_END, 0x1d }, |
215 | { SAA7127_REG_CHROMA_PHASE, 0x3f }, |
216 | { SAA7127_REG_GAINU, 0x6a }, |
217 | { SAA7127_REG_GAINV, 0x81 }, |
218 | { SAA7127_REG_BLACK_LEVEL, 0x33 }, |
219 | { SAA7127_REG_BLANKING_LEVEL, 0x35 }, |
220 | { SAA7127_REG_VBI_BLANKING, 0x35 }, |
221 | { SAA7127_REG_DAC_CONTROL, 0x08 }, |
222 | { SAA7127_REG_BURST_AMP, 0x2f }, |
223 | { SAA7127_REG_SUBC3, 0xb2 }, |
224 | { SAA7127_REG_SUBC2, 0x3b }, |
225 | { SAA7127_REG_SUBC1, 0xa3 }, |
226 | { SAA7127_REG_SUBC0, 0x28 }, |
227 | { SAA7127_REG_MULTI, 0x90 }, |
228 | { SAA7127_REG_CLOSED_CAPTION, 0x00 }, |
229 | { 0, 0 } |
230 | }; |
231 | |
232 | /* |
233 | ********************************************************************** |
234 | * |
235 | * Encoder Struct, holds the configuration state of the encoder |
236 | * |
237 | ********************************************************************** |
238 | */ |
239 | |
240 | enum saa712x_model { |
241 | SAA7127, |
242 | SAA7129, |
243 | }; |
244 | |
245 | struct saa7127_state { |
246 | struct v4l2_subdev sd; |
247 | v4l2_std_id std; |
248 | enum saa712x_model ident; |
249 | enum saa7127_input_type input_type; |
250 | enum saa7127_output_type output_type; |
251 | int video_enable; |
252 | int wss_enable; |
253 | u16 wss_mode; |
254 | int cc_enable; |
255 | u16 cc_data; |
256 | int xds_enable; |
257 | u16 xds_data; |
258 | int vps_enable; |
259 | u8 vps_data[5]; |
260 | u8 reg_2d; |
261 | u8 reg_3a; |
262 | u8 reg_3a_cb; /* colorbar bit */ |
263 | u8 reg_61; |
264 | }; |
265 | |
266 | static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) |
267 | { |
268 | return container_of(sd, struct saa7127_state, sd); |
269 | } |
270 | |
271 | static const char * const output_strs[] = |
272 | { |
273 | "S-Video + Composite" , |
274 | "Composite" , |
275 | "S-Video" , |
276 | "RGB" , |
277 | "YUV C" , |
278 | "YUV V" |
279 | }; |
280 | |
281 | static const char * const wss_strs[] = { |
282 | "invalid" , |
283 | "letterbox 14:9 center" , |
284 | "letterbox 14:9 top" , |
285 | "invalid" , |
286 | "letterbox 16:9 top" , |
287 | "invalid" , |
288 | "invalid" , |
289 | "16:9 full format anamorphic" , |
290 | "4:3 full format" , |
291 | "invalid" , |
292 | "invalid" , |
293 | "letterbox 16:9 center" , |
294 | "invalid" , |
295 | "letterbox >16:9 center" , |
296 | "14:9 full format center" , |
297 | "invalid" , |
298 | }; |
299 | |
300 | /* ----------------------------------------------------------------------- */ |
301 | |
302 | static int saa7127_read(struct v4l2_subdev *sd, u8 reg) |
303 | { |
304 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
305 | |
306 | return i2c_smbus_read_byte_data(client, command: reg); |
307 | } |
308 | |
309 | /* ----------------------------------------------------------------------- */ |
310 | |
311 | static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) |
312 | { |
313 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
314 | int i; |
315 | |
316 | for (i = 0; i < 3; i++) { |
317 | if (i2c_smbus_write_byte_data(client, command: reg, value: val) == 0) |
318 | return 0; |
319 | } |
320 | v4l2_err(sd, "I2C Write Problem\n" ); |
321 | return -1; |
322 | } |
323 | |
324 | /* ----------------------------------------------------------------------- */ |
325 | |
326 | static int saa7127_write_inittab(struct v4l2_subdev *sd, |
327 | const struct i2c_reg_value *regs) |
328 | { |
329 | while (regs->reg != 0) { |
330 | saa7127_write(sd, reg: regs->reg, val: regs->value); |
331 | regs++; |
332 | } |
333 | return 0; |
334 | } |
335 | |
336 | /* ----------------------------------------------------------------------- */ |
337 | |
338 | static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) |
339 | { |
340 | struct saa7127_state *state = to_state(sd); |
341 | int enable = (data->line != 0); |
342 | |
343 | if (enable && (data->field != 0 || data->line != 16)) |
344 | return -EINVAL; |
345 | if (state->vps_enable != enable) { |
346 | v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n" , enable ? "on" : "off" ); |
347 | saa7127_write(sd, reg: 0x54, val: enable << 7); |
348 | state->vps_enable = enable; |
349 | } |
350 | if (!enable) |
351 | return 0; |
352 | |
353 | state->vps_data[0] = data->data[2]; |
354 | state->vps_data[1] = data->data[8]; |
355 | state->vps_data[2] = data->data[9]; |
356 | state->vps_data[3] = data->data[10]; |
357 | state->vps_data[4] = data->data[11]; |
358 | v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n" , 5, state->vps_data); |
359 | saa7127_write(sd, reg: 0x55, val: state->vps_data[0]); |
360 | saa7127_write(sd, reg: 0x56, val: state->vps_data[1]); |
361 | saa7127_write(sd, reg: 0x57, val: state->vps_data[2]); |
362 | saa7127_write(sd, reg: 0x58, val: state->vps_data[3]); |
363 | saa7127_write(sd, reg: 0x59, val: state->vps_data[4]); |
364 | return 0; |
365 | } |
366 | |
367 | /* ----------------------------------------------------------------------- */ |
368 | |
369 | static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) |
370 | { |
371 | struct saa7127_state *state = to_state(sd); |
372 | u16 cc = data->data[1] << 8 | data->data[0]; |
373 | int enable = (data->line != 0); |
374 | |
375 | if (enable && (data->field != 0 || data->line != 21)) |
376 | return -EINVAL; |
377 | if (state->cc_enable != enable) { |
378 | v4l2_dbg(1, debug, sd, |
379 | "Turn CC %s\n" , enable ? "on" : "off" ); |
380 | saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, |
381 | val: (state->xds_enable << 7) | (enable << 6) | 0x11); |
382 | state->cc_enable = enable; |
383 | } |
384 | if (!enable) |
385 | return 0; |
386 | |
387 | v4l2_dbg(2, debug, sd, "CC data: %04x\n" , cc); |
388 | saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, val: cc & 0xff); |
389 | saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, val: cc >> 8); |
390 | state->cc_data = cc; |
391 | return 0; |
392 | } |
393 | |
394 | /* ----------------------------------------------------------------------- */ |
395 | |
396 | static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) |
397 | { |
398 | struct saa7127_state *state = to_state(sd); |
399 | u16 xds = data->data[1] << 8 | data->data[0]; |
400 | int enable = (data->line != 0); |
401 | |
402 | if (enable && (data->field != 1 || data->line != 21)) |
403 | return -EINVAL; |
404 | if (state->xds_enable != enable) { |
405 | v4l2_dbg(1, debug, sd, "Turn XDS %s\n" , enable ? "on" : "off" ); |
406 | saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, |
407 | val: (enable << 7) | (state->cc_enable << 6) | 0x11); |
408 | state->xds_enable = enable; |
409 | } |
410 | if (!enable) |
411 | return 0; |
412 | |
413 | v4l2_dbg(2, debug, sd, "XDS data: %04x\n" , xds); |
414 | saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, val: xds & 0xff); |
415 | saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, val: xds >> 8); |
416 | state->xds_data = xds; |
417 | return 0; |
418 | } |
419 | |
420 | /* ----------------------------------------------------------------------- */ |
421 | |
422 | static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) |
423 | { |
424 | struct saa7127_state *state = to_state(sd); |
425 | int enable = (data->line != 0); |
426 | |
427 | if (enable && (data->field != 0 || data->line != 23)) |
428 | return -EINVAL; |
429 | if (state->wss_enable != enable) { |
430 | v4l2_dbg(1, debug, sd, "Turn WSS %s\n" , enable ? "on" : "off" ); |
431 | saa7127_write(sd, reg: 0x27, val: enable << 7); |
432 | state->wss_enable = enable; |
433 | } |
434 | if (!enable) |
435 | return 0; |
436 | |
437 | saa7127_write(sd, reg: 0x26, val: data->data[0]); |
438 | saa7127_write(sd, reg: 0x27, val: 0x80 | (data->data[1] & 0x3f)); |
439 | v4l2_dbg(1, debug, sd, |
440 | "WSS mode: %s\n" , wss_strs[data->data[0] & 0xf]); |
441 | state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; |
442 | return 0; |
443 | } |
444 | |
445 | /* ----------------------------------------------------------------------- */ |
446 | |
447 | static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) |
448 | { |
449 | struct saa7127_state *state = to_state(sd); |
450 | |
451 | if (enable) { |
452 | v4l2_dbg(1, debug, sd, "Enable Video Output\n" ); |
453 | saa7127_write(sd, reg: 0x2d, val: state->reg_2d); |
454 | saa7127_write(sd, reg: 0x61, val: state->reg_61); |
455 | } else { |
456 | v4l2_dbg(1, debug, sd, "Disable Video Output\n" ); |
457 | saa7127_write(sd, reg: 0x2d, val: (state->reg_2d & 0xf0)); |
458 | saa7127_write(sd, reg: 0x61, val: (state->reg_61 | 0xc0)); |
459 | } |
460 | state->video_enable = enable; |
461 | return 0; |
462 | } |
463 | |
464 | /* ----------------------------------------------------------------------- */ |
465 | |
466 | static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) |
467 | { |
468 | struct saa7127_state *state = to_state(sd); |
469 | const struct i2c_reg_value *inittab; |
470 | |
471 | if (std & V4L2_STD_525_60) { |
472 | v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n" ); |
473 | inittab = saa7127_init_config_60hz; |
474 | state->reg_61 = SAA7127_60HZ_DAC_CONTROL; |
475 | |
476 | } else if (state->ident == SAA7129 && |
477 | (std & V4L2_STD_SECAM) && |
478 | !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { |
479 | |
480 | /* If and only if SECAM, with a SAA712[89] */ |
481 | v4l2_dbg(1, debug, sd, |
482 | "Selecting 50 Hz SECAM video Standard\n" ); |
483 | inittab = saa7127_init_config_50hz_secam; |
484 | state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; |
485 | |
486 | } else { |
487 | v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n" ); |
488 | inittab = saa7127_init_config_50hz_pal; |
489 | state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; |
490 | } |
491 | |
492 | /* Write Table */ |
493 | saa7127_write_inittab(sd, regs: inittab); |
494 | state->std = std; |
495 | return 0; |
496 | } |
497 | |
498 | /* ----------------------------------------------------------------------- */ |
499 | |
500 | static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) |
501 | { |
502 | struct saa7127_state *state = to_state(sd); |
503 | |
504 | switch (output) { |
505 | case SAA7127_OUTPUT_TYPE_RGB: |
506 | state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ |
507 | state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ |
508 | break; |
509 | |
510 | case SAA7127_OUTPUT_TYPE_COMPOSITE: |
511 | if (state->ident == SAA7129) |
512 | state->reg_2d = 0x20; /* CVBS only */ |
513 | else |
514 | state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ |
515 | state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ |
516 | break; |
517 | |
518 | case SAA7127_OUTPUT_TYPE_SVIDEO: |
519 | if (state->ident == SAA7129) |
520 | state->reg_2d = 0x18; /* Y + C */ |
521 | else |
522 | state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */ |
523 | state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ |
524 | break; |
525 | |
526 | case SAA7127_OUTPUT_TYPE_YUV_V: |
527 | state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ |
528 | state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ |
529 | break; |
530 | |
531 | case SAA7127_OUTPUT_TYPE_YUV_C: |
532 | state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ |
533 | state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ |
534 | break; |
535 | |
536 | case SAA7127_OUTPUT_TYPE_BOTH: |
537 | if (state->ident == SAA7129) |
538 | state->reg_2d = 0x38; |
539 | else |
540 | state->reg_2d = 0xbf; |
541 | state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ |
542 | break; |
543 | |
544 | default: |
545 | return -EINVAL; |
546 | } |
547 | v4l2_dbg(1, debug, sd, |
548 | "Selecting %s output type\n" , output_strs[output]); |
549 | |
550 | /* Configure Encoder */ |
551 | saa7127_write(sd, reg: 0x2d, val: state->reg_2d); |
552 | saa7127_write(sd, reg: 0x3a, val: state->reg_3a | state->reg_3a_cb); |
553 | state->output_type = output; |
554 | return 0; |
555 | } |
556 | |
557 | /* ----------------------------------------------------------------------- */ |
558 | |
559 | static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) |
560 | { |
561 | struct saa7127_state *state = to_state(sd); |
562 | |
563 | switch (input) { |
564 | case SAA7127_INPUT_TYPE_NORMAL: /* avia */ |
565 | v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n" ); |
566 | state->reg_3a_cb = 0; |
567 | break; |
568 | |
569 | case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ |
570 | v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n" ); |
571 | state->reg_3a_cb = 0x80; |
572 | break; |
573 | |
574 | default: |
575 | return -EINVAL; |
576 | } |
577 | saa7127_write(sd, reg: 0x3a, val: state->reg_3a | state->reg_3a_cb); |
578 | state->input_type = input; |
579 | return 0; |
580 | } |
581 | |
582 | /* ----------------------------------------------------------------------- */ |
583 | |
584 | static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) |
585 | { |
586 | struct saa7127_state *state = to_state(sd); |
587 | |
588 | if (state->std == std) |
589 | return 0; |
590 | return saa7127_set_std(sd, std); |
591 | } |
592 | |
593 | static int saa7127_s_routing(struct v4l2_subdev *sd, |
594 | u32 input, u32 output, u32 config) |
595 | { |
596 | struct saa7127_state *state = to_state(sd); |
597 | int rc = 0; |
598 | |
599 | if (state->input_type != input) |
600 | rc = saa7127_set_input_type(sd, input); |
601 | if (rc == 0 && state->output_type != output) |
602 | rc = saa7127_set_output_type(sd, output); |
603 | return rc; |
604 | } |
605 | |
606 | static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) |
607 | { |
608 | struct saa7127_state *state = to_state(sd); |
609 | |
610 | if (state->video_enable == enable) |
611 | return 0; |
612 | return saa7127_set_video_enable(sd, enable); |
613 | } |
614 | |
615 | static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) |
616 | { |
617 | struct saa7127_state *state = to_state(sd); |
618 | |
619 | memset(fmt->service_lines, 0, sizeof(fmt->service_lines)); |
620 | if (state->vps_enable) |
621 | fmt->service_lines[0][16] = V4L2_SLICED_VPS; |
622 | if (state->wss_enable) |
623 | fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; |
624 | if (state->cc_enable) { |
625 | fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; |
626 | fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; |
627 | } |
628 | fmt->service_set = |
629 | (state->vps_enable ? V4L2_SLICED_VPS : 0) | |
630 | (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | |
631 | (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); |
632 | return 0; |
633 | } |
634 | |
635 | static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) |
636 | { |
637 | switch (data->id) { |
638 | case V4L2_SLICED_WSS_625: |
639 | return saa7127_set_wss(sd, data); |
640 | case V4L2_SLICED_VPS: |
641 | return saa7127_set_vps(sd, data); |
642 | case V4L2_SLICED_CAPTION_525: |
643 | if (data->field == 0) |
644 | return saa7127_set_cc(sd, data); |
645 | return saa7127_set_xds(sd, data); |
646 | default: |
647 | return -EINVAL; |
648 | } |
649 | return 0; |
650 | } |
651 | |
652 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
653 | static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) |
654 | { |
655 | reg->val = saa7127_read(sd, reg: reg->reg & 0xff); |
656 | reg->size = 1; |
657 | return 0; |
658 | } |
659 | |
660 | static int saa7127_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) |
661 | { |
662 | saa7127_write(sd, reg: reg->reg & 0xff, val: reg->val & 0xff); |
663 | return 0; |
664 | } |
665 | #endif |
666 | |
667 | static int saa7127_log_status(struct v4l2_subdev *sd) |
668 | { |
669 | struct saa7127_state *state = to_state(sd); |
670 | |
671 | v4l2_info(sd, "Standard: %s\n" , (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz" ); |
672 | v4l2_info(sd, "Input: %s\n" , state->input_type ? "color bars" : "normal" ); |
673 | v4l2_info(sd, "Output: %s\n" , state->video_enable ? |
674 | output_strs[state->output_type] : "disabled" ); |
675 | v4l2_info(sd, "WSS: %s\n" , state->wss_enable ? |
676 | wss_strs[state->wss_mode] : "disabled" ); |
677 | v4l2_info(sd, "VPS: %s\n" , state->vps_enable ? "enabled" : "disabled" ); |
678 | v4l2_info(sd, "CC: %s\n" , state->cc_enable ? "enabled" : "disabled" ); |
679 | return 0; |
680 | } |
681 | |
682 | /* ----------------------------------------------------------------------- */ |
683 | |
684 | static const struct v4l2_subdev_core_ops saa7127_core_ops = { |
685 | .log_status = saa7127_log_status, |
686 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
687 | .g_register = saa7127_g_register, |
688 | .s_register = saa7127_s_register, |
689 | #endif |
690 | }; |
691 | |
692 | static const struct v4l2_subdev_video_ops saa7127_video_ops = { |
693 | .s_std_output = saa7127_s_std_output, |
694 | .s_routing = saa7127_s_routing, |
695 | .s_stream = saa7127_s_stream, |
696 | }; |
697 | |
698 | static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { |
699 | .s_vbi_data = saa7127_s_vbi_data, |
700 | .g_sliced_fmt = saa7127_g_sliced_fmt, |
701 | }; |
702 | |
703 | static const struct v4l2_subdev_ops saa7127_ops = { |
704 | .core = &saa7127_core_ops, |
705 | .video = &saa7127_video_ops, |
706 | .vbi = &saa7127_vbi_ops, |
707 | }; |
708 | |
709 | /* ----------------------------------------------------------------------- */ |
710 | |
711 | static int saa7127_probe(struct i2c_client *client) |
712 | { |
713 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
714 | struct saa7127_state *state; |
715 | struct v4l2_subdev *sd; |
716 | struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ |
717 | |
718 | /* Check if the adapter supports the needed features */ |
719 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
720 | return -EIO; |
721 | |
722 | v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n" , |
723 | client->addr << 1); |
724 | |
725 | state = devm_kzalloc(dev: &client->dev, size: sizeof(*state), GFP_KERNEL); |
726 | if (state == NULL) |
727 | return -ENOMEM; |
728 | |
729 | sd = &state->sd; |
730 | v4l2_i2c_subdev_init(sd, client, ops: &saa7127_ops); |
731 | |
732 | /* First test register 0: Bits 5-7 are a version ID (should be 0), |
733 | and bit 2 should also be 0. |
734 | This is rather general, so the second test is more specific and |
735 | looks at the 'ending point of burst in clock cycles' which is |
736 | 0x1d after a reset and not expected to ever change. */ |
737 | if ((saa7127_read(sd, reg: 0) & 0xe4) != 0 || |
738 | (saa7127_read(sd, reg: 0x29) & 0x3f) != 0x1d) { |
739 | v4l2_dbg(1, debug, sd, "saa7127 not found\n" ); |
740 | return -ENODEV; |
741 | } |
742 | |
743 | if (id->driver_data) { /* Chip type is already known */ |
744 | state->ident = id->driver_data; |
745 | } else { /* Needs detection */ |
746 | int read_result; |
747 | |
748 | /* Detect if it's an saa7129 */ |
749 | read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); |
750 | saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, val: 0xaa); |
751 | if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { |
752 | saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, |
753 | val: read_result); |
754 | state->ident = SAA7129; |
755 | strscpy(client->name, "saa7129" , I2C_NAME_SIZE); |
756 | } else { |
757 | state->ident = SAA7127; |
758 | strscpy(client->name, "saa7127" , I2C_NAME_SIZE); |
759 | } |
760 | } |
761 | |
762 | v4l2_info(sd, "%s found @ 0x%x (%s)\n" , client->name, |
763 | client->addr << 1, client->adapter->name); |
764 | |
765 | v4l2_dbg(1, debug, sd, "Configuring encoder\n" ); |
766 | saa7127_write_inittab(sd, regs: saa7127_init_config_common); |
767 | saa7127_set_std(sd, V4L2_STD_NTSC); |
768 | saa7127_set_output_type(sd, output: SAA7127_OUTPUT_TYPE_BOTH); |
769 | saa7127_set_vps(sd, data: &vbi); |
770 | saa7127_set_wss(sd, data: &vbi); |
771 | saa7127_set_cc(sd, data: &vbi); |
772 | saa7127_set_xds(sd, data: &vbi); |
773 | if (test_image == 1) |
774 | /* The Encoder has an internal Colorbar generator */ |
775 | /* This can be used for debugging */ |
776 | saa7127_set_input_type(sd, input: SAA7127_INPUT_TYPE_TEST_IMAGE); |
777 | else |
778 | saa7127_set_input_type(sd, input: SAA7127_INPUT_TYPE_NORMAL); |
779 | saa7127_set_video_enable(sd, enable: 1); |
780 | |
781 | if (state->ident == SAA7129) |
782 | saa7127_write_inittab(sd, regs: saa7129_init_config_extra); |
783 | return 0; |
784 | } |
785 | |
786 | /* ----------------------------------------------------------------------- */ |
787 | |
788 | static void saa7127_remove(struct i2c_client *client) |
789 | { |
790 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
791 | |
792 | v4l2_device_unregister_subdev(sd); |
793 | /* Turn off TV output */ |
794 | saa7127_set_video_enable(sd, enable: 0); |
795 | } |
796 | |
797 | /* ----------------------------------------------------------------------- */ |
798 | |
799 | static const struct i2c_device_id saa7127_id[] = { |
800 | { "saa7127_auto" , 0 }, /* auto-detection */ |
801 | { "saa7126" , SAA7127 }, |
802 | { "saa7127" , SAA7127 }, |
803 | { "saa7128" , SAA7129 }, |
804 | { "saa7129" , SAA7129 }, |
805 | { } |
806 | }; |
807 | MODULE_DEVICE_TABLE(i2c, saa7127_id); |
808 | |
809 | static struct i2c_driver saa7127_driver = { |
810 | .driver = { |
811 | .name = "saa7127" , |
812 | }, |
813 | .probe = saa7127_probe, |
814 | .remove = saa7127_remove, |
815 | .id_table = saa7127_id, |
816 | }; |
817 | |
818 | module_i2c_driver(saa7127_driver); |
819 | |