From patchwork Tue Apr 22 14:42:21 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thierry Reding X-Patchwork-Id: 4032991 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8D1FDBFF02 for ; Tue, 22 Apr 2014 14:44:04 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 96B35201D3 for ; Tue, 22 Apr 2014 14:44:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7F6732021B for ; Tue, 22 Apr 2014 14:43:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932642AbaDVOnq (ORCPT ); Tue, 22 Apr 2014 10:43:46 -0400 Received: from mail-ee0-f46.google.com ([74.125.83.46]:47692 "EHLO mail-ee0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932572AbaDVOnp (ORCPT ); Tue, 22 Apr 2014 10:43:45 -0400 Received: by mail-ee0-f46.google.com with SMTP id t10so4737636eei.33 for ; Tue, 22 Apr 2014 07:43:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=5Bg+31R6yDRf00W7v0VERp4nnWJUKC3XraLpZ4aOCes=; b=vYS6FpomUqvAsiUIblouxVq4AdYWWRCKJ9eNppwkKNAc4aKgBA7/Q5bavFg7bbmUWX HvqSvpG9/c1aax34WO7O7Cu8I/H3hgI5HwlrOBazZQI+xnvwJX5gw99YRhoZS5gLl4xA CYqpl6XxTqNjptU3bs53xsru2aUeUKGhhcyOqOI9rHNqWyvKpZt2qJtUeAaFnNHfRhpK Zt1Wpzwacf2YmZ4+TjQor9X0BVcamdMTRfdo16wkLbIbqy0zz8CmkovKCR3DLQO7OtoZ /c/WrZepq9SZKD/PWigFF5bFD8VwC5cD6FB1KP9J10et4CziT5pfajRdlsFvnZaTOwMz xw8A== X-Received: by 10.15.51.1 with SMTP id m1mr56819349eew.25.1398177824152; Tue, 22 Apr 2014 07:43:44 -0700 (PDT) Received: from localhost (port-21188.pppoe.wtnet.de. [46.59.144.109]) by mx.google.com with ESMTPSA id x46sm113028719een.17.2014.04.22.07.43.42 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 22 Apr 2014 07:43:43 -0700 (PDT) From: Thierry Reding To: dri-devel@lists.freedesktop.org Cc: Daniel Vetter , Imre Deak , linux-arm-kernel@lists.infradead.org, linux-samsung-soc@vger.kernel.org, intel-gfx@lists.freedesktop.org, linux-tegra@vger.kernel.org Subject: [PATCH 4/4] drm/tegra: Implement race-free hotplug detection Date: Tue, 22 Apr 2014 16:42:21 +0200 Message-Id: <1398177741-28482-4-git-send-email-thierry.reding@gmail.com> X-Mailer: git-send-email 1.9.2 In-Reply-To: <1398177741-28482-1-git-send-email-thierry.reding@gmail.com> References: <1398177741-28482-1-git-send-email-thierry.reding@gmail.com> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Thierry Reding A race condition currently exists on Tegra, where it can happen that a monitor attached via HDMI isn't detected during the initial FB helper setup, but the hotplug event happens too early to be processed by the poll helpers because they haven't been initialized yet. This happens because on some boards the HDMI driver can control the regulator that supplies the +5V pin on the HDMI connector. Therefore depending on the timing between the initialization of the HDMI driver and the rest of DRM, it's possible that the monitor returns the hotplug signal right within the window where we would miss it. Unfortunately, drm_kms_helper_poll_init() will wreak havoc when called before at least some parts of the FB helpers have been set up. This commit fixes this by splitting out the minimum of initialization required to make drm_kms_helper_poll_init() work into a separate function that can be called early. It is then safe to move all of the poll helper initialization to an earlier point in time (before the HDMI output driver has a chance to enable the +5V supply). That way if the hotplug signal is returned before the initial FB helper setup, the monitor will be forcefully detected at that point, and if the hotplug signal is returned after that it will be properly handled by the poll helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 8 ++++++-- drivers/gpu/drm/tegra/drm.h | 1 + drivers/gpu/drm/tegra/fb.c | 47 ++++++++++++++++++++++++++++++--------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 6f5b6e2f552e..4d36debe3de6 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -41,6 +41,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm_mode_config_init(drm); + err = tegra_drm_fb_prepare(drm); + if (err < 0) + return err; + + drm_kms_helper_poll_init(drm); + err = host1x_device_init(device); if (err < 0) return err; @@ -60,8 +66,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (err < 0) return err; - drm_kms_helper_poll_init(drm); - return 0; } diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 126332c3ecbb..c2d9705de1f9 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -288,6 +288,7 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, unsigned int index); bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer); +int tegra_drm_fb_prepare(struct drm_device *drm); extern int tegra_drm_fb_init(struct drm_device *drm); extern void tegra_drm_fb_exit(struct drm_device *drm); #ifdef CONFIG_DRM_TEGRA_FBDEV diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 2e3758542c89..70d0e07d353c 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -271,13 +271,9 @@ static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { .fb_probe = tegra_fbdev_probe, }; -static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, - unsigned int preferred_bpp, - unsigned int num_crtc, - unsigned int max_connectors) +static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm) { struct tegra_fbdev *fbdev; - int err; fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); if (!fbdev) { @@ -287,10 +283,21 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); + return fbdev; +} + +static int tegra_fbdev_init(struct tegra_fbdev *fbdev, + unsigned int preferred_bpp, + unsigned int num_crtc, + unsigned int max_connectors) +{ + struct drm_device *drm = fbdev->base.dev; + int err; + err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); if (err < 0) { dev_err(drm->dev, "failed to initialize DRM FB helper\n"); - goto free; + return err; } err = drm_fb_helper_single_add_all_connectors(&fbdev->base); @@ -299,21 +306,17 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm, goto fini; } - drm_helper_disable_unused_functions(drm); - err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp); if (err < 0) { dev_err(drm->dev, "failed to set initial configuration\n"); goto fini; } - return fbdev; + return 0; fini: drm_fb_helper_fini(&fbdev->base); -free: - kfree(fbdev); - return ERR_PTR(err); + return err; } static void tegra_fbdev_free(struct tegra_fbdev *fbdev) @@ -367,7 +370,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { #endif }; -int tegra_drm_fb_init(struct drm_device *drm) +int tegra_drm_fb_prepare(struct drm_device *drm) { #ifdef CONFIG_DRM_TEGRA_FBDEV struct tegra_drm *tegra = drm->dev_private; @@ -382,8 +385,7 @@ int tegra_drm_fb_init(struct drm_device *drm) drm->mode_config.funcs = &tegra_drm_mode_funcs; #ifdef CONFIG_DRM_TEGRA_FBDEV - tegra->fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc, - drm->mode_config.num_connector); + tegra->fbdev = tegra_fbdev_create(drm); if (IS_ERR(tegra->fbdev)) return PTR_ERR(tegra->fbdev); #endif @@ -391,6 +393,21 @@ int tegra_drm_fb_init(struct drm_device *drm) return 0; } +int tegra_drm_fb_init(struct drm_device *drm) +{ +#ifdef CONFIG_DRM_TEGRA_FBDEV + struct tegra_drm *tegra = drm->dev_private; + int err; + + err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc, + drm->mode_config.num_connector); + if (err < 0) + return err; +#endif + + return 0; +} + void tegra_drm_fb_exit(struct drm_device *drm) { #ifdef CONFIG_DRM_TEGRA_FBDEV