Skip to content

Commit

Permalink
drm: rcar-du: Convert LVDS encoder code to bridge driver
Browse files Browse the repository at this point in the history
The LVDS encoders used to be described in DT as part of the DU. They now
have their own DT node, linked to the DU using the OF graph bindings.
This allows moving internal LVDS encoder support to a separate driver
modelled as a DRM bridge. Backward compatibility is retained as legacy
DT is patched live to move to the new bindings.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
  • Loading branch information
Laurent Pinchart committed Mar 7, 2018
1 parent 81c0e3d commit c6a27fa
Show file tree
Hide file tree
Showing 12 changed files with 561 additions and 616 deletions.
4 changes: 2 additions & 2 deletions drivers/gpu/drm/rcar-du/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ config DRM_RCAR_DW_HDMI
Enable support for R-Car Gen3 internal HDMI encoder.

config DRM_RCAR_LVDS
bool "R-Car DU LVDS Encoder Support"
depends on DRM_RCAR_DU
tristate "R-Car DU LVDS Encoder Support"
depends on DRM && DRM_BRIDGE && OF
select DRM_PANEL
select OF_FLATTREE
select OF_OVERLAY
Expand Down
3 changes: 1 addition & 2 deletions drivers/gpu/drm/rcar-du/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ rcar-du-drm-y := rcar_du_crtc.o \
rcar_du_encoder.o \
rcar_du_group.o \
rcar_du_kms.o \
rcar_du_lvdscon.o \
rcar_du_plane.o

rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o
rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
rcar_du_of_lvds_r8a7790.dtb.o \
rcar_du_of_lvds_r8a7791.dtb.o \
Expand All @@ -18,3 +16,4 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o

obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
21 changes: 15 additions & 6 deletions drivers/gpu/drm/rcar-du/rcar_du_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "rcar_du_drv.h"
#include "rcar_du_kms.h"
#include "rcar_du_of.h"
#include "rcar_du_regs.h"

/* -----------------------------------------------------------------------------
Expand Down Expand Up @@ -74,7 +75,6 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
.port = 1,
},
},
.num_lvds = 0,
};

static const struct rcar_du_device_info rcar_du_r8a7779_info = {
Expand All @@ -95,14 +95,13 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = {
.port = 1,
},
},
.num_lvds = 0,
};

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,
.quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES,
.quirks = RCAR_DU_QUIRK_ALIGN_128B,
.num_crtcs = 3,
.routes = {
/*
Expand Down Expand Up @@ -164,7 +163,6 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = {
.port = 1,
},
},
.num_lvds = 0,
};

static const struct rcar_du_device_info rcar_du_r8a7794_info = {
Expand All @@ -186,7 +184,6 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
.port = 1,
},
},
.num_lvds = 0,
};

static const struct rcar_du_device_info rcar_du_r8a7795_info = {
Expand Down Expand Up @@ -434,7 +431,19 @@ static struct platform_driver rcar_du_platform_driver = {
},
};

module_platform_driver(rcar_du_platform_driver);
static int __init rcar_du_init(void)
{
rcar_du_of_init(rcar_du_of_table);

return platform_driver_register(&rcar_du_platform_driver);
}
module_init(rcar_du_init);

static void __exit rcar_du_exit(void)
{
platform_driver_unregister(&rcar_du_platform_driver);
}
module_exit(rcar_du_exit);

MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
Expand Down
5 changes: 0 additions & 5 deletions drivers/gpu/drm/rcar-du/rcar_du_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,12 @@ struct device;
struct drm_device;
struct drm_fbdev_cma;
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_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */
#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */

#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 */

/*
* struct rcar_du_output_routing - Output routing specification
Expand Down Expand Up @@ -70,7 +68,6 @@ struct rcar_du_device_info {

#define RCAR_DU_MAX_CRTCS 4
#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
#define RCAR_DU_MAX_LVDS 2
#define RCAR_DU_MAX_VSPS 4

struct rcar_du_device {
Expand All @@ -96,8 +93,6 @@ struct rcar_du_device {

unsigned int dpad0_source;
unsigned int vspd1_sink;

struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
};

static inline bool rcar_du_has(struct rcar_du_device *rcdu,
Expand Down
175 changes: 15 additions & 160 deletions drivers/gpu/drm/rcar-du/rcar_du_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,134 +21,22 @@
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
#include "rcar_du_kms.h"
#include "rcar_du_lvdscon.h"
#include "rcar_du_lvdsenc.h"

/* -----------------------------------------------------------------------------
* Encoder
*/

static void rcar_du_encoder_disable(struct drm_encoder *encoder)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);

if (renc->connector && renc->connector->panel) {
drm_panel_disable(renc->connector->panel);
drm_panel_unprepare(renc->connector->panel);
}

if (renc->lvds)
rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
}

static void rcar_du_encoder_enable(struct drm_encoder *encoder)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);

if (renc->lvds)
rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);

if (renc->connector && renc->connector->panel) {
drm_panel_prepare(renc->connector->panel);
drm_panel_enable(renc->connector->panel);
}
}

static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
const struct drm_display_mode *mode = &crtc_state->mode;
struct drm_connector *connector = conn_state->connector;
struct drm_device *dev = encoder->dev;

/*
* Only panel-related encoder types require validation here, everything
* else is handled by the bridge drivers.
*/
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
const struct drm_display_mode *panel_mode;

if (list_empty(&connector->modes)) {
dev_dbg(dev->dev, "encoder: empty modes list\n");
return -EINVAL;
}

panel_mode = list_first_entry(&connector->modes,
struct drm_display_mode, head);

/* We're not allowed to modify the resolution. */
if (mode->hdisplay != panel_mode->hdisplay ||
mode->vdisplay != panel_mode->vdisplay)
return -EINVAL;

/*
* The flat panel mode is fixed, just copy it to the adjusted
* mode.
*/
drm_mode_copy(adjusted_mode, panel_mode);
}

if (renc->lvds)
rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode);

return 0;
}

static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
struct drm_display_info *info = &conn_state->connector->display_info;
enum rcar_lvds_mode mode;

rcar_du_crtc_route_output(crtc_state->crtc, renc->output);

if (!renc->lvds) {
/*
* The DU driver creates connectors only for the outputs of the
* internal LVDS encoders.
*/
renc->connector = NULL;
return;
}

renc->connector = to_rcar_connector(conn_state->connector);

if (!info->num_bus_formats || !info->bus_formats) {
dev_err(encoder->dev->dev, "no LVDS bus format reported\n");
return;
}

switch (info->bus_formats[0]) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
mode = RCAR_LVDS_MODE_JEIDA;
break;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
mode = RCAR_LVDS_MODE_VESA;
break;
default:
dev_err(encoder->dev->dev,
"unsupported LVDS bus format 0x%04x\n",
info->bus_formats[0]);
return;
}

if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
mode |= RCAR_LVDS_MODE_MIRROR;

rcar_du_lvdsenc_set_mode(renc->lvds, mode);
}

static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.atomic_mode_set = rcar_du_encoder_mode_set,
.disable = rcar_du_encoder_disable,
.enable = rcar_du_encoder_enable,
.atomic_check = rcar_du_encoder_atomic_check,
};

static const struct drm_encoder_funcs encoder_funcs = {
Expand All @@ -172,33 +60,14 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
renc->output = output;
encoder = rcar_encoder_to_drm_encoder(renc);

switch (output) {
case RCAR_DU_OUTPUT_LVDS0:
renc->lvds = rcdu->lvds[0];
break;
dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
enc_node, output);

case RCAR_DU_OUTPUT_LVDS1:
renc->lvds = rcdu->lvds[1];
break;

default:
break;
}

if (enc_node) {
dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
enc_node, output);

/* Locate the DRM bridge from the encoder DT node. */
bridge = of_drm_find_bridge(enc_node);
if (!bridge) {
ret = -EPROBE_DEFER;
goto done;
}
} else {
dev_dbg(rcdu->dev,
"initializing internal encoder for output %u\n",
output);
/* Locate the DRM bridge from the encoder DT node. */
bridge = of_drm_find_bridge(enc_node);
if (!bridge) {
ret = -EPROBE_DEFER;
goto done;
}

ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
Expand All @@ -208,28 +77,14 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,

drm_encoder_helper_add(encoder, &encoder_helper_funcs);

if (bridge) {
/*
* Attach the bridge to the encoder. The bridge will create the
* connector.
*/
ret = drm_bridge_attach(encoder, bridge, NULL);
if (ret) {
drm_encoder_cleanup(encoder);
return ret;
}
} else {
/* There's no bridge, create the connector manually. */
switch (output) {
case RCAR_DU_OUTPUT_LVDS0:
case RCAR_DU_OUTPUT_LVDS1:
ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
break;

default:
ret = -EINVAL;
break;
}
/*
* Attach the bridge to the encoder. The bridge will create the
* connector.
*/
ret = drm_bridge_attach(encoder, bridge, NULL);
if (ret) {
drm_encoder_cleanup(encoder);
return ret;
}

done:
Expand Down
12 changes: 0 additions & 12 deletions drivers/gpu/drm/rcar-du/rcar_du_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,17 @@

struct drm_panel;
struct rcar_du_device;
struct rcar_du_lvdsenc;

struct rcar_du_encoder {
struct drm_encoder base;
enum rcar_du_output output;
struct rcar_du_connector *connector;
struct rcar_du_lvdsenc *lvds;
};

#define to_rcar_encoder(e) \
container_of(e, struct rcar_du_encoder, base)

#define rcar_encoder_to_drm_encoder(e) (&(e)->base)

struct rcar_du_connector {
struct drm_connector connector;
struct rcar_du_encoder *encoder;
struct drm_panel *panel;
};

#define to_rcar_connector(c) \
container_of(c, struct rcar_du_connector, connector)

int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_output output,
struct device_node *enc_node,
Expand Down
Loading

0 comments on commit c6a27fa

Please sign in to comment.