Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc4aedbf authored by Koji Matsuoka's avatar Koji Matsuoka Committed by Laurent Pinchart
Browse files

drm: rcar-du: Add DPLL support



The implementation hardcodes a workaround for the H3 ES1.x SoC
regardless of the SoC revision, as the workaround can be safely applied
on all devices in the Gen3 family without any side effect.

Signed-off-by: default avatarKoji Matsuoka <koji.matsuoka.xm@renesas.com>
Signed-off-by: default avatarUlrich Hecht <ulrich.hecht+renesas@gmail.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
parent 4739a0d4
Loading
Loading
Loading
Loading
+80 −1
Original line number Diff line number Diff line
@@ -106,9 +106,62 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
 * 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;
@@ -124,12 +177,18 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
	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;

@@ -140,7 +199,27 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
		    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;
			}
		}
	}

+1 −0
Original line number Diff line number Diff line
@@ -162,6 +162,7 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
		},
	},
	.num_lvds = 1,
	.dpll_ch =  BIT(1) | BIT(2),
};

static const struct rcar_du_device_info rcar_du_r8a7796_info = {
+1 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ struct rcar_du_device_info {
	unsigned int num_crtcs;
	struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
	unsigned int num_lvds;
	unsigned int dpll_ch;
};

#define RCAR_DU_MAX_CRTCS		4
+23 −0
Original line number Diff line number Diff line
@@ -277,6 +277,29 @@
#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
 */