diff mbox

[v1,02/19] drm: sti: add VTG driver

Message ID 1396959566-2960-3-git-send-email-benjamin.gaignard@linaro.org
State New
Headers show

Commit Message

Benjamin Gaignard April 8, 2014, 12:19 p.m. UTC
Video Time Generator drivers are used to synchronize the compositor
and tvout hardware IPs by providing line count, sample count,
synchronization signals (HSYNC, VSYNC) and top and bottom fields indication.
VTG are used by pair for each data path (main or auxiliary): one for master and one for slave.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
Signed-off-by: Vincent Abriou <vincent.abriou@st.com>
Signed-off-by: Fabien Dessenne <fabien.dessenne@st.com>

---
 drivers/gpu/drm/Kconfig             |   2 +
 drivers/gpu/drm/Makefile            |   1 +
 drivers/gpu/drm/sti/Kconfig         |  11 +
 drivers/gpu/drm/sti/Makefile        |   3 +
 drivers/gpu/drm/sti/sti_vtg.c       | 430 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_vtg.h       |  20 ++
 drivers/gpu/drm/sti/sti_vtg_utils.c |  99 +++++++++
 drivers/gpu/drm/sti/sti_vtg_utils.h |  26 +++
 8 files changed, 592 insertions(+)
 create mode 100644 drivers/gpu/drm/sti/Kconfig
 create mode 100644 drivers/gpu/drm/sti/Makefile
 create mode 100644 drivers/gpu/drm/sti/sti_vtg.c
 create mode 100644 drivers/gpu/drm/sti/sti_vtg.h
 create mode 100644 drivers/gpu/drm/sti/sti_vtg_utils.c
 create mode 100644 drivers/gpu/drm/sti/sti_vtg_utils.h
diff mbox

Patch

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index d1cc2f6..0e30029 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -201,3 +201,5 @@  source "drivers/gpu/drm/tegra/Kconfig"
 source "drivers/gpu/drm/panel/Kconfig"
 
 source "drivers/gpu/drm/bridge/Kconfig"
+
+source "drivers/gpu/drm/sti/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 5e792b0..44f7b17 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -61,6 +61,7 @@  obj-$(CONFIG_DRM_QXL) += qxl/
 obj-$(CONFIG_DRM_BOCHS) += bochs/
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STI) += sti/
 obj-y			+= i2c/
 obj-y			+= panel/
 obj-y			+= bridge/
diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
new file mode 100644
index 0000000..b3c751c
--- /dev/null
+++ b/drivers/gpu/drm/sti/Kconfig
@@ -0,0 +1,11 @@ 
+config DRM_STI
+	tristate "DRM Support for STMicroelectronics SoC stiH41x Series"
+	depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+	help
+	  Choose this option to enable DRM on STM stiH41x chipset
+
+config VTG_STI
+	tristate "Video Timing Generator for STMicroelectronics SoC stiH41x Series"
+	depends on DRM_STI
+	help
+	  Choose this option to enable VTG on STM stiH41x chipset
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
new file mode 100644
index 0000000..33216e1
--- /dev/null
+++ b/drivers/gpu/drm/sti/Makefile
@@ -0,0 +1,3 @@ 
+ccflags-y := -Iinclude/drm
+
+obj-$(CONFIG_VTG_STI) += sti_vtg.o sti_vtg_utils.o
diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c
new file mode 100644
index 0000000..d3514c1
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.c
@@ -0,0 +1,430 @@ 
+/*
+ * Copyright (C) STMicroelectronics SA 2013
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Vincent Abriou <vincent.abriou@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/notifier.h>
+
+#include "sti_vtg_utils.h"
+#include "sti_vtg.h"
+
+#define VTG_TYPE_MASTER         0
+#define VTG_TYPE_SLAVE_BY_EXT0  1
+
+/* registers offset */
+#define VTG_MODE		0x0000
+#define VTG_CLKLN		0x0008
+#define VTG_HLFLN		0x000C
+#define VTG_DRST_AUTOC		0x0010
+#define VTG_VID_TFO		0x0040
+#define VTG_VID_TFS		0x0044
+#define VTG_VID_BFO		0x0048
+#define VTG_VID_BFS		0x004C
+
+#define VTG_HOST_ITS		0x0078
+#define VTG_HOST_ITS_BCLR	0x007C
+#define VTG_HOST_ITM_BCLR	0x0088
+#define VTG_HOST_ITM_BSET	0x008C
+
+#define VTG_H_HD_1		0x00C0
+#define VTG_TOP_V_VD_1		0x00C4
+#define VTG_BOT_V_VD_1		0x00C8
+#define VTG_TOP_V_HD_1		0x00CC
+#define VTG_BOT_V_HD_1		0x00D0
+
+#define VTG_H_HD_2		0x00E0
+#define VTG_TOP_V_VD_2		0x00E4
+#define VTG_BOT_V_VD_2		0x00E8
+#define VTG_TOP_V_HD_2		0x00EC
+#define VTG_BOT_V_HD_2		0x00F0
+
+#define VTG_H_HD_3		0x0100
+#define VTG_TOP_V_VD_3		0x0104
+#define VTG_BOT_V_VD_3		0x0108
+#define VTG_TOP_V_HD_3		0x010C
+#define VTG_BOT_V_HD_3		0x0110
+
+/* IRQ mask */
+#define VTG_IRQ_MASK		((1L<<1) | (1L<<0))
+
+/* Delay introduced by the AWG in nb of pixels */
+#define AWG_DELAY_HD            (-9)
+#define AWG_DELAY_ED            (-8)
+#define AWG_DELAY_SD            (-7)
+
+static const struct of_device_id vtg_match_types[];
+
+/*
+ * STI VTG data structure
+ *
+ * @nb_reg: number of memory resources to register
+ * @reg_names: names of the memory resources to register
+ * @regs: ioremapped registers
+ */
+#define MAX_MEM_REGION 2
+#define VTG_MASTER  0
+#define VTG_SLAVE   1
+struct sti_vtg_data {
+	int nb_reg;
+	char *reg_names[MAX_MEM_REGION];
+	void __iomem *regs[MAX_MEM_REGION];
+};
+
+/*
+ * STI VTG structure
+ *
+ * @dev: pointer to device driver
+ * @data: data associated to the device
+ * @irq: VTG irq
+ * @type: VTG type (main or aux)
+ * @notifier_list: notifier callback
+ */
+struct sti_vtg {
+	struct device *dev;
+	struct sti_vtg_data data;
+	int irq;
+	int type;
+	struct raw_notifier_head notifier_list;
+};
+
+/*
+ * STiH416:
+ * --------
+ * VTG slave is connected to the VTG master by the ext0 input (ext1 input is
+ * left unconnected).
+ *
+ *                  MPE               !   !             SAS
+ *    ________                        !   !                        ________
+ *   |        |                       !   !                  ext0 |        |
+ *   |        |              _________!   !_________        ----->|        |
+ *   |  VTG   |    hsync    |         !   !         | hsync |     |  VTG   |
+ *   |        |------------>| VTAC Tx !==>! VTAC Rx |--------     |        |
+ *   | master |    vsync    |_________!   !_________| vsync       | slave  |
+ *   |        |                       !   !                  ---->|        |
+ *   |________|                       !   !                  ext1 |________|
+ *                                    !   !                            |
+ *                                    !   !                       synchro_irq
+ * */
+struct sti_vtg_data stih416_vtg_data = {
+	.nb_reg = 2,
+	.reg_names = {"master", "slave"},
+};
+
+/*
+ * STiH407:
+ * --------
+ * Only VTG master is used. There is no MPE and SAS domain => only one domain.
+ *
+ *                  MPE
+ *    ________
+ *   |        |
+ *   |        |
+ *   |  VTG   |    hsync
+ *   |        |------------>
+ *   | master |    vsync
+ *   |        |
+ *   |________|
+ *        |
+ *  synchro_irq
+ * */
+struct sti_vtg_data stih407_vtg_data = {
+	.nb_reg = 1,
+	.reg_names = {"master"},
+};
+
+static int vtg_reset(void __iomem *regs)
+{
+	writel(1, regs + VTG_DRST_AUTOC);
+	return 0;
+}
+
+static int vtg_reg_dump(struct device *dev, void __iomem *regs)
+{
+	dev_dbg(dev, "regs %p\n", regs);
+	dev_dbg(dev, "VTG_MODE 0x%x\n", readl(regs + VTG_MODE));
+	dev_dbg(dev, "VTG_CLKLN 0x%x\n", readl(regs + VTG_CLKLN));
+	dev_dbg(dev, "VTG_HLFLN 0x%x\n", readl(regs + VTG_HLFLN));
+	dev_dbg(dev, "VTG_VID_TFO 0x%x\n", readl(regs + VTG_VID_TFO));
+	dev_dbg(dev, "VTG_VID_BFO 0x%x\n", readl(regs + VTG_VID_BFO));
+	dev_dbg(dev, "VTG_VID_TFS 0x%x\n", readl(regs + VTG_VID_TFS));
+	dev_dbg(dev, "VTG_VID_BFS 0x%x\n", readl(regs + VTG_VID_BFS));
+	dev_dbg(dev, "VTG_H_HD_1 0x%x\n", readl(regs + VTG_H_HD_1));
+	dev_dbg(dev, "VTG_TOP_V_VD_1 0x%x\n", readl(regs + VTG_TOP_V_VD_1));
+	dev_dbg(dev, "VTG_BOT_V_VD_1 0x%x\n", readl(regs + VTG_BOT_V_VD_1));
+	dev_dbg(dev, "VTG_TOP_V_HD_1 0x%x\n", readl(regs + VTG_TOP_V_HD_1));
+	dev_dbg(dev, "VTG_BOT_V_HD_1 0x%x\n", readl(regs + VTG_BOT_V_HD_1));
+	dev_dbg(dev, "VTG_H_HD_2 0x%x\n", readl(regs + VTG_H_HD_2));
+	dev_dbg(dev, "VTG_TOP_V_VD_2 0x%x\n", readl(regs + VTG_TOP_V_VD_2));
+	dev_dbg(dev, "VTG_BOT_V_VD_2 0x%x\n", readl(regs + VTG_BOT_V_VD_2));
+	dev_dbg(dev, "VTG_TOP_V_HD_2 0x%x\n", readl(regs + VTG_TOP_V_HD_2));
+	dev_dbg(dev, "VTG_BOT_V_HD_2 0x%x\n", readl(regs + VTG_BOT_V_HD_2));
+	dev_dbg(dev, "VTG_H_HD_3 0x%x\n", readl(regs + VTG_H_HD_3));
+	dev_dbg(dev, "VTG_TOP_V_VD_3 0x%x\n", readl(regs + VTG_TOP_V_VD_3));
+	dev_dbg(dev, "VTG_BOT_V_VD_3 0x%x\n", readl(regs + VTG_BOT_V_VD_3));
+	dev_dbg(dev, "VTG_TOP_V_HD_3 0x%x\n", readl(regs + VTG_TOP_V_HD_3));
+	dev_dbg(dev, "VTG_BOT_V_HD_3 0x%x\n", readl(regs + VTG_BOT_V_HD_3));
+	return 0;
+}
+
+static int vtg_write_reg(void __iomem *regs,
+			 int type, const struct drm_display_mode *mode)
+{
+	int fo, fs, h_hd, v_vd, v_hd;
+
+	writel(mode->htotal, regs + VTG_CLKLN);
+	writel(mode->vtotal * 2, regs + VTG_HLFLN);
+
+	fo = (mode->vtotal - mode->vsync_start + 1) << 16;
+	fo |= mode->htotal - mode->hsync_start;
+	writel(fo, regs + VTG_VID_TFO);
+	writel(fo, regs + VTG_VID_BFO);
+
+	fs = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16;
+	fs |= mode->hdisplay + mode->htotal - mode->hsync_start;
+	writel(fs, regs + VTG_VID_TFS);
+	writel(fs, regs + VTG_VID_BFS);
+
+	/* Prepare VTG set 1 for HDMI and VTG set 3 for HD DAC */
+	h_hd = (mode->hsync_end - mode->hsync_start) << 16;
+	writel(h_hd, regs + VTG_H_HD_1);
+
+	v_vd = (mode->vsync_end - mode->vsync_start + 1) << 16;
+	v_vd |= 1;
+	writel(v_vd, regs + VTG_TOP_V_VD_1);
+	writel(v_vd, regs + VTG_BOT_V_VD_1);
+	writel(0, regs + VTG_TOP_V_HD_1);
+	writel(0, regs + VTG_BOT_V_HD_1);
+
+	/* Prepare VTG set 2 for for HD DCS */
+	writel(h_hd, regs + VTG_H_HD_2);
+	writel(v_vd, regs + VTG_TOP_V_VD_2);
+	writel(v_vd, regs + VTG_BOT_V_VD_2);
+	writel(0, regs + VTG_TOP_V_HD_2);
+	writel(0, regs + VTG_BOT_V_HD_2);
+
+	/* Prepare VTG set 3 for HD Analog in HD mode */
+	h_hd = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16;
+	h_hd |= mode->htotal + AWG_DELAY_HD;
+	writel(h_hd, regs + VTG_H_HD_3);
+
+	v_vd = (mode->vsync_end - mode->vsync_start) << 16;
+	v_vd |= mode->vtotal;
+	writel(v_vd, regs + VTG_TOP_V_VD_3);
+	writel(v_vd, regs + VTG_BOT_V_VD_3);
+
+	v_hd = (mode->htotal + AWG_DELAY_HD) << 16;
+	v_hd |= mode->htotal + AWG_DELAY_HD;
+	writel(v_hd, regs + VTG_TOP_V_HD_3);
+	writel(v_hd, regs + VTG_BOT_V_HD_3);
+
+	/* Mode */
+	writel(type, regs + VTG_MODE);
+	return 0;
+}
+
+static int vtg_disable_irq(void __iomem *regs)
+{
+	/* Clear interrupt status and mask */
+	writel(0xFFFF, regs + VTG_HOST_ITS_BCLR);
+	writel(0xFFFF, regs + VTG_HOST_ITM_BCLR);
+	return 0;
+}
+
+static int vtg_enable_irq(void __iomem *regs)
+{
+	vtg_disable_irq(regs);
+	writel(VTG_IRQ_MASK, regs + VTG_HOST_ITM_BSET);
+	return 0;
+}
+
+int vtg_set_config(struct device *dev, const struct drm_display_mode *mode)
+{
+	struct sti_vtg *vtg = dev_get_drvdata(dev);
+	struct sti_vtg_data *data = &vtg->data;
+
+	if (!vtg)
+		return 1;
+
+	if (!data->regs[VTG_MASTER])
+		return 1;
+
+	/* write slave / master configuration */
+	if (data->regs[VTG_SLAVE])
+		vtg_write_reg(data->regs[VTG_SLAVE],
+			      VTG_TYPE_SLAVE_BY_EXT0, mode);
+
+	vtg_write_reg(data->regs[VTG_MASTER], VTG_TYPE_MASTER, mode);
+
+	/* reset slave and then master */
+	if (data->regs[VTG_SLAVE])
+		vtg_reset(data->regs[VTG_SLAVE]);
+
+	vtg_reset(data->regs[VTG_MASTER]);
+
+	/* Enable irq for the vtg vblank synchro */
+	if (data->regs[VTG_SLAVE])
+		vtg_enable_irq(data->regs[VTG_SLAVE]);
+	else
+		vtg_enable_irq(data->regs[VTG_MASTER]);
+
+	if (data->regs[VTG_SLAVE])
+		vtg_reg_dump(dev, data->regs[VTG_SLAVE]);
+
+	vtg_reg_dump(dev, data->regs[VTG_MASTER]);
+
+	return 0;
+}
+
+int vtg_register_client(struct device *dev, struct notifier_block *nb)
+{
+	struct sti_vtg *vtg = dev_get_drvdata(dev);
+	return raw_notifier_chain_register(&vtg->notifier_list, nb);
+}
+
+int vtg_unregister_client(struct device *dev, struct notifier_block *nb)
+{
+	struct sti_vtg *vtg = dev_get_drvdata(dev);
+	return raw_notifier_chain_unregister(&vtg->notifier_list, nb);
+}
+
+static irqreturn_t vtg_irq_thread(int irq, void *arg)
+{
+	struct sti_vtg *vtg = arg;
+	struct sti_vtg_data *data = &vtg->data;
+	void __iomem *regs;
+	u32 status;
+
+	if (data->regs[VTG_SLAVE])
+		regs = data->regs[VTG_SLAVE];
+	else
+		regs = data->regs[VTG_MASTER];
+
+	status = readl(regs + VTG_HOST_ITS);
+
+	writel(status, regs + VTG_HOST_ITS_BCLR);
+	/* TODO: check why this sync bus write solves the problem which
+	 * is that without this line, the handler is sometimes called twice,
+	 * first with status = 1 or 2, and immediately after with status=0
+	 */
+	/* sync bus write */
+	readl(regs + VTG_HOST_ITS);
+
+	raw_notifier_call_chain(&vtg->notifier_list,
+				VTG_VBLANK_EVENT, &vtg->type);
+
+	return IRQ_HANDLED;
+}
+
+static const char *vtg_get_name(struct sti_vtg *vtg)
+{
+	if (vtg->type == VTG_MAIN)
+		return "main";
+	else if (vtg->type == VTG_AUX)
+		return "aux";
+	else
+		return "?";
+}
+
+static int vtg_compositor_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct sti_vtg *vtg;
+	struct resource *res;
+	char irq_name[32];
+	struct sti_vtg_data *data;
+	int i;
+	int ret;
+
+	if (!np)
+		return -ENXIO;
+
+	vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL);
+	if (!vtg) {
+		DRM_ERROR("Failed to allocate VTG context\n");
+		return -ENOMEM;
+	}
+
+	vtg->dev = dev;
+
+	/* populate data structure depending on compatibility */
+	BUG_ON(!of_match_node(vtg_match_types, np)->data);
+
+	memcpy(&vtg->data, of_match_node(vtg_match_types, np)->data,
+	       sizeof(struct sti_vtg_data));
+
+	data = &vtg->data;
+
+	/* Get resources */
+	for (i = 0; i < data->nb_reg; i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   data->reg_names[i]);
+		if (!res) {
+			DRM_ERROR("Invalid resource\n");
+			return -ENOMEM;
+		}
+		data->regs[i] = devm_ioremap_nocache(dev,
+						     res->start,
+						     resource_size(res));
+		if (IS_ERR(data->regs[i]))
+			return PTR_ERR(data->regs[i]);
+	}
+
+	vtg->irq = platform_get_irq_byname(pdev, "synchro_irq");
+	if (IS_ERR_VALUE(vtg->irq)) {
+		DRM_ERROR("Failed to get VTG interrupt\n");
+		return vtg->irq;
+	}
+
+	snprintf(irq_name, sizeof(irq_name), "vsync-%s", vtg_get_name(vtg));
+
+	RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list);
+
+	ret = devm_request_threaded_irq(dev, vtg->irq, NULL, vtg_irq_thread,
+					IRQF_ONESHOT, irq_name, vtg);
+	if (IS_ERR_VALUE(ret)) {
+		DRM_ERROR("Failed to register VTG interrupt\n");
+		return ret;
+	}
+
+	if (of_property_read_bool(np, "vtg-aux")) {
+		vtg->type = VTG_AUX;
+		vtg_aux = dev;
+	} else {
+		vtg->type = VTG_MAIN;
+		vtg_main = dev;
+	}
+
+	platform_set_drvdata(pdev, vtg);
+
+	DRM_INFO("%s VTG %s\n", __func__, vtg_get_name(vtg));
+
+	return 0;
+}
+
+static const struct of_device_id vtg_match_types[] = {
+	{
+	 .compatible = "st,stih416-vtg",
+	 .data = &stih416_vtg_data,
+	 },
+	{
+	 .compatible = "st,stih407-vtg",
+	 .data = &stih407_vtg_data,
+	 },
+	{ /* end node */ }
+};
+
+struct platform_driver sti_vtg_driver = {
+	.driver = {
+		   .name = "sti-vtg",
+		   .owner = THIS_MODULE,
+		   .of_match_table = vtg_match_types,
+		   },
+	.probe = vtg_compositor_probe,
+};
diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h
new file mode 100644
index 0000000..da67eb3
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.h
@@ -0,0 +1,20 @@ 
+/*
+ * Copyright (C) STMicroelectronics SA 2013
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_VTG_H_
+#define _STI_VTG_H_
+
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+
+extern struct device *vtg_main;
+extern struct device *vtg_aux;
+
+int vtg_set_config(struct device *dev, const struct drm_display_mode *mode);
+int vtg_register_client(struct device *dev, struct notifier_block *nb);
+int vtg_unregister_client(struct device *dev, struct notifier_block *nb);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_vtg_utils.c b/drivers/gpu/drm/sti/sti_vtg_utils.c
new file mode 100644
index 0000000..06bb4b4
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg_utils.c
@@ -0,0 +1,99 @@ 
+/*
+ * Copyright (C) STMicroelectronics SA 2013
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/platform_device.h>
+
+#include "sti_vtg_utils.h"
+#include "sti_vtg.h"
+
+struct device *vtg_main;
+struct device *vtg_aux;
+
+int sti_vtg_setconfig(int main_aux, const struct drm_display_mode *mode)
+{
+	if (main_aux == VTG_MAIN && vtg_main)
+		return vtg_set_config(vtg_main, mode);
+
+	if (main_aux == VTG_AUX && vtg_aux)
+		return vtg_set_config(vtg_aux, mode);
+
+	return 1;
+}
+
+int sti_vtg_register_client(int main_aux, struct notifier_block *nb)
+{
+	if (main_aux == VTG_MAIN && vtg_main)
+		return vtg_register_client(vtg_main, nb);
+
+	if (main_aux == VTG_AUX && vtg_aux)
+		return vtg_register_client(vtg_aux, nb);
+
+	return 1;
+}
+
+int sti_vtg_unregister_client(int main_aux, struct notifier_block *nb)
+{
+	if (main_aux == VTG_MAIN && vtg_main)
+		return vtg_unregister_client(vtg_main, nb);
+
+	if (main_aux == VTG_AUX && vtg_aux)
+		return vtg_unregister_client(vtg_aux, nb);
+
+	return 1;
+}
+
+/*
+ *       Active        Front        Sync       Back          Active
+ *       Region        Porch                  Porch          Region
+ * <---------------><-------->0<---------><--------><----------------->
+ *
+ *   ///////////////|                               |  ///////////////|
+ *  /////////////// |                               | /////////////// |
+ * ///////////////  |.........            ..........|///////////////  |
+ *                            0___________      x/ymin           x/ymax
+ *
+ * <--[hv]display-->                                 <--[hv]display-->
+ * <--[hv]sync_start--------->                       <--[hv]sync_start-
+ * <--[hv]sync_end----------------------->           <--[hv]sync_end---
+ * <--[hv]total------------------------------------> <--[hv]total------
+ */
+
+/*
+ * sti_vtg_get_line_number
+ *
+ * @mode: display mode to be used
+ * @y:    line
+ *
+ * Return the line number according to the display mode taking
+ * into account the Sync and Back Porch information.
+ * Video frame line numbers start at 1, y starts at 0.
+ * In interlaced modes the start line is the field line number of the odd
+ * field, but y is still defined as a progressive frame.
+ */
+u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y)
+{
+	int start_line = mode.vtotal - mode.vsync_start + 1;
+
+	if (mode.flags & DRM_MODE_FLAG_INTERLACE)
+		start_line *= 2;
+
+	return start_line + y;
+}
+
+/*
+ * sti_vtg_get_pixel_number
+ *
+ * @mode: display mode to be used
+ * @x:    row
+ *
+ * Return the pixel number according to the display mode taking
+ * into account the Sync and Back Porch information.
+ * Pixels are counted from 0.
+ */
+u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x)
+{
+	return mode.htotal - mode.hsync_start + x;
+}
diff --git a/drivers/gpu/drm/sti/sti_vtg_utils.h b/drivers/gpu/drm/sti/sti_vtg_utils.h
new file mode 100644
index 0000000..03d81ba
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg_utils.h
@@ -0,0 +1,26 @@ 
+/*
+ * Copyright (C) STMicroelectronics SA 2013
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_VTG_UTILS_H_
+#define _STI_VTG_UTILS_H_
+
+#include <drm/drmP.h>
+
+#define VTG_MAIN                0
+#define VTG_AUX                 1
+
+#define VTG_VBLANK_EVENT        1
+
+int sti_vtg_setconfig(int main_aux, const struct drm_display_mode *mode);
+int sti_vtg_register_client(int main_aux, struct notifier_block *nb);
+int sti_vtg_unregister_client(int main_aux, struct notifier_block *nb);
+
+u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y);
+u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x);
+
+#endif