Commit 2e240bee authored by Dave Airlie's avatar Dave Airlie

Merge tag 'du-next-20180925' of git://linuxtv.org/pinchartl/media into drm-next

R-Car DU support for the D3 and E3 SoCs (v4.20)
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/3289904.RCOHkcp7u8@avalon
parents 36c9c3c9 12270207
...@@ -15,10 +15,21 @@ Required properties: ...@@ -15,10 +15,21 @@ Required properties:
- "renesas,r8a7796-lvds" for R8A7796 (R-Car M3-W) compatible LVDS encoders - "renesas,r8a7796-lvds" for R8A7796 (R-Car M3-W) compatible LVDS encoders
- "renesas,r8a77970-lvds" for R8A77970 (R-Car V3M) compatible LVDS encoders - "renesas,r8a77970-lvds" for R8A77970 (R-Car V3M) compatible LVDS encoders
- "renesas,r8a77980-lvds" for R8A77980 (R-Car V3H) compatible LVDS encoders - "renesas,r8a77980-lvds" for R8A77980 (R-Car V3H) compatible LVDS encoders
- "renesas,r8a77990-lvds" for R8A77990 (R-Car E3) compatible LVDS encoders
- "renesas,r8a77995-lvds" for R8A77995 (R-Car D3) compatible LVDS encoders - "renesas,r8a77995-lvds" for R8A77995 (R-Car D3) compatible LVDS encoders
- reg: Base address and length for the memory-mapped registers - reg: Base address and length for the memory-mapped registers
- clocks: A phandle + clock-specifier pair for the functional clock - clocks: A list of phandles + clock-specifier pairs, one for each entry in
the clock-names property.
- clock-names: Name of the clocks. This property is model-dependent.
- The functional clock, which mandatory for all models, shall be listed
first, and shall be named "fck".
- On R8A77990 and R8A77995, the LVDS encoder can use the EXTAL or
DU_DOTCLKINx clocks. Those clocks are optional. When supplied they must be
named "extal" and "dclkin.x" respectively, with "x" being the DU_DOTCLKIN
numerical index.
- When the clocks property only contains the functional clock, the
clock-names property may be omitted.
- resets: A phandle + reset specifier for the module reset - resets: A phandle + reset specifier for the module reset
Required nodes: Required nodes:
......
...@@ -16,6 +16,7 @@ Required Properties: ...@@ -16,6 +16,7 @@ Required Properties:
- "renesas,du-r8a77965" for R8A77965 (R-Car M3-N) compatible DU - "renesas,du-r8a77965" for R8A77965 (R-Car M3-N) compatible DU
- "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU - "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU
- "renesas,du-r8a77980" for R8A77980 (R-Car V3H) compatible DU - "renesas,du-r8a77980" for R8A77980 (R-Car V3H) compatible DU
- "renesas,du-r8a77990" for R8A77990 (R-Car E3) compatible DU
- "renesas,du-r8a77995" for R8A77995 (R-Car D3) compatible DU - "renesas,du-r8a77995" for R8A77995 (R-Car D3) compatible DU
- reg: the memory-mapped I/O registers base address and length - reg: the memory-mapped I/O registers base address and length
...@@ -63,6 +64,7 @@ corresponding to each DU output. ...@@ -63,6 +64,7 @@ corresponding to each DU output.
R8A77965 (R-Car M3-N) DPAD 0 HDMI 0 LVDS 0 - R8A77965 (R-Car M3-N) DPAD 0 HDMI 0 LVDS 0 -
R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - - R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - -
R8A77980 (R-Car V3H) DPAD 0 LVDS 0 - - R8A77980 (R-Car V3H) DPAD 0 LVDS 0 - -
R8A77990 (R-Car E3) DPAD 0 LVDS 0 LVDS 1 -
R8A77995 (R-Car D3) DPAD 0 LVDS 0 LVDS 1 - R8A77995 (R-Car D3) DPAD 0 LVDS 0 LVDS 1 -
......
...@@ -45,6 +45,23 @@ static int thc63_attach(struct drm_bridge *bridge) ...@@ -45,6 +45,23 @@ static int thc63_attach(struct drm_bridge *bridge)
return drm_bridge_attach(bridge->encoder, thc63->next, bridge); return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
} }
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
const struct drm_display_mode *mode)
{
/*
* The THC63LVD1024 clock frequency range is 8 to 135 MHz in single-in
* mode. Note that the limits are different in dual-in, single-out mode,
* and will need to be adjusted accordingly.
*/
if (mode->clock < 8000)
return MODE_CLOCK_LOW;
if (mode->clock > 135000)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static void thc63_enable(struct drm_bridge *bridge) static void thc63_enable(struct drm_bridge *bridge)
{ {
struct thc63_dev *thc63 = to_thc63(bridge); struct thc63_dev *thc63 = to_thc63(bridge);
...@@ -77,6 +94,7 @@ static void thc63_disable(struct drm_bridge *bridge) ...@@ -77,6 +94,7 @@ static void thc63_disable(struct drm_bridge *bridge)
static const struct drm_bridge_funcs thc63_bridge_func = { static const struct drm_bridge_funcs thc63_bridge_func = {
.attach = thc63_attach, .attach = thc63_attach,
.mode_valid = thc63_mode_valid,
.enable = thc63_enable, .enable = thc63_enable,
.disable = thc63_disable, .disable = thc63_disable,
}; };
......
...@@ -57,46 +57,12 @@ static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) ...@@ -57,46 +57,12 @@ static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set)
rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set);
} }
static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set)
u32 clr, u32 set)
{ {
struct rcar_du_device *rcdu = rcrtc->group->dev; struct rcar_du_device *rcdu = rcrtc->group->dev;
u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg);
rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set;
} rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr);
static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
{
int ret;
ret = clk_prepare_enable(rcrtc->clock);
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)
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);
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
...@@ -294,6 +260,14 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) ...@@ -294,6 +260,14 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr); rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr);
escr = ESCR_DCLKSEL_DCLKIN | div; escr = ESCR_DCLKSEL_DCLKIN | div;
} else if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
/*
* Use the LVDS PLL output as the dot clock when outputting to
* the LVDS encoder on an SoC that supports this clock routing
* option. We use the clock directly in that case, without any
* additional divider.
*/
escr = ESCR_DCLKSEL_DCLKIN;
} else { } else {
struct du_clk_params params = { .diff = (unsigned long)-1 }; struct du_clk_params params = { .diff = (unsigned long)-1 };
...@@ -546,6 +520,51 @@ static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc) ...@@ -546,6 +520,51 @@ static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc)
drm_crtc_vblank_on(&rcrtc->crtc); drm_crtc_vblank_on(&rcrtc->crtc);
} }
static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
{
int ret;
/*
* Guard against double-get, as the function is called from both the
* .atomic_enable() and .atomic_begin() handlers.
*/
if (rcrtc->initialized)
return 0;
ret = clk_prepare_enable(rcrtc->clock);
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)
goto error_group;
rcar_du_crtc_setup(rcrtc);
rcrtc->initialized = true;
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);
rcrtc->initialized = false;
}
static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
{ {
bool interlaced; bool interlaced;
...@@ -556,9 +575,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) ...@@ -556,9 +575,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
* actively driven). * actively driven).
*/ */
interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE; interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE;
rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK | DSYSR_SCM_MASK, rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK | DSYSR_SCM_MASK,
(interlaced ? DSYSR_SCM_INT_VIDEO : 0) | (interlaced ? DSYSR_SCM_INT_VIDEO : 0) |
DSYSR_TVM_MASTER); DSYSR_TVM_MASTER);
rcar_du_group_start_stop(rcrtc->group, true); rcar_du_group_start_stop(rcrtc->group, true);
} }
...@@ -624,8 +643,13 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) ...@@ -624,8 +643,13 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
/* /*
* Select switch sync mode. This stops display operation and configures * Select switch sync mode. This stops display operation and configures
* the HSYNC and VSYNC signals as inputs. * the HSYNC and VSYNC signals as inputs.
*
* TODO: Find another way to stop the display for DUs that don't support
* TVM sync.
*/ */
rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH); if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_TVM_SYNC))
rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK,
DSYSR_TVM_SWITCH);
rcar_du_group_start_stop(rcrtc->group, false); rcar_du_group_start_stop(rcrtc->group, false);
} }
...@@ -639,16 +663,7 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, ...@@ -639,16 +663,7 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
{ {
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
/* rcar_du_crtc_get(rcrtc);
* If the CRTC has already been setup by the .atomic_begin() handler we
* can skip the setup stage.
*/
if (!rcrtc->initialized) {
rcar_du_crtc_get(rcrtc);
rcar_du_crtc_setup(rcrtc);
rcrtc->initialized = true;
}
rcar_du_crtc_start(rcrtc); rcar_du_crtc_start(rcrtc);
} }
...@@ -667,7 +682,6 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, ...@@ -667,7 +682,6 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
} }
spin_unlock_irq(&crtc->dev->event_lock); spin_unlock_irq(&crtc->dev->event_lock);
rcrtc->initialized = false;
rcrtc->outputs = 0; rcrtc->outputs = 0;
} }
...@@ -680,14 +694,17 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, ...@@ -680,14 +694,17 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc,
/* /*
* If a mode set is in progress we can be called with the CRTC disabled. * If a mode set is in progress we can be called with the CRTC disabled.
* We then need to first setup the CRTC in order to configure planes. * We thus need to first get and setup the CRTC in order to configure
* The .atomic_enable() handler will notice and skip the CRTC setup. * planes. We must *not* put the CRTC in .atomic_flush(), as it must be
* kept awake until the .atomic_enable() call that will follow. The get
* operation in .atomic_enable() will in that case be a no-op, and the
* CRTC will be put later in .atomic_disable().
*
* If a mode set is not in progress the CRTC is enabled, and the
* following get call will be a no-op. There is thus no need to belance
* it in .atomic_flush() either.
*/ */
if (!rcrtc->initialized) { rcar_du_crtc_get(rcrtc);
rcar_du_crtc_get(rcrtc);
rcar_du_crtc_setup(rcrtc);
rcrtc->initialized = true;
}
if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
rcar_du_vsp_atomic_begin(rcrtc); rcar_du_vsp_atomic_begin(rcrtc);
...@@ -1108,6 +1125,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, ...@@ -1108,6 +1125,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
rcrtc->group = rgrp; rcrtc->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[hwindex]; rcrtc->mmio_offset = mmio_offsets[hwindex];
rcrtc->index = hwindex; rcrtc->index = hwindex;
rcrtc->dsysr = (rcrtc->index % 2 ? 0 : DSYSR_DRES) | DSYSR_TVM_TVSYNC;
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE))
primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane; primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane;
......
...@@ -30,6 +30,7 @@ struct rcar_du_vsp; ...@@ -30,6 +30,7 @@ struct rcar_du_vsp;
* @mmio_offset: offset of the CRTC registers in the DU MMIO block * @mmio_offset: offset of the CRTC registers in the DU MMIO block
* @index: CRTC software and hardware index * @index: CRTC software and hardware index
* @initialized: whether the CRTC has been initialized and clocks enabled * @initialized: whether the CRTC has been initialized and clocks enabled
* @dsysr: cached value of the DSYSR register
* @vblank_enable: whether vblank events are enabled on this CRTC * @vblank_enable: whether vblank events are enabled on this CRTC
* @event: event to post when the pending page flip completes * @event: event to post when the pending page flip completes
* @flip_wait: wait queue used to signal page flip completion * @flip_wait: wait queue used to signal page flip completion
...@@ -50,6 +51,8 @@ struct rcar_du_crtc { ...@@ -50,6 +51,8 @@ struct rcar_du_crtc {
unsigned int index; unsigned int index;
bool initialized; bool initialized;
u32 dsysr;
bool vblank_enable; bool vblank_enable;
struct drm_pending_vblank_event *event; struct drm_pending_vblank_event *event;
wait_queue_head_t flip_wait; wait_queue_head_t flip_wait;
...@@ -103,4 +106,6 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, ...@@ -103,4 +106,6 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
enum rcar_du_output output); enum rcar_du_output output);
void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc);
void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set);
#endif /* __RCAR_DU_CRTC_H__ */ #endif /* __RCAR_DU_CRTC_H__ */
...@@ -36,7 +36,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = { ...@@ -36,7 +36,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0), .channels_mask = BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -58,7 +59,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = { ...@@ -58,7 +59,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0), .channels_mask = BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -77,7 +79,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = { ...@@ -77,7 +79,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
static const struct rcar_du_device_info rcar_du_r8a7779_info = { static const struct rcar_du_device_info rcar_du_r8a7779_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_INTERLACED, .features = RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0), .channels_mask = BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -99,7 +102,8 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { ...@@ -99,7 +102,8 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.quirks = RCAR_DU_QUIRK_ALIGN_128B, .quirks = RCAR_DU_QUIRK_ALIGN_128B,
.channels_mask = BIT(2) | BIT(1) | BIT(0), .channels_mask = BIT(2) | BIT(1) | BIT(0),
.routes = { .routes = {
...@@ -128,7 +132,8 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = { ...@@ -128,7 +132,8 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0), .channels_mask = BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -151,7 +156,8 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = { ...@@ -151,7 +156,8 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0), .channels_mask = BIT(1) | BIT(0),
.routes = { .routes = {
/* R8A7792 has two RGB outputs. */ /* R8A7792 has two RGB outputs. */
...@@ -170,7 +176,8 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = { ...@@ -170,7 +176,8 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
.gen = 2, .gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0), .channels_mask = BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -193,7 +200,8 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = { ...@@ -193,7 +200,8 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_VSP1_SOURCE
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -226,7 +234,8 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = { ...@@ -226,7 +234,8 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_VSP1_SOURCE
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(2) | BIT(1) | BIT(0), .channels_mask = BIT(2) | BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -255,7 +264,8 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = { ...@@ -255,7 +264,8 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_VSP1_SOURCE
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(3) | BIT(1) | BIT(0), .channels_mask = BIT(3) | BIT(1) | BIT(0),
.routes = { .routes = {
/* /*
...@@ -284,7 +294,8 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = { ...@@ -284,7 +294,8 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS | RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE | RCAR_DU_FEATURE_VSP1_SOURCE
| RCAR_DU_FEATURE_INTERLACED, | RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(0), .channels_mask = BIT(0),
.routes = { .routes = {
/* R8A77970 has one RGB output and one LVDS output. */ /* R8A77970 has one RGB output and one LVDS output. */
...@@ -300,6 +311,34 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = { ...@@ -300,6 +311,34 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = {
.num_lvds = 1, .num_lvds = 1,
}; };
static const struct rcar_du_device_info rcar_du_r8a7799x_info = {
.gen = 3,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE,
.channels_mask = BIT(1) | BIT(0),
.routes = {
/*
* R8A77990 and R8A77995 have one RGB output and two LVDS
* outputs.
*/
[RCAR_DU_OUTPUT_DPAD0] = {
.possible_crtcs = BIT(0) | BIT(1),
.port = 0,
},
[RCAR_DU_OUTPUT_LVDS0] = {
.possible_crtcs = BIT(0),
.port = 1,
},
[RCAR_DU_OUTPUT_LVDS1] = {
.possible_crtcs = BIT(1),
.port = 2,
},
},
.num_lvds = 2,
.lvds_clk_mask = BIT(1) | BIT(0),
};
static const struct of_device_id rcar_du_of_table[] = { static const struct of_device_id rcar_du_of_table[] = {
{ .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
{ .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
...@@ -313,6 +352,8 @@ static const struct of_device_id rcar_du_of_table[] = { ...@@ -313,6 +352,8 @@ static const struct of_device_id rcar_du_of_table[] = {
{ .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
{ .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info }, { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info },
{ .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info }, { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info },
{ .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info },
{ .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info },
{ } { }
}; };
......
...@@ -27,6 +27,7 @@ struct rcar_du_device; ...@@ -27,6 +27,7 @@ struct rcar_du_device;
#define RCAR_DU_FEATURE_EXT_CTRL_REGS BIT(1) /* Has extended control registers */ #define RCAR_DU_FEATURE_EXT_CTRL_REGS BIT(1) /* Has extended control registers */
#define RCAR_DU_FEATURE_VSP1_SOURCE BIT(2) /* Has inputs from VSP1 */ #define RCAR_DU_FEATURE_VSP1_SOURCE BIT(2) /* Has inputs from VSP1 */
#define RCAR_DU_FEATURE_INTERLACED BIT(3) /* HW supports interlaced */ #define RCAR_DU_FEATURE_INTERLACED BIT(3) /* HW supports interlaced */
#define RCAR_DU_FEATURE_TVM_SYNC BIT(4) /* Has TV switch/sync modes */
#define RCAR_DU_QUIRK_ALIGN_128B BIT(0) /* Align pitches to 128 bytes */ #define RCAR_DU_QUIRK_ALIGN_128B BIT(0) /* Align pitches to 128 bytes */
...@@ -53,6 +54,7 @@ struct rcar_du_output_routing { ...@@ -53,6 +54,7 @@ struct rcar_du_output_routing {
* @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
* @num_lvds: number of internal LVDS encoders * @num_lvds: number of internal LVDS encoders
* @dpll_mask: bit mask of DU channels equipped with a DPLL * @dpll_mask: bit mask of DU channels equipped with a DPLL
* @lvds_clk_mask: bitmask of channels that can use the LVDS clock as dot clock
*/ */
struct rcar_du_device_info { struct rcar_du_device_info {
unsigned int gen; unsigned int gen;
...@@ -62,6 +64,7 @@ struct rcar_du_device_info { ...@@ -62,6 +64,7 @@ struct rcar_du_device_info {
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_mask; unsigned int dpll_mask;
unsigned int lvds_clk_mask;
}; };
#define RCAR_DU_MAX_CRTCS 4 #define RCAR_DU_MAX_CRTCS 4
......
...@@ -56,8 +56,6 @@ static void rcar_du_group_setup_pins(struct rcar_du_group *rgrp) ...@@ -56,8 +56,6 @@ static void rcar_du_group_setup_pins(struct rcar_du_group *rgrp)
static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
{ {
struct rcar_du_device *rcdu = rgrp->dev; struct rcar_du_device *rcdu = rgrp->dev;
unsigned int possible_crtcs =
rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs;
u32 defr8 = DEFR8_CODE; u32 defr8 = DEFR8_CODE;
if (rcdu->info->gen < 3) { if (rcdu->info->gen < 3) {
...@@ -69,26 +67,71 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) ...@@ -69,26 +67,71 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
* DU instances that support it. * DU instances that support it.
*/ */
if (rgrp->index == 0) { if (rgrp->index == 0) {
if (possible_crtcs > 1) defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
if (rgrp->dev->vspd1_sink == 2) if (rgrp->dev->vspd1_sink == 2)
defr8 |= DEFR8_VSCS; defr8 |= DEFR8_VSCS;
} }
} else { } else {
/* /*
* On Gen3 VSPD routing can't be configured, but DPAD routing * On Gen3 VSPD routing can't be configured, and DPAD routing
* needs to be set despite having a single option available. * is set in the group corresponding to the DPAD output (no Gen3
* SoC has multiple DPAD sources belonging to separate groups).
*/ */
unsigned int rgb_crtc = ffs(possible_crtcs) - 1; if (rgrp->index == rcdu->dpad0_source / 2)
struct rcar_du_crtc *crtc = &rcdu->crtcs[rgb_crtc]; defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
if (crtc->index / 2 == rgrp->index)
defr8 |= DEFR8_DRGBS_DU(crtc->index);
} }
rcar_du_group_write(rgrp, DEFR8, defr8); rcar_du_group_write(rgrp, DEFR8, defr8);
} }
static void rcar_du_group_setup_didsr(struct rcar_du_group *rgrp)
{
struct rcar_du_device *rcdu = rgrp->dev;
struct rcar_du_crtc *rcrtc;
unsigned int num_crtcs = 0;
unsigned int i;
u32 didsr;
/*
* Configure input dot clock routing with a hardcoded configuration. If
* the DU channel can use the LVDS encoder output clock as the dot
* clock, do so. Otherwise route DU_DOTCLKINn signal to DUn.
*
* Each channel can then select between the dot clock configured here
* and the clock provided by the CPG through the ESCR register.
*/
if (rcdu->info->gen < 3 && rgrp->index == 0) {
/*
* On Gen2 a single register in the first group controls dot
* clock selection for all channels.
*/
rcrtc = rcdu->crtcs;
num_crtcs = rcdu->num_crtcs;
} else if (rcdu->info->gen == 3 && rgrp->num_crtcs > 1) {
/*
* On Gen3 dot clocks are setup through per-group registers,
* only available when the group has two channels.
*/
rcrtc = &rcdu->crtcs[rgrp->index * 2];
num_crtcs = rgrp->num_crtcs;
}
if (!num_crtcs)
return;
didsr = DIDSR_CODE;
for (i = 0; i < num_crtcs; ++i, ++rcrtc) {
if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index))
didsr |= DIDSR_LCDS_LVDS0(i)
| DIDSR_PDCS_CLK(i, 0);
else
didsr |= DIDSR_LCDS_DCLKIN(i)
| DIDSR_PDCS_CLK(i, 0);
}
rcar_du_group_write(rgrp, DIDSR, didsr);
}
static void rcar_du_group_setup(struct rcar_du_group *rgrp) static void rcar_du_group_setup(struct rcar_du_group *rgrp)
{ {
struct rcar_du_device *rcdu = rgrp->dev; struct rcar_du_device *rcdu = rgrp->dev;
...@@ -106,21 +149,7 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) ...@@ -106,21 +149,7 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) {
rcar_du_group_setup_defr8(rgrp); rcar_du_group_setup_defr8(rgrp);
rcar_du_group_setup_didsr(rgrp);
/*
* Configure input dot clock routing. We currently hardcode the
* configuration to routing DOTCLKINn to DUn. Register fields
* depend on the DU generation, but the resulting value is 0 in
* all cases.
*
* On Gen2 a single register in the first group controls dot
* clock selection for all channels, while on Gen3 dot clocks
* are setup through per-group registers, only available when
* the group has two channels.
*/
if ((rcdu->info->gen < 3 && rgrp->index == 0) ||
(rcdu->info->gen == 3 && rgrp->num_crtcs > 1))
rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE);
} }
if (rcdu->info->gen >= 3) if (rcdu->info->gen >= 3)
...@@ -173,9 +202,10 @@ void rcar_du_group_put(struct rcar_du_group *rgrp) ...@@ -173,9 +202,10 @@ void rcar_du_group_put(struct rcar_du_group *rgrp)
static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
{ {
rcar_du_group_write(rgrp, DSYSR, struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2];
(rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
(start ? DSYSR_DEN : DSYSR_DRES)); rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN,
start ? DSYSR_DEN : DSYSR_DRES);
} }
void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start)
......
...@@ -544,6 +544,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -544,6 +544,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
struct drm_device *dev = rcdu->ddev; struct drm_device *dev = rcdu->ddev;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct drm_fbdev_cma *fbdev; struct drm_fbdev_cma *fbdev;
unsigned int dpad0_sources;
unsigned int num_encoders; unsigned int num_encoders;
unsigned int num_groups; unsigned int num_groups;
unsigned int swindex; unsigned int swindex;
...@@ -666,6 +667,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -666,6 +667,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
encoder->possible_clones = (1 << num_encoders) - 1; encoder->possible_clones = (1 << num_encoders) - 1;
} }
/*
* Initialize the default DPAD0 source to the index of the first DU
* channel that can be connected to DPAD0. The exact value doesn't
* matter as it should be overwritten by mode setting for the RGB
* output, but it is nonetheless required to ensure a valid initial
* hardware configuration on Gen3 where DU0 can't always be connected to
* DPAD0.
*/
dpad0_sources = rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs;
rcdu->dpad0_source = ffs(dpad0_sources) - 1;
drm_mode_config_reset(dev); drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev); drm_kms_helper_poll_init(dev);
......
This diff is collapsed.
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#define LVDCR0_PLLON (1 << 4) #define LVDCR0_PLLON (1 << 4)
#define LVDCR0_PWD (1 << 2) /* Gen3 only */ #define LVDCR0_PWD (1 << 2) /* Gen3 only */
#define LVDCR0_BEN (1 << 2) /* Gen2 only */ #define LVDCR0_BEN (1 << 2) /* Gen2 only */
#define LVDCR0_LVEN (1 << 1) /* Gen2 only */ #define LVDCR0_LVEN (1 << 1)
#define LVDCR0_LVRES (1 << 0) #define LVDCR0_LVRES (1 << 0)
#define LVDCR1 0x0004 #define LVDCR1 0x0004
...@@ -27,21 +27,36 @@ ...@@ -27,21 +27,36 @@
#define LVDCR1_CLKSTBY (3 << 0) #define LVDCR1_CLKSTBY (3 << 0)
#define LVDPLLCR 0x0008 #define LVDPLLCR 0x0008
/* Gen2 & V3M */
#define LVDPLLCR_CEEN (1 << 14) #define LVDPLLCR_CEEN (1 << 14)
#define LVDPLLCR_FBEN (1 << 13) #define LVDPLLCR_FBEN (1 << 13)
#define LVDPLLCR_COSEL (1 << 12) #define LVDPLLCR_COSEL (1 << 12)
/* Gen2 */
#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0)
#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0)
#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0)
#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0)
#define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0)
/* Gen3 */ /* Gen3 but V3M,D3 and E3 */
#define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) #define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0)
#define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) #define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0)
#define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) #define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0)
#define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) #define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0)
#define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) #define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0)
/* D3 and E3 */
#define LVDPLLCR_PLLON (1 << 22)
#define LVDPLLCR_PLLSEL_PLL0 (0 << 20)
#define LVDPLLCR_PLLSEL_LVX (1 << 20)
#define LVDPLLCR_PLLSEL_PLL1 (2 << 20)
#define LVDPLLCR_CKSEL_LVX (1 << 17)
#define LVDPLLCR_CKSEL_EXTAL (3 << 17)
#define LVDPLLCR_CKSEL_DU_DOTCLKIN(n) ((5 + (n) * 2) << 17)
#define LVDPLLCR_OCKSEL (1 << 16)
#define LVDPLLCR_STP_CLKOUTE (1 << 14)
#define LVDPLLCR_OUTCLKSEL (1 << 12)
#define LVDPLLCR_CLKOUT (1 << 11)
#define LVDPLLCR_PLLE(n) ((n) << 10)
#define LVDPLLCR_PLLN(n) ((n) << 3)
#define LVDPLLCR_PLLM(n) ((n) << 0)
#define LVDCTRCR 0x000c #define LVDCTRCR 0x000c
#define LVDCTRCR_CTR3SEL_ZERO (0 << 12) #define LVDCTRCR_CTR3SEL_ZERO (0 << 12)
...@@ -71,4 +86,26 @@ ...@@ -71,4 +86,26 @@
#define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4))
#define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4))
/* All registers below are specific to D3 and E3 */
#define LVDSTRIPE 0x0014
#define LVDSTRIPE_ST_TRGSEL_DISP (0 << 2)
#define LVDSTRIPE_ST_TRGSEL_HSYNC_R (1 << 2)
#define LVDSTRIPE_ST_TRGSEL_HSYNC_F (2 << 2)
#define LVDSTRIPE_ST_SWAP (1 << 1)
#define LVDSTRIPE_ST_ON (1 << 0)
#define LVDSCR 0x0018
#define LVDSCR_DEPTH(n) (((n) - 1) << 29)
#define LVDSCR_BANDSET (1 << 28)
#define LVDSCR_TWGCNT(n) ((((n) - 256) / 16) << 24)
#define LVDSCR_SDIV(n) ((n) << 22)
#define LVDSCR_MODE (1 << 21)
#define LVDSCR_RSTN (1 << 20)
#define LVDDIV 0x001c
#define LVDDIV_DIVSEL (1 << 8)
#define LVDDIV_DIVRESET (1 << 7)
#define LVDDIV_DIVSTP (1 << 6)
#define LVDDIV_DIV(n) ((n) << 0)
#endif /* __RCAR_LVDS_REGS_H__ */ #endif /* __RCAR_LVDS_REGS_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment