* Hardware Setup
*/
+struct dpll_info {
+ unsigned int output;
+ unsigned int fdpll;
+ unsigned int n;
+ unsigned int m;
+};
+
+static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
+ struct dpll_info *dpll,
+ unsigned long input,
+ unsigned long target)
+{
+ unsigned long best_diff = (unsigned long)-1;
+ unsigned long diff;
+ unsigned int fdpll;
+ unsigned int m;
+ unsigned int n;
+
+ for (n = 39; n < 120; n++) {
+ for (m = 0; m < 4; m++) {
+ for (fdpll = 1; fdpll < 32; fdpll++) {
+ unsigned long output;
+
+ /* 1/2 (FRQSEL=1) for duty rate 50% */
+ output = input * (n + 1) / (m + 1)
+ / (fdpll + 1) / 2;
+
+ if (output >= 400000000)
+ continue;
+
+ diff = abs((long)output - (long)target);
+ if (best_diff > diff) {
+ best_diff = diff;
+ dpll->n = n;
+ dpll->m = m;
+ dpll->fdpll = fdpll;
+ dpll->output = output;
+ }
+
+ if (diff == 0)
+ goto done;
+ }
+ }
+ }
+
+done:
+ dev_dbg(rcrtc->group->dev->dev,
+ "output:%u, fdpll:%u, n:%u, m:%u, diff:%lu\n",
+ dpll->output, dpll->fdpll, dpll->n, dpll->m,
+ best_diff);
+}
+
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
{
const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
+ struct rcar_du_device *rcdu = rcrtc->group->dev;
unsigned long mode_clock = mode->clock * 1000;
unsigned long clk;
u32 value;
escr = div | ESCR_DCLKSEL_CLKS;
if (rcrtc->extclock) {
+ struct dpll_info dpll = { 0 };
unsigned long extclk;
unsigned long extrate;
unsigned long rate;
u32 extdiv;
extclk = clk_get_rate(rcrtc->extclock);
+ if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
+ rcar_du_dpll_divider(rcrtc, &dpll, extclk, mode_clock);
+ extclk = dpll.output;
+ }
+
extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
extdiv = clamp(extdiv, 1U, 64U) - 1;
abs((long)rate - (long)mode_clock)) {
dev_dbg(rcrtc->group->dev->dev,
"crtc%u: using external clock\n", rcrtc->index);
- escr = extdiv | ESCR_DCLKSEL_DCLKIN;
+
+ if (rcdu->info->dpll_ch & (1 << rcrtc->index)) {
+ u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
+ | DPLLCR_FDPLL(dpll.fdpll)
+ | DPLLCR_N(dpll.n) | DPLLCR_M(dpll.m)
+ | DPLLCR_STBY;
+
+ if (rcrtc->index == 1)
+ dpllcr |= DPLLCR_PLCS1
+ | DPLLCR_INCS_DOTCLKIN1;
+ else
+ dpllcr |= DPLLCR_PLCS0
+ | DPLLCR_INCS_DOTCLKIN0;
+
+ rcar_du_group_write(rcrtc->group, DPLLCR,
+ dpllcr);
+
+ escr = ESCR_DCLKSEL_DCLKIN | 1;
+ } else {
+ escr = ESCR_DCLKSEL_DCLKIN | extdiv;
+ }
}
}
#define DEFR10_TSEL_H3_TCON1 (0 << 1) /* DEFR102 register only (DU2/DU3) */
#define DEFR10_DEFE10 (1 << 0)
+#define DPLLCR 0x20044
+#define DPLLCR_CODE (0x95 << 24)
+#define DPLLCR_PLCS1 (1 << 23)
+/*
+ * PLCS0 is bit 21, but H3 ES1.x requires bit 20 to be set as well. As bit 20
+ * isn't implemented by other SoC in the Gen3 family it can safely be set
+ * unconditionally.
+ */
+#define DPLLCR_PLCS0 (3 << 20)
+#define DPLLCR_CLKE (1 << 18)
+#define DPLLCR_FDPLL(n) ((n) << 12)
+#define DPLLCR_N(n) ((n) << 5)
+#define DPLLCR_M(n) ((n) << 3)
+#define DPLLCR_STBY (1 << 2)
+#define DPLLCR_INCS_DOTCLKIN0 (0 << 0)
+#define DPLLCR_INCS_DOTCLKIN1 (1 << 1)
+
+#define DPLLC2R 0x20048
+#define DPLLC2R_CODE (0x95 << 24)
+#define DPLLC2R_SELC (1 << 12)
+#define DPLLC2R_M(n) ((n) << 8)
+#define DPLLC2R_FDPLL(n) ((n) << 0)
+
/* -----------------------------------------------------------------------------
* Display Timing Generation Registers
*/