Hi Prabhakar,

As mentioned in my review of your ths7353 driver we (Cisco) have a driver
for this device as well. Below is our driver so you can take the best bits
of both.

However, I wonder if instead the ths7353 driver and the already existing
ths7303 driver should just be merged. They are awfully similar.

Regards,

        Hans

>From 8db6b9c176ac6d7a7d5328fc96f38e0be0c2e8dc Mon Sep 17 00:00:00 2001
Message-Id: 
<8db6b9c176ac6d7a7d5328fc96f38e0be0c2e8dc.1359114954.git.hans.verk...@cisco.com>
From: Hans Verkuil <hans.verk...@cisco.com>
Date: Fri, 25 Jan 2013 12:55:31 +0100
Subject: [PATCH] ths7353

Signed-off-by: Martin Bugge <marbu...@cisco.com>
Signed-off-by: Hans Verkuil <hans.verk...@cisco.com>
---
 drivers/media/i2c/Kconfig       |   10 +
 drivers/media/i2c/Makefile      |    1 +
 drivers/media/i2c/ths7353.c     |  419 +++++++++++++++++++++++++++++++++++++++
 include/media/ths7353.h         |   31 +++
 include/media/v4l2-chip-ident.h |    3 +
 5 files changed, 464 insertions(+)
 create mode 100644 drivers/media/i2c/ths7353.c
 create mode 100644 include/media/ths7353.h

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 1e4b2d0..a1bbb2f 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -570,6 +570,16 @@ config VIDEO_THS7303
          To compile this driver as a module, choose M here: the
          module will be called ths7303.
 
+config VIDEO_THS7353
+       tristate "THS7353 Video Amplifier"
+       depends on I2C
+       help
+         Support for TI THS7353 video amplifier
+
+         To compile this driver as a module, choose M here: the
+         module will be called ths7353. This helps tvp7002 to amplify
+         the signals.
+
 config VIDEO_M52790
        tristate "Mitsubishi M52790 A/V switch"
        depends on VIDEO_V4L2 && I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index b1d62df..9944d06 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_VIDEO_BT856) += bt856.o
 obj-$(CONFIG_VIDEO_BT866) += bt866.o
 obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
 obj-$(CONFIG_VIDEO_THS7303) += ths7303.o
+obj-$(CONFIG_VIDEO_THS7353) += ths7353.o
 obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
 obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
 obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
diff --git a/drivers/media/i2c/ths7353.c b/drivers/media/i2c/ths7353.c
new file mode 100644
index 0000000..618cfbf
--- /dev/null
+++ b/drivers/media/i2c/ths7353.c
@@ -0,0 +1,419 @@
+/*
+ * ths7353 - Texas Instruments THS7353 Video Amplifier driver
+ *
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.  All rights 
reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/ths7353.h>
+
+MODULE_DESCRIPTION("TI THS7353 video amplifier driver");
+MODULE_AUTHOR("Martin Bugge <marbu...@cisco.com>");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+struct ths7353_state {
+       struct v4l2_subdev sd;
+       struct ths7353_platform_data pdata;
+       struct v4l2_dv_timings dv_timings;
+       int stream_on;
+};
+
+/* ----------------------------------------------------------------------- */
+
+static inline struct ths7353_state *to_state(struct v4l2_subdev *sd)
+{
+       return container_of(sd, struct ths7353_state, sd);
+}
+
+/* ----------------------------------- I2C 
------------------------------------ */
+
+static int ths7353_rd(struct v4l2_subdev *sd, u8 reg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int ths7353_wr(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+       int ret;
+       int i;
+
+       for (i = 0; i < 3; i++) {
+               ret = i2c_smbus_write_byte_data(client, reg, val);
+               if (ret == 0)
+                       return 0;
+       }
+       v4l2_err(sd, "I2C Write Problem\n");
+       return ret;
+}
+
+static inline void ths7353_wr_and_or(struct v4l2_subdev *sd, u8 reg, uint8_t 
clr_mask, uint8_t val_mask)
+{
+       ths7353_wr(sd, reg, (ths7353_rd(sd, reg) & clr_mask) | val_mask);
+}
+
+/* ----------------------------------------------------------------------- */
+
+struct channel_register_bit_decoder {
+       uint8_t stc_lpf_sel;
+       uint8_t in_mux_sel;
+       uint8_t lpf_freq_sel;
+       uint8_t in_bias_sel_dis_cont;
+};
+
+static const char *stc_lpf_sel_txt[4] = {
+       "500-kHz Filter",
+       "2.5-MHz Filter",
+       "5-MHz Filter",
+       "5-MHz Filter",
+};
+
+static const char *in_mux_sel_txt[2] = {
+       "Input A Select",
+       "Input B Select",
+};
+
+static const char *lpf_freq_sel_txt[4] = {
+       "9-MHz LPF",
+       "16-MHz LPF",
+       "35-MHz LPF",
+       "Bypass LPF",
+};
+
+static const char *in_bias_sel_dis_cont_txt[8] = {
+       "Disable Channel",
+       "Mute Function - No Output",
+       "DC Bias Select",
+       "DC Bias + 250 mV Offset Select",
+       "AC Bias Select",
+       "Sync Tip Clamp with low bias",
+       "Sync Tip Clamp with mid bias",
+       "Sync Tip Clamp with high bias",
+};
+
+static void parse_channel_register(struct channel_register_bit_decoder 
*ch_reg, u8 val)
+{
+       ch_reg->stc_lpf_sel = (val >> 6) & 0x3;
+       ch_reg->in_mux_sel = (val >> 5) & 0x1;
+       ch_reg->lpf_freq_sel = (val >> 3) & 0x3;
+       ch_reg->in_bias_sel_dis_cont = (val >> 0) & 0x7;
+}
+
+static void ths7353_log_channel_status(struct v4l2_subdev *sd, u8 reg)
+{
+       struct channel_register_bit_decoder ch_reg;
+       u8 val = ths7353_rd(sd, reg);
+
+       if ((val & 0x7) == 0) {
+               v4l2_info(sd, "Channel %d Off\n", reg);
+               return;
+       }
+
+       parse_channel_register(&ch_reg, val);
+
+       v4l2_info(sd, "Channel %d On\n", reg);
+       v4l2_info(sd, "  value 0x%x\n", val);
+       v4l2_info(sd, "  %s\n", stc_lpf_sel_txt[ch_reg.stc_lpf_sel]);
+       v4l2_info(sd, "  %s\n", in_mux_sel_txt[ch_reg.in_mux_sel]);
+       v4l2_info(sd, "  %s\n", lpf_freq_sel_txt[ch_reg.lpf_freq_sel]);
+       v4l2_info(sd, "  %s\n", 
in_bias_sel_dis_cont_txt[ch_reg.in_bias_sel_dis_cont]);
+}
+
+static void ths7353_config(struct v4l2_subdev *sd)
+{
+       struct ths7353_state *state = to_state(sd);
+       struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt;
+       struct ths7353_platform_data *pdata = &state->pdata;
+       u8 val, sel = 0;
+
+       v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+       if (!state->stream_on) {
+               v4l2_dbg(1, debug, sd, "%s: stream off\n", __func__);
+               ths7353_wr_and_or(sd, 0x01, 0xf8, 0x00);
+               ths7353_wr_and_or(sd, 0x02, 0xf8, 0x00);
+               ths7353_wr_and_or(sd, 0x03, 0xf8, 0x00);
+               return;
+       }
+
+       if (state->dv_timings.type != V4L2_DV_BT_656_1120)
+               return;
+
+       if (bt->pixelclock > 120000000) {
+               /* 1080p and SXGA/UXGA */
+               sel = 0x3;
+       } else if (bt->pixelclock > 70000000) {
+               /* 720p, 1080i, and SVGA/XGA */
+               sel = 0x2;
+       } else if (bt->pixelclock > 20000000) {
+               /* EDTV 480p/576p and VGA */
+               sel = 0x1;
+       } else {
+               /* SDTV, S-Video, 480i/576i */
+               sel = 0x0;
+       }
+
+       v4l2_dbg(1, debug, sd, "%s: filter select 0x%x\n", __func__, sel);
+
+       /*
+        * use same filter selection both on
+        *  STC LPF and Freq. LPF
+        */
+       val = (pdata->ch_1 & 0x27) | (sel << 6) | (sel << 3);
+       ths7353_wr(sd, 0x01, val);
+       val = (pdata->ch_2 & 0x27) | (sel << 6) | (sel << 3);
+       ths7353_wr(sd, 0x02, val);
+       val = (pdata->ch_3 & 0x27) | (sel << 6) | (sel << 3);
+       ths7353_wr(sd, 0x03, val);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int ths7353_g_chip_ident(struct v4l2_subdev *sd,
+                               struct v4l2_dbg_chip_ident *chip)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7353, 0);
+}
+
+static int ths7353_log_status(struct v4l2_subdev *sd)
+{
+       struct ths7353_state *state = to_state(sd);
+
+       v4l2_info(sd, "stream %s\n", state->stream_on ? "On" : "Off");
+
+       if (state->dv_timings.type == V4L2_DV_BT_656_1120) {
+               struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt;
+               u32 frame_width = bt->width + bt->hfrontporch + bt->hsync + 
bt->hbackporch;
+               u32 frame_height = bt->height + bt->vfrontporch + bt->vsync + 
bt->vbackporch;
+
+               v4l2_info(sd, "timings: %dx%d%s%d (%dx%d). Pix freq. = %d Hz. 
Polarities = 0x%x\n",
+                       bt->width, bt->height, bt->interlaced ? "i" : "p",
+                       (frame_height * frame_width) > 0 ?
+                               (int)bt->pixelclock / (frame_height * 
frame_width) : 0,
+                       frame_width, frame_height, (int)bt->pixelclock, 
bt->polarities);
+       } else {
+               v4l2_info(sd, "no timings set\n");
+       }
+
+       ths7353_log_channel_status(sd, 1);
+       ths7353_log_channel_status(sd, 2);
+       ths7353_log_channel_status(sd, 3);
+
+       return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+static int ths7353_g_register(struct v4l2_subdev *sd,
+                             struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       if (!v4l2_chip_match_i2c_client(client, &reg->match))
+               return -EINVAL;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       reg->size = 1;
+       reg->val = ths7353_rd(sd, reg->reg);
+       return 0;
+}
+
+static int ths7353_s_register(struct v4l2_subdev *sd,
+                             struct v4l2_dbg_register *reg)
+{
+       struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+       if (!v4l2_chip_match_i2c_client(client, &reg->match))
+               return -EINVAL;
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       ths7353_wr(sd, reg->reg, reg->val);
+       return 0;
+}
+#endif
+
+static const struct v4l2_subdev_core_ops ths7353_core_ops = {
+       .g_chip_ident = ths7353_g_chip_ident,
+       .log_status = ths7353_log_status,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+       .g_register = ths7353_g_register,
+       .s_register = ths7353_s_register,
+#endif
+};
+
+static int ths7353_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       struct ths7353_state *state = to_state(sd);
+
+       v4l2_dbg(1, debug, sd, "%s: %s\n", __func__, enable ? "on" : "off");
+
+       state->stream_on = enable;
+
+       ths7353_config(sd);
+
+       return 0;
+}
+
+static int ths7353_s_dv_timings(struct v4l2_subdev *sd,
+                               struct v4l2_dv_timings *timings)
+{
+       struct ths7353_state *state = to_state(sd);
+
+       v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+       if (!timings || timings->type != V4L2_DV_BT_656_1120)
+               return -EINVAL;
+
+       /* save timings */
+       state->dv_timings = *timings;
+
+       ths7353_config(sd);
+
+       return 0;
+}
+
+static const struct v4l2_subdev_video_ops ths7353_video_ops = {
+       .s_stream = ths7353_s_stream,
+       .s_dv_timings = ths7353_s_dv_timings,
+};
+
+static const struct v4l2_subdev_ops ths7353_ops = {
+       .core = &ths7353_core_ops,
+       .video = &ths7353_video_ops,
+};
+
+static int ths7353_setup(struct v4l2_subdev *sd)
+{
+       struct ths7353_state *state = to_state(sd);
+       struct ths7353_platform_data *pdata = &state->pdata;
+       int ret;
+       u8 mask;
+
+       state->stream_on = pdata->init_enable;
+
+       mask = state->stream_on ? 0xff : 0xf8;
+
+       ret = ths7353_wr(sd, 0x01, pdata->ch_1 & mask);
+       if (ret)
+               return ret;
+
+       ret = ths7353_wr(sd, 0x02, pdata->ch_2 & mask);
+       if (ret)
+               return ret;
+
+       ret = ths7353_wr(sd, 0x03, pdata->ch_3 & mask);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int ths7353_probe(struct i2c_client *client,
+                        const struct i2c_device_id *id)
+{
+       struct ths7353_state *state;
+       struct v4l2_subdev *sd;
+       struct ths7353_platform_data *pdata = client->dev.platform_data;
+       int ret = 0;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               return -ENODEV;
+
+       v4l_info(client, "chip found @ 0x%x (%s)\n",
+                client->addr << 1, client->adapter->name);
+
+       state = kzalloc(sizeof(struct ths7353_state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       if (pdata == NULL) {
+               v4l_err(client, "No platform data!\n");
+               ret = -ENODEV;
+               goto err_free;
+       }
+
+       state->pdata = *pdata;
+
+       sd = &state->sd;
+       v4l2_i2c_subdev_init(sd, client, &ths7353_ops);
+       sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+       if (ths7353_setup(sd) < 0) {
+               v4l_err(client, "init failed\n");
+               ret = -EIO;
+               goto err_free;
+       }
+
+       return ret;
+
+err_free:
+       kfree(state);
+       return ret;
+}
+
+static int ths7353_remove(struct i2c_client *client)
+{
+       struct v4l2_subdev *sd = i2c_get_clientdata(client);
+       struct ths7353_state *state = to_state(sd);
+
+       v4l2_device_unregister_subdev(sd);
+
+       kfree(state);
+       return 0;
+}
+
+static const struct i2c_device_id ths7353_id[] = {
+       { "ths7353", 0 },
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, ths7353_id);
+
+static struct i2c_driver ths7353_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = "ths7353",
+       },
+       .probe          = ths7353_probe,
+       .remove         = ths7353_remove,
+       .id_table       = ths7353_id,
+};
+
+module_i2c_driver(ths7353_driver);
+
diff --git a/include/media/ths7353.h b/include/media/ths7353.h
new file mode 100644
index 0000000..37401bf
--- /dev/null
+++ b/include/media/ths7353.h
@@ -0,0 +1,31 @@
+/*
+ * ths7353 - Texas Instruments THS7353 DVI-A Amplifier
+ *
+ * Copyright 2013 Cisco Systems, Inc. and/or its affiliates.  All rights 
reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef THS7353_H
+#define THS7353_H
+
+/* Platform dependent definitions */
+struct ths7353_platform_data {
+       u8 ch_1;
+       u8 ch_2;
+       u8 ch_3;
+       u8 init_enable;
+};
+
+#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 4ee125b..f9f687e 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -180,6 +180,9 @@ enum {
        /* module adv7343: just ident 7343 */
        V4L2_IDENT_ADV7343 = 7343,
 
+       /* module ths7353: just ident 7353 */
+       V4L2_IDENT_THS7353 = 7353,
+
        /* module adv7393: just ident 7393 */
        V4L2_IDENT_ADV7393 = 7393,
 
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to