Add support for panels connected on a DPI bus. This implementation includes DRM encoder/connector creation and attachment to the DRM device so that the only thing needed to access such a panel is to implement a DPI host in your driver and define the appropriate node in your DT (under the DPI node). Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxxxxxxxxx> --- drivers/gpu/drm/panel/panel-simple.c | 277 +++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 11bff3f..fd448ca 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -30,7 +30,9 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_mipi_dpi.h> #include <drm/drm_panel.h> struct panel_desc { @@ -851,6 +853,275 @@ static struct mipi_dsi_driver panel_simple_dsi_driver = { .shutdown = panel_simple_dsi_shutdown, }; +struct panel_desc_dpi { + struct panel_desc desc; + + enum video_bus_format format; +}; + +struct panel_dpi { + struct drm_encoder encoder; + struct drm_connector connector; + int dpms_mode; + + struct mipi_dpi_device *dev; +}; + +static const struct of_device_id dpi_of_match[] = { + { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, dsi_of_match); + +static inline struct panel_dpi * +drm_encoder_to_panel_dpi(struct drm_encoder *encoder) +{ + return container_of(encoder, struct panel_dpi, encoder); +} + +static void panel_dpi_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct panel_dpi *dpanel = drm_encoder_to_panel_dpi(encoder); + struct panel_simple *panel = mipi_dpi_get_drvdata(dpanel->dev); + + if (mode != DRM_MODE_DPMS_ON) + mode = DRM_MODE_DPMS_OFF; + + if (mode == dpanel->dpms_mode) + return; + + switch (mode) { + case DRM_MODE_DPMS_ON: + drm_panel_prepare(&panel->base); + drm_panel_enable(&panel->base); + mipi_dpi_enable(dpanel->dev); + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + mipi_dpi_disable(dpanel->dev); + drm_panel_disable(&panel->base); + drm_panel_unprepare(&panel->base); + break; + } + + dpanel->dpms_mode = mode; +} + +static bool panel_dpi_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void panel_dpi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ +} + +static void panel_dpi_encoder_prepare(struct drm_encoder *encoder) +{ + panel_dpi_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void panel_dpi_encoder_commit(struct drm_encoder *encoder) +{ + panel_dpi_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static const struct drm_encoder_helper_funcs panel_dpi_encoder_helper_funcs = { + .dpms = panel_dpi_encoder_dpms, + .mode_fixup = panel_dpi_encoder_mode_fixup, + .mode_set = panel_dpi_encoder_mode_set, + .prepare = panel_dpi_encoder_prepare, + .commit = panel_dpi_encoder_commit, +}; + +static const struct drm_encoder_funcs panel_dpi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static inline struct panel_dpi * +drm_connector_to_panel_dpi(struct drm_connector *connector) +{ + return container_of(connector, struct panel_dpi, connector); +} + +static int panel_dpi_connector_get_modes(struct drm_connector *connector) +{ + struct panel_dpi *dpanel = drm_connector_to_panel_dpi(connector); + struct panel_simple *panel = mipi_dpi_get_drvdata(dpanel->dev); + + return drm_panel_get_modes(&panel->base); +} + +static struct drm_encoder * +panel_dpi_connector_best_encoder(struct drm_connector *connector) +{ + struct panel_dpi *dpanel = drm_connector_to_panel_dpi(connector); + + return &dpanel->encoder; +} + +static int panel_dpi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs panel_dpi_connector_helper_funcs = { + .get_modes = panel_dpi_connector_get_modes, + .mode_valid = panel_dpi_connector_mode_valid, + .best_encoder = panel_dpi_connector_best_encoder, +}; + +static enum drm_connector_status +panel_dpi_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void +panel_dpi_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs panel_dpi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = panel_dpi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = panel_dpi_connector_destroy, +}; + +static int panel_simple_dpi_probe(struct mipi_dpi_device *dpi) +{ + const struct panel_desc_dpi *desc; + const struct of_device_id *id; + struct panel_simple *panel; + struct panel_dpi *dpanel; + int err; + + id = of_match_node(dpi_of_match, dpi->dev.of_node); + if (!id) + return -ENODEV; + + desc = id->data; + + dpanel = devm_kzalloc(&dpi->dev, sizeof(*dpanel), GFP_KERNEL); + if (!dpanel) + return -ENOMEM; + + dpanel->dev = dpi; + + err = panel_simple_probe(&dpi->dev, &desc->desc); + if (err < 0) + return err; + + dpanel->dpms_mode = DRM_MODE_DPMS_OFF; + dpi->supported_formats = &desc->format; + dpi->num_supported_formats = 1; + + err = mipi_dpi_attach(dpi); + if (err) + goto err_panel_remove; + + dpanel->encoder.possible_crtcs = dpi->host->possible_crtcs; + drm_encoder_helper_add(&dpanel->encoder, + &panel_dpi_encoder_helper_funcs); + err = drm_encoder_init(dpi->host->ddev, &dpanel->encoder, + &panel_dpi_encoder_funcs, DRM_MODE_ENCODER_DPI); + if (err) + goto err_dpi_detach; + + dpanel->connector.status = connector_status_connected; + drm_connector_helper_add(&dpanel->connector, + &panel_dpi_connector_helper_funcs); + err = drm_connector_init(dpi->host->ddev, &dpanel->connector, + &panel_dpi_connector_funcs, + DRM_MODE_CONNECTOR_DPI); + if (err) + goto err_encoder_cleanup; + + panel = mipi_dpi_get_drvdata(dpi); + err = drm_panel_attach(&panel->base, &dpanel->connector); + if (err) + goto err_connector_cleanup; + + err = drm_mode_connector_attach_encoder(&dpanel->connector, + &dpanel->encoder); + if (err) + goto err_panel_detach; + + err = drm_connector_register(&dpanel->connector); + if (err) + goto err_panel_detach; + + drm_reinit_primary_mode_group(dpi->host->ddev); + drm_kms_helper_hotplug_event(dpi->host->ddev); + + return 0; + +err_panel_detach: + drm_panel_detach(&panel->base); +err_connector_cleanup: + drm_connector_cleanup(&dpanel->connector); +err_encoder_cleanup: + drm_encoder_cleanup(&dpanel->encoder); +err_dpi_detach: + mipi_dpi_detach(dpi); +err_panel_remove: + panel_simple_remove(&dpi->dev); + + return err; +} + +static int panel_simple_dpi_remove(struct mipi_dpi_device *dpi) +{ + struct panel_simple *panel; + struct panel_dpi *dpanel; + + int err; + + panel = mipi_dpi_get_drvdata(dpi); + dpanel = drm_connector_to_panel_dpi(panel->base.connector); + + drm_connector_unregister(&dpanel->connector); + drm_connector_cleanup(&dpanel->connector); + + drm_panel_detach(&panel->base); + + drm_encoder_cleanup(&dpanel->encoder); + + drm_reinit_primary_mode_group(dpi->host->ddev); + + err = mipi_dpi_detach(dpi); + if (err < 0) + dev_err(&dpi->dev, "failed to detach from DPI host: %d\n", err); + + return panel_simple_remove(&dpi->dev); +} + +static void panel_simple_dpi_shutdown(struct mipi_dpi_device *dpi) +{ + panel_simple_shutdown(&dpi->dev); +} + +static struct mipi_dpi_driver panel_simple_dpi_driver = { + .driver = { + .name = "panel-simple-dpi", + .owner = THIS_MODULE, + .of_match_table = dpi_of_match, + }, + .probe = panel_simple_dpi_probe, + .remove = panel_simple_dpi_remove, + .shutdown = panel_simple_dpi_shutdown, +}; + static int __init panel_simple_init(void) { int err; @@ -865,6 +1136,12 @@ static int __init panel_simple_init(void) return err; } + if (IS_ENABLED(CONFIG_DRM_MIPI_DPI)) { + err = mipi_dpi_driver_register(&panel_simple_dpi_driver); + if (err < 0) + return err; + } + return 0; } module_init(panel_simple_init); -- 1.9.1 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel