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, ®->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, ®->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