xref: /openbmc/linux/drivers/media/i2c/ths7303.c (revision dd8c393b)
1cb7a01acSMauro Carvalho Chehab /*
288da0183SLad, Prabhakar  * ths7303/53- THS7303/53 Video Amplifier driver
3cb7a01acSMauro Carvalho Chehab  *
4cb7a01acSMauro Carvalho Chehab  * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
588da0183SLad, Prabhakar  * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
688da0183SLad, Prabhakar  *
788da0183SLad, Prabhakar  * Author: Chaithrika U S <chaithrika@ti.com>
888da0183SLad, Prabhakar  *
988da0183SLad, Prabhakar  * Contributors:
1088da0183SLad, Prabhakar  *     Hans Verkuil <hans.verkuil@cisco.com>
1188da0183SLad, Prabhakar  *     Lad, Prabhakar <prabhakar.lad@ti.com>
1288da0183SLad, Prabhakar  *     Martin Bugge <marbugge@cisco.com>
13cb7a01acSMauro Carvalho Chehab  *
14cb7a01acSMauro Carvalho Chehab  * This program is free software; you can redistribute it and/or
15cb7a01acSMauro Carvalho Chehab  * modify it under the terms of the GNU General Public License as
16cb7a01acSMauro Carvalho Chehab  * published by the Free Software Foundation version 2.
17cb7a01acSMauro Carvalho Chehab  *
18cb7a01acSMauro Carvalho Chehab  * This program is distributed .as is. WITHOUT ANY WARRANTY of any
19cb7a01acSMauro Carvalho Chehab  * kind, whether express or implied; without even the implied warranty
20cb7a01acSMauro Carvalho Chehab  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21cb7a01acSMauro Carvalho Chehab  * GNU General Public License for more details.
22cb7a01acSMauro Carvalho Chehab  */
23cb7a01acSMauro Carvalho Chehab 
24cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h>
25cb7a01acSMauro Carvalho Chehab #include <linux/module.h>
2688da0183SLad, Prabhakar #include <linux/slab.h>
27cb7a01acSMauro Carvalho Chehab 
2888da0183SLad, Prabhakar #include <media/ths7303.h>
29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h>
3088da0183SLad, Prabhakar #include <media/v4l2-device.h>
31cb7a01acSMauro Carvalho Chehab 
32ad7dcb33SManjunath Hadli #define THS7303_CHANNEL_1	1
33ad7dcb33SManjunath Hadli #define THS7303_CHANNEL_2	2
34ad7dcb33SManjunath Hadli #define THS7303_CHANNEL_3	3
35ad7dcb33SManjunath Hadli 
3688da0183SLad, Prabhakar struct ths7303_state {
3788da0183SLad, Prabhakar 	struct v4l2_subdev		sd;
38dd8c393bSLad, Prabhakar 	const struct ths7303_platform_data *pdata;
3988da0183SLad, Prabhakar 	struct v4l2_bt_timings		bt;
4088da0183SLad, Prabhakar 	int std_id;
4188da0183SLad, Prabhakar 	int stream_on;
4288da0183SLad, Prabhakar 	int driver_data;
4388da0183SLad, Prabhakar };
4488da0183SLad, Prabhakar 
45ad7dcb33SManjunath Hadli enum ths7303_filter_mode {
46ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_480I_576I,
47ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_480P_576P,
48ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_720P_1080I,
49ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_1080P,
50ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_DISABLE
51ad7dcb33SManjunath Hadli };
52ad7dcb33SManjunath Hadli 
53cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("TI THS7303 video amplifier driver");
54cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Chaithrika U S");
55cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
56cb7a01acSMauro Carvalho Chehab 
57cb7a01acSMauro Carvalho Chehab static int debug;
58cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0644);
59cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level 0-1");
60cb7a01acSMauro Carvalho Chehab 
6188da0183SLad, Prabhakar static inline struct ths7303_state *to_state(struct v4l2_subdev *sd)
6288da0183SLad, Prabhakar {
6388da0183SLad, Prabhakar 	return container_of(sd, struct ths7303_state, sd);
6488da0183SLad, Prabhakar }
6588da0183SLad, Prabhakar 
6688da0183SLad, Prabhakar static int ths7303_read(struct v4l2_subdev *sd, u8 reg)
6788da0183SLad, Prabhakar {
6888da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
6988da0183SLad, Prabhakar 
7088da0183SLad, Prabhakar 	return i2c_smbus_read_byte_data(client, reg);
7188da0183SLad, Prabhakar }
7288da0183SLad, Prabhakar 
7388da0183SLad, Prabhakar static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val)
7488da0183SLad, Prabhakar {
7588da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
7688da0183SLad, Prabhakar 	int ret;
7788da0183SLad, Prabhakar 	int i;
7888da0183SLad, Prabhakar 
7988da0183SLad, Prabhakar 	for (i = 0; i < 3; i++) {
8088da0183SLad, Prabhakar 		ret = i2c_smbus_write_byte_data(client, reg, val);
8188da0183SLad, Prabhakar 		if (ret == 0)
8288da0183SLad, Prabhakar 			return 0;
8388da0183SLad, Prabhakar 	}
8488da0183SLad, Prabhakar 	return ret;
8588da0183SLad, Prabhakar }
8688da0183SLad, Prabhakar 
87cb7a01acSMauro Carvalho Chehab /* following function is used to set ths7303 */
88ad7dcb33SManjunath Hadli int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode)
89cb7a01acSMauro Carvalho Chehab {
90ad7dcb33SManjunath Hadli 	struct i2c_client *client = v4l2_get_subdevdata(sd);
9188da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
92dd8c393bSLad, Prabhakar 	const struct ths7303_platform_data *pdata = state->pdata;
9388da0183SLad, Prabhakar 	u8 val, sel = 0;
9488da0183SLad, Prabhakar 	int err, disable = 0;
95cb7a01acSMauro Carvalho Chehab 
96ad7dcb33SManjunath Hadli 	if (!client)
97ad7dcb33SManjunath Hadli 		return -EINVAL;
98ad7dcb33SManjunath Hadli 
99ad7dcb33SManjunath Hadli 	switch (mode) {
100ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_1080P:
10188da0183SLad, Prabhakar 		sel = 0x3;	/*1080p and SXGA/UXGA */
102ad7dcb33SManjunath Hadli 		break;
103ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_720P_1080I:
10488da0183SLad, Prabhakar 		sel = 0x2;	/*720p, 1080i and SVGA/XGA */
105ad7dcb33SManjunath Hadli 		break;
106ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_480P_576P:
10788da0183SLad, Prabhakar 		sel = 0x1;	/* EDTV 480p/576p and VGA */
108ad7dcb33SManjunath Hadli 		break;
109ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_480I_576I:
11088da0183SLad, Prabhakar 		sel = 0x0;	/* SDTV, S-Video, 480i/576i */
111ad7dcb33SManjunath Hadli 		break;
112ad7dcb33SManjunath Hadli 	default:
113ad7dcb33SManjunath Hadli 		/* disable all channels */
114ad7dcb33SManjunath Hadli 		disable = 1;
115cb7a01acSMauro Carvalho Chehab 	}
11688da0183SLad, Prabhakar 
11788da0183SLad, Prabhakar 	val = (sel << 6) | (sel << 3);
118ad7dcb33SManjunath Hadli 	if (!disable)
11988da0183SLad, Prabhakar 		val |= (pdata->ch_1 & 0x27);
12088da0183SLad, Prabhakar 	err = ths7303_write(sd, THS7303_CHANNEL_1, val);
121cb7a01acSMauro Carvalho Chehab 	if (err)
122ad7dcb33SManjunath Hadli 		goto out;
123cb7a01acSMauro Carvalho Chehab 
12488da0183SLad, Prabhakar 	val = (sel << 6) | (sel << 3);
125ad7dcb33SManjunath Hadli 	if (!disable)
12688da0183SLad, Prabhakar 		val |= (pdata->ch_2 & 0x27);
12788da0183SLad, Prabhakar 	err = ths7303_write(sd, THS7303_CHANNEL_2, val);
128ad7dcb33SManjunath Hadli 	if (err)
129ad7dcb33SManjunath Hadli 		goto out;
130ad7dcb33SManjunath Hadli 
13188da0183SLad, Prabhakar 	val = (sel << 6) | (sel << 3);
13288da0183SLad, Prabhakar 	if (!disable)
13388da0183SLad, Prabhakar 		val |= (pdata->ch_3 & 0x27);
13488da0183SLad, Prabhakar 	err = ths7303_write(sd, THS7303_CHANNEL_3, val);
135ad7dcb33SManjunath Hadli 	if (err)
136ad7dcb33SManjunath Hadli 		goto out;
13788da0183SLad, Prabhakar 
13888da0183SLad, Prabhakar 	return 0;
139ad7dcb33SManjunath Hadli out:
140ad7dcb33SManjunath Hadli 	pr_info("write byte data failed\n");
141cb7a01acSMauro Carvalho Chehab 	return err;
142cb7a01acSMauro Carvalho Chehab }
143cb7a01acSMauro Carvalho Chehab 
144cb7a01acSMauro Carvalho Chehab static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
145cb7a01acSMauro Carvalho Chehab {
14688da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
14788da0183SLad, Prabhakar 
14888da0183SLad, Prabhakar 	if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) {
14988da0183SLad, Prabhakar 		state->std_id = 1;
15088da0183SLad, Prabhakar 		state->bt.pixelclock = 0;
151ad7dcb33SManjunath Hadli 		return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);
15288da0183SLad, Prabhakar 	}
15388da0183SLad, Prabhakar 
154ad7dcb33SManjunath Hadli 	return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);
155ad7dcb33SManjunath Hadli }
156ad7dcb33SManjunath Hadli 
15788da0183SLad, Prabhakar static int ths7303_config(struct v4l2_subdev *sd)
15888da0183SLad, Prabhakar {
15988da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
16088da0183SLad, Prabhakar 	int res;
16188da0183SLad, Prabhakar 
16288da0183SLad, Prabhakar 	if (!state->stream_on) {
16388da0183SLad, Prabhakar 		ths7303_write(sd, THS7303_CHANNEL_1,
16488da0183SLad, Prabhakar 			      (ths7303_read(sd, THS7303_CHANNEL_1) & 0xf8) |
16588da0183SLad, Prabhakar 			      0x00);
16688da0183SLad, Prabhakar 		ths7303_write(sd, THS7303_CHANNEL_2,
16788da0183SLad, Prabhakar 			      (ths7303_read(sd, THS7303_CHANNEL_2) & 0xf8) |
16888da0183SLad, Prabhakar 			      0x00);
16988da0183SLad, Prabhakar 		ths7303_write(sd, THS7303_CHANNEL_3,
17088da0183SLad, Prabhakar 			      (ths7303_read(sd, THS7303_CHANNEL_3) & 0xf8) |
17188da0183SLad, Prabhakar 			      0x00);
17288da0183SLad, Prabhakar 		return 0;
17388da0183SLad, Prabhakar 	}
17488da0183SLad, Prabhakar 
17588da0183SLad, Prabhakar 	if (state->bt.pixelclock > 120000000)
17688da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P);
17788da0183SLad, Prabhakar 	else if (state->bt.pixelclock > 70000000)
17888da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I);
17988da0183SLad, Prabhakar 	else if (state->bt.pixelclock > 20000000)
18088da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P);
18188da0183SLad, Prabhakar 	else if (state->std_id)
18288da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);
18388da0183SLad, Prabhakar 	else
18488da0183SLad, Prabhakar 		/* disable all channels */
18588da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);
18688da0183SLad, Prabhakar 
18788da0183SLad, Prabhakar 	return res;
18888da0183SLad, Prabhakar 
18988da0183SLad, Prabhakar }
19088da0183SLad, Prabhakar 
19188da0183SLad, Prabhakar static int ths7303_s_stream(struct v4l2_subdev *sd, int enable)
19288da0183SLad, Prabhakar {
19388da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
19488da0183SLad, Prabhakar 
19588da0183SLad, Prabhakar 	state->stream_on = enable;
19688da0183SLad, Prabhakar 
19788da0183SLad, Prabhakar 	return ths7303_config(sd);
19888da0183SLad, Prabhakar }
19988da0183SLad, Prabhakar 
200ad7dcb33SManjunath Hadli /* for setting filter for HD output */
201ad7dcb33SManjunath Hadli static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
202ad7dcb33SManjunath Hadli 			       struct v4l2_dv_timings *dv_timings)
203ad7dcb33SManjunath Hadli {
20488da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
205ad7dcb33SManjunath Hadli 
20688da0183SLad, Prabhakar 	if (!dv_timings || dv_timings->type != V4L2_DV_BT_656_1120)
20788da0183SLad, Prabhakar 		return -EINVAL;
208ad7dcb33SManjunath Hadli 
20988da0183SLad, Prabhakar 	state->bt = dv_timings->bt;
21088da0183SLad, Prabhakar 	state->std_id = 0;
21188da0183SLad, Prabhakar 
21288da0183SLad, Prabhakar 	return ths7303_config(sd);
213cb7a01acSMauro Carvalho Chehab }
214cb7a01acSMauro Carvalho Chehab 
215cb7a01acSMauro Carvalho Chehab static int ths7303_g_chip_ident(struct v4l2_subdev *sd,
216cb7a01acSMauro Carvalho Chehab 				struct v4l2_dbg_chip_ident *chip)
217cb7a01acSMauro Carvalho Chehab {
218cb7a01acSMauro Carvalho Chehab 	struct i2c_client *client = v4l2_get_subdevdata(sd);
21988da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
220cb7a01acSMauro Carvalho Chehab 
22188da0183SLad, Prabhakar 	return v4l2_chip_ident_i2c_client(client, chip, state->driver_data, 0);
222cb7a01acSMauro Carvalho Chehab }
223cb7a01acSMauro Carvalho Chehab 
224cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ths7303_video_ops = {
22588da0183SLad, Prabhakar 	.s_stream	= ths7303_s_stream,
226cb7a01acSMauro Carvalho Chehab 	.s_std_output	= ths7303_s_std_output,
227ad7dcb33SManjunath Hadli 	.s_dv_timings   = ths7303_s_dv_timings,
228cb7a01acSMauro Carvalho Chehab };
229cb7a01acSMauro Carvalho Chehab 
23088da0183SLad, Prabhakar #ifdef CONFIG_VIDEO_ADV_DEBUG
23188da0183SLad, Prabhakar 
23288da0183SLad, Prabhakar static int ths7303_g_register(struct v4l2_subdev *sd,
23388da0183SLad, Prabhakar 			      struct v4l2_dbg_register *reg)
23488da0183SLad, Prabhakar {
23588da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
23688da0183SLad, Prabhakar 
23788da0183SLad, Prabhakar 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
23888da0183SLad, Prabhakar 		return -EINVAL;
23988da0183SLad, Prabhakar 
24088da0183SLad, Prabhakar 	reg->size = 1;
24188da0183SLad, Prabhakar 	reg->val = ths7303_read(sd, reg->reg);
24288da0183SLad, Prabhakar 	return 0;
24388da0183SLad, Prabhakar }
24488da0183SLad, Prabhakar 
24588da0183SLad, Prabhakar static int ths7303_s_register(struct v4l2_subdev *sd,
246977ba3b1SHans Verkuil 			      const struct v4l2_dbg_register *reg)
24788da0183SLad, Prabhakar {
24888da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
24988da0183SLad, Prabhakar 
25088da0183SLad, Prabhakar 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
25188da0183SLad, Prabhakar 		return -EINVAL;
25288da0183SLad, Prabhakar 
25388da0183SLad, Prabhakar 	ths7303_write(sd, reg->reg, reg->val);
25488da0183SLad, Prabhakar 	return 0;
25588da0183SLad, Prabhakar }
25688da0183SLad, Prabhakar #endif
25788da0183SLad, Prabhakar 
25888da0183SLad, Prabhakar static const char * const stc_lpf_sel_txt[4] = {
25988da0183SLad, Prabhakar 	"500-kHz Filter",
26088da0183SLad, Prabhakar 	"2.5-MHz Filter",
26188da0183SLad, Prabhakar 	"5-MHz Filter",
26288da0183SLad, Prabhakar 	"5-MHz Filter",
26388da0183SLad, Prabhakar };
26488da0183SLad, Prabhakar 
26588da0183SLad, Prabhakar static const char * const in_mux_sel_txt[2] = {
26688da0183SLad, Prabhakar 	"Input A Select",
26788da0183SLad, Prabhakar 	"Input B Select",
26888da0183SLad, Prabhakar };
26988da0183SLad, Prabhakar 
27088da0183SLad, Prabhakar static const char * const lpf_freq_sel_txt[4] = {
27188da0183SLad, Prabhakar 	"9-MHz LPF",
27288da0183SLad, Prabhakar 	"16-MHz LPF",
27388da0183SLad, Prabhakar 	"35-MHz LPF",
27488da0183SLad, Prabhakar 	"Bypass LPF",
27588da0183SLad, Prabhakar };
27688da0183SLad, Prabhakar 
27788da0183SLad, Prabhakar static const char * const in_bias_sel_dis_cont_txt[8] = {
27888da0183SLad, Prabhakar 	"Disable Channel",
27988da0183SLad, Prabhakar 	"Mute Function - No Output",
28088da0183SLad, Prabhakar 	"DC Bias Select",
28188da0183SLad, Prabhakar 	"DC Bias + 250 mV Offset Select",
28288da0183SLad, Prabhakar 	"AC Bias Select",
28388da0183SLad, Prabhakar 	"Sync Tip Clamp with low bias",
28488da0183SLad, Prabhakar 	"Sync Tip Clamp with mid bias",
28588da0183SLad, Prabhakar 	"Sync Tip Clamp with high bias",
28688da0183SLad, Prabhakar };
28788da0183SLad, Prabhakar 
28888da0183SLad, Prabhakar static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg)
28988da0183SLad, Prabhakar {
29088da0183SLad, Prabhakar 	u8 val = ths7303_read(sd, reg);
29188da0183SLad, Prabhakar 
29288da0183SLad, Prabhakar 	if ((val & 0x7) == 0) {
29388da0183SLad, Prabhakar 		v4l2_info(sd, "Channel %d Off\n", reg);
29488da0183SLad, Prabhakar 		return;
29588da0183SLad, Prabhakar 	}
29688da0183SLad, Prabhakar 
29788da0183SLad, Prabhakar 	v4l2_info(sd, "Channel %d On\n", reg);
29888da0183SLad, Prabhakar 	v4l2_info(sd, "  value 0x%x\n", val);
29988da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", stc_lpf_sel_txt[(val >> 6) & 0x3]);
30088da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", in_mux_sel_txt[(val >> 5) & 0x1]);
30188da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", lpf_freq_sel_txt[(val >> 3) & 0x3]);
30288da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]);
30388da0183SLad, Prabhakar }
30488da0183SLad, Prabhakar 
30588da0183SLad, Prabhakar static int ths7303_log_status(struct v4l2_subdev *sd)
30688da0183SLad, Prabhakar {
30788da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
30888da0183SLad, Prabhakar 
30988da0183SLad, Prabhakar 	v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off");
31088da0183SLad, Prabhakar 
31188da0183SLad, Prabhakar 	if (state->bt.pixelclock) {
31288da0183SLad, Prabhakar 		struct v4l2_bt_timings *bt = bt = &state->bt;
31388da0183SLad, Prabhakar 		u32 frame_width, frame_height;
31488da0183SLad, Prabhakar 
31588da0183SLad, Prabhakar 		frame_width = bt->width + bt->hfrontporch +
31688da0183SLad, Prabhakar 			      bt->hsync + bt->hbackporch;
31788da0183SLad, Prabhakar 		frame_height = bt->height + bt->vfrontporch +
31888da0183SLad, Prabhakar 			       bt->vsync + bt->vbackporch;
31988da0183SLad, Prabhakar 		v4l2_info(sd,
32088da0183SLad, Prabhakar 			  "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n",
32188da0183SLad, Prabhakar 			  bt->width, bt->height, bt->interlaced ? "i" : "p",
32288da0183SLad, Prabhakar 			  (frame_height * frame_width) > 0 ?
32388da0183SLad, Prabhakar 			  (int)bt->pixelclock /
32488da0183SLad, Prabhakar 			  (frame_height * frame_width) : 0,
32588da0183SLad, Prabhakar 			  frame_width, frame_height,
32688da0183SLad, Prabhakar 			  (int)bt->pixelclock, bt->polarities);
32788da0183SLad, Prabhakar 	} else {
32888da0183SLad, Prabhakar 		v4l2_info(sd, "no timings set\n");
32988da0183SLad, Prabhakar 	}
33088da0183SLad, Prabhakar 
33188da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_1);
33288da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_2);
33388da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_3);
33488da0183SLad, Prabhakar 
33588da0183SLad, Prabhakar 	return 0;
33688da0183SLad, Prabhakar }
33788da0183SLad, Prabhakar 
338cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ths7303_core_ops = {
339cb7a01acSMauro Carvalho Chehab 	.g_chip_ident = ths7303_g_chip_ident,
34088da0183SLad, Prabhakar 	.log_status = ths7303_log_status,
34188da0183SLad, Prabhakar #ifdef CONFIG_VIDEO_ADV_DEBUG
34288da0183SLad, Prabhakar 	.g_register = ths7303_g_register,
34388da0183SLad, Prabhakar 	.s_register = ths7303_s_register,
34488da0183SLad, Prabhakar #endif
345cb7a01acSMauro Carvalho Chehab };
346cb7a01acSMauro Carvalho Chehab 
347cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ths7303_ops = {
348cb7a01acSMauro Carvalho Chehab 	.core	= &ths7303_core_ops,
349cb7a01acSMauro Carvalho Chehab 	.video 	= &ths7303_video_ops,
350cb7a01acSMauro Carvalho Chehab };
351cb7a01acSMauro Carvalho Chehab 
352cb7a01acSMauro Carvalho Chehab static int ths7303_probe(struct i2c_client *client,
353cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
354cb7a01acSMauro Carvalho Chehab {
35588da0183SLad, Prabhakar 	struct ths7303_platform_data *pdata = client->dev.platform_data;
35688da0183SLad, Prabhakar 	struct ths7303_state *state;
357cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
358cb7a01acSMauro Carvalho Chehab 
359dd8c393bSLad, Prabhakar 	if (pdata == NULL) {
360dd8c393bSLad, Prabhakar 		dev_err(&client->dev, "No platform data\n");
361dd8c393bSLad, Prabhakar 		return -EINVAL;
362dd8c393bSLad, Prabhakar 	}
363dd8c393bSLad, Prabhakar 
364cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
365cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
366cb7a01acSMauro Carvalho Chehab 
367cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
368cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
369cb7a01acSMauro Carvalho Chehab 
37088da0183SLad, Prabhakar 	state = devm_kzalloc(&client->dev, sizeof(struct ths7303_state),
37188da0183SLad, Prabhakar 			     GFP_KERNEL);
37288da0183SLad, Prabhakar 	if (!state)
373cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
374cb7a01acSMauro Carvalho Chehab 
375dd8c393bSLad, Prabhakar 	state->pdata = pdata;
37688da0183SLad, Prabhakar 	sd = &state->sd;
377cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &ths7303_ops);
378cb7a01acSMauro Carvalho Chehab 
37988da0183SLad, Prabhakar 	/* store the driver data to differntiate the chip */
38088da0183SLad, Prabhakar 	state->driver_data = (int)id->driver_data;
38188da0183SLad, Prabhakar 
3828524ce55SLad, Prabhakar 	/* set to default 480I_576I filter mode */
3838524ce55SLad, Prabhakar 	if (ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I) < 0) {
3848524ce55SLad, Prabhakar 		v4l_err(client, "Setting to 480I_576I filter mode failed!\n");
3858524ce55SLad, Prabhakar 		return -EINVAL;
38688da0183SLad, Prabhakar 	}
38788da0183SLad, Prabhakar 
38888da0183SLad, Prabhakar 	return 0;
389cb7a01acSMauro Carvalho Chehab }
390cb7a01acSMauro Carvalho Chehab 
391cb7a01acSMauro Carvalho Chehab static int ths7303_remove(struct i2c_client *client)
392cb7a01acSMauro Carvalho Chehab {
393cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
394cb7a01acSMauro Carvalho Chehab 
395cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
396cb7a01acSMauro Carvalho Chehab 
397cb7a01acSMauro Carvalho Chehab 	return 0;
398cb7a01acSMauro Carvalho Chehab }
399cb7a01acSMauro Carvalho Chehab 
400cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ths7303_id[] = {
40188da0183SLad, Prabhakar 	{"ths7303", V4L2_IDENT_THS7303},
40288da0183SLad, Prabhakar 	{"ths7353", V4L2_IDENT_THS7353},
403cb7a01acSMauro Carvalho Chehab 	{},
404cb7a01acSMauro Carvalho Chehab };
405cb7a01acSMauro Carvalho Chehab 
406cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ths7303_id);
407cb7a01acSMauro Carvalho Chehab 
408cb7a01acSMauro Carvalho Chehab static struct i2c_driver ths7303_driver = {
409cb7a01acSMauro Carvalho Chehab 	.driver = {
410cb7a01acSMauro Carvalho Chehab 		.owner	= THIS_MODULE,
41188da0183SLad, Prabhakar 		.name	= "ths73x3",
412cb7a01acSMauro Carvalho Chehab 	},
413cb7a01acSMauro Carvalho Chehab 	.probe		= ths7303_probe,
414cb7a01acSMauro Carvalho Chehab 	.remove		= ths7303_remove,
415cb7a01acSMauro Carvalho Chehab 	.id_table	= ths7303_id,
416cb7a01acSMauro Carvalho Chehab };
417cb7a01acSMauro Carvalho Chehab 
418cb7a01acSMauro Carvalho Chehab module_i2c_driver(ths7303_driver);
419