1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/delay.h>
32#include <linux/slab.h>
33#include <linux/wait.h>
34#include <asm/uaccess.h>
35#include <linux/i2c.h>
36#include <linux/videodev2.h>
37#include <media/v4l2-device.h>
38#include <media/v4l2-chip-ident.h>
39#include <media/v4l2-i2c-drv.h>
40
41MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
42MODULE_AUTHOR("Pauline Middelink");
43MODULE_LICENSE("GPL");
44
45
46static int debug;
47module_param(debug, int, 0);
48MODULE_PARM_DESC(debug, "Debug level (0-1)");
49
50#define SAA7110_MAX_INPUT 9
51#define SAA7110_MAX_OUTPUT 1
52
53#define SAA7110_NR_REG 0x35
54
55struct saa7110 {
56 struct v4l2_subdev sd;
57 u8 reg[SAA7110_NR_REG];
58
59 v4l2_std_id norm;
60 int input;
61 int enable;
62 int bright;
63 int contrast;
64 int hue;
65 int sat;
66
67 wait_queue_head_t wq;
68};
69
70static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd)
71{
72 return container_of(sd, struct saa7110, sd);
73}
74
75
76
77
78
79static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value)
80{
81 struct i2c_client *client = v4l2_get_subdevdata(sd);
82 struct saa7110 *decoder = to_saa7110(sd);
83
84 decoder->reg[reg] = value;
85 return i2c_smbus_write_byte_data(client, reg, value);
86}
87
88static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
89{
90 struct i2c_client *client = v4l2_get_subdevdata(sd);
91 struct saa7110 *decoder = to_saa7110(sd);
92 int ret = -1;
93 u8 reg = *data;
94
95
96 if (reg + (len - 1) > SAA7110_NR_REG)
97 return ret;
98
99
100
101 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
102 ret = i2c_master_send(client, data, len);
103
104
105 memcpy(decoder->reg + reg, data + 1, len - 1);
106 } else {
107 for (++data, --len; len; len--) {
108 ret = saa7110_write(sd, reg++, *data++);
109 if (ret < 0)
110 break;
111 }
112 }
113
114 return ret;
115}
116
117static inline int saa7110_read(struct v4l2_subdev *sd)
118{
119 struct i2c_client *client = v4l2_get_subdevdata(sd);
120
121 return i2c_smbus_read_byte(client);
122}
123
124
125
126
127
128#define FRESP_06H_COMPST 0x03
129#define FRESP_06H_SVIDEO 0x83
130
131
132static int saa7110_selmux(struct v4l2_subdev *sd, int chan)
133{
134 static const unsigned char modes[9][8] = {
135
136 {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03,
137 0x44, 0x75, 0x16},
138
139 {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03,
140 0x44, 0x75, 0x16},
141
142 {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03,
143 0x60, 0xB5, 0x05},
144
145 {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03,
146 0x60, 0xB5, 0x05},
147
148 {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83,
149 0x60, 0xB5, 0x03},
150
151 {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83,
152 0x60, 0xB5, 0x03},
153
154 {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3,
155 0x44, 0x75, 0x12},
156
157 {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13,
158 0x60, 0xB5, 0x14},
159
160 {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23,
161 0x44, 0x75, 0x21}
162 };
163 struct saa7110 *decoder = to_saa7110(sd);
164 const unsigned char *ptr = modes[chan];
165
166 saa7110_write(sd, 0x06, ptr[0]);
167 saa7110_write(sd, 0x20, ptr[1]);
168 saa7110_write(sd, 0x21, ptr[2]);
169 saa7110_write(sd, 0x22, ptr[3]);
170 saa7110_write(sd, 0x2C, ptr[4]);
171 saa7110_write(sd, 0x30, ptr[5]);
172 saa7110_write(sd, 0x31, ptr[6]);
173 saa7110_write(sd, 0x21, ptr[7]);
174 decoder->input = chan;
175
176 return 0;
177}
178
179static const unsigned char initseq[1 + SAA7110_NR_REG] = {
180 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00,
181 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90,
182 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA,
183 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F,
185 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C,
186 0x44, 0x71, 0x02, 0x8C, 0x02
187};
188
189static v4l2_std_id determine_norm(struct v4l2_subdev *sd)
190{
191 DEFINE_WAIT(wait);
192 struct saa7110 *decoder = to_saa7110(sd);
193 int status;
194
195
196 saa7110_write_block(sd, initseq, sizeof(initseq));
197 saa7110_selmux(sd, decoder->input);
198 prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
199 schedule_timeout(msecs_to_jiffies(250));
200 finish_wait(&decoder->wq, &wait);
201 status = saa7110_read(sd);
202 if (status & 0x40) {
203 v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status);
204 return decoder->norm;
205 }
206 if ((status & 3) == 0) {
207 saa7110_write(sd, 0x06, 0x83);
208 if (status & 0x20) {
209 v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status);
210
211 return V4L2_STD_NTSC;
212 }
213 v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status);
214
215 return V4L2_STD_PAL;
216 }
217
218 if (status & 0x20) {
219 v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status);
220 saa7110_write(sd, 0x0D, 0x86);
221 saa7110_write(sd, 0x0F, 0x50);
222 saa7110_write(sd, 0x11, 0x2C);
223
224 return V4L2_STD_NTSC;
225 }
226
227
228 saa7110_write(sd, 0x0D, 0x86);
229 saa7110_write(sd, 0x0F, 0x10);
230 saa7110_write(sd, 0x11, 0x59);
231
232
233 prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
234 schedule_timeout(msecs_to_jiffies(250));
235 finish_wait(&decoder->wq, &wait);
236
237 status = saa7110_read(sd);
238 if ((status & 0x03) == 0x01) {
239 v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status);
240 saa7110_write(sd, 0x0D, 0x87);
241 return V4L2_STD_SECAM;
242 }
243 v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status);
244 return V4L2_STD_PAL;
245}
246
247static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus)
248{
249 struct saa7110 *decoder = to_saa7110(sd);
250 int res = V4L2_IN_ST_NO_SIGNAL;
251 int status = saa7110_read(sd);
252
253 v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n",
254 status, (unsigned long long)decoder->norm);
255 if (!(status & 0x40))
256 res = 0;
257 if (!(status & 0x03))
258 res |= V4L2_IN_ST_NO_COLOR;
259
260 *pstatus = res;
261 return 0;
262}
263
264static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
265{
266 *(v4l2_std_id *)std = determine_norm(sd);
267 return 0;
268}
269
270static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
271{
272 struct saa7110 *decoder = to_saa7110(sd);
273
274 if (decoder->norm != std) {
275 decoder->norm = std;
276
277 if (std & V4L2_STD_NTSC) {
278 saa7110_write(sd, 0x0D, 0x86);
279 saa7110_write(sd, 0x0F, 0x50);
280 saa7110_write(sd, 0x11, 0x2C);
281
282 v4l2_dbg(1, debug, sd, "switched to NTSC\n");
283 } else if (std & V4L2_STD_PAL) {
284 saa7110_write(sd, 0x0D, 0x86);
285 saa7110_write(sd, 0x0F, 0x10);
286 saa7110_write(sd, 0x11, 0x59);
287
288 v4l2_dbg(1, debug, sd, "switched to PAL\n");
289 } else if (std & V4L2_STD_SECAM) {
290 saa7110_write(sd, 0x0D, 0x87);
291 saa7110_write(sd, 0x0F, 0x10);
292 saa7110_write(sd, 0x11, 0x59);
293
294 v4l2_dbg(1, debug, sd, "switched to SECAM\n");
295 } else {
296 return -EINVAL;
297 }
298 }
299 return 0;
300}
301
302static int saa7110_s_routing(struct v4l2_subdev *sd,
303 u32 input, u32 output, u32 config)
304{
305 struct saa7110 *decoder = to_saa7110(sd);
306
307 if (input < 0 || input >= SAA7110_MAX_INPUT) {
308 v4l2_dbg(1, debug, sd, "input=%d not available\n", input);
309 return -EINVAL;
310 }
311 if (decoder->input != input) {
312 saa7110_selmux(sd, input);
313 v4l2_dbg(1, debug, sd, "switched to input=%d\n", input);
314 }
315 return 0;
316}
317
318static int saa7110_s_stream(struct v4l2_subdev *sd, int enable)
319{
320 struct saa7110 *decoder = to_saa7110(sd);
321
322 if (decoder->enable != enable) {
323 decoder->enable = enable;
324 saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80);
325 v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off");
326 }
327 return 0;
328}
329
330static int saa7110_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
331{
332 switch (qc->id) {
333 case V4L2_CID_BRIGHTNESS:
334 return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
335 case V4L2_CID_CONTRAST:
336 case V4L2_CID_SATURATION:
337 return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
338 case V4L2_CID_HUE:
339 return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
340 default:
341 return -EINVAL;
342 }
343 return 0;
344}
345
346static int saa7110_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
347{
348 struct saa7110 *decoder = to_saa7110(sd);
349
350 switch (ctrl->id) {
351 case V4L2_CID_BRIGHTNESS:
352 ctrl->value = decoder->bright;
353 break;
354 case V4L2_CID_CONTRAST:
355 ctrl->value = decoder->contrast;
356 break;
357 case V4L2_CID_SATURATION:
358 ctrl->value = decoder->sat;
359 break;
360 case V4L2_CID_HUE:
361 ctrl->value = decoder->hue;
362 break;
363 default:
364 return -EINVAL;
365 }
366 return 0;
367}
368
369static int saa7110_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
370{
371 struct saa7110 *decoder = to_saa7110(sd);
372
373 switch (ctrl->id) {
374 case V4L2_CID_BRIGHTNESS:
375 if (decoder->bright != ctrl->value) {
376 decoder->bright = ctrl->value;
377 saa7110_write(sd, 0x19, decoder->bright);
378 }
379 break;
380 case V4L2_CID_CONTRAST:
381 if (decoder->contrast != ctrl->value) {
382 decoder->contrast = ctrl->value;
383 saa7110_write(sd, 0x13, decoder->contrast);
384 }
385 break;
386 case V4L2_CID_SATURATION:
387 if (decoder->sat != ctrl->value) {
388 decoder->sat = ctrl->value;
389 saa7110_write(sd, 0x12, decoder->sat);
390 }
391 break;
392 case V4L2_CID_HUE:
393 if (decoder->hue != ctrl->value) {
394 decoder->hue = ctrl->value;
395 saa7110_write(sd, 0x07, decoder->hue);
396 }
397 break;
398 default:
399 return -EINVAL;
400 }
401 return 0;
402}
403
404static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
405{
406 struct i2c_client *client = v4l2_get_subdevdata(sd);
407
408 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0);
409}
410
411
412
413static const struct v4l2_subdev_core_ops saa7110_core_ops = {
414 .g_chip_ident = saa7110_g_chip_ident,
415 .g_ctrl = saa7110_g_ctrl,
416 .s_ctrl = saa7110_s_ctrl,
417 .queryctrl = saa7110_queryctrl,
418 .s_std = saa7110_s_std,
419};
420
421static const struct v4l2_subdev_video_ops saa7110_video_ops = {
422 .s_routing = saa7110_s_routing,
423 .s_stream = saa7110_s_stream,
424 .querystd = saa7110_querystd,
425 .g_input_status = saa7110_g_input_status,
426};
427
428static const struct v4l2_subdev_ops saa7110_ops = {
429 .core = &saa7110_core_ops,
430 .video = &saa7110_video_ops,
431};
432
433
434
435static int saa7110_probe(struct i2c_client *client,
436 const struct i2c_device_id *id)
437{
438 struct saa7110 *decoder;
439 struct v4l2_subdev *sd;
440 int rv;
441
442
443 if (!i2c_check_functionality(client->adapter,
444 I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
445 return -ENODEV;
446
447 v4l_info(client, "chip found @ 0x%x (%s)\n",
448 client->addr << 1, client->adapter->name);
449
450 decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL);
451 if (!decoder)
452 return -ENOMEM;
453 sd = &decoder->sd;
454 v4l2_i2c_subdev_init(sd, client, &saa7110_ops);
455 decoder->norm = V4L2_STD_PAL;
456 decoder->input = 0;
457 decoder->enable = 1;
458 decoder->bright = 32768;
459 decoder->contrast = 32768;
460 decoder->hue = 32768;
461 decoder->sat = 32768;
462 init_waitqueue_head(&decoder->wq);
463
464 rv = saa7110_write_block(sd, initseq, sizeof(initseq));
465 if (rv < 0) {
466 v4l2_dbg(1, debug, sd, "init status %d\n", rv);
467 } else {
468 int ver, status;
469 saa7110_write(sd, 0x21, 0x10);
470 saa7110_write(sd, 0x0e, 0x18);
471 saa7110_write(sd, 0x0D, 0x04);
472 ver = saa7110_read(sd);
473 saa7110_write(sd, 0x0D, 0x06);
474
475 status = saa7110_read(sd);
476 v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n",
477 ver, status);
478 saa7110_write(sd, 0x0D, 0x86);
479 saa7110_write(sd, 0x0F, 0x10);
480 saa7110_write(sd, 0x11, 0x59);
481
482 }
483
484
485
486
487
488 return 0;
489}
490
491static int saa7110_remove(struct i2c_client *client)
492{
493 struct v4l2_subdev *sd = i2c_get_clientdata(client);
494
495 v4l2_device_unregister_subdev(sd);
496 kfree(to_saa7110(sd));
497 return 0;
498}
499
500
501
502static const struct i2c_device_id saa7110_id[] = {
503 { "saa7110", 0 },
504 { }
505};
506MODULE_DEVICE_TABLE(i2c, saa7110_id);
507
508static struct v4l2_i2c_driver_data v4l2_i2c_data = {
509 .name = "saa7110",
510 .probe = saa7110_probe,
511 .remove = saa7110_remove,
512 .id_table = saa7110_id,
513};
514