Commit b2eb0489 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm/next/du' of git://linuxtv.org/pinchartl/fbdev into 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
parents 4f4d89af 906eff7f
...@@ -26,6 +26,10 @@ Required Properties: ...@@ -26,6 +26,10 @@ Required Properties:
per LVDS encoder. The functional clocks must be named "du.x" with "x" per LVDS encoder. The functional clocks must be named "du.x" with "x"
being the channel numerical index. The LVDS clocks must be named being the channel numerical index. The LVDS clocks must be named
"lvds.x" with "x" being the LVDS encoder numerical index. "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: Required nodes:
......
...@@ -644,9 +644,6 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder, ...@@ -644,9 +644,6 @@ static int adv7511_encoder_mode_valid(struct drm_encoder *encoder,
if (mode->clock > 165000) if (mode->clock > 165000)
return MODE_CLOCK_HIGH; return MODE_CLOCK_HIGH;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
return MODE_OK; return MODE_OK;
} }
......
...@@ -74,39 +74,77 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) ...@@ -74,39 +74,77 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = clk_prepare_enable(rcrtc->extclock);
if (ret < 0)
goto error_clock;
ret = rcar_du_group_get(rcrtc->group); ret = rcar_du_group_get(rcrtc->group);
if (ret < 0) 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; return ret;
} }
static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
{ {
rcar_du_group_put(rcrtc->group); rcar_du_group_put(rcrtc->group);
clk_disable_unprepare(rcrtc->extclock);
clk_disable_unprepare(rcrtc->clock); clk_disable_unprepare(rcrtc->clock);
} }
static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
{ {
const struct drm_display_mode *mode = &rcrtc->crtc.mode; const struct drm_display_mode *mode = &rcrtc->crtc.mode;
unsigned long mode_clock = mode->clock * 1000;
unsigned long clk; unsigned long clk;
u32 value; u32 value;
u32 escr;
u32 div; 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); 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; 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, 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); rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
/* Signal polarities */ /* Signal polarities */
value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL) value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL) | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
| DSMR_DIPM_DE; | DSMR_DIPM_DE | DSMR_CSPM;
rcar_du_crtc_write(rcrtc, DSMR, value); rcar_du_crtc_write(rcrtc, DSMR, value);
/* Display timings */ /* Display timings */
...@@ -117,12 +155,15 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) ...@@ -117,12 +155,15 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
mode->hsync_start - 1); mode->hsync_start - 1);
rcar_du_crtc_write(rcrtc, HCR, mode->htotal - 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, VDSR, mode->crtc_vtotal -
rcar_du_crtc_write(rcrtc, VDER, mode->vtotal - mode->vsync_end + mode->crtc_vsync_end - 2);
mode->vdisplay - 2); rcar_du_crtc_write(rcrtc, VDER, mode->crtc_vtotal -
rcar_du_crtc_write(rcrtc, VSPR, mode->vtotal - mode->vsync_end + mode->crtc_vsync_end +
mode->vsync_start - 1); mode->crtc_vdisplay - 2);
rcar_du_crtc_write(rcrtc, VCR, mode->vtotal - 1); 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, DESR, mode->htotal - mode->hsync_start);
rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay);
...@@ -139,9 +180,10 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, ...@@ -139,9 +180,10 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
*/ */
rcrtc->outputs |= BIT(output); rcrtc->outputs |= BIT(output);
/* Store RGB routing to DPAD0 for R8A7790. */ /* Store RGB routing to DPAD0, the hardware will be configured when
if (rcar_du_has(rcdu, RCAR_DU_FEATURE_DEFR8) && * starting the CRTC.
output == RCAR_DU_OUTPUT_DPAD0) */
if (output == RCAR_DU_OUTPUT_DPAD0)
rcdu->dpad0_source = rcrtc->index; rcdu->dpad0_source = rcrtc->index;
} }
...@@ -217,6 +259,7 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) ...@@ -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) static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
{ {
struct drm_crtc *crtc = &rcrtc->crtc; struct drm_crtc *crtc = &rcrtc->crtc;
bool interlaced;
unsigned int i; unsigned int i;
if (rcrtc->started) if (rcrtc->started)
...@@ -252,7 +295,10 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) ...@@ -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 * sync mode (with the HSYNC and VSYNC signals configured as outputs and
* actively driven). * 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); rcar_du_group_start_stop(rcrtc->group, true);
...@@ -308,6 +354,9 @@ static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) ...@@ -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); struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (rcrtc->dpms == mode) if (rcrtc->dpms == mode)
return; return;
...@@ -486,7 +535,7 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) ...@@ -486,7 +535,7 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
status = rcar_du_crtc_read(rcrtc, DSSR); status = rcar_du_crtc_read(rcrtc, DSSR);
rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); 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); drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index);
rcar_du_crtc_finish_page_flip(rcrtc); rcar_du_crtc_finish_page_flip(rcrtc);
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
...@@ -542,12 +591,13 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) ...@@ -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 rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
struct drm_crtc *crtc = &rcrtc->crtc; struct drm_crtc *crtc = &rcrtc->crtc;
unsigned int irqflags; unsigned int irqflags;
char clk_name[5]; struct clk *clk;
char clk_name[9];
char *name; char *name;
int irq; int irq;
int ret; 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)) { if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
sprintf(clk_name, "du.%u", index); sprintf(clk_name, "du.%u", index);
name = clk_name; name = clk_name;
...@@ -561,6 +611,15 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) ...@@ -561,6 +611,15 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
return PTR_ERR(rcrtc->clock); 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->group = rgrp;
rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->mmio_offset = mmio_offsets[index];
rcrtc->index = index; rcrtc->index = index;
......
...@@ -26,6 +26,7 @@ struct rcar_du_crtc { ...@@ -26,6 +26,7 @@ struct rcar_du_crtc {
struct drm_crtc crtc; struct drm_crtc crtc;
struct clk *clock; struct clk *clock;
struct clk *extclock;
unsigned int mmio_offset; unsigned int mmio_offset;
unsigned int index; unsigned int index;
bool started; bool started;
......
...@@ -56,7 +56,8 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { ...@@ -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 = { 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, .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
.num_crtcs = 3, .num_crtcs = 3,
.routes = { .routes = {
...@@ -83,7 +84,8 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { ...@@ -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 = { 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, .num_crtcs = 2,
.routes = { .routes = {
/* R8A7791 has one RGB output, one LVDS output and one /* R8A7791 has one RGB output, one LVDS output and one
......
...@@ -27,7 +27,7 @@ struct rcar_du_device; ...@@ -27,7 +27,7 @@ struct rcar_du_device;
struct rcar_du_lvdsenc; struct rcar_du_lvdsenc;
#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ #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_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */
#define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */ #define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */
......
...@@ -46,6 +46,9 @@ static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) ...@@ -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); struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (renc->lvds) if (renc->lvds)
rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode); rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode);
} }
...@@ -190,35 +193,42 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, ...@@ -190,35 +193,42 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
} }
if (type == RCAR_DU_ENCODER_HDMI) { 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); ret = rcar_du_hdmienc_init(rcdu, renc, enc_node);
if (ret < 0) if (ret < 0)
return ret; goto done;
} else { } else {
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
encoder_type); encoder_type);
if (ret < 0) if (ret < 0)
return ret; goto done;
drm_encoder_helper_add(encoder, &encoder_helper_funcs); drm_encoder_helper_add(encoder, &encoder_helper_funcs);
} }
switch (encoder_type) { switch (encoder_type) {
case DRM_MODE_ENCODER_LVDS: 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: 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: case DRM_MODE_ENCODER_TMDS:
return rcar_du_hdmi_connector_init(rcdu, renc); ret = rcar_du_hdmi_connector_init(rcdu, renc);
break;
default: default:
return -EINVAL; ret = -EINVAL;
break;
}
done:
if (ret < 0) {
if (encoder->name)
encoder->funcs->destroy(encoder);
devm_kfree(rcdu->dev, renc);
} }
return ret;
} }
...@@ -48,9 +48,6 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) ...@@ -48,9 +48,6 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
{ {
u32 defr8 = DEFR8_CODE | DEFR8_DEFE8; 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 /* The DEFR8 register for the first group also controls RGB output
* routing to DPAD0 * routing to DPAD0
*/ */
...@@ -69,8 +66,21 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) ...@@ -69,8 +66,21 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE);
rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) {
rcar_du_group_setup_defr8(rgrp); 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 /* Use DS1PR and DS2PR to configure planes priorities and connects the
* superposition 0 to DU0 pins. DU1 pins will be configured dynamically. * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
*/ */
...@@ -149,6 +159,9 @@ static int rcar_du_set_dpad0_routing(struct rcar_du_device *rcdu) ...@@ -149,6 +159,9 @@ static int rcar_du_set_dpad0_routing(struct rcar_du_device *rcdu)
{ {
int ret; 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 /* 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 * 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 * CRTCs disabled, we need to enable the first group clock before
......
...@@ -95,6 +95,8 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, ...@@ -95,6 +95,8 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
connector = &rcon->connector; connector = &rcon->connector;
connector->display_info.width_mm = 0; connector->display_info.width_mm = 0;
connector->display_info.height_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, ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
DRM_MODE_CONNECTOR_HDMIA); DRM_MODE_CONNECTOR_HDMIA);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "rcar_du_drv.h" #include "rcar_du_drv.h"
#include "rcar_du_encoder.h" #include "rcar_du_encoder.h"
#include "rcar_du_hdmienc.h" #include "rcar_du_hdmienc.h"
#include "rcar_du_lvdsenc.h"
struct rcar_du_hdmienc { struct rcar_du_hdmienc {
struct rcar_du_encoder *renc; struct rcar_du_encoder *renc;
...@@ -36,12 +37,21 @@ static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode) ...@@ -36,12 +37,21 @@ static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode)
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder); struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (hdmienc->dpms == mode) if (hdmienc->dpms == mode)
return; return;
if (mode == DRM_MODE_DPMS_ON && hdmienc->renc->lvds)
rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode);
if (sfuncs->dpms) if (sfuncs->dpms)
sfuncs->dpms(encoder, mode); sfuncs->dpms(encoder, mode);
if (mode != DRM_MODE_DPMS_ON && hdmienc->renc->lvds)
rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode);
hdmienc->dpms = mode; hdmienc->dpms = mode;
} }
...@@ -49,8 +59,16 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder, ...@@ -49,8 +59,16 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
{ {
struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder); struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
/* The internal LVDS encoder has a clock frequency operating range of
* 30MHz to 150MHz. Clamp the clock accordingly.
*/
if (hdmienc->renc->lvds)
adjusted_mode->clock = clamp(adjusted_mode->clock,
30000, 150000);
if (sfuncs->mode_fixup == NULL) if (sfuncs->mode_fixup == NULL)
return true; return true;
......
...@@ -346,10 +346,16 @@ static int rcar_du_encoders_init(struct rcar_du_device *rcdu) ...@@ -346,10 +346,16 @@ static int rcar_du_encoders_init(struct rcar_du_device *rcdu)
/* Process the output pipeline. */ /* Process the output pipeline. */
ret = rcar_du_encoders_init_one(rcdu, output, &ep); ret = rcar_du_encoders_init_one(rcdu, output, &ep);
if (ret < 0) { if (ret < 0) {
if (ret == -EPROBE_DEFER) {
of_node_put(ep_node); of_node_put(ep_node);
return ret; return ret;
} }
dev_info(rcdu->dev,
"encoder initialization failed, skipping\n");
continue;
}
num_encoders += ret; num_encoders += ret;
} }
...@@ -413,6 +419,11 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) ...@@ -413,6 +419,11 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (ret == 0) {
dev_err(rcdu->dev, "error: no encoder could be initialized\n");
return -EINVAL;
}
num_encoders = ret; num_encoders = ret;
/* Set the possible CRTCs and possible clones. There's always at least /* Set the possible CRTCs and possible clones. There's always at least
......
...@@ -104,14 +104,22 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) ...@@ -104,14 +104,22 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane)
{ {
struct rcar_du_group *rgrp = plane->group; struct rcar_du_group *rgrp = plane->group;
unsigned int index = plane->hwindex; unsigned int index = plane->hwindex;
bool interlaced;
u32 mwr; u32 mwr;
/* Memory pitch (expressed in pixels) */ interlaced = plane->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE;
/* Memory pitch (expressed in pixels). Must be doubled for interlaced
* operation with 32bpp formats.
*/
if (plane->format->planes == 2) if (plane->format->planes == 2)
mwr = plane->pitch; mwr = plane->pitch;
else else
mwr = plane->pitch * 8 / plane->format->bpp; mwr = plane->pitch * 8 / plane->format->bpp;
if (interlaced && plane->format->bpp == 32)
mwr *= 2;
rcar_du_plane_write(rgrp, index, PnMWR, mwr); rcar_du_plane_write(rgrp, index, PnMWR, mwr);
/* The Y position is expressed in raster line units and must be doubled /* The Y position is expressed in raster line units and must be doubled
...@@ -119,17 +127,23 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane) ...@@ -119,17 +127,23 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane)
* doubling the Y position is found in the R8A7779 datasheet, but the * doubling the Y position is found in the R8A7779 datasheet, but the
* rule seems to apply there as well. * rule seems to apply there as well.
* *
* Despite not being documented, doubling seem not to be needed when
* operating in interlaced mode.
*
* Similarly, for the second plane, NV12 and NV21 formats seem to * Similarly, for the second plane, NV12 and NV21 formats seem to
* require a halved Y position value. * require a halved Y position value, in both progressive and interlaced
* modes.
*/ */
rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x);
rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y *
(plane->format->bpp == 32 ? 2 : 1)); (!interlaced && plane->format->bpp == 32 ? 2 : 1));
rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]); rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]);
if (plane->format->planes == 2) { if (plane->format->planes == 2) {
index = (index + 1) % 8; index = (index + 1) % 8;
rcar_du_plane_write(rgrp, index, PnMWR, plane->pitch);
rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x);
rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y *
(plane->format->bpp == 16 ? 2 : 1) / 2); (plane->format->bpp == 16 ? 2 : 1) / 2);
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#define DSYSR_SCM_INT_NONE (0 << 4) #define DSYSR_SCM_INT_NONE (0 << 4)
#define DSYSR_SCM_INT_SYNC (2 << 4) #define DSYSR_SCM_INT_SYNC (2 << 4)
#define DSYSR_SCM_INT_VIDEO (3 << 4) #define DSYSR_SCM_INT_VIDEO (3 << 4)
#define DSYSR_SCM_MASK (3 << 4)
#define DSMR 0x00004 #define DSMR 0x00004
#define DSMR_VSPM (1 << 28) #define DSMR_VSPM (1 << 28)
...@@ -256,8 +257,8 @@ ...@@ -256,8 +257,8 @@
#define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2)) #define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2))
#define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2)) #define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2))
#define DIDSR_LCDS_MASK(n) (3 << (8 + (n) * 2)) #define DIDSR_LCDS_MASK(n) (3 << (8 + (n) * 2))
#define DIDSR_PCDS_CLK(n, clk) (clk << ((n) * 2)) #define DIDSR_PDCS_CLK(n, clk) (clk << ((n) * 2))
#define DIDSR_PCDS_MASK(n) (3 << ((n) * 2)) #define DIDSR_PDCS_MASK(n) (3 << ((n) * 2))
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* Display Timing Generation Registers * Display Timing Generation Registers
......
...@@ -64,6 +64,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, ...@@ -64,6 +64,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
connector = &rcon->connector; connector = &rcon->connector;
connector->display_info.width_mm = 0; connector->display_info.width_mm = 0;
connector->display_info.height_mm = 0; connector->display_info.height_mm = 0;
connector->interlace_allowed = true;
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
DRM_MODE_CONNECTOR_VGA); DRM_MODE_CONNECTOR_VGA);
......
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