xref: /openbmc/linux/drivers/media/i2c/ths7303.c (revision aaeb31c0)
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 
28b5dcee22SMauro Carvalho Chehab #include <media/i2c/ths7303.h>
2988da0183SLad, Prabhakar #include <media/v4l2-device.h>
30cb7a01acSMauro Carvalho Chehab 
31ad7dcb33SManjunath Hadli #define THS7303_CHANNEL_1	1
32ad7dcb33SManjunath Hadli #define THS7303_CHANNEL_2	2
33ad7dcb33SManjunath Hadli #define THS7303_CHANNEL_3	3
34ad7dcb33SManjunath Hadli 
3588da0183SLad, Prabhakar struct ths7303_state {
3688da0183SLad, Prabhakar 	struct v4l2_subdev		sd;
37dd8c393bSLad, Prabhakar 	const struct ths7303_platform_data *pdata;
3888da0183SLad, Prabhakar 	struct v4l2_bt_timings		bt;
3988da0183SLad, Prabhakar 	int std_id;
4088da0183SLad, Prabhakar 	int stream_on;
4188da0183SLad, Prabhakar };
4288da0183SLad, Prabhakar 
43ad7dcb33SManjunath Hadli enum ths7303_filter_mode {
44ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_480I_576I,
45ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_480P_576P,
46ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_720P_1080I,
47ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_1080P,
48ad7dcb33SManjunath Hadli 	THS7303_FILTER_MODE_DISABLE
49ad7dcb33SManjunath Hadli };
50ad7dcb33SManjunath Hadli 
51cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("TI THS7303 video amplifier driver");
52cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Chaithrika U S");
53cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL");
54cb7a01acSMauro Carvalho Chehab 
to_state(struct v4l2_subdev * sd)5588da0183SLad, Prabhakar static inline struct ths7303_state *to_state(struct v4l2_subdev *sd)
5688da0183SLad, Prabhakar {
5788da0183SLad, Prabhakar 	return container_of(sd, struct ths7303_state, sd);
5888da0183SLad, Prabhakar }
5988da0183SLad, Prabhakar 
ths7303_read(struct v4l2_subdev * sd,u8 reg)6088da0183SLad, Prabhakar static int ths7303_read(struct v4l2_subdev *sd, u8 reg)
6188da0183SLad, Prabhakar {
6288da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
6388da0183SLad, Prabhakar 
6488da0183SLad, Prabhakar 	return i2c_smbus_read_byte_data(client, reg);
6588da0183SLad, Prabhakar }
6688da0183SLad, Prabhakar 
ths7303_write(struct v4l2_subdev * sd,u8 reg,u8 val)6788da0183SLad, Prabhakar static int ths7303_write(struct v4l2_subdev *sd, u8 reg, u8 val)
6888da0183SLad, Prabhakar {
6988da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
7088da0183SLad, Prabhakar 	int ret;
7188da0183SLad, Prabhakar 	int i;
7288da0183SLad, Prabhakar 
7388da0183SLad, Prabhakar 	for (i = 0; i < 3; i++) {
7488da0183SLad, Prabhakar 		ret = i2c_smbus_write_byte_data(client, reg, val);
7588da0183SLad, Prabhakar 		if (ret == 0)
7688da0183SLad, Prabhakar 			return 0;
7788da0183SLad, Prabhakar 	}
7888da0183SLad, Prabhakar 	return ret;
7988da0183SLad, Prabhakar }
8088da0183SLad, Prabhakar 
81cb7a01acSMauro Carvalho Chehab /* following function is used to set ths7303 */
ths7303_setval(struct v4l2_subdev * sd,enum ths7303_filter_mode mode)8206eb891eSRicardo Ribalda static int ths7303_setval(struct v4l2_subdev *sd,
8306eb891eSRicardo Ribalda 			  enum ths7303_filter_mode mode)
84cb7a01acSMauro Carvalho Chehab {
85ad7dcb33SManjunath Hadli 	struct i2c_client *client = v4l2_get_subdevdata(sd);
8688da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
87dd8c393bSLad, Prabhakar 	const struct ths7303_platform_data *pdata = state->pdata;
8888da0183SLad, Prabhakar 	u8 val, sel = 0;
8988da0183SLad, Prabhakar 	int err, disable = 0;
90cb7a01acSMauro Carvalho Chehab 
91ad7dcb33SManjunath Hadli 	if (!client)
92ad7dcb33SManjunath Hadli 		return -EINVAL;
93ad7dcb33SManjunath Hadli 
94ad7dcb33SManjunath Hadli 	switch (mode) {
95ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_1080P:
9688da0183SLad, Prabhakar 		sel = 0x3;	/*1080p and SXGA/UXGA */
97ad7dcb33SManjunath Hadli 		break;
98ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_720P_1080I:
9988da0183SLad, Prabhakar 		sel = 0x2;	/*720p, 1080i and SVGA/XGA */
100ad7dcb33SManjunath Hadli 		break;
101ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_480P_576P:
10288da0183SLad, Prabhakar 		sel = 0x1;	/* EDTV 480p/576p and VGA */
103ad7dcb33SManjunath Hadli 		break;
104ad7dcb33SManjunath Hadli 	case THS7303_FILTER_MODE_480I_576I:
10588da0183SLad, Prabhakar 		sel = 0x0;	/* SDTV, S-Video, 480i/576i */
106ad7dcb33SManjunath Hadli 		break;
107ad7dcb33SManjunath Hadli 	default:
108ad7dcb33SManjunath Hadli 		/* disable all channels */
109ad7dcb33SManjunath Hadli 		disable = 1;
110cb7a01acSMauro Carvalho Chehab 	}
11188da0183SLad, Prabhakar 
11288da0183SLad, Prabhakar 	val = (sel << 6) | (sel << 3);
113ad7dcb33SManjunath Hadli 	if (!disable)
11488da0183SLad, Prabhakar 		val |= (pdata->ch_1 & 0x27);
11588da0183SLad, Prabhakar 	err = ths7303_write(sd, THS7303_CHANNEL_1, val);
116cb7a01acSMauro Carvalho Chehab 	if (err)
117ad7dcb33SManjunath Hadli 		goto out;
118cb7a01acSMauro Carvalho Chehab 
11988da0183SLad, Prabhakar 	val = (sel << 6) | (sel << 3);
120ad7dcb33SManjunath Hadli 	if (!disable)
12188da0183SLad, Prabhakar 		val |= (pdata->ch_2 & 0x27);
12288da0183SLad, Prabhakar 	err = ths7303_write(sd, THS7303_CHANNEL_2, val);
123ad7dcb33SManjunath Hadli 	if (err)
124ad7dcb33SManjunath Hadli 		goto out;
125ad7dcb33SManjunath Hadli 
12688da0183SLad, Prabhakar 	val = (sel << 6) | (sel << 3);
12788da0183SLad, Prabhakar 	if (!disable)
12888da0183SLad, Prabhakar 		val |= (pdata->ch_3 & 0x27);
12988da0183SLad, Prabhakar 	err = ths7303_write(sd, THS7303_CHANNEL_3, val);
130ad7dcb33SManjunath Hadli 	if (err)
131ad7dcb33SManjunath Hadli 		goto out;
13288da0183SLad, Prabhakar 
13388da0183SLad, Prabhakar 	return 0;
134ad7dcb33SManjunath Hadli out:
135ad7dcb33SManjunath Hadli 	pr_info("write byte data failed\n");
136cb7a01acSMauro Carvalho Chehab 	return err;
137cb7a01acSMauro Carvalho Chehab }
138cb7a01acSMauro Carvalho Chehab 
ths7303_s_std_output(struct v4l2_subdev * sd,v4l2_std_id norm)139cb7a01acSMauro Carvalho Chehab static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm)
140cb7a01acSMauro Carvalho Chehab {
14188da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
14288da0183SLad, Prabhakar 
14388da0183SLad, Prabhakar 	if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) {
14488da0183SLad, Prabhakar 		state->std_id = 1;
14588da0183SLad, Prabhakar 		state->bt.pixelclock = 0;
146ad7dcb33SManjunath Hadli 		return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);
14788da0183SLad, Prabhakar 	}
14888da0183SLad, Prabhakar 
149ad7dcb33SManjunath Hadli 	return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);
150ad7dcb33SManjunath Hadli }
151ad7dcb33SManjunath Hadli 
ths7303_config(struct v4l2_subdev * sd)15288da0183SLad, Prabhakar static int ths7303_config(struct v4l2_subdev *sd)
15388da0183SLad, Prabhakar {
15488da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
15588da0183SLad, Prabhakar 	int res;
15688da0183SLad, Prabhakar 
15788da0183SLad, Prabhakar 	if (!state->stream_on) {
15888da0183SLad, Prabhakar 		ths7303_write(sd, THS7303_CHANNEL_1,
15988da0183SLad, Prabhakar 			      (ths7303_read(sd, THS7303_CHANNEL_1) & 0xf8) |
16088da0183SLad, Prabhakar 			      0x00);
16188da0183SLad, Prabhakar 		ths7303_write(sd, THS7303_CHANNEL_2,
16288da0183SLad, Prabhakar 			      (ths7303_read(sd, THS7303_CHANNEL_2) & 0xf8) |
16388da0183SLad, Prabhakar 			      0x00);
16488da0183SLad, Prabhakar 		ths7303_write(sd, THS7303_CHANNEL_3,
16588da0183SLad, Prabhakar 			      (ths7303_read(sd, THS7303_CHANNEL_3) & 0xf8) |
16688da0183SLad, Prabhakar 			      0x00);
16788da0183SLad, Prabhakar 		return 0;
16888da0183SLad, Prabhakar 	}
16988da0183SLad, Prabhakar 
17088da0183SLad, Prabhakar 	if (state->bt.pixelclock > 120000000)
17188da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P);
17288da0183SLad, Prabhakar 	else if (state->bt.pixelclock > 70000000)
17388da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I);
17488da0183SLad, Prabhakar 	else if (state->bt.pixelclock > 20000000)
17588da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P);
17688da0183SLad, Prabhakar 	else if (state->std_id)
17788da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I);
17888da0183SLad, Prabhakar 	else
17988da0183SLad, Prabhakar 		/* disable all channels */
18088da0183SLad, Prabhakar 		res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE);
18188da0183SLad, Prabhakar 
18288da0183SLad, Prabhakar 	return res;
18388da0183SLad, Prabhakar 
18488da0183SLad, Prabhakar }
18588da0183SLad, Prabhakar 
ths7303_s_stream(struct v4l2_subdev * sd,int enable)18688da0183SLad, Prabhakar static int ths7303_s_stream(struct v4l2_subdev *sd, int enable)
18788da0183SLad, Prabhakar {
18888da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
18988da0183SLad, Prabhakar 
19088da0183SLad, Prabhakar 	state->stream_on = enable;
19188da0183SLad, Prabhakar 
19288da0183SLad, Prabhakar 	return ths7303_config(sd);
19388da0183SLad, Prabhakar }
19488da0183SLad, Prabhakar 
195ad7dcb33SManjunath Hadli /* for setting filter for HD output */
ths7303_s_dv_timings(struct v4l2_subdev * sd,struct v4l2_dv_timings * dv_timings)196ad7dcb33SManjunath Hadli static int ths7303_s_dv_timings(struct v4l2_subdev *sd,
197ad7dcb33SManjunath Hadli 			       struct v4l2_dv_timings *dv_timings)
198ad7dcb33SManjunath Hadli {
19988da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
200ad7dcb33SManjunath Hadli 
20188da0183SLad, Prabhakar 	if (!dv_timings || dv_timings->type != V4L2_DV_BT_656_1120)
20288da0183SLad, Prabhakar 		return -EINVAL;
203ad7dcb33SManjunath Hadli 
20488da0183SLad, Prabhakar 	state->bt = dv_timings->bt;
20588da0183SLad, Prabhakar 	state->std_id = 0;
20688da0183SLad, Prabhakar 
20788da0183SLad, Prabhakar 	return ths7303_config(sd);
208cb7a01acSMauro Carvalho Chehab }
209cb7a01acSMauro Carvalho Chehab 
210cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops ths7303_video_ops = {
21188da0183SLad, Prabhakar 	.s_stream	= ths7303_s_stream,
212cb7a01acSMauro Carvalho Chehab 	.s_std_output	= ths7303_s_std_output,
213ad7dcb33SManjunath Hadli 	.s_dv_timings   = ths7303_s_dv_timings,
214cb7a01acSMauro Carvalho Chehab };
215cb7a01acSMauro Carvalho Chehab 
21688da0183SLad, Prabhakar #ifdef CONFIG_VIDEO_ADV_DEBUG
21788da0183SLad, Prabhakar 
ths7303_g_register(struct v4l2_subdev * sd,struct v4l2_dbg_register * reg)21888da0183SLad, Prabhakar static int ths7303_g_register(struct v4l2_subdev *sd,
21988da0183SLad, Prabhakar 			      struct v4l2_dbg_register *reg)
22088da0183SLad, Prabhakar {
22188da0183SLad, Prabhakar 	reg->size = 1;
22288da0183SLad, Prabhakar 	reg->val = ths7303_read(sd, reg->reg);
22388da0183SLad, Prabhakar 	return 0;
22488da0183SLad, Prabhakar }
22588da0183SLad, Prabhakar 
ths7303_s_register(struct v4l2_subdev * sd,const struct v4l2_dbg_register * reg)22688da0183SLad, Prabhakar static int ths7303_s_register(struct v4l2_subdev *sd,
227977ba3b1SHans Verkuil 			      const struct v4l2_dbg_register *reg)
22888da0183SLad, Prabhakar {
22988da0183SLad, Prabhakar 	ths7303_write(sd, reg->reg, reg->val);
23088da0183SLad, Prabhakar 	return 0;
23188da0183SLad, Prabhakar }
23288da0183SLad, Prabhakar #endif
23388da0183SLad, Prabhakar 
23488da0183SLad, Prabhakar static const char * const stc_lpf_sel_txt[4] = {
23588da0183SLad, Prabhakar 	"500-kHz Filter",
23688da0183SLad, Prabhakar 	"2.5-MHz Filter",
23788da0183SLad, Prabhakar 	"5-MHz Filter",
23888da0183SLad, Prabhakar 	"5-MHz Filter",
23988da0183SLad, Prabhakar };
24088da0183SLad, Prabhakar 
24188da0183SLad, Prabhakar static const char * const in_mux_sel_txt[2] = {
24288da0183SLad, Prabhakar 	"Input A Select",
24388da0183SLad, Prabhakar 	"Input B Select",
24488da0183SLad, Prabhakar };
24588da0183SLad, Prabhakar 
24688da0183SLad, Prabhakar static const char * const lpf_freq_sel_txt[4] = {
24788da0183SLad, Prabhakar 	"9-MHz LPF",
24888da0183SLad, Prabhakar 	"16-MHz LPF",
24988da0183SLad, Prabhakar 	"35-MHz LPF",
25088da0183SLad, Prabhakar 	"Bypass LPF",
25188da0183SLad, Prabhakar };
25288da0183SLad, Prabhakar 
25388da0183SLad, Prabhakar static const char * const in_bias_sel_dis_cont_txt[8] = {
25488da0183SLad, Prabhakar 	"Disable Channel",
25588da0183SLad, Prabhakar 	"Mute Function - No Output",
25688da0183SLad, Prabhakar 	"DC Bias Select",
25788da0183SLad, Prabhakar 	"DC Bias + 250 mV Offset Select",
25888da0183SLad, Prabhakar 	"AC Bias Select",
25988da0183SLad, Prabhakar 	"Sync Tip Clamp with low bias",
26088da0183SLad, Prabhakar 	"Sync Tip Clamp with mid bias",
26188da0183SLad, Prabhakar 	"Sync Tip Clamp with high bias",
26288da0183SLad, Prabhakar };
26388da0183SLad, Prabhakar 
ths7303_log_channel_status(struct v4l2_subdev * sd,u8 reg)26488da0183SLad, Prabhakar static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg)
26588da0183SLad, Prabhakar {
26688da0183SLad, Prabhakar 	u8 val = ths7303_read(sd, reg);
26788da0183SLad, Prabhakar 
26888da0183SLad, Prabhakar 	if ((val & 0x7) == 0) {
26988da0183SLad, Prabhakar 		v4l2_info(sd, "Channel %d Off\n", reg);
27088da0183SLad, Prabhakar 		return;
27188da0183SLad, Prabhakar 	}
27288da0183SLad, Prabhakar 
27388da0183SLad, Prabhakar 	v4l2_info(sd, "Channel %d On\n", reg);
27488da0183SLad, Prabhakar 	v4l2_info(sd, "  value 0x%x\n", val);
27588da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", stc_lpf_sel_txt[(val >> 6) & 0x3]);
27688da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", in_mux_sel_txt[(val >> 5) & 0x1]);
27788da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", lpf_freq_sel_txt[(val >> 3) & 0x3]);
27888da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]);
27988da0183SLad, Prabhakar }
28088da0183SLad, Prabhakar 
ths7303_log_status(struct v4l2_subdev * sd)28188da0183SLad, Prabhakar static int ths7303_log_status(struct v4l2_subdev *sd)
28288da0183SLad, Prabhakar {
28388da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
28488da0183SLad, Prabhakar 
28588da0183SLad, Prabhakar 	v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off");
28688da0183SLad, Prabhakar 
28788da0183SLad, Prabhakar 	if (state->bt.pixelclock) {
28800303f91SColin Ian King 		struct v4l2_bt_timings *bt = &state->bt;
28988da0183SLad, Prabhakar 		u32 frame_width, frame_height;
29088da0183SLad, Prabhakar 
291eacf8f9aSHans Verkuil 		frame_width = V4L2_DV_BT_FRAME_WIDTH(bt);
292eacf8f9aSHans Verkuil 		frame_height = V4L2_DV_BT_FRAME_HEIGHT(bt);
29388da0183SLad, Prabhakar 		v4l2_info(sd,
29488da0183SLad, Prabhakar 			  "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n",
29588da0183SLad, Prabhakar 			  bt->width, bt->height, bt->interlaced ? "i" : "p",
29688da0183SLad, Prabhakar 			  (frame_height * frame_width) > 0 ?
29788da0183SLad, Prabhakar 			  (int)bt->pixelclock /
29888da0183SLad, Prabhakar 			  (frame_height * frame_width) : 0,
29988da0183SLad, Prabhakar 			  frame_width, frame_height,
30088da0183SLad, Prabhakar 			  (int)bt->pixelclock, bt->polarities);
30188da0183SLad, Prabhakar 	} else {
30288da0183SLad, Prabhakar 		v4l2_info(sd, "no timings set\n");
30388da0183SLad, Prabhakar 	}
30488da0183SLad, Prabhakar 
30588da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_1);
30688da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_2);
30788da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_3);
30888da0183SLad, Prabhakar 
30988da0183SLad, Prabhakar 	return 0;
31088da0183SLad, Prabhakar }
31188da0183SLad, Prabhakar 
312cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ths7303_core_ops = {
31388da0183SLad, Prabhakar 	.log_status = ths7303_log_status,
31488da0183SLad, Prabhakar #ifdef CONFIG_VIDEO_ADV_DEBUG
31588da0183SLad, Prabhakar 	.g_register = ths7303_g_register,
31688da0183SLad, Prabhakar 	.s_register = ths7303_s_register,
31788da0183SLad, Prabhakar #endif
318cb7a01acSMauro Carvalho Chehab };
319cb7a01acSMauro Carvalho Chehab 
320cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ths7303_ops = {
321cb7a01acSMauro Carvalho Chehab 	.core	= &ths7303_core_ops,
322cb7a01acSMauro Carvalho Chehab 	.video	= &ths7303_video_ops,
323cb7a01acSMauro Carvalho Chehab };
324cb7a01acSMauro Carvalho Chehab 
ths7303_probe(struct i2c_client * client)325f15f764bSUwe Kleine-König static int ths7303_probe(struct i2c_client *client)
326cb7a01acSMauro Carvalho Chehab {
32788da0183SLad, Prabhakar 	struct ths7303_platform_data *pdata = client->dev.platform_data;
32888da0183SLad, Prabhakar 	struct ths7303_state *state;
329cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
330cb7a01acSMauro Carvalho Chehab 
331dd8c393bSLad, Prabhakar 	if (pdata == NULL) {
332dd8c393bSLad, Prabhakar 		dev_err(&client->dev, "No platform data\n");
333dd8c393bSLad, Prabhakar 		return -EINVAL;
334dd8c393bSLad, Prabhakar 	}
335dd8c393bSLad, Prabhakar 
336cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
337cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
338cb7a01acSMauro Carvalho Chehab 
339cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
340cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
341cb7a01acSMauro Carvalho Chehab 
34288da0183SLad, Prabhakar 	state = devm_kzalloc(&client->dev, sizeof(struct ths7303_state),
34388da0183SLad, Prabhakar 			     GFP_KERNEL);
34488da0183SLad, Prabhakar 	if (!state)
345cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
346cb7a01acSMauro Carvalho Chehab 
347dd8c393bSLad, Prabhakar 	state->pdata = pdata;
34888da0183SLad, Prabhakar 	sd = &state->sd;
349cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &ths7303_ops);
350cb7a01acSMauro Carvalho Chehab 
3518524ce55SLad, Prabhakar 	/* set to default 480I_576I filter mode */
3528524ce55SLad, Prabhakar 	if (ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I) < 0) {
3538524ce55SLad, Prabhakar 		v4l_err(client, "Setting to 480I_576I filter mode failed!\n");
3548524ce55SLad, Prabhakar 		return -EINVAL;
35588da0183SLad, Prabhakar 	}
35688da0183SLad, Prabhakar 
35788da0183SLad, Prabhakar 	return 0;
358cb7a01acSMauro Carvalho Chehab }
359cb7a01acSMauro Carvalho Chehab 
ths7303_remove(struct i2c_client * client)360ed5c2f5fSUwe Kleine-König static void ths7303_remove(struct i2c_client *client)
361cb7a01acSMauro Carvalho Chehab {
362cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
363cb7a01acSMauro Carvalho Chehab 
364cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
365cb7a01acSMauro Carvalho Chehab }
366cb7a01acSMauro Carvalho Chehab 
367cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ths7303_id[] = {
368e1277110SHans Verkuil 	{"ths7303", 0},
369e1277110SHans Verkuil 	{"ths7353", 0},
370cb7a01acSMauro Carvalho Chehab 	{},
371cb7a01acSMauro Carvalho Chehab };
372cb7a01acSMauro Carvalho Chehab 
373cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ths7303_id);
374cb7a01acSMauro Carvalho Chehab 
375cb7a01acSMauro Carvalho Chehab static struct i2c_driver ths7303_driver = {
376cb7a01acSMauro Carvalho Chehab 	.driver = {
37788da0183SLad, Prabhakar 		.name	= "ths73x3",
378cb7a01acSMauro Carvalho Chehab 	},
379*aaeb31c0SUwe Kleine-König 	.probe		= ths7303_probe,
380cb7a01acSMauro Carvalho Chehab 	.remove		= ths7303_remove,
381cb7a01acSMauro Carvalho Chehab 	.id_table	= ths7303_id,
382cb7a01acSMauro Carvalho Chehab };
383cb7a01acSMauro Carvalho Chehab 
384cb7a01acSMauro Carvalho Chehab module_i2c_driver(ths7303_driver);
385