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: Koji Matsuoka <koji.matsuoka.xm@renesas.com> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
This commit is contained in:
parent
4739a0d40b
commit
dc4aedbf7c
@ -106,9 +106,62 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
|
|||||||
* Hardware Setup
|
* 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)
|
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
||||||
{
|
{
|
||||||
const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
|
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 mode_clock = mode->clock * 1000;
|
||||||
unsigned long clk;
|
unsigned long clk;
|
||||||
u32 value;
|
u32 value;
|
||||||
@ -124,12 +177,18 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
|
|||||||
escr = div | ESCR_DCLKSEL_CLKS;
|
escr = div | ESCR_DCLKSEL_CLKS;
|
||||||
|
|
||||||
if (rcrtc->extclock) {
|
if (rcrtc->extclock) {
|
||||||
|
struct dpll_info dpll = { 0 };
|
||||||
unsigned long extclk;
|
unsigned long extclk;
|
||||||
unsigned long extrate;
|
unsigned long extrate;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
u32 extdiv;
|
u32 extdiv;
|
||||||
|
|
||||||
extclk = clk_get_rate(rcrtc->extclock);
|
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 = DIV_ROUND_CLOSEST(extclk, mode_clock);
|
||||||
extdiv = clamp(extdiv, 1U, 64U) - 1;
|
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)) {
|
abs((long)rate - (long)mode_clock)) {
|
||||||
dev_dbg(rcrtc->group->dev->dev,
|
dev_dbg(rcrtc->group->dev->dev,
|
||||||
"crtc%u: using external clock\n", rcrtc->index);
|
"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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +162,7 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
.num_lvds = 1,
|
.num_lvds = 1,
|
||||||
|
.dpll_ch = BIT(1) | BIT(2),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct rcar_du_device_info rcar_du_r8a7796_info = {
|
static const struct rcar_du_device_info rcar_du_r8a7796_info = {
|
||||||
|
@ -65,6 +65,7 @@ struct rcar_du_device_info {
|
|||||||
unsigned int num_crtcs;
|
unsigned int num_crtcs;
|
||||||
struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
|
struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
|
||||||
unsigned int num_lvds;
|
unsigned int num_lvds;
|
||||||
|
unsigned int dpll_ch;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RCAR_DU_MAX_CRTCS 4
|
#define RCAR_DU_MAX_CRTCS 4
|
||||||
|
@ -277,6 +277,29 @@
|
|||||||
#define DEFR10_TSEL_H3_TCON1 (0 << 1) /* DEFR102 register only (DU2/DU3) */
|
#define DEFR10_TSEL_H3_TCON1 (0 << 1) /* DEFR102 register only (DU2/DU3) */
|
||||||
#define DEFR10_DEFE10 (1 << 0)
|
#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
|
* Display Timing Generation Registers
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user