drm/fsl-dcu: add TCON driver
authorStefan Agner <stefan@agner.ch>
Wed, 2 Dec 2015 22:39:40 +0000 (14:39 -0800)
committerStefan Agner <stefan@agner.ch>
Tue, 26 Apr 2016 03:27:18 +0000 (20:27 -0700)
Add driver for the TCON (timing controller) module. The TCON module
is a separate module attached after the DCU (display controller
unit). Each DCU instance has its own, directly connected TCON
instance. The DCU's RGB and timing signals are passing through
the TCON module. TCON can provide timing signals for raw TFT panels
or operate in a bypass mode which leaves all signals unaltered.

The driver currently only supports the bypass mode.

Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Stefan Agner <stefan@agner.ch>
Documentation/devicetree/bindings/display/fsl,dcu.txt
Documentation/devicetree/bindings/display/fsl,tcon.txt [new file with mode: 0644]
MAINTAINERS
drivers/gpu/drm/fsl-dcu/Makefile
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
drivers/gpu/drm/fsl-dcu/fsl_tcon.c [new file with mode: 0644]
drivers/gpu/drm/fsl-dcu/fsl_tcon.h [new file with mode: 0644]

index 2703cf29dd0410ba1d13da1aa46839f5a87e63dc..ae55cde1b69e44f1ecb186b956cae75e743cb47c 100644 (file)
@@ -14,6 +14,9 @@ Required properties:
 - big-endian           Boolean property, LS1021A DCU registers are big-endian.
 - fsl,panel:           The phandle to panel node.
 
+Optional properties:
+- fsl,tcon:            The phandle to the timing controller node.
+
 Examples:
 dcu: dcu@2ce0000 {
        compatible = "fsl,ls1021a-dcu";
@@ -22,4 +25,5 @@ dcu: dcu@2ce0000 {
        clock-names = "dcu", "pix";
        big-endian;
        fsl,panel = <&panel>;
+       fsl,tcon = <&tcon>;
 };
diff --git a/Documentation/devicetree/bindings/display/fsl,tcon.txt b/Documentation/devicetree/bindings/display/fsl,tcon.txt
new file mode 100644 (file)
index 0000000..6fa4ab6
--- /dev/null
@@ -0,0 +1,18 @@
+Device Tree bindings for Freescale TCON Driver
+
+Required properties:
+- compatible:          Should be one of
+       * "fsl,vf610-tcon".
+
+- reg:                 Address and length of the register set for tcon.
+- clocks:              From common clock binding: handle to tcon ipg clock.
+- clock-names:         From common clock binding: Shall be "ipg".
+
+Examples:
+timing-controller@4003d000 {
+       compatible = "fsl,vf610-tcon";
+       reg = <0x4003d000 0x1000>;
+       clocks = <&clks VF610_CLK_TCON0>;
+       clock-names = "ipg";
+       status = "okay";
+};
index 61a323a6b2cfe7348c63f49eadcd858100dc8a03..84ff4a0eb0141ecaf0e062d05c5565a5f2456547 100644 (file)
@@ -3822,6 +3822,7 @@ L:        dri-devel@lists.freedesktop.org
 S:     Supported
 F:     drivers/gpu/drm/fsl-dcu/
 F:     Documentation/devicetree/bindings/display/fsl,dcu.txt
+F:     Documentation/devicetree/bindings/display/fsl,tcon.txt
 F:     Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt
 
 DRM DRIVERS FOR FREESCALE IMX
index 6ea1523ae6ec39affef7831997d660763b68b1d0..b35a292287f3476a591e8c6647ecca7920f94518 100644 (file)
@@ -3,5 +3,6 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
                 fsl_dcu_drm_rgb.o \
                 fsl_dcu_drm_plane.o \
                 fsl_dcu_drm_crtc.o \
-                fsl_dcu_drm_fbdev.o
+                fsl_dcu_drm_fbdev.o \
+                fsl_tcon.o
 obj-$(CONFIG_DRM_FSL_DCU)      += fsl-dcu-drm.o
index 093a60b5d2d3814ff10ae251b2f6b11b6921604e..f62bff249929c12b29dbaceaa5822951044debad 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "fsl_dcu_drm_crtc.h"
 #include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
 
 static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
 {
@@ -357,6 +358,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
                goto unregister_pix_clk;
        }
 
+       fsl_dev->tcon = fsl_tcon_init(dev);
+
        drm = drm_dev_alloc(driver, dev);
        if (!drm) {
                ret = -ENOMEM;
index edb210277771243a4b1821604dda323a06c4746d..5bb7c261fe9594c6d29e53c42ea065344f19eb44 100644 (file)
@@ -184,6 +184,7 @@ struct fsl_dcu_drm_device {
        int irq;
        struct clk *clk;
        struct clk *pix_clk;
+       struct fsl_tcon *tcon;
        /*protects hardware register*/
        spinlock_t irq_lock;
        struct drm_device *drm;
index 8780deba5e8aa34fc58405592144a3f7a1c99a3e..f586f1e681d76682d6bdd058ab721cf26b228018 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_panel.h>
 
 #include "fsl_dcu_drm_drv.h"
+#include "fsl_tcon.h"
 
 static int
 fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
@@ -28,10 +29,20 @@ fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
 
 static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder)
 {
+       struct drm_device *dev = encoder->dev;
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+       if (fsl_dev->tcon)
+               fsl_tcon_bypass_disable(fsl_dev->tcon);
 }
 
 static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder)
 {
+       struct drm_device *dev = encoder->dev;
+       struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
+
+       if (fsl_dev->tcon)
+               fsl_tcon_bypass_enable(fsl_dev->tcon);
 }
 
 static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
new file mode 100644 (file)
index 0000000..bbe34f1
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "fsl_tcon.h"
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
+{
+       regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+                          FSL_TCON_CTRL1_TCON_BYPASS, 0);
+}
+
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
+{
+       regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
+                          FSL_TCON_CTRL1_TCON_BYPASS,
+                          FSL_TCON_CTRL1_TCON_BYPASS);
+}
+
+static struct regmap_config fsl_tcon_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+
+       .name = "tcon",
+};
+
+static int fsl_tcon_init_regmap(struct device *dev,
+                               struct fsl_tcon *tcon,
+                               struct device_node *np)
+{
+       struct resource res;
+       void __iomem *regs;
+
+       if (of_address_to_resource(np, 0, &res))
+               return -EINVAL;
+
+       regs = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       tcon->regs = devm_regmap_init_mmio(dev, regs,
+                                          &fsl_tcon_regmap_config);
+       if (IS_ERR(tcon->regs))
+               return PTR_ERR(tcon->regs);
+
+       return 0;
+}
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev)
+{
+       struct fsl_tcon *tcon;
+       struct device_node *np;
+       int ret;
+
+       /* TCON node is not mandatory, some devices do not provide TCON */
+       np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
+       if (!np)
+               return NULL;
+
+       tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
+       if (!tcon) {
+               ret = -ENOMEM;
+               goto err_node_put;
+       }
+
+       ret = fsl_tcon_init_regmap(dev, tcon, np);
+       if (ret) {
+               dev_err(dev, "Couldn't create the TCON regmap\n");
+               goto err_node_put;
+       }
+
+       tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
+       if (IS_ERR(tcon->ipg_clk)) {
+               dev_err(dev, "Couldn't get the TCON bus clock\n");
+               goto err_node_put;
+       }
+
+       clk_prepare_enable(tcon->ipg_clk);
+
+       dev_info(dev, "Using TCON in bypass mode\n");
+
+       return tcon;
+
+err_node_put:
+       of_node_put(np);
+       return NULL;
+}
+
+void fsl_tcon_free(struct fsl_tcon *tcon)
+{
+       clk_disable_unprepare(tcon->ipg_clk);
+       clk_put(tcon->ipg_clk);
+}
+
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.h b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
new file mode 100644 (file)
index 0000000..80a7617
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 Toradex AG
+ *
+ * Stefan Agner <stefan@agner.ch>
+ *
+ * Freescale TCON device driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __FSL_TCON_H__
+#define __FSL_TCON_H__
+
+#include <linux/bitops.h>
+
+#define FSL_TCON_CTRL1                 0x0
+#define FSL_TCON_CTRL1_TCON_BYPASS     BIT(29)
+
+struct fsl_tcon {
+       struct regmap           *regs;
+       struct clk              *ipg_clk;
+};
+
+struct fsl_tcon *fsl_tcon_init(struct device *dev);
+void fsl_tcon_free(struct fsl_tcon *tcon);
+
+void fsl_tcon_bypass_disable(struct fsl_tcon *tcon);
+void fsl_tcon_bypass_enable(struct fsl_tcon *tcon);
+
+#endif /* __FSL_TCON_H__ */