xref: /openbmc/linux/drivers/media/i2c/ths7303.c (revision 977ba3b1)
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;
3888da0183SLad, Prabhakar 	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);
9288da0183SLad, Prabhakar 	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 	if (!capable(CAP_SYS_ADMIN))
24088da0183SLad, Prabhakar 		return -EPERM;
24188da0183SLad, Prabhakar 
24288da0183SLad, Prabhakar 	reg->size = 1;
24388da0183SLad, Prabhakar 	reg->val = ths7303_read(sd, reg->reg);
24488da0183SLad, Prabhakar 	return 0;
24588da0183SLad, Prabhakar }
24688da0183SLad, Prabhakar 
24788da0183SLad, Prabhakar static int ths7303_s_register(struct v4l2_subdev *sd,
248977ba3b1SHans Verkuil 			      const struct v4l2_dbg_register *reg)
24988da0183SLad, Prabhakar {
25088da0183SLad, Prabhakar 	struct i2c_client *client = v4l2_get_subdevdata(sd);
25188da0183SLad, Prabhakar 
25288da0183SLad, Prabhakar 	if (!v4l2_chip_match_i2c_client(client, &reg->match))
25388da0183SLad, Prabhakar 		return -EINVAL;
25488da0183SLad, Prabhakar 	if (!capable(CAP_SYS_ADMIN))
25588da0183SLad, Prabhakar 		return -EPERM;
25688da0183SLad, Prabhakar 
25788da0183SLad, Prabhakar 	ths7303_write(sd, reg->reg, reg->val);
25888da0183SLad, Prabhakar 	return 0;
25988da0183SLad, Prabhakar }
26088da0183SLad, Prabhakar #endif
26188da0183SLad, Prabhakar 
26288da0183SLad, Prabhakar static const char * const stc_lpf_sel_txt[4] = {
26388da0183SLad, Prabhakar 	"500-kHz Filter",
26488da0183SLad, Prabhakar 	"2.5-MHz Filter",
26588da0183SLad, Prabhakar 	"5-MHz Filter",
26688da0183SLad, Prabhakar 	"5-MHz Filter",
26788da0183SLad, Prabhakar };
26888da0183SLad, Prabhakar 
26988da0183SLad, Prabhakar static const char * const in_mux_sel_txt[2] = {
27088da0183SLad, Prabhakar 	"Input A Select",
27188da0183SLad, Prabhakar 	"Input B Select",
27288da0183SLad, Prabhakar };
27388da0183SLad, Prabhakar 
27488da0183SLad, Prabhakar static const char * const lpf_freq_sel_txt[4] = {
27588da0183SLad, Prabhakar 	"9-MHz LPF",
27688da0183SLad, Prabhakar 	"16-MHz LPF",
27788da0183SLad, Prabhakar 	"35-MHz LPF",
27888da0183SLad, Prabhakar 	"Bypass LPF",
27988da0183SLad, Prabhakar };
28088da0183SLad, Prabhakar 
28188da0183SLad, Prabhakar static const char * const in_bias_sel_dis_cont_txt[8] = {
28288da0183SLad, Prabhakar 	"Disable Channel",
28388da0183SLad, Prabhakar 	"Mute Function - No Output",
28488da0183SLad, Prabhakar 	"DC Bias Select",
28588da0183SLad, Prabhakar 	"DC Bias + 250 mV Offset Select",
28688da0183SLad, Prabhakar 	"AC Bias Select",
28788da0183SLad, Prabhakar 	"Sync Tip Clamp with low bias",
28888da0183SLad, Prabhakar 	"Sync Tip Clamp with mid bias",
28988da0183SLad, Prabhakar 	"Sync Tip Clamp with high bias",
29088da0183SLad, Prabhakar };
29188da0183SLad, Prabhakar 
29288da0183SLad, Prabhakar static void ths7303_log_channel_status(struct v4l2_subdev *sd, u8 reg)
29388da0183SLad, Prabhakar {
29488da0183SLad, Prabhakar 	u8 val = ths7303_read(sd, reg);
29588da0183SLad, Prabhakar 
29688da0183SLad, Prabhakar 	if ((val & 0x7) == 0) {
29788da0183SLad, Prabhakar 		v4l2_info(sd, "Channel %d Off\n", reg);
29888da0183SLad, Prabhakar 		return;
29988da0183SLad, Prabhakar 	}
30088da0183SLad, Prabhakar 
30188da0183SLad, Prabhakar 	v4l2_info(sd, "Channel %d On\n", reg);
30288da0183SLad, Prabhakar 	v4l2_info(sd, "  value 0x%x\n", val);
30388da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", stc_lpf_sel_txt[(val >> 6) & 0x3]);
30488da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", in_mux_sel_txt[(val >> 5) & 0x1]);
30588da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", lpf_freq_sel_txt[(val >> 3) & 0x3]);
30688da0183SLad, Prabhakar 	v4l2_info(sd, "  %s\n", in_bias_sel_dis_cont_txt[(val >> 0) & 0x7]);
30788da0183SLad, Prabhakar }
30888da0183SLad, Prabhakar 
30988da0183SLad, Prabhakar static int ths7303_log_status(struct v4l2_subdev *sd)
31088da0183SLad, Prabhakar {
31188da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
31288da0183SLad, Prabhakar 
31388da0183SLad, Prabhakar 	v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off");
31488da0183SLad, Prabhakar 
31588da0183SLad, Prabhakar 	if (state->bt.pixelclock) {
31688da0183SLad, Prabhakar 		struct v4l2_bt_timings *bt = bt = &state->bt;
31788da0183SLad, Prabhakar 		u32 frame_width, frame_height;
31888da0183SLad, Prabhakar 
31988da0183SLad, Prabhakar 		frame_width = bt->width + bt->hfrontporch +
32088da0183SLad, Prabhakar 			      bt->hsync + bt->hbackporch;
32188da0183SLad, Prabhakar 		frame_height = bt->height + bt->vfrontporch +
32288da0183SLad, Prabhakar 			       bt->vsync + bt->vbackporch;
32388da0183SLad, Prabhakar 		v4l2_info(sd,
32488da0183SLad, Prabhakar 			  "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. Polarities = 0x%x\n",
32588da0183SLad, Prabhakar 			  bt->width, bt->height, bt->interlaced ? "i" : "p",
32688da0183SLad, Prabhakar 			  (frame_height * frame_width) > 0 ?
32788da0183SLad, Prabhakar 			  (int)bt->pixelclock /
32888da0183SLad, Prabhakar 			  (frame_height * frame_width) : 0,
32988da0183SLad, Prabhakar 			  frame_width, frame_height,
33088da0183SLad, Prabhakar 			  (int)bt->pixelclock, bt->polarities);
33188da0183SLad, Prabhakar 	} else {
33288da0183SLad, Prabhakar 		v4l2_info(sd, "no timings set\n");
33388da0183SLad, Prabhakar 	}
33488da0183SLad, Prabhakar 
33588da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_1);
33688da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_2);
33788da0183SLad, Prabhakar 	ths7303_log_channel_status(sd, THS7303_CHANNEL_3);
33888da0183SLad, Prabhakar 
33988da0183SLad, Prabhakar 	return 0;
34088da0183SLad, Prabhakar }
34188da0183SLad, Prabhakar 
342cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops ths7303_core_ops = {
343cb7a01acSMauro Carvalho Chehab 	.g_chip_ident = ths7303_g_chip_ident,
34488da0183SLad, Prabhakar 	.log_status = ths7303_log_status,
34588da0183SLad, Prabhakar #ifdef CONFIG_VIDEO_ADV_DEBUG
34688da0183SLad, Prabhakar 	.g_register = ths7303_g_register,
34788da0183SLad, Prabhakar 	.s_register = ths7303_s_register,
34888da0183SLad, Prabhakar #endif
349cb7a01acSMauro Carvalho Chehab };
350cb7a01acSMauro Carvalho Chehab 
351cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops ths7303_ops = {
352cb7a01acSMauro Carvalho Chehab 	.core	= &ths7303_core_ops,
353cb7a01acSMauro Carvalho Chehab 	.video 	= &ths7303_video_ops,
354cb7a01acSMauro Carvalho Chehab };
355cb7a01acSMauro Carvalho Chehab 
35688da0183SLad, Prabhakar static int ths7303_setup(struct v4l2_subdev *sd)
35788da0183SLad, Prabhakar {
35888da0183SLad, Prabhakar 	struct ths7303_state *state = to_state(sd);
35988da0183SLad, Prabhakar 	struct ths7303_platform_data *pdata = &state->pdata;
36088da0183SLad, Prabhakar 	int ret;
36188da0183SLad, Prabhakar 	u8 mask;
36288da0183SLad, Prabhakar 
36388da0183SLad, Prabhakar 	state->stream_on = pdata->init_enable;
36488da0183SLad, Prabhakar 
36588da0183SLad, Prabhakar 	mask = state->stream_on ? 0xff : 0xf8;
36688da0183SLad, Prabhakar 
36788da0183SLad, Prabhakar 	ret = ths7303_write(sd, THS7303_CHANNEL_1, pdata->ch_1 & mask);
36888da0183SLad, Prabhakar 	if (ret)
36988da0183SLad, Prabhakar 		return ret;
37088da0183SLad, Prabhakar 
37188da0183SLad, Prabhakar 	ret = ths7303_write(sd, THS7303_CHANNEL_2, pdata->ch_2 & mask);
37288da0183SLad, Prabhakar 	if (ret)
37388da0183SLad, Prabhakar 		return ret;
37488da0183SLad, Prabhakar 
37588da0183SLad, Prabhakar 	ret = ths7303_write(sd, THS7303_CHANNEL_3, pdata->ch_3 & mask);
37688da0183SLad, Prabhakar 	if (ret)
37788da0183SLad, Prabhakar 		return ret;
37888da0183SLad, Prabhakar 
37988da0183SLad, Prabhakar 	return 0;
38088da0183SLad, Prabhakar }
38188da0183SLad, Prabhakar 
382cb7a01acSMauro Carvalho Chehab static int ths7303_probe(struct i2c_client *client,
383cb7a01acSMauro Carvalho Chehab 			const struct i2c_device_id *id)
384cb7a01acSMauro Carvalho Chehab {
38588da0183SLad, Prabhakar 	struct ths7303_platform_data *pdata = client->dev.platform_data;
38688da0183SLad, Prabhakar 	struct ths7303_state *state;
387cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
388cb7a01acSMauro Carvalho Chehab 
389cb7a01acSMauro Carvalho Chehab 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
390cb7a01acSMauro Carvalho Chehab 		return -ENODEV;
391cb7a01acSMauro Carvalho Chehab 
392cb7a01acSMauro Carvalho Chehab 	v4l_info(client, "chip found @ 0x%x (%s)\n",
393cb7a01acSMauro Carvalho Chehab 			client->addr << 1, client->adapter->name);
394cb7a01acSMauro Carvalho Chehab 
39588da0183SLad, Prabhakar 	state = devm_kzalloc(&client->dev, sizeof(struct ths7303_state),
39688da0183SLad, Prabhakar 			     GFP_KERNEL);
39788da0183SLad, Prabhakar 	if (!state)
398cb7a01acSMauro Carvalho Chehab 		return -ENOMEM;
399cb7a01acSMauro Carvalho Chehab 
40088da0183SLad, Prabhakar 	if (!pdata)
40188da0183SLad, Prabhakar 		v4l_warn(client, "No platform data, using default data!\n");
40288da0183SLad, Prabhakar 	else
40388da0183SLad, Prabhakar 		state->pdata = *pdata;
40488da0183SLad, Prabhakar 
40588da0183SLad, Prabhakar 	sd = &state->sd;
406cb7a01acSMauro Carvalho Chehab 	v4l2_i2c_subdev_init(sd, client, &ths7303_ops);
407cb7a01acSMauro Carvalho Chehab 
40888da0183SLad, Prabhakar 	/* store the driver data to differntiate the chip */
40988da0183SLad, Prabhakar 	state->driver_data = (int)id->driver_data;
41088da0183SLad, Prabhakar 
41188da0183SLad, Prabhakar 	if (ths7303_setup(sd) < 0) {
41288da0183SLad, Prabhakar 		v4l_err(client, "init failed\n");
41388da0183SLad, Prabhakar 		return -EIO;
41488da0183SLad, Prabhakar 	}
41588da0183SLad, Prabhakar 
41688da0183SLad, Prabhakar 	return 0;
417cb7a01acSMauro Carvalho Chehab }
418cb7a01acSMauro Carvalho Chehab 
419cb7a01acSMauro Carvalho Chehab static int ths7303_remove(struct i2c_client *client)
420cb7a01acSMauro Carvalho Chehab {
421cb7a01acSMauro Carvalho Chehab 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
422cb7a01acSMauro Carvalho Chehab 
423cb7a01acSMauro Carvalho Chehab 	v4l2_device_unregister_subdev(sd);
424cb7a01acSMauro Carvalho Chehab 
425cb7a01acSMauro Carvalho Chehab 	return 0;
426cb7a01acSMauro Carvalho Chehab }
427cb7a01acSMauro Carvalho Chehab 
428cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id ths7303_id[] = {
42988da0183SLad, Prabhakar 	{"ths7303", V4L2_IDENT_THS7303},
43088da0183SLad, Prabhakar 	{"ths7353", V4L2_IDENT_THS7353},
431cb7a01acSMauro Carvalho Chehab 	{},
432cb7a01acSMauro Carvalho Chehab };
433cb7a01acSMauro Carvalho Chehab 
434cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, ths7303_id);
435cb7a01acSMauro Carvalho Chehab 
436cb7a01acSMauro Carvalho Chehab static struct i2c_driver ths7303_driver = {
437cb7a01acSMauro Carvalho Chehab 	.driver = {
438cb7a01acSMauro Carvalho Chehab 		.owner	= THIS_MODULE,
43988da0183SLad, Prabhakar 		.name	= "ths73x3",
440cb7a01acSMauro Carvalho Chehab 	},
441cb7a01acSMauro Carvalho Chehab 	.probe		= ths7303_probe,
442cb7a01acSMauro Carvalho Chehab 	.remove		= ths7303_remove,
443cb7a01acSMauro Carvalho Chehab 	.id_table	= ths7303_id,
444cb7a01acSMauro Carvalho Chehab };
445cb7a01acSMauro Carvalho Chehab 
446cb7a01acSMauro Carvalho Chehab module_i2c_driver(ths7303_driver);
447