drm: rcar-du: Use the DRM panel API

Instead of parsing the panel device tree node manually, use the panel
API to delegate panel handling to a panel driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
This commit is contained in:
Laurent Pinchart 2016-11-18 03:22:37 +02:00
parent 06711e6385
commit bf7149f342
4 changed files with 50 additions and 44 deletions

View File

@ -20,6 +20,7 @@ config DRM_RCAR_HDMI
config DRM_RCAR_LVDS config DRM_RCAR_LVDS
bool "R-Car DU LVDS Encoder Support" bool "R-Car DU LVDS Encoder Support"
depends on DRM_RCAR_DU depends on DRM_RCAR_DU
select DRM_PANEL
help help
Enable support for the R-Car Display Unit embedded LVDS encoders. Enable support for the R-Car Display Unit embedded LVDS encoders.

View File

@ -16,6 +16,7 @@
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_panel.h>
#include "rcar_du_drv.h" #include "rcar_du_drv.h"
#include "rcar_du_encoder.h" #include "rcar_du_encoder.h"
@ -33,6 +34,11 @@ static void rcar_du_encoder_disable(struct drm_encoder *encoder)
{ {
struct rcar_du_encoder *renc = to_rcar_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) if (renc->lvds)
rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false); rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
} }
@ -43,6 +49,11 @@ static void rcar_du_encoder_enable(struct drm_encoder *encoder)
if (renc->lvds) if (renc->lvds)
rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true); 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, static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
@ -89,6 +100,17 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
struct rcar_du_encoder *renc = to_rcar_encoder(encoder); struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
rcar_du_crtc_route_output(crtc_state->crtc, renc->output); 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);
} }
static const struct drm_encoder_helper_funcs encoder_helper_funcs = { static const struct drm_encoder_helper_funcs encoder_helper_funcs = {

View File

@ -17,6 +17,7 @@
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
struct drm_panel;
struct rcar_du_device; struct rcar_du_device;
struct rcar_du_hdmienc; struct rcar_du_hdmienc;
struct rcar_du_lvdsenc; struct rcar_du_lvdsenc;
@ -32,6 +33,7 @@ enum rcar_du_encoder_type {
struct rcar_du_encoder { struct rcar_du_encoder {
struct drm_encoder base; struct drm_encoder base;
enum rcar_du_output output; enum rcar_du_output output;
struct rcar_du_connector *connector;
struct rcar_du_hdmienc *hdmi; struct rcar_du_hdmienc *hdmi;
struct rcar_du_lvdsenc *lvds; struct rcar_du_lvdsenc *lvds;
}; };
@ -44,6 +46,7 @@ struct rcar_du_encoder {
struct rcar_du_connector { struct rcar_du_connector {
struct drm_connector connector; struct drm_connector connector;
struct rcar_du_encoder *encoder; struct rcar_du_encoder *encoder;
struct drm_panel *panel;
}; };
#define to_rcar_connector(c) \ #define to_rcar_connector(c) \

View File

@ -15,6 +15,7 @@
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h> #include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_panel.h>
#include <video/display_timing.h> #include <video/display_timing.h>
#include <video/of_display_timing.h> #include <video/of_display_timing.h>
@ -25,47 +26,30 @@
#include "rcar_du_kms.h" #include "rcar_du_kms.h"
#include "rcar_du_lvdscon.h" #include "rcar_du_lvdscon.h"
struct rcar_du_lvds_connector {
struct rcar_du_connector connector;
struct {
unsigned int width_mm; /* Panel width in mm */
unsigned int height_mm; /* Panel height in mm */
struct videomode mode;
} panel;
};
#define to_rcar_lvds_connector(c) \
container_of(c, struct rcar_du_lvds_connector, connector.connector)
static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
{ {
struct rcar_du_lvds_connector *lvdscon = struct rcar_du_connector *rcon = to_rcar_connector(connector);
to_rcar_lvds_connector(connector);
struct drm_display_mode *mode;
mode = drm_mode_create(connector->dev); return drm_panel_get_modes(rcon->panel);
if (mode == NULL)
return 0;
mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
drm_display_mode_from_videomode(&lvdscon->panel.mode, mode);
drm_mode_probed_add(connector, mode);
return 1;
} }
static const struct drm_connector_helper_funcs connector_helper_funcs = { static const struct drm_connector_helper_funcs connector_helper_funcs = {
.get_modes = rcar_du_lvds_connector_get_modes, .get_modes = rcar_du_lvds_connector_get_modes,
}; };
static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
{
struct rcar_du_connector *rcon = to_rcar_connector(connector);
drm_panel_detach(rcon->panel);
drm_connector_cleanup(connector);
}
static const struct drm_connector_funcs connector_funcs = { static const struct drm_connector_funcs connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.reset = drm_atomic_helper_connector_reset, .reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup, .destroy = rcar_du_lvds_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
}; };
@ -75,27 +59,19 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
const struct device_node *np) const struct device_node *np)
{ {
struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc); struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
struct rcar_du_lvds_connector *lvdscon; struct rcar_du_connector *rcon;
struct drm_connector *connector; struct drm_connector *connector;
struct display_timing timing;
int ret; int ret;
lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL); rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
if (lvdscon == NULL) if (rcon == NULL)
return -ENOMEM; return -ENOMEM;
ret = of_get_display_timing(np, "panel-timing", &timing); connector = &rcon->connector;
if (ret < 0)
return ret;
videomode_from_timing(&timing, &lvdscon->panel.mode); rcon->panel = of_drm_find_panel(np);
if (!rcon->panel)
of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm); return -EPROBE_DEFER;
of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm);
connector = &lvdscon->connector.connector;
connector->display_info.width_mm = lvdscon->panel.width_mm;
connector->display_info.height_mm = lvdscon->panel.height_mm;
ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
DRM_MODE_CONNECTOR_LVDS); DRM_MODE_CONNECTOR_LVDS);
@ -112,7 +88,11 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
if (ret < 0) if (ret < 0)
return ret; return ret;
lvdscon->connector.encoder = renc; ret = drm_panel_attach(rcon->panel, connector);
if (ret < 0)
return ret;
rcon->encoder = renc;
return 0; return 0;
} }