Presently, the driver code artificially limits test pattern mode to a
single pattern with fixed color selection. It being a kernel module
parameter makes switching "test pattern" <-> "proper output" modes
on-the-fly clunky and outright impossible if the driver is built into
the kernel.

To improve the situation a bit, convert current test pattern code to
use debugfs instead by exposing "TestCtl" register. This way old
"tc_test_pattern=1" functionality can be emulated via:

    echo -n 0x78146302 > tstctl

and switch back to regular mode can be done with:

    echo -n 0x78146300 > tstctl

Note that switching to any of the test patterns, will NOT trigger link
re-establishment whereas switching to normal operation WILL. This is
done so:

a) we can isolate and verify (e)DP link functionality by switching to
   one of the test patters

b) trigger a link re-establishment by switching back to normal mode

Signed-off-by: Andrey Smirnov <andrew.smir...@gmail.com>
Cc: Andrzej Hajda <a.ha...@samsung.com>
Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
Cc: Tomi Valkeinen <tomi.valkei...@ti.com>
Cc: Cory Tusar <cory.tu...@zii.aero>
Cc: Chris Healy <cphe...@gmail.com>
Cc: Lucas Stach <l.st...@pengutronix.de>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-ker...@vger.kernel.org
---
 drivers/gpu/drm/bridge/tc358767.c | 152 ++++++++++++++++++++++++------
 1 file changed, 125 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/bridge/tc358767.c 
b/drivers/gpu/drm/bridge/tc358767.c
index 3c252ae0ee6f..12a8829e0ed1 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -17,6 +17,7 @@
 
 #include <linux/bitfield.h>
 #include <linux/clk.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -221,11 +222,10 @@
 #define COLOR_B                        GENMASK(15, 8)
 #define ENI2CFILTER            BIT(4)
 #define COLOR_BAR_MODE         GENMASK(1, 0)
+#define COLOR_BAR_MODE_NORMAL  0
 #define COLOR_BAR_MODE_BARS    2
-#define PLL_DBG                        0x0a04
 
-static bool tc_test_pattern;
-module_param_named(test, tc_test_pattern, bool, 0644);
+#define PLL_DBG                        0x0a04
 
 struct tc_edp_link {
        struct drm_dp_link      base;
@@ -263,6 +263,9 @@ struct tc_data {
 
        /* HPD pin number (0 or 1) or -ENODEV */
        int                     hpd_pin;
+
+       struct mutex            tstctl_lock;
+       bool                    enabled;
 };
 
 static inline struct tc_data *aux_to_tc(struct drm_dp_aux *a)
@@ -791,16 +794,6 @@ static int tc_set_video_mode(struct tc_data *tc,
        if (ret)
                return ret;
 
-       /* Test pattern settings */
-       ret = regmap_write(tc->regmap, TSTCTL,
-                          FIELD_PREP(COLOR_R, 120) |
-                          FIELD_PREP(COLOR_G, 20) |
-                          FIELD_PREP(COLOR_B, 99) |
-                          ENI2CFILTER |
-                          FIELD_PREP(COLOR_BAR_MODE, COLOR_BAR_MODE_BARS));
-       if (ret)
-               return ret;
-
        /* DP Main Stream Attributes */
        vid_sync_dly = hsync_len + left_margin + mode->hdisplay;
        ret = regmap_write(tc->regmap, DP0_VIDSYNCDELAY,
@@ -1152,14 +1145,6 @@ static int tc_stream_enable(struct tc_data *tc)
 
        dev_dbg(tc->dev, "enable video stream\n");
 
-       /* PXL PLL setup */
-       if (tc_test_pattern) {
-               ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
-                                   1000 * tc->mode.clock);
-               if (ret)
-                       return ret;
-       }
-
        ret = tc_set_video_mode(tc, &tc->mode);
        if (ret)
                return ret;
@@ -1188,12 +1173,8 @@ static int tc_stream_enable(struct tc_data *tc)
        if (ret)
                return ret;
        /* Set input interface */
-       value = DP0_AUDSRC_NO_INPUT;
-       if (tc_test_pattern)
-               value |= DP0_VIDSRC_COLOR_BAR;
-       else
-               value |= DP0_VIDSRC_DPI_RX;
-       ret = regmap_write(tc->regmap, SYSCTRL, value);
+       ret = regmap_write(tc->regmap, SYSCTRL,
+                          DP0_AUDSRC_NO_INPUT | DP0_VIDSRC_DPI_RX);
        if (ret)
                return ret;
 
@@ -1252,8 +1233,13 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
 {
        struct tc_data *tc = bridge_to_tc(bridge);
 
+       mutex_lock(&tc->tstctl_lock);
+
        if (!__tc_bridge_enable(tc))
                drm_panel_enable(tc->panel);
+
+       tc->enabled = true;
+       mutex_unlock(&tc->tstctl_lock);
 }
 
 static int __tc_bridge_disable(struct tc_data *tc)
@@ -1275,8 +1261,13 @@ static void tc_bridge_disable(struct drm_bridge *bridge)
 {
        struct tc_data *tc = bridge_to_tc(bridge);
 
+       mutex_lock(&tc->tstctl_lock);
+
        drm_panel_disable(tc->panel);
        __tc_bridge_disable(tc);
+
+       tc->enabled = false;
+       mutex_unlock(&tc->tstctl_lock);
 }
 
 static void tc_bridge_post_disable(struct drm_bridge *bridge)
@@ -1388,6 +1379,99 @@ static enum drm_connector_status 
tc_connector_detect(struct drm_connector *conne
                return connector_status_disconnected;
 }
 
+static int tc_tstctl_set(void *data, u64 val)
+{
+       struct tc_data *tc = data;
+       int ret;
+
+       mutex_lock(&tc->tstctl_lock);
+
+       if (!tc->enabled) {
+               dev_err(tc->dev, "bridge needs to be enabled first\n");
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       if (FIELD_GET(COLOR_BAR_MODE, val) == COLOR_BAR_MODE_NORMAL) {
+               ret = regmap_write(tc->regmap, SYSCTRL, DP0_VIDSRC_DPI_RX);
+               if (ret) {
+                       dev_err(tc->dev,
+                               "failed to select dpi video stream\n");
+                       goto unlock;
+               }
+
+               ret = regmap_write(tc->regmap, TSTCTL, val | ENI2CFILTER);
+               if (ret) {
+                       dev_err(tc->dev, "failed to set TSTCTL\n");
+                       goto unlock;
+               }
+
+               ret = tc_pxl_pll_dis(tc);
+               if (ret) {
+                       dev_err(tc->dev, "failed to disable PLL\n");
+                       goto unlock;
+               }
+
+               /*
+                * Re-establish DP link
+                */
+               ret = __tc_bridge_disable(tc);
+               if (ret)
+                       goto unlock;
+
+               ret = __tc_bridge_enable(tc);
+               if (ret)
+                       goto unlock;
+       } else {
+               ret = tc_pxl_pll_en(tc, clk_get_rate(tc->refclk),
+                                   1000 * tc->mode.clock);
+               if (ret) {
+                       dev_err(tc->dev, "failed to enable PLL\n");
+                       goto unlock;
+               }
+
+               ret = regmap_write(tc->regmap, TSTCTL, val | ENI2CFILTER);
+               if (ret) {
+                       dev_err(tc->dev, "failed to set TSTCTL\n");
+                       goto unlock;
+               }
+
+               ret = regmap_write(tc->regmap, SYSCTRL, DP0_VIDSRC_COLOR_BAR);
+               if (ret) {
+                       dev_err(tc->dev, "failed to set SYSCTRL\n");
+                       goto unlock;
+               }
+       }
+
+unlock:
+       mutex_unlock(&tc->tstctl_lock);
+       return ret;
+}
+/*
+ * "tstctl" attribute has the following format:
+ *
+ *     RR_GG_BB_0_P
+ *
+ * "RR" is red, "GG" is green and "BB" is blue color components (byte
+ * each) used for various test patterns controlled by this register.
+ *
+ * "P" represents test pattern of the bridge. Valid values are:
+ *
+ *    "0" - normal operation
+ *    "1" - solid color test pattern
+ *    "2" - color bar test pattern
+ *    "3" - "checkers" test pattern
+ *
+ * This way old "tc_test_pattern=1" functionality can be emulated via:
+ *
+ *     echo -n 0x78146302 > tstctl
+ *
+ * and switch back to regular mode can be done with:
+ *
+ *     echo -n 0 > tstctl
+ */
+DEFINE_SIMPLE_ATTRIBUTE(tc_tstctl_fops, NULL, tc_tstctl_set, "%llu\n");
+
 static const struct drm_connector_funcs tc_connector_funcs = {
        .detect = tc_connector_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
@@ -1530,9 +1614,15 @@ static irqreturn_t tc_irq_handler(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
+static void tc_remove_debugfs(void *data)
+{
+       debugfs_remove_recursive(data);
+}
+
 static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
        struct device *dev = &client->dev;
+       struct dentry *debugfs;
        struct tc_data *tc;
        int ret;
 
@@ -1541,6 +1631,7 @@ static int tc_probe(struct i2c_client *client, const 
struct i2c_device_id *id)
                return -ENOMEM;
 
        tc->dev = dev;
+       mutex_init(&tc->tstctl_lock);
 
        /* port@2 is the output port */
        ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &tc->panel, NULL);
@@ -1671,6 +1762,13 @@ static int tc_probe(struct i2c_client *client, const 
struct i2c_device_id *id)
 
        i2c_set_clientdata(client, tc);
 
+       debugfs = debugfs_create_dir(dev_name(dev), NULL);
+       if (!IS_ERR(debugfs)) {
+               debugfs_create_file_unsafe("tstctl", 0200, debugfs, tc,
+                                          &tc_tstctl_fops);
+               devm_add_action_or_reset(dev, tc_remove_debugfs, debugfs);
+       }
+
        return 0;
 }
 
-- 
2.21.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to