From patchwork Mon Oct 6 15:37:49 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris BREZILLON X-Patchwork-Id: 5038241 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 0C3709F2F1 for ; Mon, 6 Oct 2014 15:38:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D14952017D for ; Mon, 6 Oct 2014 15:38:09 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id B7B6520149 for ; Mon, 6 Oct 2014 15:38:08 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6D52F6E3D2; Mon, 6 Oct 2014 08:38:05 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail.free-electrons.com (top.free-electrons.com [176.31.233.9]) by gabe.freedesktop.org (Postfix) with ESMTP id 94BE96E3C8 for ; Mon, 6 Oct 2014 08:38:03 -0700 (PDT) Received: by mail.free-electrons.com (Postfix, from userid 106) id 788A48A0; Mon, 6 Oct 2014 17:38:08 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-2.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY, URIBL_RHS_DOB autolearn=ham version=3.3.1 Received: from localhost.localdomain (col31-4-88-188-83-94.fbx.proxad.net [88.188.83.94]) by mail.free-electrons.com (Postfix) with ESMTPSA id B09B088C; Mon, 6 Oct 2014 17:38:07 +0200 (CEST) From: Boris Brezillon To: David Airlie , dri-devel@lists.freedesktop.org Subject: [RFC 3/7] drm: support panels connected on a DPI bus Date: Mon, 6 Oct 2014 17:37:49 +0200 Message-Id: <1412609873-1894-4-git-send-email-boris.brezillon@free-electrons.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1412609873-1894-1-git-send-email-boris.brezillon@free-electrons.com> References: <1412609873-1894-1-git-send-email-boris.brezillon@free-electrons.com> Cc: Laurent Pinchart X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 #include +#include #include +#include #include 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);