summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
diff options
context:
space:
mode:
authorLaurent Pinchart2014-12-08 23:24:49 +0100
committerLaurent Pinchart2014-12-23 11:01:50 +0100
commit1b30dbde8596ca8de2497c2a50d5381dfe62ee8c (patch)
tree1949b3d8d642ddc371ad0b45a44869147629d56f /drivers/gpu/drm/rcar-du/rcar_du_crtc.c
parentdrm: rcar-du: Refactor DEFR8 feature (diff)
downloadkernel-qcow2-linux-1b30dbde8596ca8de2497c2a50d5381dfe62ee8c.tar.gz
kernel-qcow2-linux-1b30dbde8596ca8de2497c2a50d5381dfe62ee8c.tar.xz
kernel-qcow2-linux-1b30dbde8596ca8de2497c2a50d5381dfe62ee8c.zip
drm: rcar-du: Add support for external pixel clock
The DU uses the module functional clock as the default pixel clock, but supports using an externally supplied pixel clock instead. Support this by adding the external pixel clock to the DT bindings, and selecting the clock automatically at runtime based on the requested mode pixel frequency. The input clock pins to DU channels routing is configurable, but currently hardcoded to connect input clock i to channel i. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Diffstat (limited to 'drivers/gpu/drm/rcar-du/rcar_du_crtc.c')
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c60
1 files changed, 54 insertions, 6 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index cf0dca13264f..ce280bd390a9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -74,33 +74,71 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
if (ret < 0)
return ret;
+ ret = clk_prepare_enable(rcrtc->extclock);
+ if (ret < 0)
+ goto error_clock;
+
ret = rcar_du_group_get(rcrtc->group);
if (ret < 0)
- clk_disable_unprepare(rcrtc->clock);
+ goto error_group;
+
+ return 0;
+error_group:
+ clk_disable_unprepare(rcrtc->extclock);
+error_clock:
+ clk_disable_unprepare(rcrtc->clock);
return ret;
}
static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
{
rcar_du_group_put(rcrtc->group);
+
+ clk_disable_unprepare(rcrtc->extclock);
clk_disable_unprepare(rcrtc->clock);
}
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
{
const struct drm_display_mode *mode = &rcrtc->crtc.mode;
+ unsigned long mode_clock = mode->clock * 1000;
unsigned long clk;
u32 value;
+ u32 escr;
u32 div;
- /* Dot clock */
+ /* Compute the clock divisor and select the internal or external dot
+ * clock based on the requested frequency.
+ */
clk = clk_get_rate(rcrtc->clock);
- div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000);
+ div = DIV_ROUND_CLOSEST(clk, mode_clock);
div = clamp(div, 1U, 64U) - 1;
+ escr = div | ESCR_DCLKSEL_CLKS;
+
+ if (rcrtc->extclock) {
+ unsigned long extclk;
+ unsigned long extrate;
+ unsigned long rate;
+ u32 extdiv;
+
+ extclk = clk_get_rate(rcrtc->extclock);
+ extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
+ extdiv = clamp(extdiv, 1U, 64U) - 1;
+
+ rate = clk / (div + 1);
+ extrate = extclk / (extdiv + 1);
+
+ if (abs((long)extrate - (long)mode_clock) <
+ 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;
+ }
+ }
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
- ESCR_DCLKSEL_CLKS | div);
+ escr);
rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
/* Signal polarities */
@@ -543,12 +581,13 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
struct drm_crtc *crtc = &rcrtc->crtc;
unsigned int irqflags;
- char clk_name[5];
+ struct clk *clk;
+ char clk_name[9];
char *name;
int irq;
int ret;
- /* Get the CRTC clock. */
+ /* Get the CRTC clock and the optional external clock. */
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
sprintf(clk_name, "du.%u", index);
name = clk_name;
@@ -562,6 +601,15 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
return PTR_ERR(rcrtc->clock);
}
+ sprintf(clk_name, "dclkin.%u", index);
+ clk = devm_clk_get(rcdu->dev, clk_name);
+ if (!IS_ERR(clk)) {
+ rcrtc->extclock = clk;
+ } else if (PTR_ERR(rcrtc->clock) == -EPROBE_DEFER) {
+ dev_info(rcdu->dev, "can't get external clock %u\n", index);
+ return -EPROBE_DEFER;
+ }
+
rcrtc->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[index];
rcrtc->index = index;