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