drm: rcar-du: Add support for LVDS mode selection

Retrieve the LVDS mode from the panel and configure the LVDS encoder
accordingly. LVDS mode selection is static as LVDS panels can't be
hot-plugged on any of the device supported by the driver. Support for
dynamic mode selection can be implemented in the future when needed.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
This commit is contained in:
Laurent Pinchart 2016-10-03 13:07:02 +03:00
parent bf7149f342
commit e947eccbeb
3 changed files with 49 additions and 2 deletions

View File

@ -98,6 +98,8 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 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); rcar_du_crtc_route_output(crtc_state->crtc, renc->output);
@ -111,6 +113,31 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
} }
renc->connector = to_rcar_connector(conn_state->connector); 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 = { static const struct drm_encoder_helper_funcs encoder_helper_funcs = {

View File

@ -31,6 +31,7 @@ struct rcar_du_lvdsenc {
bool enabled; bool enabled;
enum rcar_lvds_input input; enum rcar_lvds_input input;
enum rcar_lvds_mode mode;
}; };
static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
@ -61,7 +62,7 @@ static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds,
/* Select the input, hardcode mode 0, enable LVDS operation and turn /* Select the input, hardcode mode 0, enable LVDS operation and turn
* bias circuitry on. * bias circuitry on.
*/ */
lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; lvdcr0 = (lvds->mode << LVDCR0_LVMD_SHIFT) | LVDCR0_BEN | LVDCR0_LVEN;
if (rcrtc->index == 2) if (rcrtc->index == 2)
lvdcr0 |= LVDCR0_DUSEL; lvdcr0 |= LVDCR0_DUSEL;
rcar_lvds_write(lvds, LVDCR0, lvdcr0); rcar_lvds_write(lvds, LVDCR0, lvdcr0);
@ -114,7 +115,7 @@ static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds,
* Turn the PLL on, set it to LVDS normal mode, wait for the startup * Turn the PLL on, set it to LVDS normal mode, wait for the startup
* delay and turn the output on. * delay and turn the output on.
*/ */
lvdcr0 = LVDCR0_PLLON; lvdcr0 = (lvds->mode << LVDCR0_LVMD_SHIFT) | LVDCR0_PLLON;
rcar_lvds_write(lvds, LVDCR0, lvdcr0); rcar_lvds_write(lvds, LVDCR0, lvdcr0);
lvdcr0 |= LVDCR0_PWD; lvdcr0 |= LVDCR0_PWD;
@ -211,6 +212,12 @@ void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
mode->clock = clamp(mode->clock, 25175, 148500); mode->clock = clamp(mode->clock, 25175, 148500);
} }
void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
enum rcar_lvds_mode mode)
{
lvds->mode = mode;
}
static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,
struct platform_device *pdev) struct platform_device *pdev)
{ {

View File

@ -26,8 +26,17 @@ enum rcar_lvds_input {
RCAR_LVDS_INPUT_DU2, RCAR_LVDS_INPUT_DU2,
}; };
/* Keep in sync with the LVDCR0.LVMD hardware register values. */
enum rcar_lvds_mode {
RCAR_LVDS_MODE_JEIDA = 0,
RCAR_LVDS_MODE_MIRROR = 1,
RCAR_LVDS_MODE_VESA = 4,
};
#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) #if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu);
void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
enum rcar_lvds_mode mode);
int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
struct drm_crtc *crtc, bool enable); struct drm_crtc *crtc, bool enable);
void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
@ -37,6 +46,10 @@ static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
{ {
return 0; return 0;
} }
static inline void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
enum rcar_lvds_mode mode)
{
}
static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
struct drm_crtc *crtc, bool enable) struct drm_crtc *crtc, bool enable)
{ {