diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 5db0224d783f..bf920c3503aa 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -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);