Skip to content

Commit

Permalink
Merge branch 'drm/next/du' of git://linuxtv.org/pinchartl/fbdev into …
Browse files Browse the repository at this point in the history
…drm-next

* 'drm/next/du' of git://linuxtv.org/pinchartl/fbdev:
  drm: rcar-du: Implement support for interlaced modes
  drm: rcar-du: Clamp DPMS states to on and off
  drm: rcar-du: Enable hotplug detection on HDMI connector
  drm: rcar-du: Output HSYNC instead of CSYNC
  drm: rcar-du: Add support for external pixel clock
  drm: rcar-du: Refactor DEFR8 feature
  drm: rcar-du: Remove LVDS and HDMI encoders chaining restriction
  drm: rcar-du: Configure pitch for chroma plane of multiplanar formats
  drm: rcar-du: Don't fail probe in case of partial encoder init error
  drm: adv7511: Remove interlaced mode check
  • Loading branch information
Dave Airlie committed Jan 21, 2015
2 parents 4f4d89a + 906eff7 commit b2eb048
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 47 deletions.
4 changes: 4 additions & 0 deletions Documentation/devicetree/bindings/video/renesas,du.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Required Properties:
per LVDS encoder. The functional clocks must be named "du.x" with "x"
being the channel numerical index. The LVDS clocks must be named
"lvds.x" with "x" being the LVDS encoder numerical index.
- In addition to the functional and encoder clocks, all DU versions also
support externally supplied pixel clocks. Those clocks are optional.
When supplied they must be named "dclkin.x" with "x" being the input
clock numerical index.

Required nodes:

Expand Down
3 changes: 0 additions & 3 deletions drivers/gpu/drm/i2c/adv7511.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,9 +644,6 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
if (mode->clock > 165000)
return MODE_CLOCK_HIGH;

if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;

return MODE_OK;
}

Expand Down
95 changes: 77 additions & 18 deletions drivers/gpu/drm/rcar-du/rcar_du_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,39 +74,77 @@ 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 */
value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
| DSMR_DIPM_DE;
| DSMR_DIPM_DE | DSMR_CSPM;
rcar_du_crtc_write(rcrtc, DSMR, value);

/* Display timings */
Expand All @@ -117,12 +155,15 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
mode->hsync_start - 1);
rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 1);

rcar_du_crtc_write(rcrtc, VDSR, mode->vtotal - mode->vsync_end - 2);
rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end +
mode->vdisplay - 2);
rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end +
mode->vsync_start - 1);
rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1);
rcar_du_crtc_write(rcrtc, VDSR, mode->crtc_vtotal -
mode->crtc_vsync_end - 2);
rcar_du_crtc_write(rcrtc, VDER, mode->crtc_vtotal -
mode->crtc_vsync_end +
mode->crtc_vdisplay - 2);
rcar_du_crtc_write(rcrtc, VSPR, mode->crtc_vtotal -
mode->crtc_vsync_end +
mode->crtc_vsync_start - 1);
rcar_du_crtc_write(rcrtc, VCR, mode->crtc_vtotal - 1);

rcar_du_crtc_write(rcrtc, DESR, mode->htotal - mode->hsync_start);
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
Expand All @@ -139,9 +180,10 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
*/
rcrtc->outputs |= BIT(output);

/* Store RGB routing to DPAD0 for R8A7790. */
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_DEFR8) &&
output == RCAR_DU_OUTPUT_DPAD0)
/* Store RGB routing to DPAD0, the hardware will be configured when
* starting the CRTC.
*/
if (output == RCAR_DU_OUTPUT_DPAD0)
rcdu->dpad0_source = rcrtc->index;
}

Expand Down Expand Up @@ -217,6 +259,7 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
{
struct drm_crtc *crtc = &rcrtc->crtc;
bool interlaced;
unsigned int i;

if (rcrtc->started)
Expand Down Expand Up @@ -252,7 +295,10 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
* sync mode (with the HSYNC and VSYNC signals configured as outputs and
* actively driven).
*/
rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER);
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_group_start_stop(rcrtc->group, true);

Expand Down Expand Up @@ -308,6 +354,9 @@ static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);

if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;

if (rcrtc->dpms == mode)
return;

Expand Down Expand Up @@ -486,7 +535,7 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
status = rcar_du_crtc_read(rcrtc, DSSR);
rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK);

if (status & DSSR_VBK) {
if (status & DSSR_FRM) {
drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
rcar_du_crtc_finish_page_flip(rcrtc);
ret = IRQ_HANDLED;
Expand Down Expand Up @@ -542,12 +591,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;
Expand All @@ -561,6 +611,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;
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/rcar-du/rcar_du_crtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct rcar_du_crtc {
struct drm_crtc crtc;

struct clk *clock;
struct clk *extclock;
unsigned int mmio_offset;
unsigned int index;
bool started;
Expand Down
6 changes: 4 additions & 2 deletions drivers/gpu/drm/rcar-du/rcar_du_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = {
};

static const struct rcar_du_device_info rcar_du_r8a7790_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS,
.quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
.num_crtcs = 3,
.routes = {
Expand All @@ -83,7 +84,8 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
};

static const struct rcar_du_device_info rcar_du_r8a7791_info = {
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8,
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
| RCAR_DU_FEATURE_EXT_CTRL_REGS,
.num_crtcs = 2,
.routes = {
/* R8A7791 has one RGB output, one LVDS output and one
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/rcar-du/rcar_du_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct rcar_du_device;
struct rcar_du_lvdsenc;

#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */
#define RCAR_DU_FEATURE_DEFR8 (1 << 1) /* Has DEFR8 register */
#define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */

#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */
#define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */
Expand Down
34 changes: 22 additions & 12 deletions drivers/gpu/drm/rcar-du/rcar_du_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);

if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;

if (renc->lvds)
rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode);
}
Expand Down Expand Up @@ -190,35 +193,42 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
}

if (type == RCAR_DU_ENCODER_HDMI) {
if (renc->lvds) {
dev_err(rcdu->dev,
"Chaining LVDS and HDMI encoders not supported\n");
return -EINVAL;
}

ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
if (ret < 0)
return ret;
goto done;
} else {
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
encoder_type);
if (ret < 0)
return ret;
goto done;

drm_encoder_helper_add(encoder, &encoder_helper_funcs);
}

switch (encoder_type) {
case DRM_MODE_ENCODER_LVDS:
return rcar_du_lvds_connector_init(rcdu, renc, con_node);
ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
break;

case DRM_MODE_ENCODER_DAC:
return rcar_du_vga_connector_init(rcdu, renc);
ret = rcar_du_vga_connector_init(rcdu, renc);
break;

case DRM_MODE_ENCODER_TMDS:
return rcar_du_hdmi_connector_init(rcdu, renc);
ret = rcar_du_hdmi_connector_init(rcdu, renc);
break;

default:
return -EINVAL;
ret = -EINVAL;
break;
}

done:
if (ret < 0) {
if (encoder->name)
encoder->funcs->destroy(encoder);
devm_kfree(rcdu->dev, renc);
}

return ret;
}
21 changes: 17 additions & 4 deletions drivers/gpu/drm/rcar-du/rcar_du_group.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
{
u32 defr8 = DEFR8_CODE | DEFR8_DEFE8;

if (!rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_DEFR8))
return;

/* The DEFR8 register for the first group also controls RGB output
* routing to DPAD0
*/
Expand All @@ -69,7 +66,20 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE);
rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5);

rcar_du_group_setup_defr8(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.
*/
rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
DIDSR_LCDS_DCLKIN(2) |
DIDSR_LCDS_DCLKIN(1) |
DIDSR_LCDS_DCLKIN(0) |
DIDSR_PDCS_CLK(2, 0) |
DIDSR_PDCS_CLK(1, 0) |
DIDSR_PDCS_CLK(0, 0));
}

/* Use DS1PR and DS2PR to configure planes priorities and connects the
* superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
Expand Down Expand Up @@ -149,6 +159,9 @@ static int rcar_du_set_dpad0_routing(struct rcar_du_device *rcdu)
{
int ret;

if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_EXT_CTRL_REGS))
return 0;

/* RGB output routing to DPAD0 is configured in the DEFR8 register of
* the first group. As this function can be called with the DU0 and DU1
* CRTCs disabled, we need to enable the first group clock before
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
connector = &rcon->connector;
connector->display_info.width_mm = 0;
connector->display_info.height_mm = 0;
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;

ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
Expand Down
Loading

0 comments on commit b2eb048

Please sign in to comment.