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:
- "renesas,r8a7796-lvds" for R8A7796 (R-Car M3-W) 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,r8a77990-lvds" for R8A77990 (R-Car E3) compatible LVDS encoders
- "renesas,r8a77995-lvds" for R8A77995 (R-Car D3) compatible LVDS encoders
- 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
Required nodes:
......
......@@ -16,6 +16,7 @@ Required Properties:
- "renesas,du-r8a77965" for R8A77965 (R-Car M3-N) compatible DU
- "renesas,du-r8a77970" for R8A77970 (R-Car V3M) 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
- reg: the memory-mapped I/O registers base address and length
......@@ -63,6 +64,7 @@ corresponding to each DU output.
R8A77965 (R-Car M3-N) DPAD 0 HDMI 0 LVDS 0 -
R8A77970 (R-Car V3M) 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 -
......
......@@ -45,6 +45,23 @@ static int thc63_attach(struct drm_bridge *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)
{
struct thc63_dev *thc63 = to_thc63(bridge);
......@@ -77,6 +94,7 @@ static void thc63_disable(struct drm_bridge *bridge)
static const struct drm_bridge_funcs thc63_bridge_func = {
.attach = thc63_attach,
.mode_valid = thc63_mode_valid,
.enable = thc63_enable,
.disable = thc63_disable,
};
......
......@@ -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);
}
static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg,
u32 clr, u32 set)
void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set)
{
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);
}
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);
rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set;
rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr);
}
/* -----------------------------------------------------------------------------
......@@ -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);
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 {
struct du_clk_params params = { .diff = (unsigned long)-1 };
......@@ -546,6 +520,51 @@ static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc)
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)
{
bool interlaced;
......@@ -556,9 +575,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
* actively driven).
*/
interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE;
rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK | DSYSR_SCM_MASK,
(interlaced ? DSYSR_SCM_INT_VIDEO : 0) |
DSYSR_TVM_MASTER);
rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK | DSYSR_SCM_MASK,
(interlaced ? DSYSR_SCM_INT_VIDEO : 0) |
DSYSR_TVM_MASTER);
rcar_du_group_start_stop(rcrtc->group, true);
}
......@@ -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
* 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);
}
......@@ -639,16 +663,7 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
{
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
/*
* 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_get(rcrtc);
rcar_du_crtc_start(rcrtc);
}
......@@ -667,7 +682,6 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
}
spin_unlock_irq(&crtc->dev->event_lock);
rcrtc->initialized = false;
rcrtc->outputs = 0;
}
......@@ -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.
* We then need to first setup the CRTC in order to configure planes.
* The .atomic_enable() handler will notice and skip the CRTC setup.
* We thus need to first get and setup the CRTC in order to configure
* 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_setup(rcrtc);
rcrtc->initialized = true;
}
rcar_du_crtc_get(rcrtc);
if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE))
rcar_du_vsp_atomic_begin(rcrtc);
......@@ -1108,6 +1125,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex,
rcrtc->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[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))
primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane;
......
......@@ -30,6 +30,7 @@ struct rcar_du_vsp;
* @mmio_offset: offset of the CRTC registers in the DU MMIO block
* @index: CRTC software and hardware index
* @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
* @event: event to post when the pending page flip completes
* @flip_wait: wait queue used to signal page flip completion
......@@ -50,6 +51,8 @@ struct rcar_du_crtc {
unsigned int index;
bool initialized;
u32 dsysr;
bool vblank_enable;
struct drm_pending_vblank_event *event;
wait_queue_head_t flip_wait;
......@@ -103,4 +106,6 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
enum rcar_du_output output);
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__ */
......@@ -36,7 +36,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = {
.gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| 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),
.routes = {
/*
......@@ -58,7 +59,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
.gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| 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),
.routes = {
/*
......@@ -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 = {
.gen = 2,
.features = RCAR_DU_FEATURE_INTERLACED,
.features = RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(1) | BIT(0),
.routes = {
/*
......@@ -99,7 +102,8 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
.gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| 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,
.channels_mask = BIT(2) | BIT(1) | BIT(0),
.routes = {
......@@ -128,7 +132,8 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = {
.gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| 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),
.routes = {
/*
......@@ -151,7 +156,8 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = {
.gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| 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),
.routes = {
/* R8A7792 has two RGB outputs. */
......@@ -170,7 +176,8 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
.gen = 2,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| 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),
.routes = {
/*
......@@ -193,7 +200,8 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| 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),
.routes = {
/*
......@@ -226,7 +234,8 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| 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),
.routes = {
/*
......@@ -255,7 +264,8 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| 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),
.routes = {
/*
......@@ -284,7 +294,8 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS
| RCAR_DU_FEATURE_VSP1_SOURCE
| RCAR_DU_FEATURE_INTERLACED,
| RCAR_DU_FEATURE_INTERLACED
| RCAR_DU_FEATURE_TVM_SYNC,
.channels_mask = BIT(0),
.routes = {
/* R8A77970 has one RGB output and one LVDS output. */
......@@ -300,6 +311,34 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = {
.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[] = {
{ .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
{ .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
......@@ -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-r8a77965", .data = &rcar_du_r8a77965_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;
#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_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 */
......@@ -53,6 +54,7 @@ struct rcar_du_output_routing {
* @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
* @num_lvds: number of internal LVDS encoders
* @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 {
unsigned int gen;
......@@ -62,6 +64,7 @@ struct rcar_du_device_info {
struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
unsigned int num_lvds;
unsigned int dpll_mask;
unsigned int lvds_clk_mask;
};
#define RCAR_DU_MAX_CRTCS 4
......
......@@ -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)
{
struct rcar_du_device *rcdu = rgrp->dev;
unsigned int possible_crtcs =
rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs;
u32 defr8 = DEFR8_CODE;
if (rcdu->info->gen < 3) {
......@@ -69,26 +67,71 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
* DU instances that support it.
*/
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)
defr8 |= DEFR8_VSCS;
}
} else {
/*
* On Gen3 VSPD routing can't be configured, but DPAD routing
* needs to be set despite having a single option available.
* On Gen3 VSPD routing can't be configured, and DPAD routing
* 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;
struct rcar_du_crtc *crtc = &rcdu->crtcs[rgb_crtc];
if (crtc->index / 2 == rgrp->index)
defr8 |= DEFR8_DRGBS_DU(crtc->index);
if (rgrp->index == rcdu->dpad0_source / 2)
defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source);
}
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)
{
struct rcar_du_device *rcdu = rgrp->dev;
......@@ -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)) {
rcar_du_group_setup_defr8(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);
rcar_du_group_setup_didsr(rgrp);
}
if (rcdu->info->gen >= 3)
......@@ -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)
{
rcar_du_group_write(rgrp, DSYSR,
(rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) |
(start ? DSYSR_DEN : DSYSR_DRES));
struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2];
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)
......
......@@ -544,6 +544,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
struct drm_device *dev = rcdu->ddev;
struct drm_encoder *encoder;
struct drm_fbdev_cma *fbdev;
unsigned int dpad0_sources;
unsigned int num_encoders;
unsigned int num_groups;
unsigned int swindex;
......@@ -666,6 +667,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
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_kms_helper_poll_init(dev);
......
This diff is collapsed.
......@@ -18,7 +18,7 @@
#define LVDCR0_PLLON (1 << 4)
#define LVDCR0_PWD (1 << 2) /* Gen3 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 LVDCR1 0x0004
......@@ -27,21 +27,36 @@
#define LVDCR1_CLKSTBY (3 << 0)
#define LVDPLLCR 0x0008
/* Gen2 & V3M */
#define LVDPLLCR_CEEN (1 << 14)
#define LVDPLLCR_FBEN (1 << 13)
#define LVDPLLCR_COSEL (1 << 12)
/* Gen2 */
#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0)
#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0)
#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0)
#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0)
#define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0)
/* Gen3 */
/* Gen3 but V3M,D3 and E3 */
#define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0)
#define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0)
#define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0)
#define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 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_CTR3SEL_ZERO (0 << 12)
......@@ -71,4 +86,26 @@
#define LVDCHCR_CHSEL_CH(n, c) ((((c) - (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__ */
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