[FYI PATCH] Cisco's ths7353 driver...

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.verkuil@xxxxxxxxx>
From: Hans Verkuil <hans.verkuil@xxxxxxxxx>
Date: Fri, 25 Jan 2013 12:55:31 +0100
Subject: [PATCH] ths7353

Signed-off-by: Martin Bugge <marbugge@xxxxxxxxx>
Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
---
 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 <marbugge@xxxxxxxxx>");
+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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux