From patchwork Wed Dec 18 22:35:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 11302373 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3689014B7 for ; Wed, 18 Dec 2019 22:36:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0A0522146E for ; Wed, 18 Dec 2019 22:36:23 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="Jg83/3ti" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726877AbfLRWgJ (ORCPT ); Wed, 18 Dec 2019 17:36:09 -0500 Received: from mail-pf1-f196.google.com ([209.85.210.196]:37484 "EHLO mail-pf1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726847AbfLRWgH (ORCPT ); Wed, 18 Dec 2019 17:36:07 -0500 Received: by mail-pf1-f196.google.com with SMTP id p14so2018255pfn.4 for ; Wed, 18 Dec 2019 14:36:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=knSoDrQ0098MK5dauuOSxmAVC9nw/Q1XBe7heJcbznI=; b=Jg83/3ti8pBuPJ70W0wck2mi3dKI43mNo+WY+tGkMmywqDVLNJxemvTwDGWljj9xeU w8040RB5g8HB8kw2l4cQ9bARRD/Z3w+NKcVeah+pd8x8A8fBux6HOJVCX2Ng1t18qc2K Wu6RrquHhMGBjFL3/HRcQ9JRRAkGtvK2+rAxo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=knSoDrQ0098MK5dauuOSxmAVC9nw/Q1XBe7heJcbznI=; b=tWy5njZW7QRfI8+j81Tgvc+0QPQTXYvi4Qp0/2PcD2co1R2DRNI3WhZF0n9nu8y2mf xV3ZlxA67YD+GYQCUsDKxvL3l4ReMKm+wNcEn4iBJNcpyi28GFykjb14urJQunF+fK49 n7WlfCcrJ3nVW/BuNN1eIZ1khXnjLZX4W0XNYpBpxFn69v/zhdboAYQmSz5FZnC3KnRu 2MjIJCRe0LnkkpYjLiOYgZROdqLDd+muKWD/ZUYKc0c+NlpZZQwDUb1GvyHLPL3yGsEk 0b6FiV/mJUypFRtk7L0yCwL/4tI+1SccVjzBTCKwI0dgFagREcrbER0RGXCHm8yVWxEk z/uA== X-Gm-Message-State: APjAAAUQ+7M+6DYqufsgWz3QiYuuIox5YUKaWfHKv2tCB1tZTseC5yae mWshs4toOlFG0xNH1zlEH9wuqsmOsHR+ZQ== X-Google-Smtp-Source: APXvYqxSidIRBK9edqddrjmU5gvDTAXqIjW3T4ZxEVOHbWPhVtppFB6mwpnwiBepHerymqDoSaejOA== X-Received: by 2002:a65:4d0b:: with SMTP id i11mr5896674pgt.340.1576708566372; Wed, 18 Dec 2019 14:36:06 -0800 (PST) Received: from tictac2.mtv.corp.google.com ([2620:15c:202:1:24fa:e766:52c9:e3b2]) by smtp.gmail.com with ESMTPSA id i9sm4709919pfk.24.2019.12.18.14.36.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Dec 2019 14:36:05 -0800 (PST) From: Douglas Anderson To: Andrzej Hajda , Neil Armstrong Cc: robdclark@chromium.org, linux-arm-msm@vger.kernel.org, bjorn.andersson@linaro.org, seanpaul@chromium.org, Jeffrey Hugo , Daniel Vetter , Douglas Anderson , Rob Clark , Jonas Karlman , linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, David Airlie , Jernej Skrabec , Laurent Pinchart Subject: [PATCH v3 8/9] drm/bridge: ti-sn65dsi86: Train at faster rates if slower ones fail Date: Wed, 18 Dec 2019 14:35:29 -0800 Message-Id: <20191218143416.v3.8.I251add713bc5c97225200894ab110ea9183434fd@changeid> X-Mailer: git-send-email 2.24.1.735.g03f4e72817-goog In-Reply-To: <20191218223530.253106-1-dianders@chromium.org> References: <20191218223530.253106-1-dianders@chromium.org> MIME-Version: 1.0 Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org If we fail training at a lower DP link rate let's now keep trying until we run out of rates to try. Basically the algorithm here is to start at the link rate that is the theoretical minimum and then slowly bump up until we run out of rates or hit the max rate of the sink. We query the sink using a DPCD read. This is, in fact, important in practice. Specifically at least one panel hooked up to the bridge (AUO B116XAK01) had a theoretical min rate more than 1.62 GHz (if run at 24 bpp) and fails to train at the next rate (2.16 GHz). It would train at 2.7 GHz, though. Signed-off-by: Douglas Anderson Tested-by: Rob Clark Reviewed-by: Rob Clark Reviewed-by: Bjorn Andersson --- Changes in v3: None Changes in v2: - Squash in maybe-uninitialized fix from Rob Clark. drivers/gpu/drm/bridge/ti-sn65dsi86.c | 71 ++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 48fb4dc72e1c..e1b817ccd9c7 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -454,7 +454,7 @@ static const unsigned int ti_sn_bridge_dp_rate_lut[] = { 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400 }; -static void ti_sn_bridge_set_dp_rate(struct ti_sn_bridge *pdata) +static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn_bridge *pdata) { unsigned int bit_rate_khz, dp_rate_mhz; unsigned int i; @@ -472,8 +472,42 @@ static void ti_sn_bridge_set_dp_rate(struct ti_sn_bridge *pdata) if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz) break; - regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG, - DP_DATARATE_MASK, DP_DATARATE(i)); + return i; +} + +static int ti_sn_bridge_get_max_dp_rate_idx(struct ti_sn_bridge *pdata) +{ + u8 data; + int ret; + + ret = drm_dp_dpcd_readb(&pdata->aux, DP_MAX_LINK_RATE, &data); + if (ret != 1) { + DRM_DEV_ERROR(pdata->dev, + "Can't read max rate (%d); assuming 5.4 GHz\n", + ret); + return ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; + } + + /* + * Return an index into ti_sn_bridge_dp_rate_lut. Just hardcode + * these indicies since it's not like the register spec is ever going + * to change and a loop would just be more complicated. Apparently + * the DP sink can only return these few rates as supported even + * though the bridge allows some rates in between. + */ + switch (data) { + case DP_LINK_BW_1_62: + return 1; + case DP_LINK_BW_2_7: + return 4; + case DP_LINK_BW_5_4: + return 7; + } + + DRM_DEV_ERROR(pdata->dev, + "Unexpected max data rate (%#x); assuming 5.4 GHz\n", + (int)data); + return ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; } static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata) @@ -530,13 +564,15 @@ static unsigned int ti_sn_get_max_lanes(struct ti_sn_bridge *pdata) return data & DP_LANE_COUNT_MASK; } -static int ti_sn_link_training(struct ti_sn_bridge *pdata) +static int ti_sn_link_training(struct ti_sn_bridge *pdata, int dp_rate_idx, + const char **last_err_str) { unsigned int val; int ret; /* set dp clk frequency value */ - ti_sn_bridge_set_dp_rate(pdata); + regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG, + DP_DATARATE_MASK, DP_DATARATE(dp_rate_idx)); /* enable DP PLL */ regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 1); @@ -545,7 +581,7 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata) val & DPPLL_SRC_DP_PLL_LOCK, 1000, 50 * 1000); if (ret) { - DRM_ERROR("DP_PLL_LOCK polling failed (%d)\n", ret); + *last_err_str = "DP_PLL_LOCK polling failed"; goto exit; } @@ -556,9 +592,9 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata) val == ML_TX_NORMAL_MODE, 1000, 500 * 1000); if (ret) { - DRM_ERROR("Training complete polling failed (%d)\n", ret); + *last_err_str = "Training complete polling failed"; } else if (val == ML_TX_MAIN_LINK_OFF) { - DRM_ERROR("Link training failed, link is off\n"); + *last_err_str = "Link training failed, link is off"; ret = -EIO; } @@ -573,8 +609,11 @@ static int ti_sn_link_training(struct ti_sn_bridge *pdata) static void ti_sn_bridge_enable(struct drm_bridge *bridge) { struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge); + const char *last_err_str = "No supported DP rate"; + int dp_rate_idx; + int max_dp_rate_idx; unsigned int val; - int ret; + int ret = -EINVAL; /* * Run with the maximum number of lanes that the DP sink supports. @@ -616,9 +655,19 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge) regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, DP_NUM_LANES_MASK, val); - ret = ti_sn_link_training(pdata); - if (ret) + /* Train until we run out of rates */ + max_dp_rate_idx = ti_sn_bridge_get_max_dp_rate_idx(pdata); + for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata); + dp_rate_idx <= max_dp_rate_idx; + dp_rate_idx++) { + ret = ti_sn_link_training(pdata, dp_rate_idx, &last_err_str); + if (!ret) + break; + } + if (ret) { + DRM_DEV_ERROR(pdata->dev, "%s (%d)\n", last_err_str, ret); return; + } /* config video parameters */ ti_sn_bridge_set_video_timings(pdata);