From patchwork Wed Mar 29 13:19:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jagan Teki X-Patchwork-Id: 13192419 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E460FC77B60 for ; Wed, 29 Mar 2023 13:21:15 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:Cc :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=TkXHeOPYr+meM3WOUKlZ34IBL/BzRbwhKPNPB70OsDo=; b=OXIJp6XhyEFcNI jmMswnGBPUs0+k79PNR06XrcxpMePMHgTHpWXpUdkBjdbWg518iKh+Ld+2U4kmNO0jfvmSIsuhJ8R v6M+qDNtR2/6+KnkBi5/sj474OpBxM3nzIDkWaKfGBPxWQvx2Z7PZH9l4r5DPGyevd9HpYIrOJsYf vl9O4pB+NspUoZXgzTf/gSHJyNQwt9ycRav7zv5RNMdNOPWk2GMthwulfhnXOg+cubsrW5xOl3fjI 5/edf4m1jxNwbfIBsNzjwiGF0OoS8lVfxoLmHJtxYJ4drLji8s4ZpsFu5d6I5Sd01eein7ItQ13xp NR+Qbxa2UwJzP05YcDmA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1phVix-000LBi-1o; Wed, 29 Mar 2023 13:20:23 +0000 Received: from mail-pj1-x1034.google.com ([2607:f8b0:4864:20::1034]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1phViI-000Kli-27 for linux-arm-kernel@lists.infradead.org; Wed, 29 Mar 2023 13:19:44 +0000 Received: by mail-pj1-x1034.google.com with SMTP id mp3-20020a17090b190300b0023fcc8ce113so18515166pjb.4 for ; Wed, 29 Mar 2023 06:19:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amarulasolutions.com; s=google; t=1680095980; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=/v0aeTjxkLPm8Geh7qWRGFN17I5ZClwfGotSGAvvgRo=; b=Yk1ZX+WLEPPdWL2SD3QOvrYJbjyVLaxAmc1SRNI16HvFnnip4H+fNCct1NxOY03yp7 q7v+fC3LCpqmBx+mZBhks3zK5jwjN0fyuv+W14RENLl4Q8PzI3cPphDKYB+9taccSGvd uOz0RemLx7vwkMenM1ACiUddbuNGOvikDWXBE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680095980; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=/v0aeTjxkLPm8Geh7qWRGFN17I5ZClwfGotSGAvvgRo=; b=KDZAYvVr5SpfPUS6AyW79E5kWtJtt+wI4yrfw+QgIyIZr9a7TCty2/nJGC2zHOHnJg Pwj/l4E2DftVhTSctF/IBjIEZ56d90pcz7EaozCjwEgzGlTI7d6tbO4p9YtWlcgaGx0S iFY9fDtwCal8xfCGIx66YnZg12wBWJFu5VTiBcAsbhGHqDUtWuwrfOP5Iz+VgIK0yi2q A17bXuHrzKGz++NNuOCYhwcGcl43FB89TBXjZ8IrVpmQKMhX1s7cmNDzv38wbsd15kHz Mt8J4qjpqoC7B1xmEV0UxjpNNMPptlpN35qNWaFaBAlV3wGA+KhsWyVHTYu9NfN/QCKn ManQ== X-Gm-Message-State: AAQBX9eFXtK5y5Vfweb0c6M0jJuYQHIkYye/yIGFLJK7SDlHi2woVN0b sL7DD7jz6WcAa/BIMiYKC/i5Rg== X-Google-Smtp-Source: AKy350a30gmaeoHI3gtRW94k9vTAreUbchLxDVDQ49J4FoNt8UN/7ACF8JX/3OzUkhDlIZRne6BRAw== X-Received: by 2002:a17:903:22cb:b0:1a1:b174:8363 with SMTP id y11-20020a17090322cb00b001a1b1748363mr23482597plg.59.1680095980475; Wed, 29 Mar 2023 06:19:40 -0700 (PDT) Received: from localhost.localdomain ([2405:201:c00a:a047:206d:4722:c4fa:e845]) by smtp.gmail.com with ESMTPSA id d9-20020a170902854900b0019c90f8c831sm22949389plo.242.2023.03.29.06.19.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Mar 2023 06:19:39 -0700 (PDT) From: Jagan Teki To: Dave Stevenson , Maxime Ripard , Maarten Lankhorst , Thomas Zimmermann , David Airlie , Daniel Vetter , Andrzej Hajda , Neil Armstrong , Robert Foss , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Sam Ravnborg , Rob Herring , Krzysztof Kozlowski Cc: linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, Marek Vasut , linux-amarula , Jagan Teki Subject: [PATCH v7 10/12] drm/bridge: Implement enable_next_first to alter bridge init order Date: Wed, 29 Mar 2023 18:49:27 +0530 Message-Id: <20230329131929.1328612-1-jagan@amarulasolutions.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230329_061942_700483_4BB0854D X-CRM114-Status: GOOD ( 27.46 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org DSI sink devices typically send the MIPI-DCS commands to the DSI host via general MIPI_DSI_DCS read and write API. The classical DSI sequence mentioned that the DSI host receives MIPI-DCS commands from the DSI sink first in order to switch HS mode properly. Once the DSI host switches to HS mode any MIPI-DCS commands from the DSI sink are unfunctional. DSI sink uses the @enable function to send the MIPI-DCS commands. In a typical DSI host, sink pipeline the @enable call chain start with the DSI host, and then the DSI sink which is the "wrong" order as DSI host @enable is called and switched to HS mode before DSI sink @enable. If the DSI host enables with the @enable_next_first flag then the @enable for the DSI sink will be called first before the @enable of the DSI host. This alter bridge init order makes sure that the MIPI-DCS commands send first and then switch to the HS mode properly by DSI host. This new flag @enable_next_first that any bridg can set to swap the order of @enable (and #disable) for that and the immediately next bridge. [enable] If a bridge sets @enable_next_first, then the @enable for the next bridge will be called first before enable of this bridge. [disable] If a bridge sets @enable_next_first, then the @disable for the next bridge will be called first before @disable of this bridge to reverse the @enable calling direction. eg: - Panel - Bridge 1 - Bridge 2 enable_next_first - Bridge 3 - Bridge 4 enable_next_first - Bridge 5 enable_next_first - Bridge 6 - Encoder Would result in enable's being called as Encoder, Bridge 6, Bridge 3, Bridge 4, Bridge 5, Bridge 1, Bridge 2, Panel. and the result in disable's being called as Panel, Bridge 2, Bridge 1, Bridge 5, Bridge 4, Bridge 3, Bridge 6, Encoder. Signed-off-by: Jagan Teki --- Changes for v7: - new patch drivers/gpu/drm/drm_bridge.c | 171 ++++++++++++++++++++++++++++++----- include/drm/drm_bridge.h | 8 ++ 2 files changed, 154 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index caf0f341e524..cdc2669b3512 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -577,6 +577,24 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_bridge_chain_mode_set); +static void drm_atomic_bridge_call_disable(struct drm_bridge *bridge, + struct drm_atomic_state *old_state) +{ + if (old_state && bridge->funcs->atomic_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_disable(bridge, old_bridge_state); + } else if (bridge->funcs->disable) { + bridge->funcs->disable(bridge); + } +} + /** * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * @bridge: bridge control structure @@ -587,33 +605,73 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set); * starting from the last bridge to the first. These are called before calling * &drm_encoder_helper_funcs.atomic_disable * + * If a bridge sets @enable_next_first, then the @disable for the next bridge + * will be called first before @disable of this bridge to reverse the @enable + * calling direction. + * + * Example: + * Bridge A ---> Bridge B ---> Bridge C ---> Bridge D ---> Bridge E + * + * With enable_next_first flag enable in Bridge A, C, D then the resulting + * @disable order would be, + * Bridge C, Bridge D, Bridge E, Bridge A, Bridge B. + * * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, struct drm_atomic_state *old_state) { struct drm_encoder *encoder; - struct drm_bridge *iter; + struct drm_bridge *iter, *next, *limit; if (!bridge) return; encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_disable) { - struct drm_bridge_state *old_bridge_state; - - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - iter); - if (WARN_ON(!old_bridge_state)) - return; - - iter->funcs->atomic_disable(iter, old_bridge_state); - } else if (iter->funcs->disable) { - iter->funcs->disable(iter); + limit = NULL; + + if (!list_is_first(&iter->chain_node, &encoder->bridge_chain)) { + next = list_prev_entry(iter, chain_node); + + if (next->enable_next_first) { + limit = bridge; + list_for_each_entry_from_reverse(next, + &encoder->bridge_chain, + chain_node) { + if (next == bridge) + break; + + if (!next->enable_next_first) { + /* Found first bridge that does NOT + * request next to be enabled first + */ + next = list_next_entry(next, chain_node); + limit = next; + break; + } + } + + list_for_each_entry_from(next, &encoder->bridge_chain, chain_node) { + /* Call requested next bridge enable in order */ + if (next == iter) + /* At the first bridge to request next + * bridges called first. + */ + break; + + drm_atomic_bridge_call_disable(next, old_state); + } + } } + drm_atomic_bridge_call_disable(iter, old_state); + + if (limit) + /* Jump all bridges that we have already disabled */ + iter = limit; + if (iter == bridge) break; } @@ -822,6 +880,24 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); +static void drm_atomic_bridge_call_enable(struct drm_bridge *bridge, + struct drm_atomic_state *old_state) +{ + if (old_state && bridge->funcs->atomic_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_enable(bridge, old_bridge_state); + } else if (bridge->funcs->enable) { + bridge->funcs->enable(bridge); + } +} + /** * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain * @bridge: bridge control structure @@ -832,31 +908,76 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable); * starting from the first bridge to the last. These are called after completing * &drm_encoder_helper_funcs.atomic_enable * + * If a bridge sets @enable_next_first, then the @enable for the next bridge + * will be called first before enable of this bridge. + * + * Example: + * Bridge A ---> Bridge B ---> Bridge C ---> Bridge D ---> Bridge E + * + * With enable_next_first flag enable in Bridge A, C, D then the resulting + * @enable order would be, + * Bridge B, Bridge A, Bridge E, Bridge D, Bridge C. + * * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge, struct drm_atomic_state *old_state) { struct drm_encoder *encoder; + struct drm_bridge *next, *limit; if (!bridge) return; encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_enable) { - struct drm_bridge_state *old_bridge_state; - - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - bridge); - if (WARN_ON(!old_bridge_state)) - return; - - bridge->funcs->atomic_enable(bridge, old_bridge_state); - } else if (bridge->funcs->enable) { - bridge->funcs->enable(bridge); + limit = NULL; + + if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) { + if (bridge->enable_next_first) { + /* next bridge had requested that next + * was enabled first, so disabled last + */ + next = list_next_entry(bridge, chain_node); + limit = next; + + list_for_each_entry_from(next, &encoder->bridge_chain, + chain_node) { + /* Find the next bridge that has NOT requested + * next to be enabled first / disabled last + */ + if (!next->enable_next_first) { + limit = next; + break; + } + + /* Last bridge has requested next to be limit + * otherwise the control move to end of chain + */ + if (list_is_last(&next->chain_node, + &encoder->bridge_chain)) { + limit = next; + break; + } + } + + /* Call these bridges in reverse order */ + list_for_each_entry_from_reverse(next, &encoder->bridge_chain, + chain_node) { + if (next == bridge) + break; + + drm_atomic_bridge_call_enable(next, old_state); + } + } } + + drm_atomic_bridge_call_enable(bridge, old_state); + + if (limit) + /* Jump all bridges that we have already enabled */ + bridge = limit; } } EXPORT_SYMBOL(drm_atomic_bridge_chain_enable); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index a1a31704b917..9879129047e4 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -752,6 +752,14 @@ struct drm_bridge { * before the peripheral. */ bool pre_enable_prev_first; + /** + * @enable_next_first: The bridge requires that the next bridge @enable + * function is called first before its @enable, and conversely for + * @disable. This is most frequently a requirement for a DSI host to + * receive MIPI-DCS commands from DSI sink first in order to switch + * HS mode properly. + */ + bool enable_next_first; /** * @ddc: Associated I2C adapter for DDC access, if any. */