summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c89
1 files changed, 86 insertions, 3 deletions
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index 30db8d1783ce..d4a52176814c 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -136,6 +136,7 @@ struct chipone {
struct drm_bridge bridge;
struct drm_display_mode mode;
struct drm_bridge *panel_bridge;
+ struct mipi_dsi_device *dsi;
struct gpio_desc *enable_gpio;
struct regulator *vdd1;
struct regulator *vdd2;
@@ -161,6 +162,87 @@ static inline int chipone_dsi_write(struct chipone *icn, const void *seq,
chipone_dsi_write(icn, d, ARRAY_SIZE(d)); \
}
+static void chipone_configure_pll(struct chipone *icn,
+ const struct drm_display_mode *mode)
+{
+ unsigned int best_p = 0, best_m = 0, best_s = 0;
+ unsigned int delta, min_delta = 0xffffffff;
+ unsigned int freq_p, freq_s, freq_out;
+ unsigned int p_min, p_max;
+ unsigned int p, m, s;
+ unsigned int fin;
+
+ /*
+ * DSI clock lane frequency (input into PLL) is calculated as:
+ * DSI_CLK = mode clock * bpp / dsi_data_lanes / 2
+ * the 2 is there because the bus is DDR.
+ *
+ * DPI pixel clock frequency (output from PLL) is mode clock.
+ *
+ * The chip contains fractional PLL which works as follows:
+ * DPI_CLK = ((DSI_CLK / P) * M) / S
+ * P is pre-divider, register PLL_REF_DIV[3:0] is 2^(n+1) divider
+ * register PLL_REF_DIV[4] is extra 1:2 divider
+ * M is integer multiplier, register PLL_INT(0) is multiplier
+ * S is post-divider, register PLL_REF_DIV[7:5] is 2^(n+1) divider
+ *
+ * It seems the PLL input clock after applying P pre-divider have
+ * to be lower than 20 MHz.
+ */
+ fin = mode->clock * mipi_dsi_pixel_format_to_bpp(icn->dsi->format) /
+ icn->dsi->lanes / 2; /* in kHz */
+
+ /* Minimum value of P predivider for PLL input in 5..20 MHz */
+ p_min = ffs(fin / 20000);
+ p_max = (fls(fin / 5000) - 1) & 0x1f;
+
+ for (p = p_min; p < p_max; p++) { /* PLL_REF_DIV[4,3:0] */
+ freq_p = fin / BIT(p + 1);
+ if (freq_p == 0) /* Divider too high */
+ break;
+
+ for (s = 0; s < 0x7; s++) { /* PLL_REF_DIV[7:5] */
+ freq_s = freq_p / BIT(s + 1);
+ if (freq_s == 0) /* Divider too high */
+ break;
+
+ m = mode->clock / freq_s;
+
+ /* Multiplier is 8 bit */
+ if (m > 0xff)
+ continue;
+
+ /* Limit PLL VCO frequency to 1 GHz */
+ freq_out = (fin * m) / BIT(p + 1);
+ if (freq_out > 1000000)
+ continue;
+
+ /* Apply post-divider */
+ freq_out /= BIT(s + 1);
+
+ delta = abs(mode->clock - freq_out);
+ if (delta < min_delta) {
+ best_p = p;
+ best_m = m;
+ best_s = s;
+ min_delta = delta;
+ }
+ }
+ }
+
+ dev_dbg(icn->dev,
+ "PLL: P[3:0]=2^%d P[4]=2*%d M=%d S[7:5]=2^%d delta=%d => DSI f_in=%d kHz ; DPI f_out=%ld kHz\n",
+ best_p, !!best_p, best_m, best_s + 1, min_delta, fin,
+ (fin * best_m) / BIT(best_p + best_s + 2));
+
+ /* Clock source selection fixed to MIPI DSI clock lane */
+ ICN6211_DSI(icn, PLL_CTRL(6), PLL_CTRL_6_MIPI_CLK);
+ ICN6211_DSI(icn, PLL_REF_DIV,
+ (best_p ? PLL_REF_DIV_Pe : 0) | /* Prefer /2 pre-divider */
+ PLL_REF_DIV_P(best_p) | PLL_REF_DIV_S(best_s));
+ ICN6211_DSI(icn, PLL_INT(0), best_m);
+}
+
static void chipone_atomic_enable(struct drm_bridge *bridge,
struct drm_bridge_state *old_bridge_state)
{
@@ -221,9 +303,9 @@ static void chipone_atomic_enable(struct drm_bridge *bridge,
((bus_flags & DRM_BUS_FLAG_DE_HIGH) ? BIST_POL_DE_POL : 0);
ICN6211_DSI(icn, BIST_POL, pol);
- ICN6211_DSI(icn, PLL_CTRL(6), PLL_CTRL_6_MIPI_CLK);
- ICN6211_DSI(icn, PLL_REF_DIV, 0x71);
- ICN6211_DSI(icn, PLL_INT(0), 0x2b);
+ /* Configure PLL settings */
+ chipone_configure_pll(icn, mode);
+
ICN6211_DSI(icn, SYS_CTRL(0), 0x40);
ICN6211_DSI(icn, SYS_CTRL(1), 0x98);
@@ -376,6 +458,7 @@ static int chipone_probe(struct mipi_dsi_device *dsi)
icn->bridge.funcs = &chipone_bridge_funcs;
icn->bridge.type = DRM_MODE_CONNECTOR_DPI;
icn->bridge.of_node = dev->of_node;
+ icn->dsi = dsi;
drm_bridge_add(&icn->bridge);