From mboxrd@z Thu Jan 1 00:00:00 1970 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 89CB6C43217 for ; Sun, 27 Mar 2022 22:42:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236992AbiC0Wnw (ORCPT ); Sun, 27 Mar 2022 18:43:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:46154 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237147AbiC0Wn2 (ORCPT ); Sun, 27 Mar 2022 18:43:28 -0400 Received: from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com [IPv6:2a00:1450:4864:20::42b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BA1464FC71 for ; Sun, 27 Mar 2022 15:41:32 -0700 (PDT) Received: by mail-wr1-x42b.google.com with SMTP id r7so16789224wrc.0 for ; Sun, 27 Mar 2022 15:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=EKML6heEWBHkARE1qiyyx9JSgMXHlnAC3GeJ9osUMQ2zGSM7ND3Xi+XyFzs5YOhur0 BixWyXwjKY1Bvi287WbFMAUD28tKYl4wTlgnwqrB5dfplYZfSlr4AygZXFwnkkIAE/BE 9QQm2Dhbyk//EhHwq2J9srfdwMrFSsAZG/MAd7dlEEgZVufj8J19jQyR2x+YuwiT689N /7YnY1JGqYiuu5IPbx6yP7be6i/vgMm76KEuXqwM2FVa5NZhmfS+tHf71r2kEbWDRNUv MQPOBqcWz5DLzay0cqCkK7Ehenhj5Awoc5+BKpS4Cj58hdlcP5wpge1k1CqUXya4WiXT czwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=vFdVSIOJYMomMgTB+Ir6aZCZwy1Cd182K6NZKwrMR6e3d1RAsIizIiembYXB6/q/Dc bwNjJ0lnijFQXVDqsd8u5JZu0WLdu61axAXlRfEZiW3lCEy1RAEO6PssXuarrC3Ysmyv zGt8PSnyGXxoiu+iYfIuQlM/pMCZ7UeCoKa/ZapAOr4dZ+PJcedkuZJNR9yg8mZHo2Bv 1dT1PC1/8I/TTik+5YSynNosEU5San51ffUnfmKCPet+MjgseljdWlVNoO6XyJAWH4Cu gqcgxGrlvEtuPMpm7L+SxTqHFxyq9ZvKbOlngNssnRz7q7UJMguxc8yEouenf8jcw25C qisQ== X-Gm-Message-State: AOAM531B1HOVZfnOJvL32T7dTaNDjQE+ulxad7Ph+HHeCMwbIomirZe3 mRVRTzz645DP8CI1nmt8UXMUtw== X-Google-Smtp-Source: ABdhPJwhLfR00h+uYq+uYY1yJox6+UwpYi7/wrMRQVKm3YC6QqxytGi2LqTren81bWZCr8wuczbegQ== X-Received: by 2002:a05:6000:2c9:b0:204:1cdb:8f8d with SMTP id o9-20020a05600002c900b002041cdb8f8dmr20055658wry.49.1648420891115; Sun, 27 Mar 2022 15:41:31 -0700 (PDT) Received: from localhost.localdomain (2a02-8440-6341-357e-3074-96af-9642-0002.rev.sfr.net. [2a02:8440:6341:357e:3074:96af:9642:2]) by smtp.gmail.com with ESMTPSA id v5-20020adfe4c5000000b001edc1e5053esm10400867wrm.82.2022.03.27.15.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Mar 2022 15:41:30 -0700 (PDT) From: Guillaume Ranquet To: airlied@linux.ie, angelogioacchino.delregno@collabora.com, chunfeng.yun@mediatek.com, chunkuang.hu@kernel.org, ck.hu@mediatek.com, daniel@ffwll.ch, deller@gmx.de, jitao.shi@mediatek.com, kishon@ti.com, krzk+dt@kernel.org, maarten.lankhorst@linux.intel.com, matthias.bgg@gmail.com, mripard@kernel.org, p.zabel@pengutronix.de, robh+dt@kernel.org, tzimmermann@suse.de, vkoul@kernel.org Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, markyacoub@google.com Subject: [PATCH v9 19/22] drm/mediatek: Add mt8195 External DisplayPort support Date: Mon, 28 Mar 2022 00:39:24 +0200 Message-Id: <20220327223927.20848-20-granquet@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220327223927.20848-1-granquet@baylibre.com> References: <20220327223927.20848-1-granquet@baylibre.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds External DisplayPort support to the mt8195 eDP driver. Signed-off-by: Guillaume Ranquet --- drivers/gpu/drm/mediatek/mtk_dp.c | 301 +++++++++++++++++++++++++++--- 1 file changed, 278 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 7cd8459cf719..9e532408f12e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -176,6 +176,11 @@ struct mtk_dp { struct drm_connector *conn; }; +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) +{ + return mtk_dp->next_bridge; +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); @@ -741,6 +746,47 @@ static int mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable) FEC_EN_DP_TRANS_P0_MASK); } +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) & + SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, 0, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) & + IRQ_STATUS_DP_TRANS_P0_MASK) >> + IRQ_STATUS_DP_TRANS_P0_SHIFT; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, 0, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + static int mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable) { u32 val = 0; @@ -932,26 +978,49 @@ static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp) return PTR_ERR(buf); } - cal_data->glb_bias_trim = - check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); - cal_data->clktx_impse = - check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[3] = - check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[3] = - check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + if (mtk_dp_is_edp(mtk_dp)) { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + } else { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8); + } kfree(buf); @@ -1080,7 +1149,10 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable) VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | VIDEO_MUTE_SW_DP_ENC0_P0_MASK); - mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + else + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); } static int mtk_dp_power_enable(struct mtk_dp *mtk_dp) @@ -1195,6 +1267,57 @@ static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp) mtk_dp_setup_tu(mtk_dp); } +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->edid_lock); + kfree(mtk_dp->edid); + mtk_dp->edid = NULL; + mutex_unlock(&mtk_dp->edid_lock); +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + bool locked; + u8 link_status[DP_LINK_STATUS_SIZE] = {}; + u32 sink_count_reg = DP_SINK_COUNT_ESI; + u32 link_status_reg = DP_LANE0_1_STATUS; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count); + if (ret < 0) { + drm_err(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret); + return ret; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status, + sizeof(link_status)); + if (!ret) { + drm_err(mtk_dp->drm_dev, "Read link status failed: %ld\n", + ret); + return ret; + } + + locked = drm_dp_channel_eq_ok(link_status, + mtk_dp->train_info.lane_count); + if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE) + mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE; + + if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING) + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, + DP_REMOTE_CONTROL_COMMAND_PENDING); + + if (DP_GET_SINK_COUNT(sink_count) && + (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) { + mtk_dp_edid_free(mtk_dp); + mtk_dp->train_info.check_cap_count = 0; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID; + msleep(20); + } + + return 0; +} + static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes, u8 dpcd_adjust_req[2]) { @@ -1677,6 +1800,115 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + int event; + u8 buf[DP_RECEIVER_CAP_SIZE] = {}; + + event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected : + connector_status_disconnected; + + if (event < 0) + return IRQ_HANDLED; + + if (mtk_dp->drm_dev) { + dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); + } + + if (mtk_dp->train_info.cable_state_change) { + mtk_dp->train_info.cable_state_change = false; + + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + + if (!mtk_dp->train_info.cable_plugged_in || + !mtk_dp_plug_state(mtk_dp)) { + mtk_dp_video_mute(mtk_dp, true); + + mtk_dp_initialize_priv_data(mtk_dp); + mtk_dp_set_idle_pattern(mtk_dp, true); + if (mtk_dp->has_fec) + mtk_dp_fec_enable(mtk_dp, false); + + mtk_dp_edid_free(mtk_dp); + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL, + DP_PWR_STATE_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL_LANE, + DP_PWR_STATE_MASK); + drm_dp_read_dpcd_caps(&mtk_dp->aux, buf); + mtk_dp->train_info.link_rate = + min_t(int, MTK_DP_MAX_LINK_RATE, + buf[DP_MAX_LINK_RATE]); + mtk_dp->train_info.lane_count = + min_t(int, MTK_DP_MAX_LANES, + drm_dp_max_lane_count(buf)); + } + } + + if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) { + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); + mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT; + mtk_dp_hpd_sink_event(mtk_dp); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp) +{ + bool connected; + u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp); + u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp); + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; + + train_info->irq_status |= hwirq_status | swirq_status; + + if (!train_info->irq_status) + return IRQ_HANDLED; + + connected = mtk_dp_plug_state(mtk_dp); + if (connected || !train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + else if (!connected || train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + + if (!(train_info->irq_status & + (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT))) + return IRQ_HANDLED; + + if (train_info->irq_status & MTK_DP_HPD_CONNECT) { + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + train_info->cable_plugged_in = true; + } else { + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + train_info->cable_plugged_in = false; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + } + train_info->cable_state_change = true; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + u32 irq_status; + + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); + + if (!irq_status) + return IRQ_HANDLED; + + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) + return mtk_dp_hpd_isr_handler(mtk_dp); + + return IRQ_HANDLED; +} + static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp, struct platform_device *pdev) { @@ -2110,6 +2342,7 @@ static int mtk_dp_probe(struct platform_device *pdev) struct mtk_dp *mtk_dp; struct device *dev = &pdev->dev; int ret; + int irq_num = 0; mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); if (!mtk_dp) @@ -2117,9 +2350,18 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->dev = dev; + irq_num = platform_get_irq(pdev, 0); + if (irq_num < 0) + return dev_err_probe(dev, irq_num, "failed to request dp irq resource\n"); + mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); - if (IS_ERR(mtk_dp->next_bridge)) + if (IS_ERR(mtk_dp->next_bridge) && PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { + dev_info(dev, + "No panel connected in devicetree, continuing as external DP\n"); + mtk_dp->next_bridge = NULL; + } else if (IS_ERR(mtk_dp->next_bridge)) { return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), "Failed to get bridge\n"); + } ret = mtk_dp_dt_parse(mtk_dp, pdev); if (ret) @@ -2129,6 +2371,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->aux.name = "aux_mtk_dp"; mtk_dp->aux.transfer = mtk_dp_aux_transfer; + ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event, + mtk_dp_hpd_event_thread, + IRQ_TYPE_LEVEL_HIGH, dev_name(dev), + mtk_dp); + if (ret) + return dev_err_probe(dev, -EPROBE_DEFER, "failed to request mediatek dptx irq\n"); + mutex_init(&mtk_dp->dp_lock); mutex_init(&mtk_dp->edid_lock); @@ -2145,8 +2394,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; - mtk_dp->bridge.ops = DRM_BRIDGE_OP_EDID; + mtk_dp->bridge.ops = + DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; drm_bridge_add(&mtk_dp->bridge); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; + else + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -2201,6 +2455,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume); static const struct of_device_id mtk_dp_of_match[] = { { .compatible = "mediatek,mt8195-edp-tx", }, + { .compatible = "mediatek,mt8195-dp-tx", }, {}, }; MODULE_DEVICE_TABLE(of, mtk_dp_of_match); -- 2.34.1 From mboxrd@z Thu Jan 1 00:00:00 1970 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 gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (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 2E475C433EF for ; Sun, 27 Mar 2022 22:41:44 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6C3DC10E5AB; Sun, 27 Mar 2022 22:41:37 +0000 (UTC) Received: from mail-wr1-x430.google.com (mail-wr1-x430.google.com [IPv6:2a00:1450:4864:20::430]) by gabe.freedesktop.org (Postfix) with ESMTPS id A2BC510E59C for ; Sun, 27 Mar 2022 22:41:32 +0000 (UTC) Received: by mail-wr1-x430.google.com with SMTP id b19so17869031wrh.11 for ; Sun, 27 Mar 2022 15:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=EKML6heEWBHkARE1qiyyx9JSgMXHlnAC3GeJ9osUMQ2zGSM7ND3Xi+XyFzs5YOhur0 BixWyXwjKY1Bvi287WbFMAUD28tKYl4wTlgnwqrB5dfplYZfSlr4AygZXFwnkkIAE/BE 9QQm2Dhbyk//EhHwq2J9srfdwMrFSsAZG/MAd7dlEEgZVufj8J19jQyR2x+YuwiT689N /7YnY1JGqYiuu5IPbx6yP7be6i/vgMm76KEuXqwM2FVa5NZhmfS+tHf71r2kEbWDRNUv MQPOBqcWz5DLzay0cqCkK7Ehenhj5Awoc5+BKpS4Cj58hdlcP5wpge1k1CqUXya4WiXT czwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=jC1/tZlYIi4Vt47OU9EGWE0QSqTpSlLYXyvQQowLCnStsHdd2MkKE9QChUoFKPq4OD PLwrm2bPgi6/x/dl+uybW1Gz8NxMpqZ/DjC+pZsqRgM9Liap340B5dpZe1J4jnJ6Ol28 ICkIZyEaIELid/YwLxMesoVmsOqvrxbjhmSakSSdPjpj+XmNQS52E+WDnGN2gkWEyUVg VRqtwupMZ6mdi1LtrViaqPdN2uDOXg4QvmvelYD3SK0r/+SRqIvGJPM8iDFZf2T4dFoU xPArrA1axEBRgEfxMiUhbmGPllnTzn8vpUcTBGrSmtWpApIDiPMKS2Qjn9J6JMGf/iUY GFng== X-Gm-Message-State: AOAM532w9mZRiGJ+4qpuEgePCavieMWvgla73KZZZPQBQcLtxcT3mzbs cmB1xrS6k51/B1VVrWpWk11z5Q== X-Google-Smtp-Source: ABdhPJwhLfR00h+uYq+uYY1yJox6+UwpYi7/wrMRQVKm3YC6QqxytGi2LqTren81bWZCr8wuczbegQ== X-Received: by 2002:a05:6000:2c9:b0:204:1cdb:8f8d with SMTP id o9-20020a05600002c900b002041cdb8f8dmr20055658wry.49.1648420891115; Sun, 27 Mar 2022 15:41:31 -0700 (PDT) Received: from localhost.localdomain (2a02-8440-6341-357e-3074-96af-9642-0002.rev.sfr.net. [2a02:8440:6341:357e:3074:96af:9642:2]) by smtp.gmail.com with ESMTPSA id v5-20020adfe4c5000000b001edc1e5053esm10400867wrm.82.2022.03.27.15.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Mar 2022 15:41:30 -0700 (PDT) From: Guillaume Ranquet To: airlied@linux.ie, angelogioacchino.delregno@collabora.com, chunfeng.yun@mediatek.com, chunkuang.hu@kernel.org, ck.hu@mediatek.com, daniel@ffwll.ch, deller@gmx.de, jitao.shi@mediatek.com, kishon@ti.com, krzk+dt@kernel.org, maarten.lankhorst@linux.intel.com, matthias.bgg@gmail.com, mripard@kernel.org, p.zabel@pengutronix.de, robh+dt@kernel.org, tzimmermann@suse.de, vkoul@kernel.org Subject: [PATCH v9 19/22] drm/mediatek: Add mt8195 External DisplayPort support Date: Mon, 28 Mar 2022 00:39:24 +0200 Message-Id: <20220327223927.20848-20-granquet@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220327223927.20848-1-granquet@baylibre.com> References: <20220327223927.20848-1-granquet@baylibre.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, linux-arm-kernel@lists.infradead.org, markyacoub@google.com Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" This patch adds External DisplayPort support to the mt8195 eDP driver. Signed-off-by: Guillaume Ranquet --- drivers/gpu/drm/mediatek/mtk_dp.c | 301 +++++++++++++++++++++++++++--- 1 file changed, 278 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 7cd8459cf719..9e532408f12e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -176,6 +176,11 @@ struct mtk_dp { struct drm_connector *conn; }; +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) +{ + return mtk_dp->next_bridge; +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); @@ -741,6 +746,47 @@ static int mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable) FEC_EN_DP_TRANS_P0_MASK); } +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) & + SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, 0, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) & + IRQ_STATUS_DP_TRANS_P0_MASK) >> + IRQ_STATUS_DP_TRANS_P0_SHIFT; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, 0, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + static int mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable) { u32 val = 0; @@ -932,26 +978,49 @@ static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp) return PTR_ERR(buf); } - cal_data->glb_bias_trim = - check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); - cal_data->clktx_impse = - check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[3] = - check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[3] = - check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + if (mtk_dp_is_edp(mtk_dp)) { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + } else { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8); + } kfree(buf); @@ -1080,7 +1149,10 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable) VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | VIDEO_MUTE_SW_DP_ENC0_P0_MASK); - mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + else + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); } static int mtk_dp_power_enable(struct mtk_dp *mtk_dp) @@ -1195,6 +1267,57 @@ static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp) mtk_dp_setup_tu(mtk_dp); } +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->edid_lock); + kfree(mtk_dp->edid); + mtk_dp->edid = NULL; + mutex_unlock(&mtk_dp->edid_lock); +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + bool locked; + u8 link_status[DP_LINK_STATUS_SIZE] = {}; + u32 sink_count_reg = DP_SINK_COUNT_ESI; + u32 link_status_reg = DP_LANE0_1_STATUS; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count); + if (ret < 0) { + drm_err(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret); + return ret; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status, + sizeof(link_status)); + if (!ret) { + drm_err(mtk_dp->drm_dev, "Read link status failed: %ld\n", + ret); + return ret; + } + + locked = drm_dp_channel_eq_ok(link_status, + mtk_dp->train_info.lane_count); + if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE) + mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE; + + if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING) + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, + DP_REMOTE_CONTROL_COMMAND_PENDING); + + if (DP_GET_SINK_COUNT(sink_count) && + (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) { + mtk_dp_edid_free(mtk_dp); + mtk_dp->train_info.check_cap_count = 0; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID; + msleep(20); + } + + return 0; +} + static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes, u8 dpcd_adjust_req[2]) { @@ -1677,6 +1800,115 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + int event; + u8 buf[DP_RECEIVER_CAP_SIZE] = {}; + + event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected : + connector_status_disconnected; + + if (event < 0) + return IRQ_HANDLED; + + if (mtk_dp->drm_dev) { + dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); + } + + if (mtk_dp->train_info.cable_state_change) { + mtk_dp->train_info.cable_state_change = false; + + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + + if (!mtk_dp->train_info.cable_plugged_in || + !mtk_dp_plug_state(mtk_dp)) { + mtk_dp_video_mute(mtk_dp, true); + + mtk_dp_initialize_priv_data(mtk_dp); + mtk_dp_set_idle_pattern(mtk_dp, true); + if (mtk_dp->has_fec) + mtk_dp_fec_enable(mtk_dp, false); + + mtk_dp_edid_free(mtk_dp); + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL, + DP_PWR_STATE_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL_LANE, + DP_PWR_STATE_MASK); + drm_dp_read_dpcd_caps(&mtk_dp->aux, buf); + mtk_dp->train_info.link_rate = + min_t(int, MTK_DP_MAX_LINK_RATE, + buf[DP_MAX_LINK_RATE]); + mtk_dp->train_info.lane_count = + min_t(int, MTK_DP_MAX_LANES, + drm_dp_max_lane_count(buf)); + } + } + + if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) { + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); + mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT; + mtk_dp_hpd_sink_event(mtk_dp); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp) +{ + bool connected; + u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp); + u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp); + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; + + train_info->irq_status |= hwirq_status | swirq_status; + + if (!train_info->irq_status) + return IRQ_HANDLED; + + connected = mtk_dp_plug_state(mtk_dp); + if (connected || !train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + else if (!connected || train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + + if (!(train_info->irq_status & + (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT))) + return IRQ_HANDLED; + + if (train_info->irq_status & MTK_DP_HPD_CONNECT) { + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + train_info->cable_plugged_in = true; + } else { + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + train_info->cable_plugged_in = false; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + } + train_info->cable_state_change = true; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + u32 irq_status; + + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); + + if (!irq_status) + return IRQ_HANDLED; + + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) + return mtk_dp_hpd_isr_handler(mtk_dp); + + return IRQ_HANDLED; +} + static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp, struct platform_device *pdev) { @@ -2110,6 +2342,7 @@ static int mtk_dp_probe(struct platform_device *pdev) struct mtk_dp *mtk_dp; struct device *dev = &pdev->dev; int ret; + int irq_num = 0; mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); if (!mtk_dp) @@ -2117,9 +2350,18 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->dev = dev; + irq_num = platform_get_irq(pdev, 0); + if (irq_num < 0) + return dev_err_probe(dev, irq_num, "failed to request dp irq resource\n"); + mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); - if (IS_ERR(mtk_dp->next_bridge)) + if (IS_ERR(mtk_dp->next_bridge) && PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { + dev_info(dev, + "No panel connected in devicetree, continuing as external DP\n"); + mtk_dp->next_bridge = NULL; + } else if (IS_ERR(mtk_dp->next_bridge)) { return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), "Failed to get bridge\n"); + } ret = mtk_dp_dt_parse(mtk_dp, pdev); if (ret) @@ -2129,6 +2371,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->aux.name = "aux_mtk_dp"; mtk_dp->aux.transfer = mtk_dp_aux_transfer; + ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event, + mtk_dp_hpd_event_thread, + IRQ_TYPE_LEVEL_HIGH, dev_name(dev), + mtk_dp); + if (ret) + return dev_err_probe(dev, -EPROBE_DEFER, "failed to request mediatek dptx irq\n"); + mutex_init(&mtk_dp->dp_lock); mutex_init(&mtk_dp->edid_lock); @@ -2145,8 +2394,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; - mtk_dp->bridge.ops = DRM_BRIDGE_OP_EDID; + mtk_dp->bridge.ops = + DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; drm_bridge_add(&mtk_dp->bridge); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; + else + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -2201,6 +2455,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume); static const struct of_device_id mtk_dp_of_match[] = { { .compatible = "mediatek,mt8195-edp-tx", }, + { .compatible = "mediatek,mt8195-dp-tx", }, {}, }; MODULE_DEVICE_TABLE(of, mtk_dp_of_match); -- 2.34.1 From mboxrd@z Thu Jan 1 00:00:00 1970 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 221C4C433FE for ; Sun, 27 Mar 2022 22:49:45 +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:References:In-Reply-To: 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: List-Owner; bh=FJuqOfpmuYxNB0LhEtOXHZkIh1XGPi1FAk5ELM3qbsU=; b=C6dlZeqk/cHNtD Fus6gXU4z7xW8qwYrKDXO4l9NAAbvAlMr/NsjDA4npjZVUpO3rd5DaaCmvBvE5jidR+wl7TjadLIM jsY1BVtDVRMi/7tHR3zWEKy0c65oXMqqjICuo5nJCRCpry151V7NfchxVfpIZ7BssFMwudN5A2bkc be2sSKeM1sevfnsUrauoBA75KFUV1XI4IeopAcIX6kVlw1iN5lmfqlqY2bdjoXyEbnBB5+bCIJKtH cctRMvcFP1oydA7BIpZNqkjyOIF2p0X7fX03kLgP4lQpm6f7TqIkE2AiuvLS0wOhuwY/Zg5ixWFdV cbFrqD6tce/NptuSHdhw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYbhg-006ZrA-Ae; Sun, 27 Mar 2022 22:49:44 +0000 Received: from mail-wr1-x42a.google.com ([2a00:1450:4864:20::42a]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYbZk-006VID-Ky for linux-phy@lists.infradead.org; Sun, 27 Mar 2022 22:41:41 +0000 Received: by mail-wr1-x42a.google.com with SMTP id a1so17889982wrh.10 for ; Sun, 27 Mar 2022 15:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=EKML6heEWBHkARE1qiyyx9JSgMXHlnAC3GeJ9osUMQ2zGSM7ND3Xi+XyFzs5YOhur0 BixWyXwjKY1Bvi287WbFMAUD28tKYl4wTlgnwqrB5dfplYZfSlr4AygZXFwnkkIAE/BE 9QQm2Dhbyk//EhHwq2J9srfdwMrFSsAZG/MAd7dlEEgZVufj8J19jQyR2x+YuwiT689N /7YnY1JGqYiuu5IPbx6yP7be6i/vgMm76KEuXqwM2FVa5NZhmfS+tHf71r2kEbWDRNUv MQPOBqcWz5DLzay0cqCkK7Ehenhj5Awoc5+BKpS4Cj58hdlcP5wpge1k1CqUXya4WiXT czwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=QHCUR8r+c4SWlRmrOZ781E/lAN9NWjJH68jGiSwD2I/Pim2KURB9DJfB9ZQhZbKhki SsaGSWMECAepgr09W1g7JhoBreyblSrOheaHrplRRlwlUAHGpGe0SL5AB5OEwZlyb9Jc kNFB/6upLjy1kmW9yCfKBh4nFemFiyVdPFlmFdv+S9DVNfkyfl5/zgTFjh80nDS5V8Fl p/9qAFAOgObP42tU/QbD3EcwWww4kz8zbbG1nsUM6UszGgsk0jZWWiZKWOCRinjgOu4n J9P+iqT3UwJF3wJilQ0i5Hn/gMcyf1BxOwA3CYawz+O/CXHE5EaHRCjJgINJ2tLE0ywB y+Tw== X-Gm-Message-State: AOAM532wVyqGQmke+YhN7UodFgjxMzSa6wD/q9pznOE9b02A1VUBLzIJ sr9e61SYNOhAWUj0a4p3iFZM5w== X-Google-Smtp-Source: ABdhPJwhLfR00h+uYq+uYY1yJox6+UwpYi7/wrMRQVKm3YC6QqxytGi2LqTren81bWZCr8wuczbegQ== X-Received: by 2002:a05:6000:2c9:b0:204:1cdb:8f8d with SMTP id o9-20020a05600002c900b002041cdb8f8dmr20055658wry.49.1648420891115; Sun, 27 Mar 2022 15:41:31 -0700 (PDT) Received: from localhost.localdomain (2a02-8440-6341-357e-3074-96af-9642-0002.rev.sfr.net. [2a02:8440:6341:357e:3074:96af:9642:2]) by smtp.gmail.com with ESMTPSA id v5-20020adfe4c5000000b001edc1e5053esm10400867wrm.82.2022.03.27.15.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Mar 2022 15:41:30 -0700 (PDT) From: Guillaume Ranquet To: airlied@linux.ie, angelogioacchino.delregno@collabora.com, chunfeng.yun@mediatek.com, chunkuang.hu@kernel.org, ck.hu@mediatek.com, daniel@ffwll.ch, deller@gmx.de, jitao.shi@mediatek.com, kishon@ti.com, krzk+dt@kernel.org, maarten.lankhorst@linux.intel.com, matthias.bgg@gmail.com, mripard@kernel.org, p.zabel@pengutronix.de, robh+dt@kernel.org, tzimmermann@suse.de, vkoul@kernel.org Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, markyacoub@google.com Subject: [PATCH v9 19/22] drm/mediatek: Add mt8195 External DisplayPort support Date: Mon, 28 Mar 2022 00:39:24 +0200 Message-Id: <20220327223927.20848-20-granquet@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220327223927.20848-1-granquet@baylibre.com> References: <20220327223927.20848-1-granquet@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220327_154132_820243_417FCDE8 X-CRM114-Status: GOOD ( 18.21 ) X-BeenThere: linux-phy@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux Phy Mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-phy" Errors-To: linux-phy-bounces+linux-phy=archiver.kernel.org@lists.infradead.org This patch adds External DisplayPort support to the mt8195 eDP driver. Signed-off-by: Guillaume Ranquet --- drivers/gpu/drm/mediatek/mtk_dp.c | 301 +++++++++++++++++++++++++++--- 1 file changed, 278 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 7cd8459cf719..9e532408f12e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -176,6 +176,11 @@ struct mtk_dp { struct drm_connector *conn; }; +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) +{ + return mtk_dp->next_bridge; +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); @@ -741,6 +746,47 @@ static int mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable) FEC_EN_DP_TRANS_P0_MASK); } +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) & + SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, 0, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) & + IRQ_STATUS_DP_TRANS_P0_MASK) >> + IRQ_STATUS_DP_TRANS_P0_SHIFT; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, 0, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + static int mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable) { u32 val = 0; @@ -932,26 +978,49 @@ static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp) return PTR_ERR(buf); } - cal_data->glb_bias_trim = - check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); - cal_data->clktx_impse = - check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[3] = - check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[3] = - check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + if (mtk_dp_is_edp(mtk_dp)) { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + } else { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8); + } kfree(buf); @@ -1080,7 +1149,10 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable) VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | VIDEO_MUTE_SW_DP_ENC0_P0_MASK); - mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + else + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); } static int mtk_dp_power_enable(struct mtk_dp *mtk_dp) @@ -1195,6 +1267,57 @@ static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp) mtk_dp_setup_tu(mtk_dp); } +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->edid_lock); + kfree(mtk_dp->edid); + mtk_dp->edid = NULL; + mutex_unlock(&mtk_dp->edid_lock); +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + bool locked; + u8 link_status[DP_LINK_STATUS_SIZE] = {}; + u32 sink_count_reg = DP_SINK_COUNT_ESI; + u32 link_status_reg = DP_LANE0_1_STATUS; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count); + if (ret < 0) { + drm_err(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret); + return ret; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status, + sizeof(link_status)); + if (!ret) { + drm_err(mtk_dp->drm_dev, "Read link status failed: %ld\n", + ret); + return ret; + } + + locked = drm_dp_channel_eq_ok(link_status, + mtk_dp->train_info.lane_count); + if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE) + mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE; + + if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING) + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, + DP_REMOTE_CONTROL_COMMAND_PENDING); + + if (DP_GET_SINK_COUNT(sink_count) && + (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) { + mtk_dp_edid_free(mtk_dp); + mtk_dp->train_info.check_cap_count = 0; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID; + msleep(20); + } + + return 0; +} + static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes, u8 dpcd_adjust_req[2]) { @@ -1677,6 +1800,115 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + int event; + u8 buf[DP_RECEIVER_CAP_SIZE] = {}; + + event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected : + connector_status_disconnected; + + if (event < 0) + return IRQ_HANDLED; + + if (mtk_dp->drm_dev) { + dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); + } + + if (mtk_dp->train_info.cable_state_change) { + mtk_dp->train_info.cable_state_change = false; + + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + + if (!mtk_dp->train_info.cable_plugged_in || + !mtk_dp_plug_state(mtk_dp)) { + mtk_dp_video_mute(mtk_dp, true); + + mtk_dp_initialize_priv_data(mtk_dp); + mtk_dp_set_idle_pattern(mtk_dp, true); + if (mtk_dp->has_fec) + mtk_dp_fec_enable(mtk_dp, false); + + mtk_dp_edid_free(mtk_dp); + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL, + DP_PWR_STATE_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL_LANE, + DP_PWR_STATE_MASK); + drm_dp_read_dpcd_caps(&mtk_dp->aux, buf); + mtk_dp->train_info.link_rate = + min_t(int, MTK_DP_MAX_LINK_RATE, + buf[DP_MAX_LINK_RATE]); + mtk_dp->train_info.lane_count = + min_t(int, MTK_DP_MAX_LANES, + drm_dp_max_lane_count(buf)); + } + } + + if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) { + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); + mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT; + mtk_dp_hpd_sink_event(mtk_dp); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp) +{ + bool connected; + u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp); + u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp); + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; + + train_info->irq_status |= hwirq_status | swirq_status; + + if (!train_info->irq_status) + return IRQ_HANDLED; + + connected = mtk_dp_plug_state(mtk_dp); + if (connected || !train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + else if (!connected || train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + + if (!(train_info->irq_status & + (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT))) + return IRQ_HANDLED; + + if (train_info->irq_status & MTK_DP_HPD_CONNECT) { + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + train_info->cable_plugged_in = true; + } else { + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + train_info->cable_plugged_in = false; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + } + train_info->cable_state_change = true; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + u32 irq_status; + + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); + + if (!irq_status) + return IRQ_HANDLED; + + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) + return mtk_dp_hpd_isr_handler(mtk_dp); + + return IRQ_HANDLED; +} + static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp, struct platform_device *pdev) { @@ -2110,6 +2342,7 @@ static int mtk_dp_probe(struct platform_device *pdev) struct mtk_dp *mtk_dp; struct device *dev = &pdev->dev; int ret; + int irq_num = 0; mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); if (!mtk_dp) @@ -2117,9 +2350,18 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->dev = dev; + irq_num = platform_get_irq(pdev, 0); + if (irq_num < 0) + return dev_err_probe(dev, irq_num, "failed to request dp irq resource\n"); + mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); - if (IS_ERR(mtk_dp->next_bridge)) + if (IS_ERR(mtk_dp->next_bridge) && PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { + dev_info(dev, + "No panel connected in devicetree, continuing as external DP\n"); + mtk_dp->next_bridge = NULL; + } else if (IS_ERR(mtk_dp->next_bridge)) { return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), "Failed to get bridge\n"); + } ret = mtk_dp_dt_parse(mtk_dp, pdev); if (ret) @@ -2129,6 +2371,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->aux.name = "aux_mtk_dp"; mtk_dp->aux.transfer = mtk_dp_aux_transfer; + ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event, + mtk_dp_hpd_event_thread, + IRQ_TYPE_LEVEL_HIGH, dev_name(dev), + mtk_dp); + if (ret) + return dev_err_probe(dev, -EPROBE_DEFER, "failed to request mediatek dptx irq\n"); + mutex_init(&mtk_dp->dp_lock); mutex_init(&mtk_dp->edid_lock); @@ -2145,8 +2394,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; - mtk_dp->bridge.ops = DRM_BRIDGE_OP_EDID; + mtk_dp->bridge.ops = + DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; drm_bridge_add(&mtk_dp->bridge); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; + else + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -2201,6 +2455,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume); static const struct of_device_id mtk_dp_of_match[] = { { .compatible = "mediatek,mt8195-edp-tx", }, + { .compatible = "mediatek,mt8195-dp-tx", }, {}, }; MODULE_DEVICE_TABLE(of, mtk_dp_of_match); -- 2.34.1 -- linux-phy mailing list linux-phy@lists.infradead.org https://lists.infradead.org/mailman/listinfo/linux-phy From mboxrd@z Thu Jan 1 00:00:00 1970 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 580F2C433F5 for ; Sun, 27 Mar 2022 22:51:22 +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:References:In-Reply-To: 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: List-Owner; bh=EJ0rQSHxKSIpqYANXfRmfwcfGPesOu+RV+2PAmukLrM=; b=BA4curo77BnG3l HpziGL6yryrGk5ca03ZIxRD5+uqAhvdhZ7HZeMNT8b3cpD6VRN9y6Urvp5I0RYYCYTZ+5VC4zeCX2 WNJXbi2lk88guF0ofusCsWG3mbnIkXarnAUcIxLVH9qnJVgszhola06ax+JmnAcJgsBb9xUKe7xxr egcHTOk5gqY3/sYLsWmz/0kC5xjKnP2qHOJf1CtEhW/MjVKR/3w3k0E4nxaFfW426zqbTNB0OBori /alH3x0Kifrna+C5VT4iPsn4Q2tpBj6SAvROmvpaLSIqa8JmrfU6ykDXSYpU7/re5VsI8fY1RRi1z 4WPlJmwnOQ0+y2f+9Cog==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYbhn-006ZrQ-02; Sun, 27 Mar 2022 22:49:52 +0000 Received: from mail-wr1-x434.google.com ([2a00:1450:4864:20::434]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYbZk-006VIB-KB for linux-arm-kernel@lists.infradead.org; Sun, 27 Mar 2022 22:41:43 +0000 Received: by mail-wr1-x434.google.com with SMTP id h4so17876263wrc.13 for ; Sun, 27 Mar 2022 15:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=EKML6heEWBHkARE1qiyyx9JSgMXHlnAC3GeJ9osUMQ2zGSM7ND3Xi+XyFzs5YOhur0 BixWyXwjKY1Bvi287WbFMAUD28tKYl4wTlgnwqrB5dfplYZfSlr4AygZXFwnkkIAE/BE 9QQm2Dhbyk//EhHwq2J9srfdwMrFSsAZG/MAd7dlEEgZVufj8J19jQyR2x+YuwiT689N /7YnY1JGqYiuu5IPbx6yP7be6i/vgMm76KEuXqwM2FVa5NZhmfS+tHf71r2kEbWDRNUv MQPOBqcWz5DLzay0cqCkK7Ehenhj5Awoc5+BKpS4Cj58hdlcP5wpge1k1CqUXya4WiXT czwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=aSwPqRaCx7jkzQPBlxPC+jhrxkbdIBA6qwUEOeBdQMY7Uu8jcPYDDT24WinXA8bS/c XHZEkveLgqvaP5J5ePUs5M87w3iGp9GfYz6BhrvefPPs/5fw/Vu12R7YQkAR/F32bgmx kPIP7dybDw9EjpQoWCJZ8R7tNVquZtFGJIn1O7+nTQkjgIOgHP+xbE8BrIuaJF7lec7Z iRy7a1jjfumzSiSYDMkMlIOovdKTWqgAg3vuVMcdIFdchAZjX2MOfp8uk5dPfXLoPnI5 HfO9FH/h6kjr1BHx6ho0ID7FQQbqY8pN5ZMFZy7LsPOLXpfMD3f0jpDC3doTnZ0UdHYq CveQ== X-Gm-Message-State: AOAM532BOUHYgXZKJ1MndX8knfqiwlyr7Eq5+s99kSNyJWlm1d5aIwe5 b/Rts42CGcDcVz780qQZ4tuyPw== X-Google-Smtp-Source: ABdhPJwhLfR00h+uYq+uYY1yJox6+UwpYi7/wrMRQVKm3YC6QqxytGi2LqTren81bWZCr8wuczbegQ== X-Received: by 2002:a05:6000:2c9:b0:204:1cdb:8f8d with SMTP id o9-20020a05600002c900b002041cdb8f8dmr20055658wry.49.1648420891115; Sun, 27 Mar 2022 15:41:31 -0700 (PDT) Received: from localhost.localdomain (2a02-8440-6341-357e-3074-96af-9642-0002.rev.sfr.net. [2a02:8440:6341:357e:3074:96af:9642:2]) by smtp.gmail.com with ESMTPSA id v5-20020adfe4c5000000b001edc1e5053esm10400867wrm.82.2022.03.27.15.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Mar 2022 15:41:30 -0700 (PDT) From: Guillaume Ranquet To: airlied@linux.ie, angelogioacchino.delregno@collabora.com, chunfeng.yun@mediatek.com, chunkuang.hu@kernel.org, ck.hu@mediatek.com, daniel@ffwll.ch, deller@gmx.de, jitao.shi@mediatek.com, kishon@ti.com, krzk+dt@kernel.org, maarten.lankhorst@linux.intel.com, matthias.bgg@gmail.com, mripard@kernel.org, p.zabel@pengutronix.de, robh+dt@kernel.org, tzimmermann@suse.de, vkoul@kernel.org Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, markyacoub@google.com Subject: [PATCH v9 19/22] drm/mediatek: Add mt8195 External DisplayPort support Date: Mon, 28 Mar 2022 00:39:24 +0200 Message-Id: <20220327223927.20848-20-granquet@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220327223927.20848-1-granquet@baylibre.com> References: <20220327223927.20848-1-granquet@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220327_154132_839771_732FD8A0 X-CRM114-Status: GOOD ( 19.73 ) 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: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org This patch adds External DisplayPort support to the mt8195 eDP driver. Signed-off-by: Guillaume Ranquet --- drivers/gpu/drm/mediatek/mtk_dp.c | 301 +++++++++++++++++++++++++++--- 1 file changed, 278 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 7cd8459cf719..9e532408f12e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -176,6 +176,11 @@ struct mtk_dp { struct drm_connector *conn; }; +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) +{ + return mtk_dp->next_bridge; +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); @@ -741,6 +746,47 @@ static int mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable) FEC_EN_DP_TRANS_P0_MASK); } +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) & + SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, 0, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) & + IRQ_STATUS_DP_TRANS_P0_MASK) >> + IRQ_STATUS_DP_TRANS_P0_SHIFT; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, 0, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + static int mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable) { u32 val = 0; @@ -932,26 +978,49 @@ static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp) return PTR_ERR(buf); } - cal_data->glb_bias_trim = - check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); - cal_data->clktx_impse = - check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[3] = - check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[3] = - check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + if (mtk_dp_is_edp(mtk_dp)) { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + } else { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8); + } kfree(buf); @@ -1080,7 +1149,10 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable) VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | VIDEO_MUTE_SW_DP_ENC0_P0_MASK); - mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + else + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); } static int mtk_dp_power_enable(struct mtk_dp *mtk_dp) @@ -1195,6 +1267,57 @@ static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp) mtk_dp_setup_tu(mtk_dp); } +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->edid_lock); + kfree(mtk_dp->edid); + mtk_dp->edid = NULL; + mutex_unlock(&mtk_dp->edid_lock); +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + bool locked; + u8 link_status[DP_LINK_STATUS_SIZE] = {}; + u32 sink_count_reg = DP_SINK_COUNT_ESI; + u32 link_status_reg = DP_LANE0_1_STATUS; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count); + if (ret < 0) { + drm_err(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret); + return ret; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status, + sizeof(link_status)); + if (!ret) { + drm_err(mtk_dp->drm_dev, "Read link status failed: %ld\n", + ret); + return ret; + } + + locked = drm_dp_channel_eq_ok(link_status, + mtk_dp->train_info.lane_count); + if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE) + mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE; + + if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING) + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, + DP_REMOTE_CONTROL_COMMAND_PENDING); + + if (DP_GET_SINK_COUNT(sink_count) && + (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) { + mtk_dp_edid_free(mtk_dp); + mtk_dp->train_info.check_cap_count = 0; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID; + msleep(20); + } + + return 0; +} + static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes, u8 dpcd_adjust_req[2]) { @@ -1677,6 +1800,115 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + int event; + u8 buf[DP_RECEIVER_CAP_SIZE] = {}; + + event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected : + connector_status_disconnected; + + if (event < 0) + return IRQ_HANDLED; + + if (mtk_dp->drm_dev) { + dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); + } + + if (mtk_dp->train_info.cable_state_change) { + mtk_dp->train_info.cable_state_change = false; + + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + + if (!mtk_dp->train_info.cable_plugged_in || + !mtk_dp_plug_state(mtk_dp)) { + mtk_dp_video_mute(mtk_dp, true); + + mtk_dp_initialize_priv_data(mtk_dp); + mtk_dp_set_idle_pattern(mtk_dp, true); + if (mtk_dp->has_fec) + mtk_dp_fec_enable(mtk_dp, false); + + mtk_dp_edid_free(mtk_dp); + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL, + DP_PWR_STATE_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL_LANE, + DP_PWR_STATE_MASK); + drm_dp_read_dpcd_caps(&mtk_dp->aux, buf); + mtk_dp->train_info.link_rate = + min_t(int, MTK_DP_MAX_LINK_RATE, + buf[DP_MAX_LINK_RATE]); + mtk_dp->train_info.lane_count = + min_t(int, MTK_DP_MAX_LANES, + drm_dp_max_lane_count(buf)); + } + } + + if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) { + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); + mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT; + mtk_dp_hpd_sink_event(mtk_dp); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp) +{ + bool connected; + u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp); + u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp); + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; + + train_info->irq_status |= hwirq_status | swirq_status; + + if (!train_info->irq_status) + return IRQ_HANDLED; + + connected = mtk_dp_plug_state(mtk_dp); + if (connected || !train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + else if (!connected || train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + + if (!(train_info->irq_status & + (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT))) + return IRQ_HANDLED; + + if (train_info->irq_status & MTK_DP_HPD_CONNECT) { + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + train_info->cable_plugged_in = true; + } else { + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + train_info->cable_plugged_in = false; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + } + train_info->cable_state_change = true; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + u32 irq_status; + + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); + + if (!irq_status) + return IRQ_HANDLED; + + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) + return mtk_dp_hpd_isr_handler(mtk_dp); + + return IRQ_HANDLED; +} + static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp, struct platform_device *pdev) { @@ -2110,6 +2342,7 @@ static int mtk_dp_probe(struct platform_device *pdev) struct mtk_dp *mtk_dp; struct device *dev = &pdev->dev; int ret; + int irq_num = 0; mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); if (!mtk_dp) @@ -2117,9 +2350,18 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->dev = dev; + irq_num = platform_get_irq(pdev, 0); + if (irq_num < 0) + return dev_err_probe(dev, irq_num, "failed to request dp irq resource\n"); + mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); - if (IS_ERR(mtk_dp->next_bridge)) + if (IS_ERR(mtk_dp->next_bridge) && PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { + dev_info(dev, + "No panel connected in devicetree, continuing as external DP\n"); + mtk_dp->next_bridge = NULL; + } else if (IS_ERR(mtk_dp->next_bridge)) { return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), "Failed to get bridge\n"); + } ret = mtk_dp_dt_parse(mtk_dp, pdev); if (ret) @@ -2129,6 +2371,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->aux.name = "aux_mtk_dp"; mtk_dp->aux.transfer = mtk_dp_aux_transfer; + ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event, + mtk_dp_hpd_event_thread, + IRQ_TYPE_LEVEL_HIGH, dev_name(dev), + mtk_dp); + if (ret) + return dev_err_probe(dev, -EPROBE_DEFER, "failed to request mediatek dptx irq\n"); + mutex_init(&mtk_dp->dp_lock); mutex_init(&mtk_dp->edid_lock); @@ -2145,8 +2394,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; - mtk_dp->bridge.ops = DRM_BRIDGE_OP_EDID; + mtk_dp->bridge.ops = + DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; drm_bridge_add(&mtk_dp->bridge); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; + else + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -2201,6 +2455,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume); static const struct of_device_id mtk_dp_of_match[] = { { .compatible = "mediatek,mt8195-edp-tx", }, + { .compatible = "mediatek,mt8195-dp-tx", }, {}, }; MODULE_DEVICE_TABLE(of, mtk_dp_of_match); -- 2.34.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From mboxrd@z Thu Jan 1 00:00:00 1970 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 E3C7EC433F5 for ; Sun, 27 Mar 2022 22:51:53 +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:References:In-Reply-To: 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: List-Owner; bh=7yONsEIMAWpGVzHR583AFZ+YYMZ9BuU8qvEdle0Y4tI=; b=ZsAZVLB9Zq3mQv 1yJu5rn+r4vGy4X3hYBfbQipYjPHvCqCYkqFou7hyzgaaoC79Ok9Nv+j3TZ2neqlY86n4z574yRXI y9W/5NKTRP47JkRQaMdAUmp/CHW1oH3tlhaspj3kCnJ8r+dLr8aGx+FtqBUFdJ55C608QAKp3o7EI mUxplhDwJZ9PgyQSs1sSaZcVjPDXM/b5zP7NpnxfSUzil9tfyCYJpd6pr9FVwLd5as8Awfq9s4gEA F50QiLh3HlUN9oj1aiAAvVpPYx/kvPQ6xz4NZZsrcAqZWyc6v82JC598MsylmeeE3EHDc6j8KupsI vByV5CwmmXSbsFuHUlyQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYbje-006apu-QC; Sun, 27 Mar 2022 22:51:46 +0000 Received: from mail-wr1-x42e.google.com ([2a00:1450:4864:20::42e]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nYbZk-006VIC-S6 for linux-mediatek@lists.infradead.org; Sun, 27 Mar 2022 22:41:45 +0000 Received: by mail-wr1-x42e.google.com with SMTP id w4so17862791wrg.12 for ; Sun, 27 Mar 2022 15:41:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20210112.gappssmtp.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=EKML6heEWBHkARE1qiyyx9JSgMXHlnAC3GeJ9osUMQ2zGSM7ND3Xi+XyFzs5YOhur0 BixWyXwjKY1Bvi287WbFMAUD28tKYl4wTlgnwqrB5dfplYZfSlr4AygZXFwnkkIAE/BE 9QQm2Dhbyk//EhHwq2J9srfdwMrFSsAZG/MAd7dlEEgZVufj8J19jQyR2x+YuwiT689N /7YnY1JGqYiuu5IPbx6yP7be6i/vgMm76KEuXqwM2FVa5NZhmfS+tHf71r2kEbWDRNUv MQPOBqcWz5DLzay0cqCkK7Ehenhj5Awoc5+BKpS4Cj58hdlcP5wpge1k1CqUXya4WiXT czwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pWDBUqHr7U7WLBcTgCiyJ33WLBjatxwS5P2S/A7RDBQ=; b=MAPSCAPLoKwodSRwdlqLAE5fUG7K48II3GFwAK68dEC2BGRnm6z01BGnc/f4bYX5K/ Qv2raj6xhBMBc+5rhZ2ZKf5/UEDGc/dYFpYw1nNx/pZnxAKmsBNPsauLLHrNqItCbPpn eoZV7eEOKVt0wlHCk8tCByr7fweUjSzwu7S9XYrVQGEVmxpZ/6GjuoOoECETwoucc02Z WGKhv9irk057CaVABFout+k+BEuUFaZMTEp5IgnM9iluqxAVPYPe+KvA8CF8nMnJL31Q OtGhBdB9ZBCeWQ5o/pstNaQ1c5NXbcncW2SBOfqgvGn2gk2wjfjXX/6jB5mpKl8qjUBc +J0A== X-Gm-Message-State: AOAM533vVQ7DWtXZmB3Em0Okr6JipCVTF4MDK98m8m+QbgbuHs6LD+FW w1niCW0AH6AKxk+tqPU7M/WfgA== X-Google-Smtp-Source: ABdhPJwhLfR00h+uYq+uYY1yJox6+UwpYi7/wrMRQVKm3YC6QqxytGi2LqTren81bWZCr8wuczbegQ== X-Received: by 2002:a05:6000:2c9:b0:204:1cdb:8f8d with SMTP id o9-20020a05600002c900b002041cdb8f8dmr20055658wry.49.1648420891115; Sun, 27 Mar 2022 15:41:31 -0700 (PDT) Received: from localhost.localdomain (2a02-8440-6341-357e-3074-96af-9642-0002.rev.sfr.net. [2a02:8440:6341:357e:3074:96af:9642:2]) by smtp.gmail.com with ESMTPSA id v5-20020adfe4c5000000b001edc1e5053esm10400867wrm.82.2022.03.27.15.41.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 27 Mar 2022 15:41:30 -0700 (PDT) From: Guillaume Ranquet To: airlied@linux.ie, angelogioacchino.delregno@collabora.com, chunfeng.yun@mediatek.com, chunkuang.hu@kernel.org, ck.hu@mediatek.com, daniel@ffwll.ch, deller@gmx.de, jitao.shi@mediatek.com, kishon@ti.com, krzk+dt@kernel.org, maarten.lankhorst@linux.intel.com, matthias.bgg@gmail.com, mripard@kernel.org, p.zabel@pengutronix.de, robh+dt@kernel.org, tzimmermann@suse.de, vkoul@kernel.org Cc: devicetree@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org, markyacoub@google.com Subject: [PATCH v9 19/22] drm/mediatek: Add mt8195 External DisplayPort support Date: Mon, 28 Mar 2022 00:39:24 +0200 Message-Id: <20220327223927.20848-20-granquet@baylibre.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220327223927.20848-1-granquet@baylibre.com> References: <20220327223927.20848-1-granquet@baylibre.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220327_154132_990250_2B3D9DB4 X-CRM114-Status: GOOD ( 18.41 ) X-BeenThere: linux-mediatek@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "Linux-mediatek" Errors-To: linux-mediatek-bounces+linux-mediatek=archiver.kernel.org@lists.infradead.org This patch adds External DisplayPort support to the mt8195 eDP driver. Signed-off-by: Guillaume Ranquet --- drivers/gpu/drm/mediatek/mtk_dp.c | 301 +++++++++++++++++++++++++++--- 1 file changed, 278 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 7cd8459cf719..9e532408f12e 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -176,6 +176,11 @@ struct mtk_dp { struct drm_connector *conn; }; +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) +{ + return mtk_dp->next_bridge; +} + static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) { return container_of(b, struct mtk_dp, bridge); @@ -741,6 +746,47 @@ static int mtk_dp_fec_enable(struct mtk_dp *mtk_dp, bool enable) FEC_EN_DP_TRANS_P0_MASK); } +static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) & + SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, irq_status, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_35C8, 0, + SW_IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + +static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp) +{ + int ret; + + u8 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) & + IRQ_STATUS_DP_TRANS_P0_MASK) >> + IRQ_STATUS_DP_TRANS_P0_SHIFT; + + if (irq_status) { + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, irq_status, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + MTK_UPD_BITS_OR_OUT(mtk_dp, MTK_DP_TRANS_P0_3418, 0, + IRQ_CLR_DP_TRANS_P0_MASK, ret, out); + } + + return irq_status; + +out: + return ret; +} + static int mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable) { u32 val = 0; @@ -932,26 +978,49 @@ static int mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp) return PTR_ERR(buf); } - cal_data->glb_bias_trim = - check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); - cal_data->clktx_impse = - check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[0] = - check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[1] = - check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[2] = - check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); - cal_data->ln_tx_impsel_pmos[3] = - check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); - cal_data->ln_tx_impsel_nmos[3] = - check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + if (mtk_dp_is_edp(mtk_dp)) { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[3] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 9) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[2] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[2] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[2] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[2] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[2] & 0xf, 0x8); + } else { + cal_data->glb_bias_trim = + check_cal_data_valid(1, 0x1e, (buf[0] >> 27) & 0x1f, 0xf); + cal_data->clktx_impse = + check_cal_data_valid(1, 0xe, (buf[0] >> 13) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 28) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[0] = + check_cal_data_valid(1, 0xe, (buf[1] >> 24) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 20) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[1] = + check_cal_data_valid(1, 0xe, (buf[1] >> 16) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 12) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[2] = + check_cal_data_valid(1, 0xe, (buf[1] >> 8) & 0xf, 0x8); + cal_data->ln_tx_impsel_pmos[3] = + check_cal_data_valid(1, 0xe, (buf[1] >> 4) & 0xf, 0x8); + cal_data->ln_tx_impsel_nmos[3] = + check_cal_data_valid(1, 0xe, buf[1] & 0xf, 0x8); + } kfree(buf); @@ -1080,7 +1149,10 @@ static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable) VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | VIDEO_MUTE_SW_DP_ENC0_P0_MASK); - mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, enable); + else + mtk_dp_sip_atf_call(MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); } static int mtk_dp_power_enable(struct mtk_dp *mtk_dp) @@ -1195,6 +1267,57 @@ static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp) mtk_dp_setup_tu(mtk_dp); } +static void mtk_dp_edid_free(struct mtk_dp *mtk_dp) +{ + mutex_lock(&mtk_dp->edid_lock); + kfree(mtk_dp->edid); + mtk_dp->edid = NULL; + mutex_unlock(&mtk_dp->edid_lock); +} + +static int mtk_dp_hpd_sink_event(struct mtk_dp *mtk_dp) +{ + ssize_t ret; + u8 sink_count; + bool locked; + u8 link_status[DP_LINK_STATUS_SIZE] = {}; + u32 sink_count_reg = DP_SINK_COUNT_ESI; + u32 link_status_reg = DP_LANE0_1_STATUS; + + ret = drm_dp_dpcd_readb(&mtk_dp->aux, sink_count_reg, &sink_count); + if (ret < 0) { + drm_err(mtk_dp->drm_dev, "Read sink count failed: %ld\n", ret); + return ret; + } + + ret = drm_dp_dpcd_read(&mtk_dp->aux, link_status_reg, link_status, + sizeof(link_status)); + if (!ret) { + drm_err(mtk_dp->drm_dev, "Read link status failed: %ld\n", + ret); + return ret; + } + + locked = drm_dp_channel_eq_ok(link_status, + mtk_dp->train_info.lane_count); + if (!locked && mtk_dp->train_state > MTK_DP_TRAIN_STATE_TRAINING_PRE) + mtk_dp->train_state = MTK_DP_TRAIN_STATE_TRAINING_PRE; + + if (link_status[1] & DP_REMOTE_CONTROL_COMMAND_PENDING) + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DEVICE_SERVICE_IRQ_VECTOR, + DP_REMOTE_CONTROL_COMMAND_PENDING); + + if (DP_GET_SINK_COUNT(sink_count) && + (link_status[2] & DP_DOWNSTREAM_PORT_STATUS_CHANGED)) { + mtk_dp_edid_free(mtk_dp); + mtk_dp->train_info.check_cap_count = 0; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_CHECKEDID; + msleep(20); + } + + return 0; +} + static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes, u8 dpcd_adjust_req[2]) { @@ -1677,6 +1800,115 @@ static void mtk_dp_init_port(struct mtk_dp *mtk_dp) mtk_dp_digital_sw_reset(mtk_dp); } +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + int event; + u8 buf[DP_RECEIVER_CAP_SIZE] = {}; + + event = mtk_dp_plug_state(mtk_dp) ? connector_status_connected : + connector_status_disconnected; + + if (event < 0) + return IRQ_HANDLED; + + if (mtk_dp->drm_dev) { + dev_info(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); + } + + if (mtk_dp->train_info.cable_state_change) { + mtk_dp->train_info.cable_state_change = false; + + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + + if (!mtk_dp->train_info.cable_plugged_in || + !mtk_dp_plug_state(mtk_dp)) { + mtk_dp_video_mute(mtk_dp, true); + + mtk_dp_initialize_priv_data(mtk_dp); + mtk_dp_set_idle_pattern(mtk_dp, true); + if (mtk_dp->has_fec) + mtk_dp_fec_enable(mtk_dp, false); + + mtk_dp_edid_free(mtk_dp); + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL, + DP_PWR_STATE_MASK); + } else { + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, + DP_PWR_STATE_BANDGAP_TPLL_LANE, + DP_PWR_STATE_MASK); + drm_dp_read_dpcd_caps(&mtk_dp->aux, buf); + mtk_dp->train_info.link_rate = + min_t(int, MTK_DP_MAX_LINK_RATE, + buf[DP_MAX_LINK_RATE]); + mtk_dp->train_info.lane_count = + min_t(int, MTK_DP_MAX_LANES, + drm_dp_max_lane_count(buf)); + } + } + + if (mtk_dp->train_info.irq_status & MTK_DP_HPD_INTERRUPT) { + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); + mtk_dp->train_info.irq_status &= ~MTK_DP_HPD_INTERRUPT; + mtk_dp_hpd_sink_event(mtk_dp); + } + + return IRQ_HANDLED; +} + +static irqreturn_t mtk_dp_hpd_isr_handler(struct mtk_dp *mtk_dp) +{ + bool connected; + u16 swirq_status = mtk_dp_swirq_get_clear(mtk_dp); + u8 hwirq_status = mtk_dp_hwirq_get_clear(mtk_dp); + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; + + train_info->irq_status |= hwirq_status | swirq_status; + + if (!train_info->irq_status) + return IRQ_HANDLED; + + connected = mtk_dp_plug_state(mtk_dp); + if (connected || !train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + else if (!connected || train_info->cable_plugged_in) + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + + if (!(train_info->irq_status & + (MTK_DP_HPD_CONNECT | MTK_DP_HPD_DISCONNECT))) + return IRQ_HANDLED; + + if (train_info->irq_status & MTK_DP_HPD_CONNECT) { + train_info->irq_status &= ~MTK_DP_HPD_CONNECT; + train_info->cable_plugged_in = true; + } else { + train_info->irq_status &= ~MTK_DP_HPD_DISCONNECT; + train_info->cable_plugged_in = false; + mtk_dp->train_state = MTK_DP_TRAIN_STATE_STARTUP; + } + train_info->cable_state_change = true; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) +{ + struct mtk_dp *mtk_dp = dev; + u32 irq_status; + + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); + + if (!irq_status) + return IRQ_HANDLED; + + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) + return mtk_dp_hpd_isr_handler(mtk_dp); + + return IRQ_HANDLED; +} + static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp, struct platform_device *pdev) { @@ -2110,6 +2342,7 @@ static int mtk_dp_probe(struct platform_device *pdev) struct mtk_dp *mtk_dp; struct device *dev = &pdev->dev; int ret; + int irq_num = 0; mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL); if (!mtk_dp) @@ -2117,9 +2350,18 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->dev = dev; + irq_num = platform_get_irq(pdev, 0); + if (irq_num < 0) + return dev_err_probe(dev, irq_num, "failed to request dp irq resource\n"); + mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); - if (IS_ERR(mtk_dp->next_bridge)) + if (IS_ERR(mtk_dp->next_bridge) && PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { + dev_info(dev, + "No panel connected in devicetree, continuing as external DP\n"); + mtk_dp->next_bridge = NULL; + } else if (IS_ERR(mtk_dp->next_bridge)) { return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), "Failed to get bridge\n"); + } ret = mtk_dp_dt_parse(mtk_dp, pdev); if (ret) @@ -2129,6 +2371,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->aux.name = "aux_mtk_dp"; mtk_dp->aux.transfer = mtk_dp_aux_transfer; + ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event, + mtk_dp_hpd_event_thread, + IRQ_TYPE_LEVEL_HIGH, dev_name(dev), + mtk_dp); + if (ret) + return dev_err_probe(dev, -EPROBE_DEFER, "failed to request mediatek dptx irq\n"); + mutex_init(&mtk_dp->dp_lock); mutex_init(&mtk_dp->edid_lock); @@ -2145,8 +2394,13 @@ static int mtk_dp_probe(struct platform_device *pdev) mtk_dp->bridge.of_node = dev->of_node; mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; - mtk_dp->bridge.ops = DRM_BRIDGE_OP_EDID; + mtk_dp->bridge.ops = + DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD; drm_bridge_add(&mtk_dp->bridge); + if (mtk_dp_is_edp(mtk_dp)) + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; + else + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -2201,6 +2455,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume); static const struct of_device_id mtk_dp_of_match[] = { { .compatible = "mediatek,mt8195-edp-tx", }, + { .compatible = "mediatek,mt8195-dp-tx", }, {}, }; MODULE_DEVICE_TABLE(of, mtk_dp_of_match); -- 2.34.1 _______________________________________________ Linux-mediatek mailing list Linux-mediatek@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-mediatek