Commit e947eccb authored by Laurent Pinchart's avatar Laurent Pinchart

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: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
parent bf7149f3
......@@ -98,6 +98,8 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
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);
......@@ -111,6 +113,31 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
}
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 = {
......
......@@ -31,6 +31,7 @@ struct rcar_du_lvdsenc {
bool enabled;
enum rcar_lvds_input input;
enum rcar_lvds_mode mode;
};
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
* bias circuitry on.
*/
lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN;
lvdcr0 = (lvds->mode << LVDCR0_LVMD_SHIFT) | LVDCR0_BEN | LVDCR0_LVEN;
if (rcrtc->index == 2)
lvdcr0 |= LVDCR0_DUSEL;
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
* delay and turn the output on.
*/
lvdcr0 = LVDCR0_PLLON;
lvdcr0 = (lvds->mode << LVDCR0_LVMD_SHIFT) | LVDCR0_PLLON;
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
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);
}
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,
struct platform_device *pdev)
{
......
......@@ -26,8 +26,17 @@ enum rcar_lvds_input {
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)
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,
struct drm_crtc *crtc, bool enable);
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;
}
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,
struct drm_crtc *crtc, bool enable)
{
......
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