drm: bridge: icn6211: Add support for external REFCLK
authorMarek Vasut <marex@denx.de>
Mon, 1 Aug 2022 13:17:47 +0000 (15:17 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Fri, 26 Aug 2022 11:54:52 +0000 (13:54 +0200)
The ICN6211 is capable of deriving its internal PLL clock from either
MIPI DSI HS clock, external REFCLK clock, or even internal oscillator.
Currently supported is only the first option. Add support for external
REFCLK clock input in addition to that.

There is little difference between these options, except that in case
of MIPI DSI HS clock input, the HS clock are pre-divided by a fixed /4
divider before being fed to the PLL input, while in case of external
REFCLK, the RECLK clock are fed directly into the PLL input.

Per exceptionally poor documentation, the REFCLK must be in range of
10..154 MHz.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Robert Foss <robert.foss@linaro.org>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: dri-devel@lists.freedesktop.org
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20220801131747.183041-2-marex@denx.de
drivers/gpu/drm/bridge/chipone-icn6211.c

index 5db0224..bf920c3 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/bitfield.h>
 #include <linux/bits.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -151,6 +152,8 @@ struct chipone {
        struct regulator *vdd1;
        struct regulator *vdd2;
        struct regulator *vdd3;
+       struct clk *refclk;
+       unsigned long refclk_rate;
        bool interface_i2c;
 };
 
@@ -273,7 +276,10 @@ static void chipone_configure_pll(struct chipone *icn,
         * It seems the PLL input clock after applying P pre-divider have
         * to be lower than 20 MHz.
         */
-       fin = icn->dsi->hs_rate / 4; /* in Hz */
+       if (icn->refclk)
+               fin = icn->refclk_rate;
+       else
+               fin = icn->dsi->hs_rate / 4; /* in Hz */
 
        /* Minimum value of P predivider for PLL input in 5..20 MHz */
        p_min = clamp(DIV_ROUND_UP(fin, 20000000), 1U, 31U);
@@ -318,16 +324,18 @@ static void chipone_configure_pll(struct chipone *icn,
        best_p_pot = !(best_p & 1);
 
        dev_dbg(icn->dev,
-               "PLL: P[3:0]=%d P[4]=2*%d M=%d S[7:5]=2^%d delta=%d => DSI f_in=%d Hz ; DPI f_out=%d Hz\n",
+               "PLL: P[3:0]=%d P[4]=2*%d M=%d S[7:5]=2^%d delta=%d => DSI f_in(%s)=%d Hz ; DPI f_out=%d Hz\n",
                best_p >> best_p_pot, best_p_pot, best_m, best_s + 1,
-               min_delta, fin, (fin * best_m) / (best_p << (best_s + 1)));
+               min_delta, icn->refclk ? "EXT" : "DSI", fin,
+               (fin * best_m) / (best_p << (best_s + 1)));
 
        ref_div = PLL_REF_DIV_P(best_p >> best_p_pot) | PLL_REF_DIV_S(best_s);
        if (best_p_pot) /* Prefer /2 pre-divider */
                ref_div |= PLL_REF_DIV_Pe;
 
-       /* Clock source selection fixed to MIPI DSI clock lane */
-       chipone_writeb(icn, PLL_CTRL(6), PLL_CTRL_6_MIPI_CLK);
+       /* Clock source selection either external clock or MIPI DSI clock lane */
+       chipone_writeb(icn, PLL_CTRL(6),
+                      icn->refclk ? PLL_CTRL_6_EXTERNAL : PLL_CTRL_6_MIPI_CLK);
        chipone_writeb(icn, PLL_REF_DIV, ref_div);
        chipone_writeb(icn, PLL_INT(0), best_m);
 }
@@ -463,6 +471,11 @@ static void chipone_atomic_pre_enable(struct drm_bridge *bridge,
                                      "failed to enable VDD3 regulator: %d\n", ret);
        }
 
+       ret = clk_prepare_enable(icn->refclk);
+       if (ret)
+               DRM_DEV_ERROR(icn->dev,
+                             "failed to enable RECLK clock: %d\n", ret);
+
        gpiod_set_value(icn->enable_gpio, 1);
 
        usleep_range(10000, 11000);
@@ -473,6 +486,8 @@ static void chipone_atomic_post_disable(struct drm_bridge *bridge,
 {
        struct chipone *icn = bridge_to_chipone(bridge);
 
+       clk_disable_unprepare(icn->refclk);
+
        if (icn->vdd1)
                regulator_disable(icn->vdd1);
 
@@ -618,6 +633,20 @@ static int chipone_parse_dt(struct chipone *icn)
        struct device *dev = icn->dev;
        int ret;
 
+       icn->refclk = devm_clk_get_optional(dev, "refclk");
+       if (IS_ERR(icn->refclk)) {
+               ret = PTR_ERR(icn->refclk);
+               DRM_DEV_ERROR(dev, "failed to get REFCLK clock: %d\n", ret);
+               return ret;
+       } else if (icn->refclk) {
+               icn->refclk_rate = clk_get_rate(icn->refclk);
+               if (icn->refclk_rate < 10000000 || icn->refclk_rate > 154000000) {
+                       DRM_DEV_ERROR(dev, "REFCLK out of range: %ld Hz\n",
+                                     icn->refclk_rate);
+                       return -EINVAL;
+               }
+       }
+
        icn->vdd1 = devm_regulator_get_optional(dev, "vdd1");
        if (IS_ERR(icn->vdd1)) {
                ret = PTR_ERR(icn->vdd1);