xref: /openbmc/linux/drivers/media/i2c/tw2804.c (revision aaeb31c0)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
212be52a9SHans Verkuil /*
312be52a9SHans Verkuil  * Copyright (C) 2005-2006 Micronas USA Inc.
412be52a9SHans Verkuil  */
512be52a9SHans Verkuil 
612be52a9SHans Verkuil #include <linux/module.h>
712be52a9SHans Verkuil #include <linux/init.h>
812be52a9SHans Verkuil #include <linux/i2c.h>
912be52a9SHans Verkuil #include <linux/videodev2.h>
1012be52a9SHans Verkuil #include <linux/ioctl.h>
1112be52a9SHans Verkuil #include <linux/slab.h>
1212be52a9SHans Verkuil #include <media/v4l2-subdev.h>
1312be52a9SHans Verkuil #include <media/v4l2-device.h>
1412be52a9SHans Verkuil #include <media/v4l2-ctrls.h>
1512be52a9SHans Verkuil 
1612be52a9SHans Verkuil #define TW2804_REG_AUTOGAIN		0x02
1712be52a9SHans Verkuil #define TW2804_REG_HUE			0x0f
1812be52a9SHans Verkuil #define TW2804_REG_SATURATION		0x10
1912be52a9SHans Verkuil #define TW2804_REG_CONTRAST		0x11
2012be52a9SHans Verkuil #define TW2804_REG_BRIGHTNESS		0x12
2112be52a9SHans Verkuil #define TW2804_REG_COLOR_KILLER		0x14
2212be52a9SHans Verkuil #define TW2804_REG_GAIN			0x3c
2312be52a9SHans Verkuil #define TW2804_REG_CHROMA_GAIN		0x3d
2412be52a9SHans Verkuil #define TW2804_REG_BLUE_BALANCE		0x3e
2512be52a9SHans Verkuil #define TW2804_REG_RED_BALANCE		0x3f
2612be52a9SHans Verkuil 
2712be52a9SHans Verkuil struct tw2804 {
2812be52a9SHans Verkuil 	struct v4l2_subdev sd;
2912be52a9SHans Verkuil 	struct v4l2_ctrl_handler hdl;
3012be52a9SHans Verkuil 	u8 channel:2;
3112be52a9SHans Verkuil 	u8 input:1;
3212be52a9SHans Verkuil 	int norm;
3312be52a9SHans Verkuil };
3412be52a9SHans Verkuil 
3512be52a9SHans Verkuil static const u8 global_registers[] = {
3612be52a9SHans Verkuil 	0x39, 0x00,
3712be52a9SHans Verkuil 	0x3a, 0xff,
3812be52a9SHans Verkuil 	0x3b, 0x84,
3912be52a9SHans Verkuil 	0x3c, 0x80,
4012be52a9SHans Verkuil 	0x3d, 0x80,
4112be52a9SHans Verkuil 	0x3e, 0x82,
4212be52a9SHans Verkuil 	0x3f, 0x82,
438dbee53bSVolokh Konstantin 	0x78, 0x00,
4412be52a9SHans Verkuil 	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
4512be52a9SHans Verkuil };
4612be52a9SHans Verkuil 
4712be52a9SHans Verkuil static const u8 channel_registers[] = {
4812be52a9SHans Verkuil 	0x01, 0xc4,
4912be52a9SHans Verkuil 	0x02, 0xa5,
5012be52a9SHans Verkuil 	0x03, 0x20,
5112be52a9SHans Verkuil 	0x04, 0xd0,
5212be52a9SHans Verkuil 	0x05, 0x20,
5312be52a9SHans Verkuil 	0x06, 0xd0,
5412be52a9SHans Verkuil 	0x07, 0x88,
5512be52a9SHans Verkuil 	0x08, 0x20,
5612be52a9SHans Verkuil 	0x09, 0x07,
5712be52a9SHans Verkuil 	0x0a, 0xf0,
5812be52a9SHans Verkuil 	0x0b, 0x07,
5912be52a9SHans Verkuil 	0x0c, 0xf0,
6012be52a9SHans Verkuil 	0x0d, 0x40,
6112be52a9SHans Verkuil 	0x0e, 0xd2,
6212be52a9SHans Verkuil 	0x0f, 0x80,
6312be52a9SHans Verkuil 	0x10, 0x80,
6412be52a9SHans Verkuil 	0x11, 0x80,
6512be52a9SHans Verkuil 	0x12, 0x80,
6612be52a9SHans Verkuil 	0x13, 0x1f,
6712be52a9SHans Verkuil 	0x14, 0x00,
6812be52a9SHans Verkuil 	0x15, 0x00,
6912be52a9SHans Verkuil 	0x16, 0x00,
7012be52a9SHans Verkuil 	0x17, 0x00,
7112be52a9SHans Verkuil 	0x18, 0xff,
7212be52a9SHans Verkuil 	0x19, 0xff,
7312be52a9SHans Verkuil 	0x1a, 0xff,
7412be52a9SHans Verkuil 	0x1b, 0xff,
7512be52a9SHans Verkuil 	0x1c, 0xff,
7612be52a9SHans Verkuil 	0x1d, 0xff,
7712be52a9SHans Verkuil 	0x1e, 0xff,
7812be52a9SHans Verkuil 	0x1f, 0xff,
7912be52a9SHans Verkuil 	0x20, 0x07,
8012be52a9SHans Verkuil 	0x21, 0x07,
8112be52a9SHans Verkuil 	0x22, 0x00,
8212be52a9SHans Verkuil 	0x23, 0x91,
8312be52a9SHans Verkuil 	0x24, 0x51,
8412be52a9SHans Verkuil 	0x25, 0x03,
8512be52a9SHans Verkuil 	0x26, 0x00,
8612be52a9SHans Verkuil 	0x27, 0x00,
8712be52a9SHans Verkuil 	0x28, 0x00,
8812be52a9SHans Verkuil 	0x29, 0x00,
8912be52a9SHans Verkuil 	0x2a, 0x00,
9012be52a9SHans Verkuil 	0x2b, 0x00,
9112be52a9SHans Verkuil 	0x2c, 0x00,
9212be52a9SHans Verkuil 	0x2d, 0x00,
9312be52a9SHans Verkuil 	0x2e, 0x00,
9412be52a9SHans Verkuil 	0x2f, 0x00,
9512be52a9SHans Verkuil 	0x30, 0x00,
9612be52a9SHans Verkuil 	0x31, 0x00,
9712be52a9SHans Verkuil 	0x32, 0x00,
9812be52a9SHans Verkuil 	0x33, 0x00,
9912be52a9SHans Verkuil 	0x34, 0x00,
10012be52a9SHans Verkuil 	0x35, 0x00,
10112be52a9SHans Verkuil 	0x36, 0x00,
10212be52a9SHans Verkuil 	0x37, 0x00,
10312be52a9SHans Verkuil 	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
10412be52a9SHans Verkuil };
10512be52a9SHans Verkuil 
write_reg(struct i2c_client * client,u8 reg,u8 value,u8 channel)10612be52a9SHans Verkuil static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel)
10712be52a9SHans Verkuil {
10812be52a9SHans Verkuil 	return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
10912be52a9SHans Verkuil }
11012be52a9SHans Verkuil 
write_regs(struct i2c_client * client,const u8 * regs,u8 channel)11112be52a9SHans Verkuil static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel)
11212be52a9SHans Verkuil {
11312be52a9SHans Verkuil 	int ret;
11412be52a9SHans Verkuil 	int i;
11512be52a9SHans Verkuil 
11612be52a9SHans Verkuil 	for (i = 0; regs[i] != 0xff; i += 2) {
11712be52a9SHans Verkuil 		ret = i2c_smbus_write_byte_data(client,
11812be52a9SHans Verkuil 				regs[i] | (channel << 6), regs[i + 1]);
11912be52a9SHans Verkuil 		if (ret < 0)
12012be52a9SHans Verkuil 			return ret;
12112be52a9SHans Verkuil 	}
12212be52a9SHans Verkuil 	return 0;
12312be52a9SHans Verkuil }
12412be52a9SHans Verkuil 
read_reg(struct i2c_client * client,u8 reg,u8 channel)12512be52a9SHans Verkuil static int read_reg(struct i2c_client *client, u8 reg, u8 channel)
12612be52a9SHans Verkuil {
12712be52a9SHans Verkuil 	return i2c_smbus_read_byte_data(client, (reg) | (channel << 6));
12812be52a9SHans Verkuil }
12912be52a9SHans Verkuil 
to_state(struct v4l2_subdev * sd)13012be52a9SHans Verkuil static inline struct tw2804 *to_state(struct v4l2_subdev *sd)
13112be52a9SHans Verkuil {
13212be52a9SHans Verkuil 	return container_of(sd, struct tw2804, sd);
13312be52a9SHans Verkuil }
13412be52a9SHans Verkuil 
to_state_from_ctrl(struct v4l2_ctrl * ctrl)13512be52a9SHans Verkuil static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl)
13612be52a9SHans Verkuil {
13712be52a9SHans Verkuil 	return container_of(ctrl->handler, struct tw2804, hdl);
13812be52a9SHans Verkuil }
13912be52a9SHans Verkuil 
tw2804_log_status(struct v4l2_subdev * sd)14012be52a9SHans Verkuil static int tw2804_log_status(struct v4l2_subdev *sd)
14112be52a9SHans Verkuil {
14212be52a9SHans Verkuil 	struct tw2804 *state = to_state(sd);
14312be52a9SHans Verkuil 
14412be52a9SHans Verkuil 	v4l2_info(sd, "Standard: %s\n",
14512be52a9SHans Verkuil 			state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz");
14612be52a9SHans Verkuil 	v4l2_info(sd, "Channel: %d\n", state->channel);
14712be52a9SHans Verkuil 	v4l2_info(sd, "Input: %d\n", state->input);
14812be52a9SHans Verkuil 	return v4l2_ctrl_subdev_log_status(sd);
14912be52a9SHans Verkuil }
15012be52a9SHans Verkuil 
15112be52a9SHans Verkuil /*
15212be52a9SHans Verkuil  * These volatile controls are needed because all four channels share
15312be52a9SHans Verkuil  * these controls. So a change made to them through one channel would
15412be52a9SHans Verkuil  * require another channel to be updated.
15512be52a9SHans Verkuil  *
15612be52a9SHans Verkuil  * Normally this would have been done in a different way, but since the one
15712be52a9SHans Verkuil  * board that uses this driver sees this single chip as if it was on four
15812be52a9SHans Verkuil  * different i2c adapters (each adapter belonging to a separate instance of
15912be52a9SHans Verkuil  * the same USB driver) there is no reliable method that I have found to let
16012be52a9SHans Verkuil  * the instances know about each other.
16112be52a9SHans Verkuil  *
16212be52a9SHans Verkuil  * So implementing these global registers as volatile is the best we can do.
16312be52a9SHans Verkuil  */
tw2804_g_volatile_ctrl(struct v4l2_ctrl * ctrl)16412be52a9SHans Verkuil static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
16512be52a9SHans Verkuil {
16612be52a9SHans Verkuil 	struct tw2804 *state = to_state_from_ctrl(ctrl);
16712be52a9SHans Verkuil 	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
16812be52a9SHans Verkuil 
16912be52a9SHans Verkuil 	switch (ctrl->id) {
17012be52a9SHans Verkuil 	case V4L2_CID_GAIN:
17112be52a9SHans Verkuil 		ctrl->val = read_reg(client, TW2804_REG_GAIN, 0);
17212be52a9SHans Verkuil 		return 0;
17312be52a9SHans Verkuil 
17412be52a9SHans Verkuil 	case V4L2_CID_CHROMA_GAIN:
17512be52a9SHans Verkuil 		ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0);
17612be52a9SHans Verkuil 		return 0;
17712be52a9SHans Verkuil 
17812be52a9SHans Verkuil 	case V4L2_CID_BLUE_BALANCE:
17912be52a9SHans Verkuil 		ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0);
18012be52a9SHans Verkuil 		return 0;
18112be52a9SHans Verkuil 
18212be52a9SHans Verkuil 	case V4L2_CID_RED_BALANCE:
18312be52a9SHans Verkuil 		ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0);
18412be52a9SHans Verkuil 		return 0;
18512be52a9SHans Verkuil 	}
18612be52a9SHans Verkuil 	return 0;
18712be52a9SHans Verkuil }
18812be52a9SHans Verkuil 
tw2804_s_ctrl(struct v4l2_ctrl * ctrl)18912be52a9SHans Verkuil static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl)
19012be52a9SHans Verkuil {
19112be52a9SHans Verkuil 	struct tw2804 *state = to_state_from_ctrl(ctrl);
19212be52a9SHans Verkuil 	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
19312be52a9SHans Verkuil 	int addr;
19412be52a9SHans Verkuil 	int reg;
19512be52a9SHans Verkuil 
19612be52a9SHans Verkuil 	switch (ctrl->id) {
19712be52a9SHans Verkuil 	case V4L2_CID_AUTOGAIN:
19812be52a9SHans Verkuil 		addr = TW2804_REG_AUTOGAIN;
19912be52a9SHans Verkuil 		reg = read_reg(client, addr, state->channel);
20012be52a9SHans Verkuil 		if (reg < 0)
20112be52a9SHans Verkuil 			return reg;
20212be52a9SHans Verkuil 		if (ctrl->val == 0)
20312be52a9SHans Verkuil 			reg &= ~(1 << 7);
20412be52a9SHans Verkuil 		else
20512be52a9SHans Verkuil 			reg |= 1 << 7;
20612be52a9SHans Verkuil 		return write_reg(client, addr, reg, state->channel);
20712be52a9SHans Verkuil 
20812be52a9SHans Verkuil 	case V4L2_CID_COLOR_KILLER:
20912be52a9SHans Verkuil 		addr = TW2804_REG_COLOR_KILLER;
21012be52a9SHans Verkuil 		reg = read_reg(client, addr, state->channel);
21112be52a9SHans Verkuil 		if (reg < 0)
21212be52a9SHans Verkuil 			return reg;
21312be52a9SHans Verkuil 		reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03);
21412be52a9SHans Verkuil 		return write_reg(client, addr, reg, state->channel);
21512be52a9SHans Verkuil 
21612be52a9SHans Verkuil 	case V4L2_CID_GAIN:
21712be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0);
21812be52a9SHans Verkuil 
21912be52a9SHans Verkuil 	case V4L2_CID_CHROMA_GAIN:
22012be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0);
22112be52a9SHans Verkuil 
22212be52a9SHans Verkuil 	case V4L2_CID_BLUE_BALANCE:
22312be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0);
22412be52a9SHans Verkuil 
22512be52a9SHans Verkuil 	case V4L2_CID_RED_BALANCE:
22612be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0);
22712be52a9SHans Verkuil 
22812be52a9SHans Verkuil 	case V4L2_CID_BRIGHTNESS:
22912be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_BRIGHTNESS,
23012be52a9SHans Verkuil 				ctrl->val, state->channel);
23112be52a9SHans Verkuil 
23212be52a9SHans Verkuil 	case V4L2_CID_CONTRAST:
23312be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_CONTRAST,
23412be52a9SHans Verkuil 				ctrl->val, state->channel);
23512be52a9SHans Verkuil 
23612be52a9SHans Verkuil 	case V4L2_CID_SATURATION:
23712be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_SATURATION,
23812be52a9SHans Verkuil 				ctrl->val, state->channel);
23912be52a9SHans Verkuil 
24012be52a9SHans Verkuil 	case V4L2_CID_HUE:
24112be52a9SHans Verkuil 		return write_reg(client, TW2804_REG_HUE,
24212be52a9SHans Verkuil 				ctrl->val, state->channel);
24312be52a9SHans Verkuil 
24412be52a9SHans Verkuil 	default:
24512be52a9SHans Verkuil 		break;
24612be52a9SHans Verkuil 	}
24712be52a9SHans Verkuil 	return -EINVAL;
24812be52a9SHans Verkuil }
24912be52a9SHans Verkuil 
tw2804_s_std(struct v4l2_subdev * sd,v4l2_std_id norm)25012be52a9SHans Verkuil static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
25112be52a9SHans Verkuil {
25212be52a9SHans Verkuil 	struct tw2804 *dec = to_state(sd);
25312be52a9SHans Verkuil 	struct i2c_client *client = v4l2_get_subdevdata(sd);
25412be52a9SHans Verkuil 	bool is_60hz = norm & V4L2_STD_525_60;
25512be52a9SHans Verkuil 	u8 regs[] = {
25612be52a9SHans Verkuil 		0x01, is_60hz ? 0xc4 : 0x84,
25712be52a9SHans Verkuil 		0x09, is_60hz ? 0x07 : 0x04,
25812be52a9SHans Verkuil 		0x0a, is_60hz ? 0xf0 : 0x20,
25912be52a9SHans Verkuil 		0x0b, is_60hz ? 0x07 : 0x04,
26012be52a9SHans Verkuil 		0x0c, is_60hz ? 0xf0 : 0x20,
26112be52a9SHans Verkuil 		0x0d, is_60hz ? 0x40 : 0x4a,
26212be52a9SHans Verkuil 		0x16, is_60hz ? 0x00 : 0x40,
26312be52a9SHans Verkuil 		0x17, is_60hz ? 0x00 : 0x40,
26412be52a9SHans Verkuil 		0x20, is_60hz ? 0x07 : 0x0f,
26512be52a9SHans Verkuil 		0x21, is_60hz ? 0x07 : 0x0f,
26612be52a9SHans Verkuil 		0xff, 0xff,
26712be52a9SHans Verkuil 	};
26812be52a9SHans Verkuil 
26912be52a9SHans Verkuil 	write_regs(client, regs, dec->channel);
27012be52a9SHans Verkuil 	dec->norm = norm;
27112be52a9SHans Verkuil 	return 0;
27212be52a9SHans Verkuil }
27312be52a9SHans Verkuil 
tw2804_s_video_routing(struct v4l2_subdev * sd,u32 input,u32 output,u32 config)27412be52a9SHans Verkuil static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
27512be52a9SHans Verkuil 	u32 config)
27612be52a9SHans Verkuil {
27712be52a9SHans Verkuil 	struct tw2804 *dec = to_state(sd);
27812be52a9SHans Verkuil 	struct i2c_client *client = v4l2_get_subdevdata(sd);
27912be52a9SHans Verkuil 	int reg;
28012be52a9SHans Verkuil 
28112be52a9SHans Verkuil 	if (config && config - 1 != dec->channel) {
28212be52a9SHans Verkuil 		if (config > 4) {
28312be52a9SHans Verkuil 			dev_err(&client->dev,
28412be52a9SHans Verkuil 				"channel %d is not between 1 and 4!\n", config);
28512be52a9SHans Verkuil 			return -EINVAL;
28612be52a9SHans Verkuil 		}
28712be52a9SHans Verkuil 		dec->channel = config - 1;
28812be52a9SHans Verkuil 		dev_dbg(&client->dev, "initializing TW2804 channel %d\n",
28912be52a9SHans Verkuil 			dec->channel);
29012be52a9SHans Verkuil 		if (dec->channel == 0 &&
29112be52a9SHans Verkuil 				write_regs(client, global_registers, 0) < 0) {
29212be52a9SHans Verkuil 			dev_err(&client->dev,
29312be52a9SHans Verkuil 				"error initializing TW2804 global registers\n");
29412be52a9SHans Verkuil 			return -EIO;
29512be52a9SHans Verkuil 		}
29612be52a9SHans Verkuil 		if (write_regs(client, channel_registers, dec->channel) < 0) {
29712be52a9SHans Verkuil 			dev_err(&client->dev,
29812be52a9SHans Verkuil 				"error initializing TW2804 channel %d\n",
29912be52a9SHans Verkuil 				dec->channel);
30012be52a9SHans Verkuil 			return -EIO;
30112be52a9SHans Verkuil 		}
30212be52a9SHans Verkuil 	}
30312be52a9SHans Verkuil 
30412be52a9SHans Verkuil 	if (input > 1)
30512be52a9SHans Verkuil 		return -EINVAL;
30612be52a9SHans Verkuil 
30712be52a9SHans Verkuil 	if (input == dec->input)
30812be52a9SHans Verkuil 		return 0;
30912be52a9SHans Verkuil 
31012be52a9SHans Verkuil 	reg = read_reg(client, 0x22, dec->channel);
31112be52a9SHans Verkuil 
31212be52a9SHans Verkuil 	if (reg >= 0) {
31312be52a9SHans Verkuil 		if (input == 0)
31412be52a9SHans Verkuil 			reg &= ~(1 << 2);
31512be52a9SHans Verkuil 		else
31612be52a9SHans Verkuil 			reg |= 1 << 2;
31712be52a9SHans Verkuil 		reg = write_reg(client, 0x22, reg, dec->channel);
31812be52a9SHans Verkuil 	}
31912be52a9SHans Verkuil 
32012be52a9SHans Verkuil 	if (reg >= 0)
32112be52a9SHans Verkuil 		dec->input = input;
32212be52a9SHans Verkuil 	else
32312be52a9SHans Verkuil 		return reg;
32412be52a9SHans Verkuil 	return 0;
32512be52a9SHans Verkuil }
32612be52a9SHans Verkuil 
32712be52a9SHans Verkuil static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
32812be52a9SHans Verkuil 	.g_volatile_ctrl = tw2804_g_volatile_ctrl,
32912be52a9SHans Verkuil 	.s_ctrl = tw2804_s_ctrl,
33012be52a9SHans Verkuil };
33112be52a9SHans Verkuil 
33212be52a9SHans Verkuil static const struct v4l2_subdev_video_ops tw2804_video_ops = {
3338774bed9SLaurent Pinchart 	.s_std = tw2804_s_std,
33412be52a9SHans Verkuil 	.s_routing = tw2804_s_video_routing,
33512be52a9SHans Verkuil };
33612be52a9SHans Verkuil 
33712be52a9SHans Verkuil static const struct v4l2_subdev_core_ops tw2804_core_ops = {
33812be52a9SHans Verkuil 	.log_status = tw2804_log_status,
33912be52a9SHans Verkuil };
34012be52a9SHans Verkuil 
34112be52a9SHans Verkuil static const struct v4l2_subdev_ops tw2804_ops = {
34212be52a9SHans Verkuil 	.core = &tw2804_core_ops,
34312be52a9SHans Verkuil 	.video = &tw2804_video_ops,
34412be52a9SHans Verkuil };
34512be52a9SHans Verkuil 
tw2804_probe(struct i2c_client * client)3464f484686SUwe Kleine-König static int tw2804_probe(struct i2c_client *client)
34712be52a9SHans Verkuil {
34812be52a9SHans Verkuil 	struct i2c_adapter *adapter = client->adapter;
34912be52a9SHans Verkuil 	struct tw2804 *state;
35012be52a9SHans Verkuil 	struct v4l2_subdev *sd;
35112be52a9SHans Verkuil 	struct v4l2_ctrl *ctrl;
35212be52a9SHans Verkuil 	int err;
35312be52a9SHans Verkuil 
35412be52a9SHans Verkuil 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
35512be52a9SHans Verkuil 		return -ENODEV;
35612be52a9SHans Verkuil 
357c02b211dSLaurent Pinchart 	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
35812be52a9SHans Verkuil 	if (state == NULL)
35912be52a9SHans Verkuil 		return -ENOMEM;
36012be52a9SHans Verkuil 	sd = &state->sd;
36112be52a9SHans Verkuil 	v4l2_i2c_subdev_init(sd, client, &tw2804_ops);
36212be52a9SHans Verkuil 	state->channel = -1;
36312be52a9SHans Verkuil 	state->norm = V4L2_STD_NTSC;
36412be52a9SHans Verkuil 
36512be52a9SHans Verkuil 	v4l2_ctrl_handler_init(&state->hdl, 10);
36612be52a9SHans Verkuil 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
36712be52a9SHans Verkuil 				V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
36812be52a9SHans Verkuil 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
36912be52a9SHans Verkuil 				V4L2_CID_CONTRAST, 0, 255, 1, 128);
37012be52a9SHans Verkuil 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
37112be52a9SHans Verkuil 				V4L2_CID_SATURATION, 0, 255, 1, 128);
37212be52a9SHans Verkuil 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
37312be52a9SHans Verkuil 				V4L2_CID_HUE, 0, 255, 1, 128);
37412be52a9SHans Verkuil 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
37512be52a9SHans Verkuil 				V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
37612be52a9SHans Verkuil 	v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
37712be52a9SHans Verkuil 				V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
37812be52a9SHans Verkuil 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
37912be52a9SHans Verkuil 				V4L2_CID_GAIN, 0, 255, 1, 128);
38012be52a9SHans Verkuil 	if (ctrl)
38112be52a9SHans Verkuil 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
38212be52a9SHans Verkuil 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
38312be52a9SHans Verkuil 				V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128);
38412be52a9SHans Verkuil 	if (ctrl)
38512be52a9SHans Verkuil 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
38612be52a9SHans Verkuil 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
38712be52a9SHans Verkuil 				V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122);
38812be52a9SHans Verkuil 	if (ctrl)
38912be52a9SHans Verkuil 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
39012be52a9SHans Verkuil 	ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
39112be52a9SHans Verkuil 				V4L2_CID_RED_BALANCE, 0, 255, 1, 122);
39212be52a9SHans Verkuil 	if (ctrl)
39312be52a9SHans Verkuil 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
39412be52a9SHans Verkuil 	sd->ctrl_handler = &state->hdl;
39512be52a9SHans Verkuil 	err = state->hdl.error;
39612be52a9SHans Verkuil 	if (err) {
39712be52a9SHans Verkuil 		v4l2_ctrl_handler_free(&state->hdl);
39812be52a9SHans Verkuil 		return err;
39912be52a9SHans Verkuil 	}
40012be52a9SHans Verkuil 
40112be52a9SHans Verkuil 	v4l_info(client, "chip found @ 0x%02x (%s)\n",
40212be52a9SHans Verkuil 			client->addr << 1, client->adapter->name);
40312be52a9SHans Verkuil 
40412be52a9SHans Verkuil 	return 0;
40512be52a9SHans Verkuil }
40612be52a9SHans Verkuil 
tw2804_remove(struct i2c_client * client)407ed5c2f5fSUwe Kleine-König static void tw2804_remove(struct i2c_client *client)
40812be52a9SHans Verkuil {
40912be52a9SHans Verkuil 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
41012be52a9SHans Verkuil 	struct tw2804 *state = to_state(sd);
41112be52a9SHans Verkuil 
41212be52a9SHans Verkuil 	v4l2_device_unregister_subdev(sd);
41312be52a9SHans Verkuil 	v4l2_ctrl_handler_free(&state->hdl);
41412be52a9SHans Verkuil }
41512be52a9SHans Verkuil 
41612be52a9SHans Verkuil static const struct i2c_device_id tw2804_id[] = {
41712be52a9SHans Verkuil 	{ "tw2804", 0 },
41812be52a9SHans Verkuil 	{ }
41912be52a9SHans Verkuil };
42012be52a9SHans Verkuil MODULE_DEVICE_TABLE(i2c, tw2804_id);
42112be52a9SHans Verkuil 
42212be52a9SHans Verkuil static struct i2c_driver tw2804_driver = {
42312be52a9SHans Verkuil 	.driver = {
42412be52a9SHans Verkuil 		.name	= "tw2804",
42512be52a9SHans Verkuil 	},
426*aaeb31c0SUwe Kleine-König 	.probe		= tw2804_probe,
42712be52a9SHans Verkuil 	.remove		= tw2804_remove,
42812be52a9SHans Verkuil 	.id_table	= tw2804_id,
42912be52a9SHans Verkuil };
43012be52a9SHans Verkuil 
43112be52a9SHans Verkuil module_i2c_driver(tw2804_driver);
43212be52a9SHans Verkuil 
43312be52a9SHans Verkuil MODULE_LICENSE("GPL v2");
43412be52a9SHans Verkuil MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver");
43512be52a9SHans Verkuil MODULE_AUTHOR("Micronas USA Inc");
436