[go: nahoru, domu]

1/*
2 * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
3 *
4 * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include <linux/module.h>
22#include <linux/init.h>
23#include <linux/delay.h>
24#include <linux/types.h>
25#include <linux/slab.h>
26#include <asm/uaccess.h>
27#include <linux/i2c.h>
28#include <linux/videodev2.h>
29#include <media/v4l2-device.h>
30#include <media/v4l2-ctrls.h>
31
32MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
33MODULE_AUTHOR("Laurent Pinchart");
34MODULE_LICENSE("GPL");
35
36static int debug;
37module_param(debug, int, 0);
38MODULE_PARM_DESC(debug, "Debug level (0-1)");
39
40
41#define VPX_TIMEOUT_COUNT  10
42
43/* ----------------------------------------------------------------------- */
44
45struct vpx3220 {
46	struct v4l2_subdev sd;
47	struct v4l2_ctrl_handler hdl;
48	unsigned char reg[255];
49
50	v4l2_std_id norm;
51	int input;
52	int enable;
53};
54
55static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
56{
57	return container_of(sd, struct vpx3220, sd);
58}
59
60static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
61{
62	return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
63}
64
65static char *inputs[] = { "internal", "composite", "svideo" };
66
67/* ----------------------------------------------------------------------- */
68
69static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
70{
71	struct i2c_client *client = v4l2_get_subdevdata(sd);
72	struct vpx3220 *decoder = i2c_get_clientdata(client);
73
74	decoder->reg[reg] = value;
75	return i2c_smbus_write_byte_data(client, reg, value);
76}
77
78static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
79{
80	struct i2c_client *client = v4l2_get_subdevdata(sd);
81
82	return i2c_smbus_read_byte_data(client, reg);
83}
84
85static int vpx3220_fp_status(struct v4l2_subdev *sd)
86{
87	unsigned char status;
88	unsigned int i;
89
90	for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
91		status = vpx3220_read(sd, 0x29);
92
93		if (!(status & 4))
94			return 0;
95
96		udelay(10);
97
98		if (need_resched())
99			cond_resched();
100	}
101
102	return -1;
103}
104
105static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
106{
107	struct i2c_client *client = v4l2_get_subdevdata(sd);
108
109	/* Write the 16-bit address to the FPWR register */
110	if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
111		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
112		return -1;
113	}
114
115	if (vpx3220_fp_status(sd) < 0)
116		return -1;
117
118	/* Write the 16-bit data to the FPDAT register */
119	if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
120		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
121		return -1;
122	}
123
124	return 0;
125}
126
127static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
128{
129	struct i2c_client *client = v4l2_get_subdevdata(sd);
130	s16 data;
131
132	/* Write the 16-bit address to the FPRD register */
133	if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
134		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
135		return -1;
136	}
137
138	if (vpx3220_fp_status(sd) < 0)
139		return -1;
140
141	/* Read the 16-bit data from the FPDAT register */
142	data = i2c_smbus_read_word_data(client, 0x28);
143	if (data == -1) {
144		v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
145		return -1;
146	}
147
148	return swab16(data);
149}
150
151static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
152{
153	u8 reg;
154	int ret = -1;
155
156	while (len >= 2) {
157		reg = *data++;
158		ret = vpx3220_write(sd, reg, *data++);
159		if (ret < 0)
160			break;
161		len -= 2;
162	}
163
164	return ret;
165}
166
167static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
168		const u16 *data, unsigned int len)
169{
170	u8 reg;
171	int ret = 0;
172
173	while (len > 1) {
174		reg = *data++;
175		ret |= vpx3220_fp_write(sd, reg, *data++);
176		len -= 2;
177	}
178
179	return ret;
180}
181
182/* ---------------------------------------------------------------------- */
183
184static const unsigned short init_ntsc[] = {
185	0x1c, 0x00,		/* NTSC tint angle */
186	0x88, 17,		/* Window 1 vertical */
187	0x89, 240,		/* Vertical lines in */
188	0x8a, 240,		/* Vertical lines out */
189	0x8b, 000,		/* Horizontal begin */
190	0x8c, 640,		/* Horizontal length */
191	0x8d, 640,		/* Number of pixels */
192	0x8f, 0xc00,		/* Disable window 2 */
193	0xf0, 0x73,		/* 13.5 MHz transport, Forced
194				 * mode, latch windows */
195	0xf2, 0x13,		/* NTSC M, composite input */
196	0xe7, 0x1e1,		/* Enable vertical standard
197				 * locking @ 240 lines */
198};
199
200static const unsigned short init_pal[] = {
201	0x88, 23,		/* Window 1 vertical begin */
202	0x89, 288,		/* Vertical lines in (16 lines
203				 * skipped by the VFE) */
204	0x8a, 288,		/* Vertical lines out (16 lines
205				 * skipped by the VFE) */
206	0x8b, 16,		/* Horizontal begin */
207	0x8c, 768,		/* Horizontal length */
208	0x8d, 784, 		/* Number of pixels
209				 * Must be >= Horizontal begin + Horizontal length */
210	0x8f, 0xc00,		/* Disable window 2 */
211	0xf0, 0x77,		/* 13.5 MHz transport, Forced
212				 * mode, latch windows */
213	0xf2, 0x3d1,		/* PAL B,G,H,I, composite input */
214	0xe7, 0x241,		/* PAL/SECAM set to 288 lines */
215};
216
217static const unsigned short init_secam[] = {
218	0x88, 23,		/* Window 1 vertical begin */
219	0x89, 288,		/* Vertical lines in (16 lines
220				 * skipped by the VFE) */
221	0x8a, 288,		/* Vertical lines out (16 lines
222				 * skipped by the VFE) */
223	0x8b, 16,		/* Horizontal begin */
224	0x8c, 768,		/* Horizontal length */
225	0x8d, 784,		/* Number of pixels
226				 * Must be >= Horizontal begin + Horizontal length */
227	0x8f, 0xc00,		/* Disable window 2 */
228	0xf0, 0x77,		/* 13.5 MHz transport, Forced
229				 * mode, latch windows */
230	0xf2, 0x3d5,		/* SECAM, composite input */
231	0xe7, 0x241,		/* PAL/SECAM set to 288 lines */
232};
233
234static const unsigned char init_common[] = {
235	0xf2, 0x00,		/* Disable all outputs */
236	0x33, 0x0d,		/* Luma : VIN2, Chroma : CIN
237				 * (clamp off) */
238	0xd8, 0xa8,		/* HREF/VREF active high, VREF
239				 * pulse = 2, Odd/Even flag */
240	0x20, 0x03,		/* IF compensation 0dB/oct */
241	0xe0, 0xff,		/* Open up all comparators */
242	0xe1, 0x00,
243	0xe2, 0x7f,
244	0xe3, 0x80,
245	0xe4, 0x7f,
246	0xe5, 0x80,
247	0xe6, 0x00,		/* Brightness set to 0 */
248	0xe7, 0xe0,		/* Contrast to 1.0, noise shaping
249				 * 10 to 8 2-bit error diffusion */
250	0xe8, 0xf8,		/* YUV422, CbCr binary offset,
251				 * ... (p.32) */
252	0xea, 0x18,		/* LLC2 connected, output FIFO
253				 * reset with VACTintern */
254	0xf0, 0x8a,		/* Half full level to 10, bus
255				 * shuffler [7:0, 23:16, 15:8] */
256	0xf1, 0x18,		/* Single clock, sync mode, no
257				 * FE delay, no HLEN counter */
258	0xf8, 0x12,		/* Port A, PIXCLK, HF# & FE#
259				 * strength to 2 */
260	0xf9, 0x24,		/* Port B, HREF, VREF, PREF &
261				 * ALPHA strength to 4 */
262};
263
264static const unsigned short init_fp[] = {
265	0x59, 0,
266	0xa0, 2070,		/* ACC reference */
267	0xa3, 0,
268	0xa4, 0,
269	0xa8, 30,
270	0xb2, 768,
271	0xbe, 27,
272	0x58, 0,
273	0x26, 0,
274	0x4b, 0x298,		/* PLL gain */
275};
276
277
278static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
279{
280	struct vpx3220 *decoder = to_vpx3220(sd);
281
282	vpx3220_write_block(sd, init_common, sizeof(init_common));
283	vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
284	if (decoder->norm & V4L2_STD_NTSC)
285		vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
286	else if (decoder->norm & V4L2_STD_PAL)
287		vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
288	else if (decoder->norm & V4L2_STD_SECAM)
289		vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
290	else
291		vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
292	return 0;
293}
294
295static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
296{
297	int res = V4L2_IN_ST_NO_SIGNAL, status;
298	v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
299
300	status = vpx3220_fp_read(sd, 0x0f3);
301
302	v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
303
304	if (status < 0)
305		return status;
306
307	if ((status & 0x20) == 0) {
308		res = 0;
309
310		switch (status & 0x18) {
311		case 0x00:
312		case 0x10:
313		case 0x14:
314		case 0x18:
315			std &= V4L2_STD_PAL;
316			break;
317
318		case 0x08:
319			std &= V4L2_STD_SECAM;
320			break;
321
322		case 0x04:
323		case 0x0c:
324		case 0x1c:
325			std &= V4L2_STD_NTSC;
326			break;
327		}
328	} else {
329		std = V4L2_STD_UNKNOWN;
330	}
331	if (pstd)
332		*pstd = std;
333	if (pstatus)
334		*pstatus = res;
335	return 0;
336}
337
338static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
339{
340	v4l2_dbg(1, debug, sd, "querystd\n");
341	return vpx3220_status(sd, NULL, std);
342}
343
344static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
345{
346	v4l2_dbg(1, debug, sd, "g_input_status\n");
347	return vpx3220_status(sd, status, NULL);
348}
349
350static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
351{
352	struct vpx3220 *decoder = to_vpx3220(sd);
353	int temp_input;
354
355	/* Here we back up the input selection because it gets
356	   overwritten when we fill the registers with the
357	   chosen video norm */
358	temp_input = vpx3220_fp_read(sd, 0xf2);
359
360	v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
361	if (std & V4L2_STD_NTSC) {
362		vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
363		v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
364	} else if (std & V4L2_STD_PAL) {
365		vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
366		v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
367	} else if (std & V4L2_STD_SECAM) {
368		vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
369		v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
370	} else {
371		return -EINVAL;
372	}
373
374	decoder->norm = std;
375
376	/* And here we set the backed up video input again */
377	vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
378	udelay(10);
379	return 0;
380}
381
382static int vpx3220_s_routing(struct v4l2_subdev *sd,
383			     u32 input, u32 output, u32 config)
384{
385	int data;
386
387	/* RJ:   input = 0: ST8 (PCTV) input
388		 input = 1: COMPOSITE  input
389		 input = 2: SVHS       input  */
390
391	const int input_vals[3][2] = {
392		{0x0c, 0},
393		{0x0d, 0},
394		{0x0e, 1}
395	};
396
397	if (input > 2)
398		return -EINVAL;
399
400	v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
401
402	vpx3220_write(sd, 0x33, input_vals[input][0]);
403
404	data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
405	if (data < 0)
406		return data;
407	/* 0x0010 is required to latch the setting */
408	vpx3220_fp_write(sd, 0xf2,
409			data | (input_vals[input][1] << 5) | 0x0010);
410
411	udelay(10);
412	return 0;
413}
414
415static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
416{
417	v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
418
419	vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
420	return 0;
421}
422
423static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
424{
425	struct v4l2_subdev *sd = to_sd(ctrl);
426
427	switch (ctrl->id) {
428	case V4L2_CID_BRIGHTNESS:
429		vpx3220_write(sd, 0xe6, ctrl->val);
430		return 0;
431	case V4L2_CID_CONTRAST:
432		/* Bit 7 and 8 is for noise shaping */
433		vpx3220_write(sd, 0xe7, ctrl->val + 192);
434		return 0;
435	case V4L2_CID_SATURATION:
436		vpx3220_fp_write(sd, 0xa0, ctrl->val);
437		return 0;
438	case V4L2_CID_HUE:
439		vpx3220_fp_write(sd, 0x1c, ctrl->val);
440		return 0;
441	}
442	return -EINVAL;
443}
444
445/* ----------------------------------------------------------------------- */
446
447static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
448	.s_ctrl = vpx3220_s_ctrl,
449};
450
451static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
452	.init = vpx3220_init,
453	.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
454	.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
455	.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
456	.g_ctrl = v4l2_subdev_g_ctrl,
457	.s_ctrl = v4l2_subdev_s_ctrl,
458	.queryctrl = v4l2_subdev_queryctrl,
459	.querymenu = v4l2_subdev_querymenu,
460};
461
462static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
463	.s_std = vpx3220_s_std,
464	.s_routing = vpx3220_s_routing,
465	.s_stream = vpx3220_s_stream,
466	.querystd = vpx3220_querystd,
467	.g_input_status = vpx3220_g_input_status,
468};
469
470static const struct v4l2_subdev_ops vpx3220_ops = {
471	.core = &vpx3220_core_ops,
472	.video = &vpx3220_video_ops,
473};
474
475/* -----------------------------------------------------------------------
476 * Client management code
477 */
478
479static int vpx3220_probe(struct i2c_client *client,
480			const struct i2c_device_id *id)
481{
482	struct vpx3220 *decoder;
483	struct v4l2_subdev *sd;
484	const char *name = NULL;
485	u8 ver;
486	u16 pn;
487
488	/* Check if the adapter supports the needed features */
489	if (!i2c_check_functionality(client->adapter,
490		I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
491		return -ENODEV;
492
493	decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
494	if (decoder == NULL)
495		return -ENOMEM;
496	sd = &decoder->sd;
497	v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
498	decoder->norm = V4L2_STD_PAL;
499	decoder->input = 0;
500	decoder->enable = 1;
501	v4l2_ctrl_handler_init(&decoder->hdl, 4);
502	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
503		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
504	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
505		V4L2_CID_CONTRAST, 0, 63, 1, 32);
506	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
507		V4L2_CID_SATURATION, 0, 4095, 1, 2048);
508	v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
509		V4L2_CID_HUE, -512, 511, 1, 0);
510	sd->ctrl_handler = &decoder->hdl;
511	if (decoder->hdl.error) {
512		int err = decoder->hdl.error;
513
514		v4l2_ctrl_handler_free(&decoder->hdl);
515		return err;
516	}
517	v4l2_ctrl_handler_setup(&decoder->hdl);
518
519	ver = i2c_smbus_read_byte_data(client, 0x00);
520	pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
521		i2c_smbus_read_byte_data(client, 0x01);
522	if (ver == 0xec) {
523		switch (pn) {
524		case 0x4680:
525			name = "vpx3220a";
526			break;
527		case 0x4260:
528			name = "vpx3216b";
529			break;
530		case 0x4280:
531			name = "vpx3214c";
532			break;
533		}
534	}
535	if (name)
536		v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
537			client->addr << 1, client->adapter->name);
538	else
539		v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
540			ver, pn, client->addr << 1, client->adapter->name);
541
542	vpx3220_write_block(sd, init_common, sizeof(init_common));
543	vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
544	/* Default to PAL */
545	vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
546	return 0;
547}
548
549static int vpx3220_remove(struct i2c_client *client)
550{
551	struct v4l2_subdev *sd = i2c_get_clientdata(client);
552	struct vpx3220 *decoder = to_vpx3220(sd);
553
554	v4l2_device_unregister_subdev(sd);
555	v4l2_ctrl_handler_free(&decoder->hdl);
556
557	return 0;
558}
559
560static const struct i2c_device_id vpx3220_id[] = {
561	{ "vpx3220a", 0 },
562	{ "vpx3216b", 0 },
563	{ "vpx3214c", 0 },
564	{ }
565};
566MODULE_DEVICE_TABLE(i2c, vpx3220_id);
567
568static struct i2c_driver vpx3220_driver = {
569	.driver = {
570		.owner	= THIS_MODULE,
571		.name	= "vpx3220",
572	},
573	.probe		= vpx3220_probe,
574	.remove		= vpx3220_remove,
575	.id_table	= vpx3220_id,
576};
577
578module_i2c_driver(vpx3220_driver);
579