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:
Родитель
4739a0d40b
Коммит
dc4aedbf7c
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче