From nobody Wed Apr 1 08:38:30 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 55AEB37C920 for ; Wed, 1 Apr 2026 03:39:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014751; cv=none; b=aRVoZbkTQCXstPkdlSMEaNNS/+9I4vrNumSE1XpZTKhy0aURHJbQlnR5GU7AFnDQVw35b094/3N7lvZXnGmrMdJbEziT/Ph5DaCuuMaw0v0U+RyfpK+Qac1RgxYvBPugy0XKYGILskINgP1/zy19Lg8ZMHcvIMdBL8Bpv3tHiOw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014751; c=relaxed/simple; bh=HdbXexRQxat6bPhP8QfezUe2ACGcWPU+qvPWqirSBVc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=d7/SW6XkFoAs4KzzfAkfpBr9ijY/wxBJpO+GvXDsjxFEqoFs/Blzct3BiXY5IMBPBLrATxhSpZdI7i+7FfeWyZE4nRFPeuYr+NWyn2ORO2aW3yBDOuzhb11qlJ2CNPOVh5R2Ih02q7/P4D9lhPtkxXkpqZ3I4SVRZOhPyVdfCzc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=fGW9ahYH; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=TZQfJvp1; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="fGW9ahYH"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="TZQfJvp1" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6312Ixgd1005237 for ; Wed, 1 Apr 2026 03:39:04 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= tjr+bWUUXR8TXpnpC0s9ljtne5oqp7da1JCMSS5Say0=; b=fGW9ahYHmkoLIfR4 Q8wzsRuGJmJIMkiJJmbNEQin5LY00I5AYLY8JdZEPwlIj1DDMNKJ1LzstSoBiu8c qQWp090Hd2YgaIH7b5Pm6mQpfP2w3U3vJcK1jPEt80SG1XRPoQN3YA60dwe6u3e9 mxNhAyFCIfeBhisiZSdwq4WHTFE3IU9/WWstczKVjI6opAgjvIPJ4FHutUW89IYi 73QU1R5q/mlqBLdUy7fNKSLJSS8Xn/UOzXiBxMMlShuqHFDObDEJhLN4V/BwLlCd AEn0SUhIpQshwoKy8LuvogQFnTmNPnj2hE7p95w4RvuWX7HLoL1AVg4X1VtcmAik NfihTA== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4d8tfjg8jr-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 01 Apr 2026 03:39:03 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-50b317c40acso60967531cf.1 for ; Tue, 31 Mar 2026 20:39:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1775014742; x=1775619542; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=tjr+bWUUXR8TXpnpC0s9ljtne5oqp7da1JCMSS5Say0=; b=TZQfJvp1HqQWVp2kUdySO+tHAeQ/ezbY+QWw5L1IUCR+loq/SifBBNTMKpLJQOi2k3 qMO+2dtYlGfaZqrHEtKJhP1l4WeZMqX3LRQHsR38Esw0LMNRAQGI7LxBs+C57xlAjmj3 5wIDS/S+XuI5K2OCEN6EdE8my4xcJ2YbwN6vVhMx0r3DDrfmRS5KxHWdY97+57mixvtE Mb7qop2cM3rD+EtzyqChYHFwloG+wrdjH1/OVpEt6CUqeMJe8kbIVTyFrTBwQg0KUvZE m3NCuC5U/g1nuLop714NsIVmtE+ZFR90wBfu9VJfqANg6nP0Q3hXgQbZXNXO77TXNucX oYqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775014742; x=1775619542; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=tjr+bWUUXR8TXpnpC0s9ljtne5oqp7da1JCMSS5Say0=; b=acYCVyqdFBxsveLicHkJ+p1pkUzlqbuJ3kcHzsaS10aYZ8FPB8vDtsZjNj0NlaAf5X h6O3yCUTYkcaPh2b9ebcwvtZOQv+fLRePtpxyEUULBX4BfeOfLGhpqhwAtT8TOL+1o/B IsyOlJe9F+jXdevWOzek//6tAnrpgYmDkEBVGdy3BBHlMS7RHgAhnElVTzpXDR0tsCyF TDTebMnfGrhwPhlsDJtTZbSEWnsF6hKhXhzqiFPGxmGUr3RJxtSkLDzVkfOeESldzM+P 9q+OhfVvOBN2QcnLUhwbYJg51cDfSlRgXVEW6HHczVaMG/kmObVs9YXfX17wXH/WZb2G OeiA== X-Gm-Message-State: AOJu0YwKOpEtOsvjkhwMj5KwJqfDGS7bAW5ILTnPKRMvu8PhCcLqQ8vJ ysUlKlWNHRlxuoAsljmHi4t63wMgxMzRcQPoaeCd93MP3EVgjDoZNcqU3ldEwA61yDppo7JdGrF hTXpQJriO1MnFKMePO1MTqZu5lWEQ7c3ogv5BN7Hsp3K1HfO7l3Bk9vIu9x64RXzZCsQ= X-Gm-Gg: ATEYQzxDMYY1HnetKfaWqWYKxcrNljCQc/rsQ6OkiqQkuAEDGaHf0M3Bf1LuRo8MdrS VB8PiDgVJxJJRDi3Pqa2RoEc+H8fwEvxn756UnQXxIcVEHIeIApq521fY6jPnLPHd+2bFfkEHIg zbHB3TtE0C7dnPcjy8jYlR2UR74G9bknI5aLnILuRCLG7QKGvswwmDp0j3vOL6aIbdmXiJMG7uK IxCqiiYE8TeTexCrBtWdGmZW5Y/W1ggr35fb0kQ8Md+lYWPpXymZrOiUdMwdWryDBMEEhTBUIpz Aixh34XYgGrQaCmHnPom8SW0BCl4Pf8CBTKGnycPh11ZfN2WP4nZ1dmyBchY+kE5Fvr9EXxPbIJ OR/oYcLvVgqFVHmhNuV5GMRpun0NJ/NJKrCfn2xf27JTYYI06xJlo6unuRrUa1apZQZkHeQCBwK XJD8OSx8Wp30OgkJkLpCFcmCqlK1G8/xDeVHc= X-Received: by 2002:a05:622a:124f:b0:50b:2e8c:6ae0 with SMTP id d75a77b69052e-50d3bce3f0fmr28402961cf.45.1775014740488; Tue, 31 Mar 2026 20:39:00 -0700 (PDT) X-Received: by 2002:a05:622a:124f:b0:50b:2e8c:6ae0 with SMTP id d75a77b69052e-50d3bce3f0fmr28402461cf.45.1775014739309; Tue, 31 Mar 2026 20:38:59 -0700 (PDT) Received: from umbar.lan (2001-14ba-a073-af00-264b-feff-fe8b-be8a.rev.dnainternet.fi. [2001:14ba:a073:af00:264b:feff:fe8b:be8a]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-38c83730f7csm24404851fa.16.2026.03.31.20.38.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 20:38:56 -0700 (PDT) From: Dmitry Baryshkov Date: Wed, 01 Apr 2026 06:38:46 +0300 Subject: [PATCH v8 1/4] drm/msm/hdmi: switch to generic PHY subsystem Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260401-fd-hdmi-phy-v8-1-51b0e98edf6c@oss.qualcomm.com> References: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> In-Reply-To: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> To: Rob Clark , Dmitry Baryshkov , Abhinav Kumar , Jessica Zhang , Sean Paul , Marijn Suijten , David Airlie , Simona Vetter , Vinod Koul , Neil Armstrong Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, dri-devel@lists.freedesktop.org, freedreno@lists.freedesktop.org, linux-phy@lists.infradead.org, Dmitry Baryshkov , Konrad Dybcio X-Mailer: b4 0.15.1 X-Developer-Signature: v=1; a=openpgp-sha256; l=187292; i=dmitry.baryshkov@oss.qualcomm.com; h=from:subject:message-id; bh=Mh8sziVorwsNBhW3jYPvoGvaIF+w6hXNUq6ydzMUMT8=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBpzJNLzio6OkGvMM94birZUf/QllplY+P5/Io7n hZvgwGW1lGJATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCacyTSwAKCRCLPIo+Aiko 1bpBB/4nRST8PCIm1r0b/F5uzLNTZwhDLFK1cWoh1gnqb0bTu5lQ36uK1zhqgBmLAtmK7oe5xwH u8PQcho6z3SWT+f69K+J6ppEEYfIbZlT4JHwHayXwKZvWU5expi13QR6LFBNkVcj2l3gEK4j2VH c/QejYv5z/vzCo4wVJ5gx6E2FPs7aqk/kc0/0sjZWjs9Hbg7MYxZ284slaqSeRP+URxb4CkJn2a X0LESyxxQST9PBvChN+sRUKoaNoCvnCzrVBlPpl/NWXCc31x1tvopttS6mZor2XTETYN+wKB/yo Xr8lMCBXvXTo3h3M2g+UhsyA5qsyW+iw8LiG3xPnCCzpEi/H X-Developer-Key: i=dmitry.baryshkov@oss.qualcomm.com; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A X-Proofpoint-GUID: u-JMP3A-Iv_108irzhCVmqqBwnZvyCJ- X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDAxMDAyNiBTYWx0ZWRfXy1bP5JULrJuA 96mauFIjbEqW+X29yEGa2YSnkXKwjVF8P6/XwOdTGIsjpnkK+k5l12zs8e7ul8FBUYwQNZbkcpC c+856wT9j3S16lZ6xEOoyVg5l5vsMflyFHbhOvZVXfEiCPQ38FMGsqzb7PuN0bLm91Ao/xb9HrA kXVGI4G4SX/PNjiB4nRA1UrfnF5jcCrAqFVdEUQ5ESVHyo9ECEatTjlagLcy+kWkaqarb9BXyze SgloNj3cRMb4adgsjEBXcfiz8/Tpunk9w50ILgFoV5EFWsZmSvXBm8HQdRvYwUyGUFy/uDrPBx+ sCd1GqQTzNFzTOLnkc/87l/7zUrYdCoZc4jzvKahDmTbxRoka/1yUl0ZIdJhoWzz82eGQH555aG CfReXjmxzF+8fr83pCfNPlbsNHNANR4/W6kv2Jxu9daXu/1t37Lfgz8FGEH/U8mblXpecrRw7Qv Tv8rVuAwEZDXWlRHXGg== X-Authority-Analysis: v=2.4 cv=fJc0HJae c=1 sm=1 tr=0 ts=69cc9357 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=A5OVakUREuEA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=Um2Pa8k9VHT-vaBCBUpS:22 a=e5mUnYsNAAAA:8 a=KKAkSRfTAAAA:8 a=EUspDBNiAAAA:8 a=pGLkceISAAAA:8 a=4EzAlzZ1jyUCRA57W_QA:9 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 a=Vxmtnl_E_bksehYqCbjh:22 a=cvBusfyB2V15izCimMoJ:22 X-Proofpoint-ORIG-GUID: u-JMP3A-Iv_108irzhCVmqqBwnZvyCJ- X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-01_01,2026-03-31_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 phishscore=0 suspectscore=0 lowpriorityscore=0 adultscore=0 priorityscore=1501 spamscore=0 clxscore=1015 bulkscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2603050001 definitions=main-2604010026 From: Dmitry Baryshkov Change the MSM HDMI driver to use generic PHY subsystem. Moving PHY drivers allows better code sharing with the rest of the PHY system. Signed-off-by: Dmitry Baryshkov Acked-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov --- drivers/gpu/drm/msm/Makefile | 7 - drivers/gpu/drm/msm/hdmi/hdmi.c | 59 +- drivers/gpu/drm/msm/hdmi/hdmi.h | 80 +-- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 80 ++- drivers/gpu/drm/msm/hdmi/hdmi_phy.c | 226 ------- drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c | 51 -- drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c | 761 -------------------= --- drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c | 765 -------------------= ---- drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c | 141 ----- drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c | 44 -- drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c | 460 -------------- drivers/gpu/drm/msm/registers/display/hdmi.xml | 537 ---------------- drivers/phy/qualcomm/Kconfig | 24 + drivers/phy/qualcomm/Makefile | 14 + drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c | 71 +++ drivers/phy/qualcomm/phy-qcom-hdmi-28lpm.c | 478 ++++++++++++++ drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c | 186 ++++++ drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c | 213 +++++++ drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h | 59 ++ drivers/phy/qualcomm/phy-qcom-qmp-hdmi-base.c | 187 ++++++ drivers/phy/qualcomm/phy-qcom-qmp-hdmi-msm8996.c | 440 +++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp-hdmi-msm8998.c | 489 +++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp-hdmi.h | 49 ++ 23 files changed, 2254 insertions(+), 3167 deletions(-) diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 8b94c5f1cb68..caad271a0283 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -36,13 +36,6 @@ msm-display-$(CONFIG_DRM_MSM_HDMI) +=3D \ hdmi/hdmi_bridge.o \ hdmi/hdmi_hpd.o \ hdmi/hdmi_i2c.o \ - hdmi/hdmi_phy.o \ - hdmi/hdmi_phy_8960.o \ - hdmi/hdmi_phy_8996.o \ - hdmi/hdmi_phy_8998.o \ - hdmi/hdmi_phy_8x60.o \ - hdmi/hdmi_phy_8x74.o \ - hdmi/hdmi_pll_8960.o \ =20 msm-display-$(CONFIG_DRM_MSM_MDP4) +=3D \ disp/mdp4/mdp4_crtc.o \ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdm= i.c index 852abb2466f0..d66bbad5f10c 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -10,6 +10,7 @@ #include #include #include +#include =20 #include #include @@ -76,44 +77,6 @@ static void msm_hdmi_destroy(struct hdmi *hdmi) msm_hdmi_i2c_destroy(hdmi->i2c); } =20 -static void msm_hdmi_put_phy(struct hdmi *hdmi) -{ - if (hdmi->phy_dev) { - put_device(hdmi->phy_dev); - hdmi->phy =3D NULL; - hdmi->phy_dev =3D NULL; - } -} - -static int msm_hdmi_get_phy(struct hdmi *hdmi) -{ - struct platform_device *pdev =3D hdmi->pdev; - struct platform_device *phy_pdev; - struct device_node *phy_node; - - phy_node =3D of_parse_phandle(dev_of_node(&pdev->dev), "phys", 0); - if (!phy_node) { - DRM_DEV_ERROR(&pdev->dev, "cannot find phy device\n"); - return -ENXIO; - } - - phy_pdev =3D of_find_device_by_node(phy_node); - of_node_put(phy_node); - - if (!phy_pdev) - return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "phy driver is not ready= \n"); - - hdmi->phy =3D platform_get_drvdata(phy_pdev); - if (!hdmi->phy) { - put_device(&phy_pdev->dev); - return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "phy driver is not ready= \n"); - } - - hdmi->phy_dev =3D &phy_pdev->dev; - - return 0; -} - /* construct hdmi at bind/probe time, grab all the resources. If * we are to EPROBE_DEFER we want to do it here, rather than later * at modeset_init() time @@ -355,34 +318,30 @@ static int msm_hdmi_dev_probe(struct platform_device = *pdev) if (hdmi->hpd_gpiod) gpiod_set_consumer_name(hdmi->hpd_gpiod, "HDMI_HPD"); =20 - ret =3D msm_hdmi_get_phy(hdmi); - if (ret) - return ret; + hdmi->phy =3D devm_phy_get(dev, NULL); + if (IS_ERR(hdmi->phy)) + return dev_err_probe(dev, PTR_ERR(hdmi->phy), + "failed to get phy\n"); =20 ret =3D devm_pm_runtime_enable(dev); if (ret) - goto err_put_phy; + goto err; =20 platform_set_drvdata(pdev, hdmi); =20 ret =3D component_add(dev, &msm_hdmi_ops); if (ret) - goto err_put_phy; + goto err; =20 return 0; =20 -err_put_phy: - msm_hdmi_put_phy(hdmi); +err: return ret; } =20 static void msm_hdmi_dev_remove(struct platform_device *pdev) { - struct hdmi *hdmi =3D dev_get_drvdata(&pdev->dev); - component_del(&pdev->dev, &msm_hdmi_ops); - - msm_hdmi_put_phy(hdmi); } =20 static int msm_hdmi_runtime_suspend(struct device *dev) @@ -451,12 +410,10 @@ static struct platform_driver msm_hdmi_driver =3D { =20 void __init msm_hdmi_register(void) { - msm_hdmi_phy_driver_register(); platform_driver_register(&msm_hdmi_driver); } =20 void __exit msm_hdmi_unregister(void) { platform_driver_unregister(&msm_hdmi_driver); - msm_hdmi_phy_driver_unregister(); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdm= i.h index 49433f7727c3..5c58342d370f 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -19,7 +19,6 @@ #include "msm_drv.h" #include "hdmi.xml.h" =20 -struct hdmi_phy; struct hdmi_platform_config; =20 struct hdmi_audio { @@ -55,8 +54,7 @@ struct hdmi { =20 struct gpio_desc *hpd_gpiod; =20 - struct hdmi_phy *phy; - struct device *phy_dev; + struct phy *phy; =20 struct i2c_adapter *i2c; struct drm_connector *connector; @@ -117,82 +115,6 @@ static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, = u32 reg) return readl(hdmi->qfprom_mmio + reg); } =20 -/* - * hdmi phy: - */ - -enum hdmi_phy_type { - MSM_HDMI_PHY_8x60, - MSM_HDMI_PHY_8960, - MSM_HDMI_PHY_8x74, - MSM_HDMI_PHY_8996, - MSM_HDMI_PHY_8998, - MSM_HDMI_PHY_MAX, -}; - -struct hdmi_phy_cfg { - enum hdmi_phy_type type; - void (*powerup)(struct hdmi_phy *phy, unsigned long pixclock); - void (*powerdown)(struct hdmi_phy *phy); - const char * const *reg_names; - int num_regs; - const char * const *clk_names; - int num_clks; -}; - -extern const struct hdmi_phy_cfg msm_hdmi_phy_8x60_cfg; -extern const struct hdmi_phy_cfg msm_hdmi_phy_8960_cfg; -extern const struct hdmi_phy_cfg msm_hdmi_phy_8x74_cfg; -extern const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg; -extern const struct hdmi_phy_cfg msm_hdmi_phy_8998_cfg; - -struct hdmi_phy { - struct platform_device *pdev; - void __iomem *mmio; - struct hdmi_phy_cfg *cfg; - const struct hdmi_phy_funcs *funcs; - struct regulator_bulk_data *regs; - struct clk **clks; -}; - -static inline void hdmi_phy_write(struct hdmi_phy *phy, u32 reg, u32 data) -{ - writel(data, phy->mmio + reg); -} - -static inline u32 hdmi_phy_read(struct hdmi_phy *phy, u32 reg) -{ - return readl(phy->mmio + reg); -} - -int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy); -void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy); -void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long pixclock); -void msm_hdmi_phy_powerdown(struct hdmi_phy *phy); -void __init msm_hdmi_phy_driver_register(void); -void __exit msm_hdmi_phy_driver_unregister(void); - -#ifdef CONFIG_COMMON_CLK -int msm_hdmi_pll_8960_init(struct platform_device *pdev); -int msm_hdmi_pll_8996_init(struct platform_device *pdev); -int msm_hdmi_pll_8998_init(struct platform_device *pdev); -#else -static inline int msm_hdmi_pll_8960_init(struct platform_device *pdev) -{ - return -ENODEV; -} - -static inline int msm_hdmi_pll_8996_init(struct platform_device *pdev) -{ - return -ENODEV; -} - -static inline int msm_hdmi_pll_8998_init(struct platform_device *pdev) -{ - return -ENODEV; -} -#endif - /* * audio: */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/h= dmi/hdmi_bridge.c index a9eb6489c520..26399c679c13 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -5,6 +5,7 @@ */ =20 #include +#include #include #include #include @@ -13,43 +14,6 @@ #include "msm_kms.h" #include "hdmi.h" =20 -static void msm_hdmi_power_on(struct drm_bridge *bridge) -{ - struct drm_device *dev =3D bridge->dev; - struct hdmi_bridge *hdmi_bridge =3D to_hdmi_bridge(bridge); - struct hdmi *hdmi =3D hdmi_bridge->hdmi; - int ret; - - pm_runtime_resume_and_get(&hdmi->pdev->dev); - - if (hdmi->extp_clk) { - DBG("pixclock: %lu", hdmi->pixclock); - ret =3D clk_set_rate(hdmi->extp_clk, hdmi->pixclock); - if (ret) - DRM_DEV_ERROR(dev->dev, "failed to set extp clk rate: %d\n", ret); - - ret =3D clk_prepare_enable(hdmi->extp_clk); - if (ret) - DRM_DEV_ERROR(dev->dev, "failed to enable extp clk: %d\n", ret); - } -} - -static void power_off(struct drm_bridge *bridge) -{ - struct hdmi_bridge *hdmi_bridge =3D to_hdmi_bridge(bridge); - struct hdmi *hdmi =3D hdmi_bridge->hdmi; - - /* TODO do we need to wait for final vblank somewhere before - * cutting the clocks? - */ - mdelay(16 + 4); - - if (hdmi->extp_clk) - clk_disable_unprepare(hdmi->extp_clk); - - pm_runtime_put(&hdmi->pdev->dev); -} - #define AVI_IFRAME_LINE_NUMBER 1 #define SPD_IFRAME_LINE_NUMBER 1 #define VENSPEC_IFRAME_LINE_NUMBER 3 @@ -287,11 +251,12 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct = drm_bridge *bridge, { struct hdmi_bridge *hdmi_bridge =3D to_hdmi_bridge(bridge); struct hdmi *hdmi =3D hdmi_bridge->hdmi; - struct hdmi_phy *phy =3D hdmi->phy; struct drm_encoder *encoder =3D bridge->encoder; struct drm_connector *connector; struct drm_connector_state *conn_state; struct drm_crtc_state *crtc_state; + union phy_configure_opts phy_opts =3D {}; + int ret; =20 DBG("power up"); =20 @@ -305,8 +270,8 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct dr= m_bridge *bridge, =20 mutex_lock(&hdmi->state_mutex); if (!hdmi->power_on) { - msm_hdmi_phy_resource_enable(phy); - msm_hdmi_power_on(bridge); + phy_init(hdmi->phy); + pm_runtime_get_sync(&hdmi->pdev->dev); hdmi->power_on =3D true; } mutex_unlock(&hdmi->state_mutex); @@ -316,7 +281,23 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct d= rm_bridge *bridge, =20 drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); =20 - msm_hdmi_phy_powerup(phy, hdmi->pixclock); + phy_opts.hdmi.tmds_char_rate =3D conn_state->hdmi.tmds_char_rate; + phy_opts.hdmi.bpc =3D 8; + phy_configure(hdmi->phy, &phy_opts); + + ret =3D phy_power_on(hdmi->phy); + if (WARN_ON(ret)) + return; + + if (hdmi->extp_clk) { + ret =3D clk_set_rate(hdmi->extp_clk, hdmi->pixclock); + if (ret) + DRM_DEV_ERROR(bridge->dev->dev, "failed to set extp clk rate: %d\n", re= t); + + ret =3D clk_prepare_enable(hdmi->extp_clk); + if (ret) + DRM_DEV_ERROR(bridge->dev->dev, "failed to enable extp clk: %d\n", ret); + } =20 msm_hdmi_set_mode(hdmi, true); =20 @@ -329,7 +310,6 @@ static void msm_hdmi_bridge_atomic_post_disable(struct = drm_bridge *bridge, { struct hdmi_bridge *hdmi_bridge =3D to_hdmi_bridge(bridge); struct hdmi *hdmi =3D hdmi_bridge->hdmi; - struct hdmi_phy *phy =3D hdmi->phy; =20 if (hdmi->hdcp_ctrl) msm_hdmi_hdcp_off(hdmi->hdcp_ctrl); @@ -340,14 +320,24 @@ static void msm_hdmi_bridge_atomic_post_disable(struc= t drm_bridge *bridge, mutex_lock(&hdmi->state_mutex); msm_hdmi_set_mode(hdmi, hdmi->hpd_enabled); =20 - msm_hdmi_phy_powerdown(phy); + /* TODO do we need to wait for final vblank somewhere before + * cutting the clocks? + */ + mdelay(16 + 4); + + if (hdmi->extp_clk) + clk_disable_unprepare(hdmi->extp_clk); + + phy_power_off(hdmi->phy); =20 if (hdmi->power_on) { - power_off(bridge); + + pm_runtime_put(&hdmi->pdev->dev); + hdmi->power_on =3D false; if (hdmi->connector->display_info.is_hdmi) msm_hdmi_audio_update(hdmi); - msm_hdmi_phy_resource_disable(phy); + phy_exit(hdmi->phy); } mutex_unlock(&hdmi->state_mutex); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi= /hdmi_phy.c deleted file mode 100644 index eb1088755cb3..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. - */ - -#include -#include - -#include "hdmi.h" - -static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy) -{ - struct hdmi_phy_cfg *cfg =3D phy->cfg; - struct device *dev =3D &phy->pdev->dev; - int i, ret; - - phy->regs =3D devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]), - GFP_KERNEL); - if (!phy->regs) - return -ENOMEM; - - phy->clks =3D devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]), - GFP_KERNEL); - if (!phy->clks) - return -ENOMEM; - - for (i =3D 0; i < cfg->num_regs; i++) - phy->regs[i].supply =3D cfg->reg_names[i]; - - ret =3D devm_regulator_bulk_get(dev, cfg->num_regs, phy->regs); - if (ret) { - if (ret !=3D -EPROBE_DEFER) - DRM_DEV_ERROR(dev, "failed to get phy regulators: %d\n", ret); - - return ret; - } - - for (i =3D 0; i < cfg->num_clks; i++) { - struct clk *clk; - - clk =3D msm_clk_get(phy->pdev, cfg->clk_names[i]); - if (IS_ERR(clk)) { - ret =3D PTR_ERR(clk); - DRM_DEV_ERROR(dev, "failed to get phy clock: %s (%d)\n", - cfg->clk_names[i], ret); - return ret; - } - - phy->clks[i] =3D clk; - } - - return 0; -} - -int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy) -{ - struct hdmi_phy_cfg *cfg =3D phy->cfg; - struct device *dev =3D &phy->pdev->dev; - int i, ret =3D 0; - - ret =3D pm_runtime_resume_and_get(dev); - if (ret) { - DRM_DEV_ERROR(dev, "runtime resume failed: %d\n", ret); - return ret; - } - - ret =3D regulator_bulk_enable(cfg->num_regs, phy->regs); - if (ret) { - DRM_DEV_ERROR(dev, "failed to enable regulators: (%d)\n", ret); - return ret; - } - - for (i =3D 0; i < cfg->num_clks; i++) { - ret =3D clk_prepare_enable(phy->clks[i]); - if (ret) - DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n", - cfg->clk_names[i], ret); - } - - return ret; -} - -void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy) -{ - struct hdmi_phy_cfg *cfg =3D phy->cfg; - struct device *dev =3D &phy->pdev->dev; - int i; - - for (i =3D cfg->num_clks - 1; i >=3D 0; i--) - clk_disable_unprepare(phy->clks[i]); - - regulator_bulk_disable(cfg->num_regs, phy->regs); - - pm_runtime_put_sync(dev); -} - -void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long pixclock) -{ - if (!phy || !phy->cfg->powerup) - return; - - phy->cfg->powerup(phy, pixclock); -} - -void msm_hdmi_phy_powerdown(struct hdmi_phy *phy) -{ - if (!phy || !phy->cfg->powerdown) - return; - - phy->cfg->powerdown(phy); -} - -static int msm_hdmi_phy_pll_init(struct platform_device *pdev, - enum hdmi_phy_type type) -{ - int ret; - - switch (type) { - case MSM_HDMI_PHY_8960: - ret =3D msm_hdmi_pll_8960_init(pdev); - break; - case MSM_HDMI_PHY_8996: - ret =3D msm_hdmi_pll_8996_init(pdev); - break; - case MSM_HDMI_PHY_8998: - ret =3D msm_hdmi_pll_8998_init(pdev); - break; - /* - * we don't have PLL support for these, don't report an error for now - */ - case MSM_HDMI_PHY_8x60: - case MSM_HDMI_PHY_8x74: - default: - ret =3D 0; - break; - } - - return ret; -} - -static int msm_hdmi_phy_probe(struct platform_device *pdev) -{ - struct device *dev =3D &pdev->dev; - struct hdmi_phy *phy; - int ret; - - phy =3D devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENODEV; - - phy->cfg =3D (struct hdmi_phy_cfg *)of_device_get_match_data(dev); - if (!phy->cfg) - return -ENODEV; - - phy->mmio =3D msm_ioremap(pdev, "hdmi_phy"); - if (IS_ERR(phy->mmio)) { - DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__); - return -ENOMEM; - } - - phy->pdev =3D pdev; - - ret =3D msm_hdmi_phy_resource_init(phy); - if (ret) - return ret; - - pm_runtime_enable(&pdev->dev); - - ret =3D msm_hdmi_phy_resource_enable(phy); - if (ret) - return ret; - - ret =3D msm_hdmi_phy_pll_init(pdev, phy->cfg->type); - if (ret) { - DRM_DEV_ERROR(dev, "couldn't init PLL\n"); - msm_hdmi_phy_resource_disable(phy); - return ret; - } - - msm_hdmi_phy_resource_disable(phy); - - platform_set_drvdata(pdev, phy); - - return 0; -} - -static void msm_hdmi_phy_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); -} - -static const struct of_device_id msm_hdmi_phy_dt_match[] =3D { - { .compatible =3D "qcom,hdmi-phy-8660", - .data =3D &msm_hdmi_phy_8x60_cfg }, - { .compatible =3D "qcom,hdmi-phy-8960", - .data =3D &msm_hdmi_phy_8960_cfg }, - { .compatible =3D "qcom,hdmi-phy-8974", - .data =3D &msm_hdmi_phy_8x74_cfg }, - { .compatible =3D "qcom,hdmi-phy-8084", - .data =3D &msm_hdmi_phy_8x74_cfg }, - { .compatible =3D "qcom,hdmi-phy-8996", - .data =3D &msm_hdmi_phy_8996_cfg }, - { .compatible =3D "qcom,hdmi-phy-8998", - .data =3D &msm_hdmi_phy_8998_cfg }, - {} -}; -MODULE_DEVICE_TABLE(of, msm_hdmi_phy_dt_match); - -static struct platform_driver msm_hdmi_phy_platform_driver =3D { - .probe =3D msm_hdmi_phy_probe, - .remove =3D msm_hdmi_phy_remove, - .driver =3D { - .name =3D "msm_hdmi_phy", - .of_match_table =3D msm_hdmi_phy_dt_match, - }, -}; - -void __init msm_hdmi_phy_driver_register(void) -{ - platform_driver_register(&msm_hdmi_phy_platform_driver); -} - -void __exit msm_hdmi_phy_driver_unregister(void) -{ - platform_driver_unregister(&msm_hdmi_phy_platform_driver); -} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm= /hdmi/hdmi_phy_8960.c deleted file mode 100644 index cfa8fc494199..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Red Hat - * Author: Rob Clark - */ - -#include "hdmi.h" - -static void hdmi_phy_8960_powerup(struct hdmi_phy *phy, - unsigned long pixclock) -{ - DBG("pixclock: %lu", pixclock); - - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG0, 0x1b); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG1, 0xf2); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG4, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG5, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG6, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG7, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG8, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG9, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG10, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG11, 0x00); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG3, 0x20); -} - -static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy) -{ - DBG(""); - - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x7f); -} - -static const char * const hdmi_phy_8960_reg_names[] =3D { - "core-vdda", -}; - -static const char * const hdmi_phy_8960_clk_names[] =3D { - "slave_iface", -}; - -const struct hdmi_phy_cfg msm_hdmi_phy_8960_cfg =3D { - .type =3D MSM_HDMI_PHY_8960, - .powerup =3D hdmi_phy_8960_powerup, - .powerdown =3D hdmi_phy_8960_powerdown, - .reg_names =3D hdmi_phy_8960_reg_names, - .num_regs =3D ARRAY_SIZE(hdmi_phy_8960_reg_names), - .clk_names =3D hdmi_phy_8960_clk_names, - .num_clks =3D ARRAY_SIZE(hdmi_phy_8960_clk_names), -}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c b/drivers/gpu/drm/msm= /hdmi/hdmi_phy_8996.c deleted file mode 100644 index 36e928b0fd5a..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c +++ /dev/null @@ -1,761 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. - */ - -#include -#include - -#include "hdmi.h" - -#define HDMI_VCO_MAX_FREQ 12000000000UL -#define HDMI_VCO_MIN_FREQ 8000000000UL - -#define HDMI_PCLK_MAX_FREQ 600000000 -#define HDMI_PCLK_MIN_FREQ 25000000 - -#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL -#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL -#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL -#define HDMI_CORECLK_DIV 5 -#define HDMI_DEFAULT_REF_CLOCK 19200000 -#define HDMI_PLL_CMP_CNT 1024 - -#define HDMI_PLL_POLL_MAX_READS 100 -#define HDMI_PLL_POLL_TIMEOUT_US 150 - -#define HDMI_NUM_TX_CHANNEL 4 - -struct hdmi_pll_8996 { - struct platform_device *pdev; - struct clk_hw clk_hw; - - /* pll mmio base */ - void __iomem *mmio_qserdes_com; - /* tx channel base */ - void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL]; -}; - -#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8996, clk_hw) - -struct hdmi_8996_phy_pll_reg_cfg { - u32 tx_lx_lane_mode[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL]; - u32 com_svs_mode_clk_sel; - u32 com_hsclk_sel; - u32 com_pll_cctrl_mode0; - u32 com_pll_rctrl_mode0; - u32 com_cp_ctrl_mode0; - u32 com_dec_start_mode0; - u32 com_div_frac_start1_mode0; - u32 com_div_frac_start2_mode0; - u32 com_div_frac_start3_mode0; - u32 com_integloop_gain0_mode0; - u32 com_integloop_gain1_mode0; - u32 com_lock_cmp_en; - u32 com_lock_cmp1_mode0; - u32 com_lock_cmp2_mode0; - u32 com_lock_cmp3_mode0; - u32 com_core_clk_en; - u32 com_coreclk_div; - u32 com_vco_tune_ctrl; - - u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_vmode_ctrl1[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_vmode_ctrl2[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_res_code_lane_tx[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_hp_pd_enables[HDMI_NUM_TX_CHANNEL]; - - u32 phy_mode; -}; - -struct hdmi_8996_post_divider { - u64 vco_freq; - int hsclk_divsel; - int vco_ratio; - int tx_band_sel; - int half_rate_mode; -}; - -static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8996 *pll) -{ - return platform_get_drvdata(pll->pdev); -} - -static inline void hdmi_pll_write(struct hdmi_pll_8996 *pll, int offset, - u32 data) -{ - writel(data, pll->mmio_qserdes_com + offset); -} - -static inline u32 hdmi_pll_read(struct hdmi_pll_8996 *pll, int offset) -{ - return readl(pll->mmio_qserdes_com + offset); -} - -static inline void hdmi_tx_chan_write(struct hdmi_pll_8996 *pll, int chann= el, - int offset, int data) -{ - writel(data, pll->mmio_qserdes_tx[channel] + offset); -} - -static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk, - bool gen_ssc) -{ - if ((frac_start !=3D 0) || gen_ssc) - return (11000000 / (ref_clk / 20)); - - return 0x23; -} - -static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc) -{ - if ((frac_start !=3D 0) || gen_ssc) - return 0x16; - - return 0x10; -} - -static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc) -{ - if ((frac_start !=3D 0) || gen_ssc) - return 0x28; - - return 0x1; -} - -static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref= _clk, - bool gen_ssc) -{ - int digclk_divsel =3D bclk >=3D HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; - u64 base; - - if ((frac_start !=3D 0) || gen_ssc) - base =3D (64 * ref_clk) / HDMI_DEFAULT_REF_CLOCK; - else - base =3D (1022 * ref_clk) / 100; - - base <<=3D digclk_divsel; - - return (base <=3D 2046 ? base : 2046); -} - -static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk) -{ - u64 dividend =3D HDMI_PLL_CMP_CNT * fdata; - u32 divisor =3D ref_clk * 10; - u32 rem; - - rem =3D do_div(dividend, divisor); - if (rem > (divisor >> 1)) - dividend++; - - return dividend - 1; -} - -static inline u64 pll_cmp_to_fdata(u32 pll_cmp, unsigned long ref_clk) -{ - u64 fdata =3D ((u64)pll_cmp) * ref_clk * 10; - - do_div(fdata, HDMI_PLL_CMP_CNT); - - return fdata; -} - -static int pll_get_post_div(struct hdmi_8996_post_divider *pd, u64 bclk) -{ - int ratio[] =3D { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 }; - int hs_divsel[] =3D { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 }; - int tx_band_sel[] =3D { 0, 1, 2, 3 }; - u64 vco_freq[60]; - u64 vco, vco_optimal; - int half_rate_mode =3D 0; - int vco_optimal_index, vco_freq_index; - int i, j; - -retry: - vco_optimal =3D HDMI_VCO_MAX_FREQ; - vco_optimal_index =3D -1; - vco_freq_index =3D 0; - for (i =3D 0; i < 15; i++) { - for (j =3D 0; j < 4; j++) { - u32 ratio_mult =3D ratio[i] << tx_band_sel[j]; - - vco =3D bclk >> half_rate_mode; - vco *=3D ratio_mult; - vco_freq[vco_freq_index++] =3D vco; - } - } - - for (i =3D 0; i < 60; i++) { - u64 vco_tmp =3D vco_freq[i]; - - if ((vco_tmp >=3D HDMI_VCO_MIN_FREQ) && - (vco_tmp <=3D vco_optimal)) { - vco_optimal =3D vco_tmp; - vco_optimal_index =3D i; - } - } - - if (vco_optimal_index =3D=3D -1) { - if (!half_rate_mode) { - half_rate_mode =3D 1; - goto retry; - } - } else { - pd->vco_freq =3D vco_optimal; - pd->tx_band_sel =3D tx_band_sel[vco_optimal_index % 4]; - pd->vco_ratio =3D ratio[vco_optimal_index / 4]; - pd->hsclk_divsel =3D hs_divsel[vco_optimal_index / 4]; - - return 0; - } - - return -EINVAL; -} - -static int pll_calculate(unsigned long pix_clk, unsigned long ref_clk, - struct hdmi_8996_phy_pll_reg_cfg *cfg) -{ - struct hdmi_8996_post_divider pd; - u64 bclk; - u64 tmds_clk; - u64 dec_start; - u64 frac_start; - u64 fdata; - u32 pll_divisor; - u32 rem; - u32 cpctrl; - u32 rctrl; - u32 cctrl; - u32 integloop_gain; - u32 pll_cmp; - int i, ret; - - /* bit clk =3D 10 * pix_clk */ - bclk =3D ((u64)pix_clk) * 10; - - if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) - tmds_clk =3D pix_clk >> 2; - else - tmds_clk =3D pix_clk; - - ret =3D pll_get_post_div(&pd, bclk); - if (ret) - return ret; - - dec_start =3D pd.vco_freq; - pll_divisor =3D 4 * ref_clk; - do_div(dec_start, pll_divisor); - - frac_start =3D pd.vco_freq * (1 << 20); - - rem =3D do_div(frac_start, pll_divisor); - frac_start -=3D dec_start * (1 << 20); - if (rem > (pll_divisor >> 1)) - frac_start++; - - cpctrl =3D pll_get_cpctrl(frac_start, ref_clk, false); - rctrl =3D pll_get_rctrl(frac_start, false); - cctrl =3D pll_get_cctrl(frac_start, false); - integloop_gain =3D pll_get_integloop_gain(frac_start, bclk, - ref_clk, false); - - fdata =3D pd.vco_freq; - do_div(fdata, pd.vco_ratio); - - pll_cmp =3D pll_get_pll_cmp(fdata, ref_clk); - - DBG("VCO freq: %llu", pd.vco_freq); - DBG("fdata: %llu", fdata); - DBG("pix_clk: %lu", pix_clk); - DBG("tmds clk: %llu", tmds_clk); - DBG("HSCLK_SEL: %d", pd.hsclk_divsel); - DBG("DEC_START: %llu", dec_start); - DBG("DIV_FRAC_START: %llu", frac_start); - DBG("PLL_CPCTRL: %u", cpctrl); - DBG("PLL_RCTRL: %u", rctrl); - DBG("PLL_CCTRL: %u", cctrl); - DBG("INTEGLOOP_GAIN: %u", integloop_gain); - DBG("TX_BAND: %d", pd.tx_band_sel); - DBG("PLL_CMP: %u", pll_cmp); - - /* Convert these values to register specific values */ - if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) - cfg->com_svs_mode_clk_sel =3D 1; - else - cfg->com_svs_mode_clk_sel =3D 2; - - cfg->com_hsclk_sel =3D (0x20 | pd.hsclk_divsel); - cfg->com_pll_cctrl_mode0 =3D cctrl; - cfg->com_pll_rctrl_mode0 =3D rctrl; - cfg->com_cp_ctrl_mode0 =3D cpctrl; - cfg->com_dec_start_mode0 =3D dec_start; - cfg->com_div_frac_start1_mode0 =3D (frac_start & 0xff); - cfg->com_div_frac_start2_mode0 =3D ((frac_start & 0xff00) >> 8); - cfg->com_div_frac_start3_mode0 =3D ((frac_start & 0xf0000) >> 16); - cfg->com_integloop_gain0_mode0 =3D (integloop_gain & 0xff); - cfg->com_integloop_gain1_mode0 =3D ((integloop_gain & 0xf00) >> 8); - cfg->com_lock_cmp1_mode0 =3D (pll_cmp & 0xff); - cfg->com_lock_cmp2_mode0 =3D ((pll_cmp & 0xff00) >> 8); - cfg->com_lock_cmp3_mode0 =3D ((pll_cmp & 0x30000) >> 16); - cfg->com_lock_cmp_en =3D 0x0; - cfg->com_core_clk_en =3D 0x2c; - cfg->com_coreclk_div =3D HDMI_CORECLK_DIV; - cfg->phy_mode =3D (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; - cfg->com_vco_tune_ctrl =3D 0x0; - - cfg->tx_lx_lane_mode[0] =3D - cfg->tx_lx_lane_mode[2] =3D 0x43; - - cfg->tx_lx_hp_pd_enables[0] =3D - cfg->tx_lx_hp_pd_enables[1] =3D - cfg->tx_lx_hp_pd_enables[2] =3D 0x0c; - cfg->tx_lx_hp_pd_enables[3] =3D 0x3; - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) - cfg->tx_lx_tx_band[i] =3D pd.tx_band_sel + 4; - - if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { - cfg->tx_lx_tx_drv_lvl[0] =3D - cfg->tx_lx_tx_drv_lvl[1] =3D - cfg->tx_lx_tx_drv_lvl[2] =3D 0x25; - cfg->tx_lx_tx_drv_lvl[3] =3D 0x22; - - cfg->tx_lx_tx_emp_post1_lvl[0] =3D - cfg->tx_lx_tx_emp_post1_lvl[1] =3D - cfg->tx_lx_tx_emp_post1_lvl[2] =3D 0x23; - cfg->tx_lx_tx_emp_post1_lvl[3] =3D 0x27; - - cfg->tx_lx_vmode_ctrl1[0] =3D - cfg->tx_lx_vmode_ctrl1[1] =3D - cfg->tx_lx_vmode_ctrl1[2] =3D - cfg->tx_lx_vmode_ctrl1[3] =3D 0x00; - - cfg->tx_lx_vmode_ctrl2[0] =3D - cfg->tx_lx_vmode_ctrl2[1] =3D - cfg->tx_lx_vmode_ctrl2[2] =3D 0x0D; - - cfg->tx_lx_vmode_ctrl2[3] =3D 0x00; - } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - cfg->tx_lx_tx_drv_lvl[i] =3D 0x25; - cfg->tx_lx_tx_emp_post1_lvl[i] =3D 0x23; - cfg->tx_lx_vmode_ctrl1[i] =3D 0x00; - } - - cfg->tx_lx_vmode_ctrl2[0] =3D - cfg->tx_lx_vmode_ctrl2[1] =3D - cfg->tx_lx_vmode_ctrl2[2] =3D 0x0D; - cfg->tx_lx_vmode_ctrl2[3] =3D 0x00; - } else { - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - cfg->tx_lx_tx_drv_lvl[i] =3D 0x20; - cfg->tx_lx_tx_emp_post1_lvl[i] =3D 0x20; - cfg->tx_lx_vmode_ctrl1[i] =3D 0x00; - cfg->tx_lx_vmode_ctrl2[i] =3D 0x0E; - } - } - - DBG("com_svs_mode_clk_sel =3D 0x%x", cfg->com_svs_mode_clk_sel); - DBG("com_hsclk_sel =3D 0x%x", cfg->com_hsclk_sel); - DBG("com_lock_cmp_en =3D 0x%x", cfg->com_lock_cmp_en); - DBG("com_pll_cctrl_mode0 =3D 0x%x", cfg->com_pll_cctrl_mode0); - DBG("com_pll_rctrl_mode0 =3D 0x%x", cfg->com_pll_rctrl_mode0); - DBG("com_cp_ctrl_mode0 =3D 0x%x", cfg->com_cp_ctrl_mode0); - DBG("com_dec_start_mode0 =3D 0x%x", cfg->com_dec_start_mode0); - DBG("com_div_frac_start1_mode0 =3D 0x%x", cfg->com_div_frac_start1_mode0); - DBG("com_div_frac_start2_mode0 =3D 0x%x", cfg->com_div_frac_start2_mode0); - DBG("com_div_frac_start3_mode0 =3D 0x%x", cfg->com_div_frac_start3_mode0); - DBG("com_integloop_gain0_mode0 =3D 0x%x", cfg->com_integloop_gain0_mode0); - DBG("com_integloop_gain1_mode0 =3D 0x%x", cfg->com_integloop_gain1_mode0); - DBG("com_lock_cmp1_mode0 =3D 0x%x", cfg->com_lock_cmp1_mode0); - DBG("com_lock_cmp2_mode0 =3D 0x%x", cfg->com_lock_cmp2_mode0); - DBG("com_lock_cmp3_mode0 =3D 0x%x", cfg->com_lock_cmp3_mode0); - DBG("com_core_clk_en =3D 0x%x", cfg->com_core_clk_en); - DBG("com_coreclk_div =3D 0x%x", cfg->com_coreclk_div); - DBG("phy_mode =3D 0x%x", cfg->phy_mode); - - DBG("tx_l0_lane_mode =3D 0x%x", cfg->tx_lx_lane_mode[0]); - DBG("tx_l2_lane_mode =3D 0x%x", cfg->tx_lx_lane_mode[2]); - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - DBG("tx_l%d_tx_band =3D 0x%x", i, cfg->tx_lx_tx_band[i]); - DBG("tx_l%d_tx_drv_lvl =3D 0x%x", i, cfg->tx_lx_tx_drv_lvl[i]); - DBG("tx_l%d_tx_emp_post1_lvl =3D 0x%x", i, - cfg->tx_lx_tx_emp_post1_lvl[i]); - DBG("tx_l%d_vmode_ctrl1 =3D 0x%x", i, cfg->tx_lx_vmode_ctrl1[i]); - DBG("tx_l%d_vmode_ctrl2 =3D 0x%x", i, cfg->tx_lx_vmode_ctrl2[i]); - } - - return 0; -} - -static int hdmi_8996_pll_set_clk_rate(struct clk_hw *hw, unsigned long rat= e, - unsigned long parent_rate) -{ - struct hdmi_pll_8996 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - struct hdmi_8996_phy_pll_reg_cfg cfg; - int i, ret; - - memset(&cfg, 0x00, sizeof(cfg)); - - ret =3D pll_calculate(rate, parent_rate, &cfg); - if (ret) { - DRM_ERROR("PLL calculation failed\n"); - return ret; - } - - /* Initially shut down PHY */ - DBG("Disabling PHY"); - hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x0); - udelay(500); - - /* Power up sequence */ - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x04); - - hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESETSM_CNTRL, 0x20); - hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX0_TX1_LANE_CTL, 0x0F); - hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX2_TX3_LANE_CTL, 0x0F); - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_CLKBUF_ENABLE, - 0x03); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_TX_BAND, - cfg.tx_lx_tx_band[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_RESET_TSYNC_EN, - 0x03); - } - - hdmi_tx_chan_write(pll, 0, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, - cfg.tx_lx_lane_mode[0]); - hdmi_tx_chan_write(pll, 2, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, - cfg.tx_lx_lane_mode[2]); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_EN_SEL, 0x37); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYS_CLK_CTRL, 0x02); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_ENABLE1, 0x0E); - - /* Bypass VCO calibration */ - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SVS_MODE_CLK_SEL, - cfg.com_svs_mode_clk_sel); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_TRIM, 0x0F); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_IVCO, 0x0F); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_CTRL, - cfg.com_vco_tune_ctrl); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x06); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_SELECT, 0x30); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_HSCLK_SEL, - cfg.com_hsclk_sel); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP_EN, - cfg.com_lock_cmp_en); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE0, - cfg.com_pll_cctrl_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE0, - cfg.com_pll_rctrl_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE0, - cfg.com_cp_ctrl_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE0, - cfg.com_dec_start_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0, - cfg.com_div_frac_start1_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0, - cfg.com_div_frac_start2_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0, - cfg.com_div_frac_start3_mode0); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0, - cfg.com_integloop_gain0_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0, - cfg.com_integloop_gain1_mode0); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0, - cfg.com_lock_cmp1_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0, - cfg.com_lock_cmp2_mode0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0, - cfg.com_lock_cmp3_mode0); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAP, 0x00); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORE_CLK_EN, - cfg.com_core_clk_en); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV, - cfg.com_coreclk_div); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CMN_CONFIG, 0x02); - - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESCODE_DIV_NUM, 0x15); - - /* TX lanes setup (TX 0/1/2/3) */ - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL, - cfg.tx_lx_tx_drv_lvl[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_TX_EMP_POST1_LVL, - cfg.tx_lx_tx_emp_post1_lvl[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL1, - cfg.tx_lx_vmode_ctrl1[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL2, - cfg.tx_lx_vmode_ctrl2[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL_OFFSET, - 0x00); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_OFFSET, - 0x00); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_TRAN_DRVR_EMP_EN, - 0x03); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_PARRATE_REC_DETECT_IDLE_EN, - 0x40); - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_HP_PD_ENABLES, - cfg.tx_lx_hp_pd_enables[i]); - } - - hdmi_phy_write(phy, REG_HDMI_8996_PHY_MODE, cfg.phy_mode); - hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1F); - - /* - * Ensure that vco configuration gets flushed to hardware before - * enabling the PLL - */ - wmb(); - - return 0; -} - -static int hdmi_8996_phy_ready_status(struct hdmi_phy *phy) -{ - u32 nb_tries =3D HDMI_PLL_POLL_MAX_READS; - unsigned long timeout =3D HDMI_PLL_POLL_TIMEOUT_US; - u32 status; - int phy_ready =3D 0; - - DBG("Waiting for PHY ready"); - - while (nb_tries--) { - status =3D hdmi_phy_read(phy, REG_HDMI_8996_PHY_STATUS); - phy_ready =3D status & BIT(0); - - if (phy_ready) - break; - - udelay(timeout); - } - - DBG("PHY is %sready", phy_ready ? "" : "*not* "); - - return phy_ready; -} - -static int hdmi_8996_pll_lock_status(struct hdmi_pll_8996 *pll) -{ - u32 status; - int nb_tries =3D HDMI_PLL_POLL_MAX_READS; - unsigned long timeout =3D HDMI_PLL_POLL_TIMEOUT_US; - int pll_locked =3D 0; - - DBG("Waiting for PLL lock"); - - while (nb_tries--) { - status =3D hdmi_pll_read(pll, - REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); - pll_locked =3D status & BIT(0); - - if (pll_locked) - break; - - udelay(timeout); - } - - DBG("HDMI PLL is %slocked", pll_locked ? "" : "*not* "); - - return pll_locked; -} - -static int hdmi_8996_pll_prepare(struct clk_hw *hw) -{ - struct hdmi_pll_8996 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - int i, ret =3D 0; - - hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x1); - udelay(100); - - hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); - udelay(100); - - ret =3D hdmi_8996_pll_lock_status(pll); - if (!ret) - return ret; - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) - hdmi_tx_chan_write(pll, i, - REG_HDMI_PHY_QSERDES_TX_LX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, - 0x6F); - - /* Disable SSC */ - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER1, 0x0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER2, 0x0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE1, 0x0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE2, 0x0); - hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_EN_CENTER, 0x2); - - ret =3D hdmi_8996_phy_ready_status(phy); - if (!ret) - return ret; - - /* Restart the retiming buffer */ - hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x18); - udelay(1); - hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); - - return 0; -} - -static int hdmi_8996_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - req->rate =3D clamp_t(unsigned long, req->rate, HDMI_PCLK_MIN_FREQ, HDMI_= PCLK_MAX_FREQ); - - return 0; -} - -static unsigned long hdmi_8996_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct hdmi_pll_8996 *pll =3D hw_clk_to_pll(hw); - u64 fdata; - u32 cmp1, cmp2, cmp3, pll_cmp; - - cmp1 =3D hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0); - cmp2 =3D hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0); - cmp3 =3D hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0); - - pll_cmp =3D cmp1 | (cmp2 << 8) | (cmp3 << 16); - - fdata =3D pll_cmp_to_fdata(pll_cmp + 1, parent_rate); - - do_div(fdata, 10); - - return fdata; -} - -static void hdmi_8996_pll_unprepare(struct clk_hw *hw) -{ - struct hdmi_pll_8996 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - - hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x6); - usleep_range(100, 150); -} - -static int hdmi_8996_pll_is_enabled(struct clk_hw *hw) -{ - struct hdmi_pll_8996 *pll =3D hw_clk_to_pll(hw); - u32 status; - int pll_locked; - - status =3D hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); - pll_locked =3D status & BIT(0); - - return pll_locked; -} - -static const struct clk_ops hdmi_8996_pll_ops =3D { - .set_rate =3D hdmi_8996_pll_set_clk_rate, - .determine_rate =3D hdmi_8996_pll_determine_rate, - .recalc_rate =3D hdmi_8996_pll_recalc_rate, - .prepare =3D hdmi_8996_pll_prepare, - .unprepare =3D hdmi_8996_pll_unprepare, - .is_enabled =3D hdmi_8996_pll_is_enabled, -}; - -static const struct clk_init_data pll_init =3D { - .name =3D "hdmipll", - .ops =3D &hdmi_8996_pll_ops, - .parent_data =3D (const struct clk_parent_data[]){ - { .fw_name =3D "xo", .name =3D "xo_board" }, - }, - .num_parents =3D 1, - .flags =3D CLK_IGNORE_UNUSED, -}; - -int msm_hdmi_pll_8996_init(struct platform_device *pdev) -{ - struct device *dev =3D &pdev->dev; - struct hdmi_pll_8996 *pll; - int i, ret; - - pll =3D devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); - if (!pll) - return -ENOMEM; - - pll->pdev =3D pdev; - - pll->mmio_qserdes_com =3D msm_ioremap(pdev, "hdmi_pll"); - if (IS_ERR(pll->mmio_qserdes_com)) { - DRM_DEV_ERROR(dev, "failed to map pll base\n"); - return -ENOMEM; - } - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - char name[32]; - - snprintf(name, sizeof(name), "hdmi_tx_l%d", i); - - pll->mmio_qserdes_tx[i] =3D msm_ioremap(pdev, name); - if (IS_ERR(pll->mmio_qserdes_tx[i])) { - DRM_DEV_ERROR(dev, "failed to map pll base\n"); - return -ENOMEM; - } - } - pll->clk_hw.init =3D &pll_init; - - ret =3D devm_clk_hw_register(dev, &pll->clk_hw); - if (ret) { - DRM_DEV_ERROR(dev, "failed to register pll clock\n"); - return ret; - } - - ret =3D devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &pll->clk_= hw); - if (ret) { - DRM_DEV_ERROR(dev, "%s: failed to register clk provider: %d\n", __func__= , ret); - return ret; - } - - return 0; -} - -static const char * const hdmi_phy_8996_reg_names[] =3D { - "vddio", - "vcca", -}; - -static const char * const hdmi_phy_8996_clk_names[] =3D { - "iface", "ref", -}; - -const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg =3D { - .type =3D MSM_HDMI_PHY_8996, - .reg_names =3D hdmi_phy_8996_reg_names, - .num_regs =3D ARRAY_SIZE(hdmi_phy_8996_reg_names), - .clk_names =3D hdmi_phy_8996_clk_names, - .num_clks =3D ARRAY_SIZE(hdmi_phy_8996_clk_names), -}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c b/drivers/gpu/drm/msm= /hdmi/hdmi_phy_8998.c deleted file mode 100644 index a86ff3706369..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c +++ /dev/null @@ -1,765 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. - * Copyright (c) 2024 Freebox SAS - */ - -#include -#include - -#include "hdmi.h" - -#define HDMI_VCO_MAX_FREQ 12000000000UL -#define HDMI_VCO_MIN_FREQ 8000000000UL - -#define HDMI_PCLK_MAX_FREQ 600000000 -#define HDMI_PCLK_MIN_FREQ 25000000 - -#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL -#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL -#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL -#define HDMI_CORECLK_DIV 5 -#define HDMI_DEFAULT_REF_CLOCK 19200000 -#define HDMI_PLL_CMP_CNT 1024 - -#define HDMI_PLL_POLL_MAX_READS 100 -#define HDMI_PLL_POLL_TIMEOUT_US 150 - -#define HDMI_NUM_TX_CHANNEL 4 - -struct hdmi_pll_8998 { - struct platform_device *pdev; - struct clk_hw clk_hw; - unsigned long rate; - - /* pll mmio base */ - void __iomem *mmio_qserdes_com; - /* tx channel base */ - void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL]; -}; - -#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8998, clk_hw) - -struct hdmi_8998_phy_pll_reg_cfg { - u32 com_svs_mode_clk_sel; - u32 com_hsclk_sel; - u32 com_pll_cctrl_mode0; - u32 com_pll_rctrl_mode0; - u32 com_cp_ctrl_mode0; - u32 com_dec_start_mode0; - u32 com_div_frac_start1_mode0; - u32 com_div_frac_start2_mode0; - u32 com_div_frac_start3_mode0; - u32 com_integloop_gain0_mode0; - u32 com_integloop_gain1_mode0; - u32 com_lock_cmp_en; - u32 com_lock_cmp1_mode0; - u32 com_lock_cmp2_mode0; - u32 com_lock_cmp3_mode0; - u32 com_core_clk_en; - u32 com_coreclk_div_mode0; - - u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_pre_driver_1[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_pre_driver_2[HDMI_NUM_TX_CHANNEL]; - u32 tx_lx_res_code_offset[HDMI_NUM_TX_CHANNEL]; - - u32 phy_mode; -}; - -struct hdmi_8998_post_divider { - u64 vco_freq; - int hsclk_divsel; - int vco_ratio; - int tx_band_sel; - int half_rate_mode; -}; - -static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8998 *pll) -{ - return platform_get_drvdata(pll->pdev); -} - -static inline void hdmi_pll_write(struct hdmi_pll_8998 *pll, int offset, - u32 data) -{ - writel(data, pll->mmio_qserdes_com + offset); -} - -static inline u32 hdmi_pll_read(struct hdmi_pll_8998 *pll, int offset) -{ - return readl(pll->mmio_qserdes_com + offset); -} - -static inline void hdmi_tx_chan_write(struct hdmi_pll_8998 *pll, int chann= el, - int offset, int data) -{ - writel(data, pll->mmio_qserdes_tx[channel] + offset); -} - -static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk, - bool gen_ssc) -{ - if ((frac_start !=3D 0) || gen_ssc) - return 0x8; - - return 0x30; -} - -static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc) -{ - if ((frac_start !=3D 0) || gen_ssc) - return 0x16; - - return 0x18; -} - -static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc) -{ - if ((frac_start !=3D 0) || gen_ssc) - return 0x34; - - return 0x2; -} - -static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref= _clk, - bool gen_ssc) -{ - int digclk_divsel =3D bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; - u64 base; - - if ((frac_start !=3D 0) || gen_ssc) - base =3D 0x3F; - else - base =3D 0xC4; - - base <<=3D (digclk_divsel =3D=3D 2 ? 1 : 0); - - return base; -} - -static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk) -{ - u64 dividend =3D HDMI_PLL_CMP_CNT * fdata; - u32 divisor =3D ref_clk * 10; - u32 rem; - - rem =3D do_div(dividend, divisor); - if (rem > (divisor >> 1)) - dividend++; - - return dividend - 1; -} - -#define HDMI_REF_CLOCK_HZ ((u64)19200000) -#define HDMI_MHZ_TO_HZ ((u64)1000000) -static int pll_get_post_div(struct hdmi_8998_post_divider *pd, u64 bclk) -{ - static const u32 ratio_list[] =3D {1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 25}; - static const u32 band_list[] =3D {0, 1, 2, 3}; - u32 const sz_ratio =3D ARRAY_SIZE(ratio_list); - u32 const sz_band =3D ARRAY_SIZE(band_list); - u32 const cmp_cnt =3D 1024; - u32 const th_min =3D 500, th_max =3D 1000; - u32 half_rate_mode =3D 0; - u32 list_elements; - int optimal_index; - u32 i, j, k; - u32 found_hsclk_divsel =3D 0, found_vco_ratio; - u32 found_tx_band_sel; - u64 const min_freq =3D HDMI_VCO_MIN_FREQ, max_freq =3D HDMI_VCO_MAX_FREQ; - u64 freq_list[ARRAY_SIZE(ratio_list) * ARRAY_SIZE(band_list)]; - u64 found_vco_freq; - u64 freq_optimal; - -find_optimal_index: - freq_optimal =3D max_freq; - optimal_index =3D -1; - list_elements =3D 0; - - for (i =3D 0; i < sz_ratio; i++) { - for (j =3D 0; j < sz_band; j++) { - u64 freq =3D div_u64(bclk, (1 << half_rate_mode)); - - freq *=3D (ratio_list[i] * (1 << band_list[j])); - freq_list[list_elements++] =3D freq; - } - } - - for (k =3D 0; k < ARRAY_SIZE(freq_list); k++) { - u32 const clks_pll_div =3D 2, core_clk_div =3D 5; - u32 const rng1 =3D 16, rng2 =3D 8; - u32 th1, th2; - u64 core_clk, rvar1, rem; - - core_clk =3D div_u64(freq_list[k], - ratio_list[k / sz_band] * clks_pll_div * - core_clk_div); - - rvar1 =3D HDMI_REF_CLOCK_HZ * rng1 * HDMI_MHZ_TO_HZ; - rvar1 =3D div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem); - if (rem > ((cmp_cnt * core_clk) >> 1)) - rvar1++; - th1 =3D rvar1; - - rvar1 =3D HDMI_REF_CLOCK_HZ * rng2 * HDMI_MHZ_TO_HZ; - rvar1 =3D div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem); - if (rem > ((cmp_cnt * core_clk) >> 1)) - rvar1++; - th2 =3D rvar1; - - if (freq_list[k] >=3D min_freq && - freq_list[k] <=3D max_freq) { - if ((th1 >=3D th_min && th1 <=3D th_max) || - (th2 >=3D th_min && th2 <=3D th_max)) { - if (freq_list[k] <=3D freq_optimal) { - freq_optimal =3D freq_list[k]; - optimal_index =3D k; - } - } - } - } - - if (optimal_index =3D=3D -1) { - if (!half_rate_mode) { - half_rate_mode =3D 1; - goto find_optimal_index; - } else { - return -EINVAL; - } - } else { - found_vco_ratio =3D ratio_list[optimal_index / sz_band]; - found_tx_band_sel =3D band_list[optimal_index % sz_band]; - found_vco_freq =3D freq_optimal; - } - - switch (found_vco_ratio) { - case 1: - found_hsclk_divsel =3D 15; - break; - case 2: - found_hsclk_divsel =3D 0; - break; - case 3: - found_hsclk_divsel =3D 4; - break; - case 4: - found_hsclk_divsel =3D 8; - break; - case 5: - found_hsclk_divsel =3D 12; - break; - case 6: - found_hsclk_divsel =3D 1; - break; - case 9: - found_hsclk_divsel =3D 5; - break; - case 10: - found_hsclk_divsel =3D 2; - break; - case 12: - found_hsclk_divsel =3D 9; - break; - case 15: - found_hsclk_divsel =3D 13; - break; - case 25: - found_hsclk_divsel =3D 14; - break; - } - - pd->vco_freq =3D found_vco_freq; - pd->tx_band_sel =3D found_tx_band_sel; - pd->vco_ratio =3D found_vco_ratio; - pd->hsclk_divsel =3D found_hsclk_divsel; - - return 0; -} - -static int pll_calculate(unsigned long pix_clk, unsigned long ref_clk, - struct hdmi_8998_phy_pll_reg_cfg *cfg) -{ - struct hdmi_8998_post_divider pd; - u64 bclk; - u64 dec_start; - u64 frac_start; - u64 fdata; - u32 pll_divisor; - u32 rem; - u32 cpctrl; - u32 rctrl; - u32 cctrl; - u32 integloop_gain; - u32 pll_cmp; - int i, ret; - - /* bit clk =3D 10 * pix_clk */ - bclk =3D ((u64)pix_clk) * 10; - - ret =3D pll_get_post_div(&pd, bclk); - if (ret) - return ret; - - dec_start =3D pd.vco_freq; - pll_divisor =3D 4 * ref_clk; - do_div(dec_start, pll_divisor); - - frac_start =3D pd.vco_freq * (1 << 20); - - rem =3D do_div(frac_start, pll_divisor); - frac_start -=3D dec_start * (1 << 20); - if (rem > (pll_divisor >> 1)) - frac_start++; - - cpctrl =3D pll_get_cpctrl(frac_start, ref_clk, false); - rctrl =3D pll_get_rctrl(frac_start, false); - cctrl =3D pll_get_cctrl(frac_start, false); - integloop_gain =3D pll_get_integloop_gain(frac_start, bclk, - ref_clk, false); - - fdata =3D pd.vco_freq; - do_div(fdata, pd.vco_ratio); - - pll_cmp =3D pll_get_pll_cmp(fdata, ref_clk); - - /* Convert these values to register specific values */ - if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) - cfg->com_svs_mode_clk_sel =3D 1; - else - cfg->com_svs_mode_clk_sel =3D 2; - - cfg->com_hsclk_sel =3D (0x20 | pd.hsclk_divsel); - cfg->com_pll_cctrl_mode0 =3D cctrl; - cfg->com_pll_rctrl_mode0 =3D rctrl; - cfg->com_cp_ctrl_mode0 =3D cpctrl; - cfg->com_dec_start_mode0 =3D dec_start; - cfg->com_div_frac_start1_mode0 =3D (frac_start & 0xff); - cfg->com_div_frac_start2_mode0 =3D ((frac_start & 0xff00) >> 8); - cfg->com_div_frac_start3_mode0 =3D ((frac_start & 0xf0000) >> 16); - cfg->com_integloop_gain0_mode0 =3D (integloop_gain & 0xff); - cfg->com_integloop_gain1_mode0 =3D ((integloop_gain & 0xf00) >> 8); - cfg->com_lock_cmp1_mode0 =3D (pll_cmp & 0xff); - cfg->com_lock_cmp2_mode0 =3D ((pll_cmp & 0xff00) >> 8); - cfg->com_lock_cmp3_mode0 =3D ((pll_cmp & 0x30000) >> 16); - cfg->com_lock_cmp_en =3D 0x0; - cfg->com_core_clk_en =3D 0x2c; - cfg->com_coreclk_div_mode0 =3D HDMI_CORECLK_DIV; - cfg->phy_mode =3D (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x5 : 0x4; - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) - cfg->tx_lx_tx_band[i] =3D pd.tx_band_sel; - - if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { - cfg->tx_lx_tx_drv_lvl[0] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[1] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[2] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[3] =3D 0x0f; - cfg->tx_lx_tx_emp_post1_lvl[0] =3D 0x03; - cfg->tx_lx_tx_emp_post1_lvl[1] =3D 0x02; - cfg->tx_lx_tx_emp_post1_lvl[2] =3D 0x03; - cfg->tx_lx_tx_emp_post1_lvl[3] =3D 0x00; - cfg->tx_lx_pre_driver_1[0] =3D 0x00; - cfg->tx_lx_pre_driver_1[1] =3D 0x00; - cfg->tx_lx_pre_driver_1[2] =3D 0x00; - cfg->tx_lx_pre_driver_1[3] =3D 0x00; - cfg->tx_lx_pre_driver_2[0] =3D 0x1C; - cfg->tx_lx_pre_driver_2[1] =3D 0x1C; - cfg->tx_lx_pre_driver_2[2] =3D 0x1C; - cfg->tx_lx_pre_driver_2[3] =3D 0x00; - cfg->tx_lx_res_code_offset[0] =3D 0x03; - cfg->tx_lx_res_code_offset[1] =3D 0x00; - cfg->tx_lx_res_code_offset[2] =3D 0x00; - cfg->tx_lx_res_code_offset[3] =3D 0x03; - } else if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) { - cfg->tx_lx_tx_drv_lvl[0] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[1] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[2] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[3] =3D 0x0f; - cfg->tx_lx_tx_emp_post1_lvl[0] =3D 0x03; - cfg->tx_lx_tx_emp_post1_lvl[1] =3D 0x03; - cfg->tx_lx_tx_emp_post1_lvl[2] =3D 0x03; - cfg->tx_lx_tx_emp_post1_lvl[3] =3D 0x00; - cfg->tx_lx_pre_driver_1[0] =3D 0x00; - cfg->tx_lx_pre_driver_1[1] =3D 0x00; - cfg->tx_lx_pre_driver_1[2] =3D 0x00; - cfg->tx_lx_pre_driver_1[3] =3D 0x00; - cfg->tx_lx_pre_driver_2[0] =3D 0x16; - cfg->tx_lx_pre_driver_2[1] =3D 0x16; - cfg->tx_lx_pre_driver_2[2] =3D 0x16; - cfg->tx_lx_pre_driver_2[3] =3D 0x18; - cfg->tx_lx_res_code_offset[0] =3D 0x03; - cfg->tx_lx_res_code_offset[1] =3D 0x00; - cfg->tx_lx_res_code_offset[2] =3D 0x00; - cfg->tx_lx_res_code_offset[3] =3D 0x00; - } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { - cfg->tx_lx_tx_drv_lvl[0] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[1] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[2] =3D 0x0f; - cfg->tx_lx_tx_drv_lvl[3] =3D 0x0f; - cfg->tx_lx_tx_emp_post1_lvl[0] =3D 0x05; - cfg->tx_lx_tx_emp_post1_lvl[1] =3D 0x05; - cfg->tx_lx_tx_emp_post1_lvl[2] =3D 0x05; - cfg->tx_lx_tx_emp_post1_lvl[3] =3D 0x00; - cfg->tx_lx_pre_driver_1[0] =3D 0x00; - cfg->tx_lx_pre_driver_1[1] =3D 0x00; - cfg->tx_lx_pre_driver_1[2] =3D 0x00; - cfg->tx_lx_pre_driver_1[3] =3D 0x00; - cfg->tx_lx_pre_driver_2[0] =3D 0x0E; - cfg->tx_lx_pre_driver_2[1] =3D 0x0E; - cfg->tx_lx_pre_driver_2[2] =3D 0x0E; - cfg->tx_lx_pre_driver_2[3] =3D 0x0E; - cfg->tx_lx_res_code_offset[0] =3D 0x00; - cfg->tx_lx_res_code_offset[1] =3D 0x00; - cfg->tx_lx_res_code_offset[2] =3D 0x00; - cfg->tx_lx_res_code_offset[3] =3D 0x00; - } else { - cfg->tx_lx_tx_drv_lvl[0] =3D 0x01; - cfg->tx_lx_tx_drv_lvl[1] =3D 0x01; - cfg->tx_lx_tx_drv_lvl[2] =3D 0x01; - cfg->tx_lx_tx_drv_lvl[3] =3D 0x00; - cfg->tx_lx_tx_emp_post1_lvl[0] =3D 0x00; - cfg->tx_lx_tx_emp_post1_lvl[1] =3D 0x00; - cfg->tx_lx_tx_emp_post1_lvl[2] =3D 0x00; - cfg->tx_lx_tx_emp_post1_lvl[3] =3D 0x00; - cfg->tx_lx_pre_driver_1[0] =3D 0x00; - cfg->tx_lx_pre_driver_1[1] =3D 0x00; - cfg->tx_lx_pre_driver_1[2] =3D 0x00; - cfg->tx_lx_pre_driver_1[3] =3D 0x00; - cfg->tx_lx_pre_driver_2[0] =3D 0x16; - cfg->tx_lx_pre_driver_2[1] =3D 0x16; - cfg->tx_lx_pre_driver_2[2] =3D 0x16; - cfg->tx_lx_pre_driver_2[3] =3D 0x18; - cfg->tx_lx_res_code_offset[0] =3D 0x00; - cfg->tx_lx_res_code_offset[1] =3D 0x00; - cfg->tx_lx_res_code_offset[2] =3D 0x00; - cfg->tx_lx_res_code_offset[3] =3D 0x00; - } - - return 0; -} - -static int hdmi_8998_pll_set_clk_rate(struct clk_hw *hw, unsigned long rat= e, - unsigned long parent_rate) -{ - struct hdmi_pll_8998 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - struct hdmi_8998_phy_pll_reg_cfg cfg =3D {}; - int i, ret; - - ret =3D pll_calculate(rate, parent_rate, &cfg); - if (ret) { - DRM_ERROR("PLL calculation failed\n"); - return ret; - } - - /* Initially shut down PHY */ - hdmi_phy_write(phy, REG_HDMI_8998_PHY_PD_CTL, 0x0); - udelay(500); - - /* Power up sequence */ - hdmi_phy_write(phy, REG_HDMI_8998_PHY_PD_CTL, 0x1); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_RESETSM_CNTRL, 0x20); - hdmi_phy_write(phy, REG_HDMI_8998_PHY_CMN_CTRL, 0x6); - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_INTERFACE_SELECT_TX_BAND, - cfg.tx_lx_tx_band[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_CLKBUF_TERM_ENABLE, - 0x1); - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_LANE_MODE, - 0x20); - } - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE, 0x02= ); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x= 0B); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SYSCLK_EN_SEL, 0x37); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SYS_CLK_CTRL, 0x02); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CLK_ENABLE1, 0x0E); - - /* Bypass VCO calibration */ - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_SVS_MODE_CLK_SEL, - cfg.com_svs_mode_clk_sel); - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_PLL_IVCO, 0x07); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_VCO_TUNE_CTRL, 0x00); - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CLK_SEL, 0x30); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_HSCLK_SEL, - cfg.com_hsclk_sel); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP_EN, - cfg.com_lock_cmp_en); - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_PLL_CCTRL_MODE0, - cfg.com_pll_cctrl_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_PLL_RCTRL_MODE0, - cfg.com_pll_rctrl_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CP_CTRL_MODE0, - cfg.com_cp_ctrl_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DEC_START_MODE0, - cfg.com_dec_start_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0, - cfg.com_div_frac_start1_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0, - cfg.com_div_frac_start2_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0, - cfg.com_div_frac_start3_mode0); - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0, - cfg.com_integloop_gain0_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0, - cfg.com_integloop_gain1_mode0); - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP1_MODE0, - cfg.com_lock_cmp1_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP2_MODE0, - cfg.com_lock_cmp2_mode0); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_LOCK_CMP3_MODE0, - cfg.com_lock_cmp3_mode0); - - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_VCO_TUNE_MAP, 0x00); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CORE_CLK_EN, - cfg.com_core_clk_en); - hdmi_pll_write(pll, REG_HDMI_8998_PHY_QSERDES_COM_CORECLK_DIV_MODE0, - cfg.com_coreclk_div_mode0); - - /* TX lanes setup (TX 0/1/2/3) */ - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_DRV_LVL, - cfg.tx_lx_tx_drv_lvl[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_EMP_POST1_LVL, - cfg.tx_lx_tx_emp_post1_lvl[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_PRE_DRIVER_1, - cfg.tx_lx_pre_driver_1[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_PRE_DRIVER_2, - cfg.tx_lx_pre_driver_2[i]); - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET, - cfg.tx_lx_res_code_offset[i]); - } - - hdmi_phy_write(phy, REG_HDMI_8998_PHY_MODE, cfg.phy_mode); - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_LANE_CONFIG, - 0x10); - } - - /* - * Ensure that vco configuration gets flushed to hardware before - * enabling the PLL - */ - wmb(); - - pll->rate =3D rate; - - return 0; -} - -static int hdmi_8998_phy_ready_status(struct hdmi_phy *phy) -{ - u32 nb_tries =3D HDMI_PLL_POLL_MAX_READS; - unsigned long timeout =3D HDMI_PLL_POLL_TIMEOUT_US; - u32 status; - int phy_ready =3D 0; - - while (nb_tries--) { - status =3D hdmi_phy_read(phy, REG_HDMI_8998_PHY_STATUS); - phy_ready =3D status & BIT(0); - - if (phy_ready) - break; - - udelay(timeout); - } - - return phy_ready; -} - -static int hdmi_8998_pll_lock_status(struct hdmi_pll_8998 *pll) -{ - u32 status; - int nb_tries =3D HDMI_PLL_POLL_MAX_READS; - unsigned long timeout =3D HDMI_PLL_POLL_TIMEOUT_US; - int pll_locked =3D 0; - - while (nb_tries--) { - status =3D hdmi_pll_read(pll, - REG_HDMI_8998_PHY_QSERDES_COM_C_READY_STATUS); - pll_locked =3D status & BIT(0); - - if (pll_locked) - break; - - udelay(timeout); - } - - return pll_locked; -} - -static int hdmi_8998_pll_prepare(struct clk_hw *hw) -{ - struct hdmi_pll_8998 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - int i, ret =3D 0; - - hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x1); - udelay(100); - - hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x59); - udelay(100); - - ret =3D hdmi_8998_pll_lock_status(pll); - if (!ret) - return ret; - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - hdmi_tx_chan_write(pll, i, - REG_HDMI_8998_PHY_TXn_LANE_CONFIG, 0x1F); - } - - /* Ensure all registers are flushed to hardware */ - wmb(); - - ret =3D hdmi_8998_phy_ready_status(phy); - if (!ret) - return ret; - - /* Restart the retiming buffer */ - hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x58); - udelay(1); - hdmi_phy_write(phy, REG_HDMI_8998_PHY_CFG, 0x59); - - /* Ensure all registers are flushed to hardware */ - wmb(); - - return 0; -} - -static int hdmi_8998_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - req->rate =3D clamp_t(unsigned long, req->rate, HDMI_PCLK_MIN_FREQ, HDMI_= PCLK_MAX_FREQ); - - return 0; -} - -static unsigned long hdmi_8998_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct hdmi_pll_8998 *pll =3D hw_clk_to_pll(hw); - return pll->rate; -} - -static void hdmi_8998_pll_unprepare(struct clk_hw *hw) -{ - struct hdmi_pll_8998 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - - hdmi_phy_write(phy, REG_HDMI_8998_PHY_PD_CTL, 0); - usleep_range(100, 150); -} - -static int hdmi_8998_pll_is_enabled(struct clk_hw *hw) -{ - struct hdmi_pll_8998 *pll =3D hw_clk_to_pll(hw); - u32 status; - int pll_locked; - - status =3D hdmi_pll_read(pll, REG_HDMI_8998_PHY_QSERDES_COM_C_READY_STATU= S); - pll_locked =3D status & BIT(0); - - return pll_locked; -} - -static const struct clk_ops hdmi_8998_pll_ops =3D { - .set_rate =3D hdmi_8998_pll_set_clk_rate, - .determine_rate =3D hdmi_8998_pll_determine_rate, - .recalc_rate =3D hdmi_8998_pll_recalc_rate, - .prepare =3D hdmi_8998_pll_prepare, - .unprepare =3D hdmi_8998_pll_unprepare, - .is_enabled =3D hdmi_8998_pll_is_enabled, -}; - -static const struct clk_init_data pll_init =3D { - .name =3D "hdmipll", - .ops =3D &hdmi_8998_pll_ops, - .parent_data =3D (const struct clk_parent_data[]){ - { .fw_name =3D "xo", .name =3D "xo_board" }, - }, - .num_parents =3D 1, - .flags =3D CLK_IGNORE_UNUSED, -}; - -int msm_hdmi_pll_8998_init(struct platform_device *pdev) -{ - struct device *dev =3D &pdev->dev; - struct hdmi_pll_8998 *pll; - int ret, i; - - pll =3D devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); - if (!pll) - return -ENOMEM; - - pll->pdev =3D pdev; - - pll->mmio_qserdes_com =3D msm_ioremap(pdev, "hdmi_pll"); - if (IS_ERR(pll->mmio_qserdes_com)) { - DRM_DEV_ERROR(dev, "failed to map pll base\n"); - return -ENOMEM; - } - - for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { - char name[32]; - - snprintf(name, sizeof(name), "hdmi_tx_l%d", i); - - pll->mmio_qserdes_tx[i] =3D msm_ioremap(pdev, name); - if (IS_ERR(pll->mmio_qserdes_tx[i])) { - DRM_DEV_ERROR(dev, "failed to map pll base\n"); - return -ENOMEM; - } - } - pll->clk_hw.init =3D &pll_init; - - ret =3D devm_clk_hw_register(dev, &pll->clk_hw); - if (ret) { - DRM_DEV_ERROR(dev, "failed to register pll clock\n"); - return ret; - } - - ret =3D devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &pll->clk_= hw); - if (ret) { - DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret); - return ret; - } - - return 0; -} - -static const char * const hdmi_phy_8998_reg_names[] =3D { - "vddio", - "vcca", -}; - -static const char * const hdmi_phy_8998_clk_names[] =3D { - "iface", "ref", "xo", -}; - -const struct hdmi_phy_cfg msm_hdmi_phy_8998_cfg =3D { - .type =3D MSM_HDMI_PHY_8998, - .reg_names =3D hdmi_phy_8998_reg_names, - .num_regs =3D ARRAY_SIZE(hdmi_phy_8998_reg_names), - .clk_names =3D hdmi_phy_8998_clk_names, - .num_clks =3D ARRAY_SIZE(hdmi_phy_8998_clk_names), -}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm= /hdmi/hdmi_phy_8x60.c deleted file mode 100644 index 10ee91818364..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Red Hat - * Author: Rob Clark - */ - -#include - -#include "hdmi.h" - -static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy, - unsigned long pixclock) -{ - /* De-serializer delay D/C for non-lbk mode: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG0, - HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(3)); - - if (pixclock =3D=3D 27000000) { - /* video_format =3D=3D HDMI_VFRMT_720x480p60_16_9 */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG1, - HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | - HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(3)); - } else { - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG1, - HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | - HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(4)); - } - - /* No matter what, start from the power down mode: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_PWRGEN | - HDMI_8x60_PHY_REG2_PD_PLL | - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); - - /* Turn PowerGen on: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_PLL | - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); - - /* Turn PLL power on: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); - - /* Write to HIGH after PLL power down de-assert: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG3, - HDMI_8x60_PHY_REG3_PLL_ENABLE); - - /* ASIC power on; PHY REG9 =3D 0 */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG9, 0); - - /* Enable PLL lock detect, PLL lock det will go high after lock - * Enable the re-time logic - */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG12, - HDMI_8x60_PHY_REG12_RETIMING_EN | - HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN); - - /* Drivers are on: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_DESER); - - /* If the RX detector is needed: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_RCV_SENSE_EN | - HDMI_8x60_PHY_REG2_PD_DESER); - - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG4, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG5, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG6, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG7, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG8, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG9, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG10, 0); - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG11, 0); - - /* If we want to use lock enable based on counting: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG12, - HDMI_8x60_PHY_REG12_RETIMING_EN | - HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN | - HDMI_8x60_PHY_REG12_FORCE_LOCK); -} - -static void hdmi_phy_8x60_powerdown(struct hdmi_phy *phy) -{ - /* Assert RESET PHY from controller */ - hdmi_phy_write(phy, REG_HDMI_PHY_CTRL, - HDMI_PHY_CTRL_SW_RESET); - udelay(10); - /* De-assert RESET PHY from controller */ - hdmi_phy_write(phy, REG_HDMI_PHY_CTRL, 0); - /* Turn off Driver */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); - udelay(10); - /* Disable PLL */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG3, 0); - /* Power down PHY, but keep RX-sense: */ - hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_RCV_SENSE_EN | - HDMI_8x60_PHY_REG2_PD_PWRGEN | - HDMI_8x60_PHY_REG2_PD_PLL | - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); -} - -static const char * const hdmi_phy_8x60_reg_names[] =3D { - "core-vdda", -}; - -static const char * const hdmi_phy_8x60_clk_names[] =3D { - "slave_iface", -}; - -const struct hdmi_phy_cfg msm_hdmi_phy_8x60_cfg =3D { - .type =3D MSM_HDMI_PHY_8x60, - .powerup =3D hdmi_phy_8x60_powerup, - .powerdown =3D hdmi_phy_8x60_powerdown, - .reg_names =3D hdmi_phy_8x60_reg_names, - .num_regs =3D ARRAY_SIZE(hdmi_phy_8x60_reg_names), - .clk_names =3D hdmi_phy_8x60_clk_names, - .num_clks =3D ARRAY_SIZE(hdmi_phy_8x60_clk_names), -}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm= /hdmi/hdmi_phy_8x74.c deleted file mode 100644 index 6f40820d9071..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2013 Red Hat - * Author: Rob Clark - */ - -#include "hdmi.h" - -static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy, - unsigned long pixclock) -{ - hdmi_phy_write(phy, REG_HDMI_8x74_ANA_CFG0, 0x1b); - hdmi_phy_write(phy, REG_HDMI_8x74_ANA_CFG1, 0xf2); - hdmi_phy_write(phy, REG_HDMI_8x74_BIST_CFG0, 0x0); - hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN0, 0x0); - hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN1, 0x0); - hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN2, 0x0); - hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN3, 0x0); - hdmi_phy_write(phy, REG_HDMI_8x74_PD_CTRL1, 0x20); -} - -static void hdmi_phy_8x74_powerdown(struct hdmi_phy *phy) -{ - hdmi_phy_write(phy, REG_HDMI_8x74_PD_CTRL0, 0x7f); -} - -static const char * const hdmi_phy_8x74_reg_names[] =3D { - "core-vdda", - "vddio", -}; - -static const char * const hdmi_phy_8x74_clk_names[] =3D { - "iface", "alt_iface" -}; - -const struct hdmi_phy_cfg msm_hdmi_phy_8x74_cfg =3D { - .type =3D MSM_HDMI_PHY_8x74, - .powerup =3D hdmi_phy_8x74_powerup, - .powerdown =3D hdmi_phy_8x74_powerdown, - .reg_names =3D hdmi_phy_8x74_reg_names, - .num_regs =3D ARRAY_SIZE(hdmi_phy_8x74_reg_names), - .clk_names =3D hdmi_phy_8x74_clk_names, - .num_clks =3D ARRAY_SIZE(hdmi_phy_8x74_clk_names), -}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c b/drivers/gpu/drm/msm= /hdmi/hdmi_pll_8960.c deleted file mode 100644 index 6ba6bbdb7e05..000000000000 --- a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c +++ /dev/null @@ -1,460 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. - * Copyright (C) 2013 Red Hat - * Author: Rob Clark - */ - -#include -#include - -#include "hdmi.h" - -struct hdmi_pll_8960 { - struct platform_device *pdev; - struct clk_hw clk_hw; - void __iomem *mmio; - - unsigned long pixclk; -}; - -#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw) - -/* - * HDMI PLL: - * - * To get the parent clock setup properly, we need to plug in hdmi pll - * configuration into common-clock-framework. - */ - -struct pll_rate { - unsigned long rate; - int num_reg; - struct { - u32 val; - u32 reg; - } conf[32]; -}; - -/* NOTE: keep sorted highest freq to lowest: */ -static const struct pll_rate freqtbl[] =3D { - { 154000000, 14, { - { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - } - }, - /* 1080p60/1080p50 case */ - { 148500000, 27, { - { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, - { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, - { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, - { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, - { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - } - }, - { 108000000, 13, { - { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - } - }, - /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */ - { 74250000, 8, { - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - } - }, - { 74176000, 14, { - { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - } - }, - { 65000000, 14, { - { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - } - }, - /* 480p60/480i60 */ - { 27030000, 18, { - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - } - }, - /* 576p50/576i50 */ - { 27000000, 27, { - { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, - { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, - { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, - { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, - { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - } - }, - /* 640x480p60 */ - { 25200000, 27, { - { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, - { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, - { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, - { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, - { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, - { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - } - }, -}; - -static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data) -{ - writel(data, pll->mmio + reg); -} - -static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg) -{ - return readl(pll->mmio + reg); -} - -static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll) -{ - return platform_get_drvdata(pll->pdev); -} - -static int hdmi_pll_enable(struct clk_hw *hw) -{ - struct hdmi_pll_8960 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - int timeout_count, pll_lock_retry =3D 10; - unsigned int val; - - DBG(""); - - /* Assert PLL S/W reset */ - pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d); - pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10); - pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a); - - /* Wait for a short time before de-asserting - * to allow the hardware to complete its job. - * This much of delay should be fine for hardware - * to assert and de-assert. - */ - udelay(10); - - /* De-assert PLL S/W reset */ - pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d); - - val =3D hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); - val |=3D HDMI_8960_PHY_REG12_SW_RESET; - /* Assert PHY S/W reset */ - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); - val &=3D ~HDMI_8960_PHY_REG12_SW_RESET; - /* - * Wait for a short time before de-asserting to allow the hardware to - * complete its job. This much of delay should be fine for hardware to - * assert and de-assert. - */ - udelay(10); - /* De-assert PHY S/W reset */ - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x3f); - - val =3D hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); - val |=3D HDMI_8960_PHY_REG12_PWRDN_B; - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); - /* Wait 10 us for enabling global power for PHY */ - mb(); - udelay(10); - - val =3D pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B); - val |=3D HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B; - val &=3D ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL; - pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val); - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x80); - - timeout_count =3D 1000; - while (--pll_lock_retry > 0) { - /* are we there yet? */ - val =3D pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0); - if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK) - break; - - udelay(1); - - if (--timeout_count > 0) - continue; - - /* - * PLL has still not locked. - * Do a software reset and try again - * Assert PLL S/W reset first - */ - pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d); - udelay(10); - pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d); - - /* - * Wait for a short duration for the PLL calibration - * before checking if the PLL gets locked - */ - udelay(350); - - timeout_count =3D 1000; - } - - return 0; -} - -static void hdmi_pll_disable(struct clk_hw *hw) -{ - struct hdmi_pll_8960 *pll =3D hw_clk_to_pll(hw); - struct hdmi_phy *phy =3D pll_get_phy(pll); - unsigned int val; - - DBG(""); - - val =3D hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); - val &=3D ~HDMI_8960_PHY_REG12_PWRDN_B; - hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); - - val =3D pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B); - val |=3D HDMI_8960_PHY_REG12_SW_RESET; - val &=3D ~HDMI_8960_PHY_REG12_PWRDN_B; - pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val); - /* Make sure HDMI PHY/PLL are powered down */ - mb(); -} - -static const struct pll_rate *find_rate(unsigned long rate) -{ - int i; - - for (i =3D 1; i < ARRAY_SIZE(freqtbl); i++) - if (rate > freqtbl[i].rate) - return &freqtbl[i - 1]; - - return &freqtbl[i - 1]; -} - -static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct hdmi_pll_8960 *pll =3D hw_clk_to_pll(hw); - - return pll->pixclk; -} - -static int hdmi_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - const struct pll_rate *pll_rate =3D find_rate(req->rate); - - req->rate =3D pll_rate->rate; - - return 0; -} - -static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct hdmi_pll_8960 *pll =3D hw_clk_to_pll(hw); - const struct pll_rate *pll_rate =3D find_rate(rate); - int i; - - DBG("rate=3D%lu", rate); - - for (i =3D 0; i < pll_rate->num_reg; i++) - pll_write(pll, pll_rate->conf[i].reg, pll_rate->conf[i].val); - - pll->pixclk =3D rate; - - return 0; -} - -static const struct clk_ops hdmi_pll_ops =3D { - .enable =3D hdmi_pll_enable, - .disable =3D hdmi_pll_disable, - .recalc_rate =3D hdmi_pll_recalc_rate, - .determine_rate =3D hdmi_pll_determine_rate, - .set_rate =3D hdmi_pll_set_rate, -}; - -static const struct clk_parent_data hdmi_pll_parents[] =3D { - { .fw_name =3D "pxo", .name =3D "pxo_board" }, -}; - -static struct clk_init_data pll_init =3D { - .name =3D "hdmi_pll", - .ops =3D &hdmi_pll_ops, - .parent_data =3D hdmi_pll_parents, - .num_parents =3D ARRAY_SIZE(hdmi_pll_parents), - .flags =3D CLK_IGNORE_UNUSED, -}; - -int msm_hdmi_pll_8960_init(struct platform_device *pdev) -{ - struct device *dev =3D &pdev->dev; - struct hdmi_pll_8960 *pll; - int i, ret; - - /* sanity check: */ - for (i =3D 0; i < (ARRAY_SIZE(freqtbl) - 1); i++) - if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate)) - return -EINVAL; - - pll =3D devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); - if (!pll) - return -ENOMEM; - - pll->mmio =3D msm_ioremap(pdev, "hdmi_pll"); - if (IS_ERR(pll->mmio)) { - DRM_DEV_ERROR(dev, "failed to map pll base\n"); - return -ENOMEM; - } - - pll->pdev =3D pdev; - pll->clk_hw.init =3D &pll_init; - - ret =3D devm_clk_hw_register(dev, &pll->clk_hw); - if (ret < 0) { - DRM_DEV_ERROR(dev, "failed to register pll clock\n"); - return ret; - } - - ret =3D devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &pll->clk_= hw); - if (ret) { - DRM_DEV_ERROR(dev, "%s: failed to register clk provider: %d\n", __func__= , ret); - return ret; - } - - return 0; -} diff --git a/drivers/gpu/drm/msm/registers/display/hdmi.xml b/drivers/gpu/d= rm/msm/registers/display/hdmi.xml index 0ebb96297dae..1d44aa26c833 100644 --- a/drivers/gpu/drm/msm/registers/display/hdmi.xml +++ b/drivers/gpu/drm/msm/registers/display/hdmi.xml @@ -564,541 +564,4 @@ xsi:schemaLocation=3D"https://gitlab.freedesktop.org/= freedreno/ rules-fd.xsd"> =20 =20 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 60a0ead127fa..2c0f5547c5de 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -42,6 +42,20 @@ config PHY_QCOM_IPQ806X_SATA depends on OF select GENERIC_PHY =20 +config PHY_QCOM_HDMI + tristate "Qualcomm MSM8x60/MSM8960/MSM8974 HDMI PHY driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on OF + depends on COMMON_CLK + default DRM_MSM_HDMI && ARCH_QCOM && ARM + select GENERIC_PHY + help + Enable this to support the Qualcomm HDMI PHY presend on 32-bit platform= s: + MSM8260, MSM8660, MSM8960, MSM8974, APQ8060, APQ8064, APQ8074 and APQ80= 84. + + Note, this driver is not used on MSM899x platforms, which use + PHY_QCOM_QMP_HDMI instead. + config PHY_QCOM_PCIE2 tristate "Qualcomm PCIe Gen2 PHY Driver" depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST) @@ -68,6 +82,16 @@ config PHY_QCOM_QMP_COMBO Enable this to support the QMP Combo PHY transceiver that is used with USB3 and DisplayPort controllers on Qualcomm chips. =20 +config PHY_QCOM_QMP_HDMI + tristate "Qualcomm QMP HDMI PHY Driver" + default PHY_QCOM_QMP && DRM_MSM_HDMI + help + Enable this to support the QMP HDMI PHY transceiver that is used + with HDMI output on Qualcomm MSM8996 and MSM8998 chips. + + Note, this driver is not used on Qualcomm 32-bit platforms, which use + PHY_QCOM_HDMI instead. + config PHY_QCOM_QMP_PCIE tristate "Qualcomm QMP PCIe PHY Driver" depends on PCI || COMPILE_TEST diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index b71a6a0bed3f..60946c14514a 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -4,17 +4,31 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) +=3D phy-qcom-apq8064= -sata.o obj-$(CONFIG_PHY_QCOM_EDP) +=3D phy-qcom-edp.o obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) +=3D phy-qcom-ipq4019-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) +=3D phy-qcom-ipq806x-sata.o +obj-$(CONFIG_PHY_QCOM_HDMI) +=3D phy-qcom-hdmi.o + +phy-qcom-hdmi-y :=3D \ + phy-qcom-hdmi-preqmp.o \ + phy-qcom-hdmi-28hpm.o \ + phy-qcom-hdmi-28lpm.o \ + phy-qcom-hdmi-45nm.o \ + obj-$(CONFIG_PHY_QCOM_M31_USB) +=3D phy-qcom-m31.o obj-$(CONFIG_PHY_QCOM_M31_EUSB) +=3D phy-qcom-m31-eusb2.o obj-$(CONFIG_PHY_QCOM_PCIE2) +=3D phy-qcom-pcie2.o =20 obj-$(CONFIG_PHY_QCOM_QMP_COMBO) +=3D phy-qcom-qmp-combo.o phy-qcom-qmp-us= bc.o +obj-$(CONFIG_PHY_QCOM_QMP_HDMI) +=3D phy-qcom-qmp-hdmi.o obj-$(CONFIG_PHY_QCOM_QMP_PCIE) +=3D phy-qcom-qmp-pcie.o obj-$(CONFIG_PHY_QCOM_QMP_PCIE_8996) +=3D phy-qcom-qmp-pcie-msm8996.o obj-$(CONFIG_PHY_QCOM_QMP_UFS) +=3D phy-qcom-qmp-ufs.o obj-$(CONFIG_PHY_QCOM_QMP_USB) +=3D phy-qcom-qmp-usb.o obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) +=3D phy-qcom-qmp-usb-legacy.o =20 +phy-qcom-qmp-hdmi-y :=3D \ + phy-qcom-qmp-hdmi-base.o \ + phy-qcom-qmp-hdmi-msm8996.o \ + phy-qcom-qmp-hdmi-msm8998.o \ + obj-$(CONFIG_PHY_QCOM_QUSB2) +=3D phy-qcom-qusb2.o obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) +=3D phy-qcom-eusb2-repeater.o obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) +=3D phy-qcom-uniphy-pcie-28lp.o diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c b/drivers/phy/qualc= omm/phy-qcom-hdmi-28hpm.c new file mode 100644 index 000000000000..720757f8f393 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Red Hat + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + * Author: Rob Clark + */ + +#include +#include + +#include "phy-qcom-hdmi-preqmp.h" + +#define REG_HDMI_8x74_ANA_CFG0 0x00000000 +#define REG_HDMI_8x74_ANA_CFG1 0x00000004 +#define REG_HDMI_8x74_ANA_CFG2 0x00000008 +#define REG_HDMI_8x74_ANA_CFG3 0x0000000c +#define REG_HDMI_8x74_PD_CTRL0 0x00000010 +#define REG_HDMI_8x74_PD_CTRL1 0x00000014 +#define REG_HDMI_8x74_GLB_CFG 0x00000018 +#define REG_HDMI_8x74_DCC_CFG0 0x0000001c +#define REG_HDMI_8x74_DCC_CFG1 0x00000020 +#define REG_HDMI_8x74_TXCAL_CFG0 0x00000024 +#define REG_HDMI_8x74_TXCAL_CFG1 0x00000028 +#define REG_HDMI_8x74_TXCAL_CFG2 0x0000002c +#define REG_HDMI_8x74_TXCAL_CFG3 0x00000030 +#define REG_HDMI_8x74_BIST_CFG0 0x00000034 +#define REG_HDMI_8x74_BIST_PATN0 0x0000003c +#define REG_HDMI_8x74_BIST_PATN1 0x00000040 +#define REG_HDMI_8x74_BIST_PATN2 0x00000044 +#define REG_HDMI_8x74_BIST_PATN3 0x00000048 +#define REG_HDMI_8x74_STATUS 0x0000005c + +static int qcom_hdmi_msm8974_phy_power_on(struct qcom_hdmi_preqmp_phy *hdm= i_phy) +{ + writel(0x1b, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG0); + writel(0xf2, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG1); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_BIST_CFG0); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_BIST_PATN0); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_BIST_PATN1); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_BIST_PATN2); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_BIST_PATN3); + writel(0x20, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL1); + + return 0; +} + +static int qcom_hdmi_msm8974_phy_power_off(struct qcom_hdmi_preqmp_phy *hd= mi_phy) +{ + writel(0x7f, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL0); + + return 0; +} + +const struct clk_parent_data msm8974_hdmi_pll_parent =3D { + .fw_name =3D "xo", .name =3D "xo_board", +}; + +const struct qcom_hdmi_preqmp_cfg msm8974_hdmi_phy_cfg =3D { + .clk_names =3D { "iface", "alt_iface" }, + .num_clks =3D 2, + + .reg_names =3D { "vddio", "core-vdda" }, + .reg_init_load =3D { 100000, 10000 }, + .num_regs =3D 2, + + .power_on =3D qcom_hdmi_msm8974_phy_power_on, + .power_off =3D qcom_hdmi_msm8974_phy_power_off, + + .pll_parent =3D &msm8974_hdmi_pll_parent, +}; diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-28lpm.c b/drivers/phy/qualc= omm/phy-qcom-hdmi-28lpm.c new file mode 100644 index 000000000000..9473aff24718 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-28lpm.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Red Hat + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + * Author: Rob Clark + */ + +#include +#include +#include + +#include "phy-qcom-hdmi-preqmp.h" + +#define REG_HDMI_8960_PHY_REG0 0x00000000 + +#define REG_HDMI_8960_PHY_REG1 0x00000004 + +#define REG_HDMI_8960_PHY_REG2 0x00000008 + +#define REG_HDMI_8960_PHY_REG3 0x0000000c + +#define REG_HDMI_8960_PHY_REG4 0x00000010 + +#define REG_HDMI_8960_PHY_REG5 0x00000014 + +#define REG_HDMI_8960_PHY_REG6 0x00000018 + +#define REG_HDMI_8960_PHY_REG7 0x0000001c + +#define REG_HDMI_8960_PHY_REG8 0x00000020 + +#define REG_HDMI_8960_PHY_REG9 0x00000024 + +#define REG_HDMI_8960_PHY_REG10 0x00000028 + +#define REG_HDMI_8960_PHY_REG11 0x0000002c + +#define REG_HDMI_8960_PHY_REG12 0x00000030 +#define HDMI_8960_PHY_REG12_SW_RESET BIT(5) +#define HDMI_8960_PHY_REG12_PWRDN_B BIT(7) + +#define REG_HDMI_8960_PHY_REG_BIST_CFG 0x00000034 + +#define REG_HDMI_8960_PHY_DEBUG_BUS_SEL 0x00000038 + +#define REG_HDMI_8960_PHY_REG_MISC0 0x0000003c + +#define REG_HDMI_8960_PHY_REG13 0x00000040 + +#define REG_HDMI_8960_PHY_REG14 0x00000044 + +#define REG_HDMI_8960_PHY_REG15 0x00000048 + +#define REG_HDMI_8960_PHY_PLL_REFCLK_CFG 0x00000000 +#define HDMI_8960_PHY_PLL_VCO_DIV GENMASK(5, 4) +#define HDMI_8960_PHY_DBLR_EN BIT(3) +#define HDMI_8960_PHY_CLK0_DIV GENMASK(2, 1) + +#define REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG 0x00000004 + +#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 0x00000008 + +#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 0x0000000c + +#define REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG 0x00000010 + +#define REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG 0x00000014 + +#define REG_HDMI_8960_PHY_PLL_PWRDN_B 0x00000018 +#define HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL 0x00000002 +#define HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B 0x00000008 + +#define REG_HDMI_8960_PHY_PLL_SDM_CFG0 0x0000001c +#define HDMI_8960_PHY_SDM_BYP BIT(6) +#define HDMI_8960_PHY_BYP_DIV GENMASK(5, 0) + +#define REG_HDMI_8960_PHY_PLL_SDM_CFG1 0x00000020 +#define HDMI_8960_PHY_DITHER BIT(6) +#define HDMI_8960_PHY_DC_OFFSET GENMASK(5, 0) + +#define REG_HDMI_8960_PHY_PLL_SDM_CFG2 0x00000024 + +#define REG_HDMI_8960_PHY_PLL_SDM_CFG3 0x00000028 + +#define REG_HDMI_8960_PHY_PLL_SDM_CFG4 0x0000002c + +#define REG_HDMI_8960_PHY_PLL_SSC_CFG0 0x00000030 + +#define REG_HDMI_8960_PHY_PLL_SSC_CFG1 0x00000034 + +#define REG_HDMI_8960_PHY_PLL_SSC_CFG2 0x00000038 + +#define REG_HDMI_8960_PHY_PLL_SSC_CFG3 0x0000003c + +#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 0x00000040 + +#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 0x00000044 + +#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 0x00000048 + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 0x0000004c + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 0x00000050 + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 0x00000054 + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 0x00000058 + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 0x0000005c + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 0x00000060 + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 0x00000064 + +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 0x00000068 + +#define REG_HDMI_8960_PHY_PLL_DEBUG_SEL 0x0000006c + +#define REG_HDMI_8960_PHY_PLL_MISC0 0x00000070 + +#define REG_HDMI_8960_PHY_PLL_MISC1 0x00000074 + +#define REG_HDMI_8960_PHY_PLL_MISC2 0x00000078 + +#define REG_HDMI_8960_PHY_PLL_MISC3 0x0000007c + +#define REG_HDMI_8960_PHY_PLL_MISC4 0x00000080 + +#define REG_HDMI_8960_PHY_PLL_MISC5 0x00000084 + +#define REG_HDMI_8960_PHY_PLL_MISC6 0x00000088 + +#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS0 0x0000008c + +#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS1 0x00000090 + +#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS2 0x00000094 + +#define REG_HDMI_8960_PHY_PLL_STATUS0 0x00000098 +#define HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK 0x00000001 + +#define REG_HDMI_8960_PHY_PLL_STATUS1 0x0000009c + +/* FIXME: verify boundaries */ +#define HDMI_8960_VCO_MAX_FREQ 1125000000UL +#define HDMI_8960_VCO_MIN_FREQ 540000000UL + +#define HDMI_8960_COMMON_DIV 5 + +static inline void write16(u16 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel(val >> 8, reg + 4); +} + +static inline void write24(u32 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel((val >> 8) & 0xff, reg + 4); + writel(val >> 16, reg + 8); +} + +static inline u32 read24(void __iomem *reg) +{ + u32 val =3D readl(reg); + + val |=3D readl(reg + 4) << 8; + val |=3D readl(reg + 8) << 16; + + return val; +} + +/* This function is close to UNIPHY, but it has slighly different fields */ +static unsigned long qcom_28lpm_recalc(struct qcom_hdmi_preqmp_phy *hdmi_p= hy, + unsigned long parent_rate) +{ + unsigned long rate; + u32 refclk_cfg; + u32 dc_offset; + u64 fraq_n; + u32 val; + + refclk_cfg =3D readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_REFCLK_CFG= ); + parent_rate /=3D FIELD_GET(HDMI_8960_PHY_CLK0_DIV, refclk_cfg) + 1; + if (refclk_cfg & HDMI_8960_PHY_DBLR_EN) + parent_rate *=3D 2; + + val =3D readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG0); + if (val & HDMI_8960_PHY_SDM_BYP) { + dc_offset =3D FIELD_GET(HDMI_8960_PHY_BYP_DIV, val); + fraq_n =3D 0; + } else { + val =3D readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG1); + dc_offset =3D FIELD_GET(HDMI_8960_PHY_DC_OFFSET, val); + fraq_n =3D read24(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG2); + } + + rate =3D (dc_offset + 1) * parent_rate; + rate +=3D div_u64(fraq_n * parent_rate, 0x10000); + + return rate; +} + +/* This function is close to UNIPHY, but it has slighly different fields */ +static int qcom_28lpm_set_rate(struct qcom_hdmi_preqmp_phy *hdmi_phy, unsi= gned long parent_rate, + unsigned long vco_freq, u32 div_idx) +{ + unsigned int pixclk =3D hdmi_phy->hdmi_opts.tmds_char_rate; + unsigned int int_ref_freq; + unsigned int div; + unsigned int dc_offset; + unsigned int sdm_freq_seed; + unsigned int val; + bool sdm_mode =3D false; + u32 refclk_cfg; + u32 lf_cfg0; + u32 lf_cfg1; + u64 tmp; + + dev_dbg(hdmi_phy->dev, "rate=3D%u, div =3D %d, vco =3D %lu", pixclk, div,= vco_freq); + + if (vco_freq % (parent_rate / 2) =3D=3D 0) { + refclk_cfg =3D FIELD_PREP(HDMI_8960_PHY_CLK0_DIV, 1); + int_ref_freq =3D parent_rate / 2; + } else { + refclk_cfg =3D HDMI_8960_PHY_DBLR_EN; + int_ref_freq =3D parent_rate * 2; + sdm_mode =3D true; + } + + dc_offset =3D vco_freq / int_ref_freq - 1; + tmp =3D vco_freq % int_ref_freq; + tmp *=3D 0x10000; + sdm_freq_seed =3D div_u64(tmp, int_ref_freq); + + val =3D FIELD_PREP(HDMI_8960_PHY_PLL_VCO_DIV, div_idx) | refclk_cfg; + writel(val, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_REFCLK_CFG); + + lf_cfg0 =3D dc_offset >=3D 30 ? 0 : (dc_offset >=3D 16 ? 0x10 : 0x20); + lf_cfg0 +=3D sdm_mode ? 0 : 1; + + /* XXX: 0xc3 instead of 0x33 for qcs404 */ + lf_cfg1 =3D dc_offset >=3D 30 ? 0x33 : (dc_offset >=3D 16 ? 0xbb : 0xf9); + + writel(lf_cfg0, + hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0); + writel(lf_cfg1, + hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1); + + if (sdm_mode) + writel(dc_offset, + hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG0); + else + writel(HDMI_8960_PHY_SDM_BYP | dc_offset, + hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG0); + + writel(HDMI_8960_PHY_DITHER | dc_offset, + hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG1); + + write24(sdm_freq_seed, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG2= ); + + write16(vco_freq / 1000, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_VCOCAL= _CFG0); + + writel(0x3b, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2); + + return 0; +} + +static const unsigned int qcom_hdmi_8960_divs[] =3D {1, 2, 4, 6}; + +static unsigned long qcom_hdmi_8960_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D hw_clk_to_phy(hw); + u32 refclk_cfg =3D readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_REFCLK= _CFG); + u32 div_idx =3D FIELD_GET(HDMI_8960_PHY_PLL_VCO_DIV, refclk_cfg); + unsigned long rate =3D qcom_28lpm_recalc(hdmi_phy, parent_rate); + + return rate / HDMI_8960_COMMON_DIV / qcom_hdmi_8960_divs[div_idx]; +} + +static int qcom_hdmi_8960_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long long min_freq =3D HDMI_8960_VCO_MIN_FREQ / HDMI_8960_COMMON= _DIV; + unsigned long long max_freq =3D HDMI_8960_VCO_MAX_FREQ / HDMI_8960_COMMON= _DIV; + + req->rate =3D clamp(req->rate, min_freq / 6, max_freq); + + return 0; +} + +static const struct clk_ops qcom_hdmi_8960_pll_ops =3D { + .recalc_rate =3D qcom_hdmi_8960_pll_recalc_rate, + .determine_rate =3D qcom_hdmi_8960_pll_determine_rate, +}; + +static int qcom_hdmi_msm8960_phy_pll_enable(struct qcom_hdmi_preqmp_phy *h= dmi_phy) +{ + int pll_lock_retry =3D 10; + unsigned int val; + int ret; + + /* Assert PLL S/W reset */ + writel(0x8d, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2); + writel(0x10, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0); + writel(0x1a, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1); + + /* Wait for a short time before de-asserting + * to allow the hardware to complete its job. + * This much of delay should be fine for hardware + * to assert and de-assert. + */ + udelay(10); + + /* De-assert PLL S/W reset */ + writel(0x0d, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2); + + val =3D readl(hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + val |=3D HDMI_8960_PHY_REG12_SW_RESET; + /* Assert PHY S/W reset */ + writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + val &=3D ~HDMI_8960_PHY_REG12_SW_RESET; + /* + * Wait for a short time before de-asserting to allow the hardware to + * complete its job. This much of delay should be fine for hardware to + * assert and de-assert. + */ + udelay(10); + /* De-assert PHY S/W reset */ + writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + writel(0x3f, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2); + + val =3D readl(hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + val |=3D HDMI_8960_PHY_REG12_PWRDN_B; + writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + /* Wait 10 us for enabling global power for PHY */ + mb(); + udelay(10); + + val =3D readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B); + val |=3D HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B; + val &=3D ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL; + writel(val, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B); + writel(0x80, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2); + + while (--pll_lock_retry > 0) { + ret =3D readl_poll_timeout(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_STA= TUS0, + val, val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK, + 1, 1000); + if (!ret) + break; + + /* + * PLL has still not locked. + * Do a software reset and try again + * Assert PLL S/W reset first + */ + writel(0x8d, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2); + udelay(10); + writel(0x0d, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2); + + /* + * Wait for a short duration for the PLL calibration + * before checking if the PLL gets locked + */ + udelay(350); + } + + return ret; +} + +static int qcom_hdmi_msm8960_phy_find_div(unsigned long long pixclk) +{ + unsigned long long min_freq =3D HDMI_8960_VCO_MIN_FREQ / HDMI_8960_COMMON= _DIV; + int i; + + if (pixclk > HDMI_8960_VCO_MAX_FREQ / HDMI_8960_COMMON_DIV) + return -E2BIG; + + for (i =3D 0; i < ARRAY_SIZE(qcom_hdmi_8960_divs); i++) { + if (pixclk >=3D min_freq / qcom_hdmi_8960_divs[i]) + return i; + } + + return -EINVAL; +} + +static int qcom_hdmi_msm8960_phy_set_rate(struct qcom_hdmi_preqmp_phy *hdm= i_phy) +{ + unsigned long long pixclk =3D hdmi_phy->hdmi_opts.tmds_char_rate; + /* XXX: 19.2 for qcs404 */ + unsigned long parent_rate =3D 27000000; + unsigned long vco_freq; + int div_idx; + u32 div; + + div_idx =3D qcom_hdmi_msm8960_phy_find_div(pixclk); + if (WARN_ON(div_idx < 0)) + return div_idx; + + div =3D qcom_hdmi_8960_divs[div_idx]; + vco_freq =3D pixclk * HDMI_8960_COMMON_DIV * div; + + return qcom_28lpm_set_rate(hdmi_phy, parent_rate, vco_freq, div_idx); +} + +static void qcom_hdmi_msm8960_phy_pll_disable(struct qcom_hdmi_preqmp_phy = *hdmi_phy) +{ + unsigned int val; + + val =3D readl(hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + val &=3D ~HDMI_8960_PHY_REG12_PWRDN_B; + writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12); + + val =3D readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B); + val |=3D HDMI_8960_PHY_REG12_SW_RESET; + val &=3D ~HDMI_8960_PHY_REG12_PWRDN_B; + writel(val, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B); + /* Make sure HDMI PHY/PLL are powered down */ + mb(); +} + +static int qcom_hdmi_msm8960_phy_power_on(struct qcom_hdmi_preqmp_phy *hdm= i_phy) +{ + int ret; + + ret =3D qcom_hdmi_msm8960_phy_set_rate(hdmi_phy); + if (ret) + return ret; + + ret =3D qcom_hdmi_msm8960_phy_pll_enable(hdmi_phy); + if (ret) + return ret; + + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2); + writel(0x1b, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG0); + writel(0xf2, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG1); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG4); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG5); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG6); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG7); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG8); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG9); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG10); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG11); + writel(0x20, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG3); + + return 0; +} + +static int qcom_hdmi_msm8960_phy_power_off(struct qcom_hdmi_preqmp_phy *hd= mi_phy) +{ + writel(0x7f, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2); + + qcom_hdmi_msm8960_phy_pll_disable(hdmi_phy); + + return 0; +} + +const struct clk_parent_data msm8960_hdmi_pll_parent =3D { + .fw_name =3D "pxo", .name =3D "pxo_board", +}; + +const struct qcom_hdmi_preqmp_cfg msm8960_hdmi_phy_cfg =3D { + .clk_names =3D { "slave_iface" }, + .num_clks =3D 1, + + .reg_names =3D { "core-vdda" }, + .num_regs =3D 1, + + .power_on =3D qcom_hdmi_msm8960_phy_power_on, + .power_off =3D qcom_hdmi_msm8960_phy_power_off, + + .pll_ops =3D &qcom_hdmi_8960_pll_ops, + .pll_parent =3D &msm8960_hdmi_pll_parent, +}; diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c b/drivers/phy/qualco= mm/phy-qcom-hdmi-45nm.c new file mode 100644 index 000000000000..bb7834d1d421 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Red Hat + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + * Author: Rob Clark + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-qcom-hdmi-preqmp.h" + +#define REG_HDMI_8x60_PHY_REG0 0x00000000 +#define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c + +#define REG_HDMI_8x60_PHY_REG1 0x00000004 +#define HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK 0x000000f0 +#define HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK 0x0000000f + +#define REG_HDMI_8x60_PHY_REG2 0x00000008 +#define HDMI_8x60_PHY_REG2_PD_DESER 0x00000001 +#define HDMI_8x60_PHY_REG2_PD_DRIVE_1 0x00000002 +#define HDMI_8x60_PHY_REG2_PD_DRIVE_2 0x00000004 +#define HDMI_8x60_PHY_REG2_PD_DRIVE_3 0x00000008 +#define HDMI_8x60_PHY_REG2_PD_DRIVE_4 0x00000010 +#define HDMI_8x60_PHY_REG2_PD_PLL 0x00000020 +#define HDMI_8x60_PHY_REG2_PD_PWRGEN 0x00000040 +#define HDMI_8x60_PHY_REG2_RCV_SENSE_EN 0x00000080 + +#define REG_HDMI_8x60_PHY_REG3 0x0000000c +#define HDMI_8x60_PHY_REG3_PLL_ENABLE 0x00000001 + +#define REG_HDMI_8x60_PHY_REG4 0x00000010 + +#define REG_HDMI_8x60_PHY_REG5 0x00000014 + +#define REG_HDMI_8x60_PHY_REG6 0x00000018 + +#define REG_HDMI_8x60_PHY_REG7 0x0000001c + +#define REG_HDMI_8x60_PHY_REG8 0x00000020 + +#define REG_HDMI_8x60_PHY_REG9 0x00000024 + +#define REG_HDMI_8x60_PHY_REG10 0x00000028 + +#define REG_HDMI_8x60_PHY_REG11 0x0000002c + +#define REG_HDMI_8x60_PHY_REG12 0x00000030 +#define HDMI_8x60_PHY_REG12_RETIMING_EN 0x00000001 +#define HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN 0x00000002 +#define HDMI_8x60_PHY_REG12_FORCE_LOCK 0x00000010 + +static int qcom_hdmi_msm8x60_phy_power_on(struct qcom_hdmi_preqmp_phy *hdm= i_phy) +{ + unsigned long pixclock =3D hdmi_phy->hdmi_opts.tmds_char_rate; + + /* De-serializer delay D/C for non-lbk mode: */ + writel(FIELD_PREP(HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK, 3), + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG0); + + if (pixclock =3D=3D 27 * HZ_PER_MHZ) { + /* video_format =3D=3D HDMI_VFRMT_720x480p60_16_9 */ + writel(FIELD_PREP(HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK, 5) | + FIELD_PREP(HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK, 3), + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG1); + } else { + writel(FIELD_PREP(HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK, 5) | + FIELD_PREP(HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK, 4), + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG1); + } + + /* No matter what, start from the power down mode: */ + writel(HDMI_8x60_PHY_REG2_PD_PWRGEN | + HDMI_8x60_PHY_REG2_PD_PLL | + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + + /* Turn PowerGen on: */ + writel(HDMI_8x60_PHY_REG2_PD_PLL | + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + + /* Turn PLL power on: */ + writel(HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + + /* Write to HIGH after PLL power down de-assert: */ + writel(HDMI_8x60_PHY_REG3_PLL_ENABLE, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG3); + + /* ASIC power on; PHY REG9 =3D 0 */ + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG9); + + /* Enable PLL lock detect, PLL lock det will go high after lock + * Enable the re-time logic + */ + writel(HDMI_8x60_PHY_REG12_RETIMING_EN | + HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG12); + + /* Drivers are on: */ + writel(HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + + /* If the RX detector is needed: */ + writel(HDMI_8x60_PHY_REG2_RCV_SENSE_EN | + HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG4); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG5); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG6); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG7); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG8); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG9); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG10); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG11); + + /* If we want to use lock enable based on counting: */ + writel(HDMI_8x60_PHY_REG12_RETIMING_EN | + HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN | + HDMI_8x60_PHY_REG12_FORCE_LOCK, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG12); + + return 0; +} + +static int qcom_hdmi_msm8x60_phy_power_off(struct qcom_hdmi_preqmp_phy *hd= mi_phy) +{ + /* Turn off Driver */ + writel(HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + udelay(10); + /* Disable PLL */ + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG3); + /* Power down PHY, but keep RX-sense: */ + writel(HDMI_8x60_PHY_REG2_RCV_SENSE_EN | + HDMI_8x60_PHY_REG2_PD_PWRGEN | + HDMI_8x60_PHY_REG2_PD_PLL | + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER, + hdmi_phy->phy_reg + REG_HDMI_8x60_PHY_REG2); + + return 0; +} + +const struct qcom_hdmi_preqmp_cfg msm8x60_hdmi_phy_cfg =3D { + .clk_names =3D { "slave_iface" }, + .num_clks =3D 1, + + .reg_names =3D { "core-vdda" }, + .num_regs =3D 1, + + .power_on =3D qcom_hdmi_msm8x60_phy_power_on, + .power_off =3D qcom_hdmi_msm8x60_phy_power_off, + + /* FIXME: no PLL support */ +}; diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c b/drivers/phy/qual= comm/phy-qcom-hdmi-preqmp.c new file mode 100644 index 000000000000..4e8a397fcdb2 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Red Hat + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + */ + +#include +#include + +#include "phy-qcom-hdmi-preqmp.h" + +static int qcom_hdmi_preqmp_phy_init(struct phy *phy) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D phy_get_drvdata(phy); + + return pm_runtime_resume_and_get(hdmi_phy->dev); +} + +static int qcom_hdmi_preqmp_phy_exit(struct phy *phy) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D phy_get_drvdata(phy); + + pm_runtime_put(hdmi_phy->dev); + + return 0; +} + +static int qcom_hdmi_preqmp_phy_power_on(struct phy *phy) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D phy_get_drvdata(phy); + + return hdmi_phy->power_on(hdmi_phy); +}; + +static int qcom_hdmi_preqmp_phy_power_off(struct phy *phy) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D phy_get_drvdata(phy); + + return hdmi_phy->power_off(hdmi_phy); +}; + +static int qcom_hdmi_preqmp_phy_configure(struct phy *phy, union phy_confi= gure_opts *opts) +{ + const struct phy_configure_opts_hdmi *hdmi_opts =3D &opts->hdmi; + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D phy_get_drvdata(phy); + int ret =3D 0; + + memcpy(&hdmi_phy->hdmi_opts, hdmi_opts, sizeof(*hdmi_opts)); + + return ret; +} + +static int __maybe_unused qcom_hdmi_preqmp_runtime_resume(struct device *d= ev) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D dev_get_drvdata(dev); + int ret; + + ret =3D regulator_bulk_enable(hdmi_phy->num_regs, hdmi_phy->regs); + if (ret) + return ret; + + ret =3D clk_bulk_prepare_enable(hdmi_phy->num_clks, hdmi_phy->clks); + if (ret) + goto out_disable_supplies; + + return 0; + +out_disable_supplies: + regulator_bulk_disable(hdmi_phy->num_regs, hdmi_phy->regs); + + return ret; +} + +static int __maybe_unused qcom_hdmi_preqmp_runtime_suspend(struct device *= dev) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(hdmi_phy->num_clks, hdmi_phy->clks); + regulator_bulk_disable(hdmi_phy->num_regs, hdmi_phy->regs); + + return 0; +} + +static const struct phy_ops qcom_hdmi_preqmp_phy_ops =3D { + .init =3D qcom_hdmi_preqmp_phy_init, + .configure =3D qcom_hdmi_preqmp_phy_configure, + .power_on =3D qcom_hdmi_preqmp_phy_power_on, + .power_off =3D qcom_hdmi_preqmp_phy_power_off, + .exit =3D qcom_hdmi_preqmp_phy_exit, + .owner =3D THIS_MODULE, +}; + +static int qcom_hdmi_preqmp_probe(struct platform_device *pdev) +{ + struct clk_init_data init; + struct phy_provider *phy_provider; + struct device *dev =3D &pdev->dev; + struct qcom_hdmi_preqmp_phy *hdmi_phy; + const struct qcom_hdmi_preqmp_cfg *cfg; + int i, ret; + + cfg =3D of_device_get_match_data(dev); + if (!cfg) + return -EINVAL; + + hdmi_phy =3D devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); + if (!hdmi_phy) + return -ENOMEM; + + hdmi_phy->dev =3D dev; + + hdmi_phy->phy_reg =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hdmi_phy->phy_reg)) + return PTR_ERR(hdmi_phy->phy_reg); + + hdmi_phy->pll_reg =3D devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(hdmi_phy->pll_reg)) + return PTR_ERR(hdmi_phy->pll_reg); + + hdmi_phy->num_clks =3D cfg->num_clks; + for (i =3D 0; i < cfg->num_clks; i++) + hdmi_phy->clks[i].id =3D cfg->clk_names[i]; + ret =3D devm_clk_bulk_get(dev, hdmi_phy->num_clks, hdmi_phy->clks); + if (ret) + return ret; + + hdmi_phy->num_regs =3D cfg->num_regs; + for (i =3D 0; i < cfg->num_regs; i++) { + hdmi_phy->regs[i].supply =3D cfg->reg_names[i]; + hdmi_phy->regs[i].init_load_uA =3D cfg->reg_init_load[i]; + } + ret =3D devm_regulator_bulk_get(dev, hdmi_phy->num_regs, hdmi_phy->regs); + if (ret) + return ret; + + hdmi_phy->power_on =3D cfg->power_on; + hdmi_phy->power_off =3D cfg->power_off; + + platform_set_drvdata(pdev, hdmi_phy); + + ret =3D devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + + ret =3D pm_runtime_resume_and_get(&pdev->dev); + if (ret) + return ret; + + /* FIXME: msm8x60 doesn't yet have PLL ops */ + if (cfg->pll_ops) { + init.name =3D "hdmipll"; + init.ops =3D cfg->pll_ops; + init.flags =3D CLK_GET_RATE_NOCACHE; + init.parent_data =3D cfg->pll_parent; + init.num_parents =3D 1; + + hdmi_phy->pll_hw.init =3D &init; + ret =3D devm_clk_hw_register(hdmi_phy->dev, &hdmi_phy->pll_hw); + if (ret) + goto err; + + ret =3D devm_of_clk_add_hw_provider(hdmi_phy->dev, of_clk_hw_simple_get, + &hdmi_phy->pll_hw); + if (ret) + goto err; + } + + hdmi_phy->phy =3D devm_phy_create(dev, pdev->dev.of_node, &qcom_hdmi_preq= mp_phy_ops); + if (IS_ERR(hdmi_phy->phy)) { + ret =3D PTR_ERR(hdmi_phy->phy); + goto err; + } + + phy_set_drvdata(hdmi_phy->phy, hdmi_phy); + + phy_provider =3D devm_of_phy_provider_register(dev, of_phy_simple_xlate); + pm_runtime_put(&pdev->dev); + return PTR_ERR_OR_ZERO(phy_provider); + +err: + pm_runtime_put(&pdev->dev); + return ret; +} + +static const struct of_device_id qcom_hdmi_preqmp_of_match_table[] =3D { + { .compatible =3D "qcom,hdmi-phy-8084", .data =3D &msm8974_hdmi_phy_cfg, = }, + { .compatible =3D "qcom,hdmi-phy-8660", .data =3D &msm8x60_hdmi_phy_cfg, = }, + { .compatible =3D "qcom,hdmi-phy-8960", .data =3D &msm8960_hdmi_phy_cfg, = }, + { .compatible =3D "qcom,hdmi-phy-8974", .data =3D &msm8974_hdmi_phy_cfg, = }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_hdmi_preqmp_of_match_table); + +DEFINE_RUNTIME_DEV_PM_OPS(qcom_hdmi_preqmp_pm_ops, + qcom_hdmi_preqmp_runtime_suspend, + qcom_hdmi_preqmp_runtime_resume, + NULL); + +static struct platform_driver qcom_hdmi_preqmp_driver =3D { + .probe =3D qcom_hdmi_preqmp_probe, + .driver =3D { + .name =3D "qcom-preqmp-hdmi-phy", + .of_match_table =3D qcom_hdmi_preqmp_of_match_table, + .pm =3D &qcom_hdmi_preqmp_pm_ops, + }, +}; + +module_platform_driver(qcom_hdmi_preqmp_driver); + +MODULE_AUTHOR("Dmitry Baryshkov "); +MODULE_DESCRIPTION("Qualcomm MSMpreqmp HDMI PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h b/drivers/phy/qual= comm/phy-qcom-hdmi-preqmp.h new file mode 100644 index 000000000000..dda230616d76 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2013 Red Hat + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + */ + +#ifndef PHY_QCOM_HDMI_PREQMP_H +#define PHY_QCOM_HDMI_PREQMP_H + +#include +#include +#include +#include + +#define MAX_CLKS 2 +#define MAX_SUPPLIES 2 + +struct qcom_hdmi_preqmp_phy { + struct device *dev; + struct phy *phy; + void __iomem *pll_reg; + void __iomem *phy_reg; + + struct phy_configure_opts_hdmi hdmi_opts; + + struct clk_hw pll_hw; + struct clk_bulk_data clks[MAX_CLKS]; + int num_clks; + + struct regulator_bulk_data regs[MAX_SUPPLIES]; + int num_regs; + + int (*power_on)(struct qcom_hdmi_preqmp_phy *phy); + int (*power_off)(struct qcom_hdmi_preqmp_phy *phy); +}; + +#define hw_clk_to_phy(x) container_of(x, struct qcom_hdmi_preqmp_phy, pll_= hw) + +struct qcom_hdmi_preqmp_cfg { + const char * const clk_names[MAX_CLKS]; + int num_clks; + + const char * const reg_names[MAX_SUPPLIES]; + int reg_init_load[MAX_SUPPLIES]; + int num_regs; + + int (*power_on)(struct qcom_hdmi_preqmp_phy *phy); + int (*power_off)(struct qcom_hdmi_preqmp_phy *phy); + + const struct clk_ops *pll_ops; + const struct clk_parent_data *pll_parent; +}; + +extern const struct qcom_hdmi_preqmp_cfg msm8x60_hdmi_phy_cfg; +extern const struct qcom_hdmi_preqmp_cfg msm8960_hdmi_phy_cfg; +extern const struct qcom_hdmi_preqmp_cfg msm8974_hdmi_phy_cfg; + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-base.c b/drivers/phy/qu= alcomm/phy-qcom-qmp-hdmi-base.c new file mode 100644 index 000000000000..8ac907b14430 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-base.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + */ + +#include +#include +#include + +#include "phy-qcom-qmp-hdmi.h" + +int qmp_hdmi_phy_init(struct phy *phy) +{ + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + + return pm_runtime_resume_and_get(hdmi_phy->dev); +} + +int qmp_hdmi_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + const struct phy_configure_opts_hdmi *hdmi_opts =3D &opts->hdmi; + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + int ret =3D 0; + + memcpy(&hdmi_phy->hdmi_opts, hdmi_opts, sizeof(*hdmi_opts)); + + return ret; +} + +int qmp_hdmi_phy_exit(struct phy *phy) +{ + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + + pm_runtime_put(hdmi_phy->dev); + + return 0; +} + +static int __maybe_unused qmp_hdmi_runtime_resume(struct device *dev) +{ + struct qmp_hdmi_phy *hdmi_phy =3D dev_get_drvdata(dev); + int ret; + + ret =3D regulator_bulk_enable(ARRAY_SIZE(hdmi_phy->supplies), hdmi_phy->s= upplies); + if (ret) + return ret; + + ret =3D clk_bulk_prepare_enable(ARRAY_SIZE(hdmi_phy->clks), hdmi_phy->clk= s); + if (ret) + goto out_disable_supplies; + + return 0; + +out_disable_supplies: + regulator_bulk_disable(ARRAY_SIZE(hdmi_phy->supplies), hdmi_phy->supplies= ); + + return ret; +} + +static int __maybe_unused qmp_hdmi_runtime_suspend(struct device *dev) +{ + struct qmp_hdmi_phy *hdmi_phy =3D dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(ARRAY_SIZE(hdmi_phy->clks), hdmi_phy->clks); + regulator_bulk_disable(ARRAY_SIZE(hdmi_phy->supplies), hdmi_phy->supplies= ); + + return 0; +} + +static int qmp_hdmi_probe(struct platform_device *pdev) +{ + struct clk_init_data init =3D { + .name =3D "hdmipll", + .parent_data =3D (const struct clk_parent_data[]) { + { .fw_name =3D "xo", .name =3D "xo_board" }, + }, + .flags =3D CLK_GET_RATE_NOCACHE, + .num_parents =3D 1, + }; + const struct qmp_hdmi_cfg *cfg =3D of_device_get_match_data(&pdev->dev); + struct phy_provider *phy_provider; + struct device *dev =3D &pdev->dev; + struct qmp_hdmi_phy *hdmi_phy; + int ret, i; + + hdmi_phy =3D devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); + if (!hdmi_phy) + return -ENOMEM; + + hdmi_phy->dev =3D dev; + + hdmi_phy->serdes =3D devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hdmi_phy->serdes)) + return PTR_ERR(hdmi_phy->serdes); + + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + hdmi_phy->tx[i] =3D devm_platform_ioremap_resource(pdev, 1 + i); + if (IS_ERR(hdmi_phy->tx[i])) + return PTR_ERR(hdmi_phy->tx[i]); + } + + hdmi_phy->phy_reg =3D devm_platform_ioremap_resource(pdev, 5); + if (IS_ERR(hdmi_phy->phy_reg)) + return PTR_ERR(hdmi_phy->phy_reg); + + hdmi_phy->clks[0].id =3D "iface"; + hdmi_phy->clks[1].id =3D "ref"; + ret =3D devm_clk_bulk_get(dev, ARRAY_SIZE(hdmi_phy->clks), hdmi_phy->clks= ); + if (ret) + return ret; + + hdmi_phy->supplies[0].supply =3D "vddio"; + hdmi_phy->supplies[0].init_load_uA =3D 100000; + hdmi_phy->supplies[1].supply =3D "vcca"; + hdmi_phy->supplies[1].init_load_uA =3D 10000; + ret =3D devm_regulator_bulk_get(dev, ARRAY_SIZE(hdmi_phy->supplies), hdmi= _phy->supplies); + if (ret) + return ret; + + platform_set_drvdata(pdev, hdmi_phy); + + ret =3D devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; + + ret =3D pm_runtime_resume_and_get(&pdev->dev); + if (ret) + return ret; + + init.ops =3D cfg->pll_ops; + hdmi_phy->pll_hw.init =3D &init; + ret =3D devm_clk_hw_register(hdmi_phy->dev, &hdmi_phy->pll_hw); + if (ret) + goto err; + + ret =3D devm_of_clk_add_hw_provider(hdmi_phy->dev, of_clk_hw_simple_get, = &hdmi_phy->pll_hw); + if (ret) + goto err; + + hdmi_phy->phy =3D devm_phy_create(dev, pdev->dev.of_node, cfg->phy_ops); + if (IS_ERR(hdmi_phy->phy)) { + ret =3D PTR_ERR(hdmi_phy->phy); + goto err; + } + + phy_set_drvdata(hdmi_phy->phy, hdmi_phy); + + phy_provider =3D devm_of_phy_provider_register(dev, of_phy_simple_xlate); + pm_runtime_put(&pdev->dev); + return PTR_ERR_OR_ZERO(phy_provider); + +err: + pm_runtime_put(&pdev->dev); + return ret; +} + +static const struct of_device_id qmp_hdmi_of_match_table[] =3D { + { + .compatible =3D "qcom,hdmi-phy-8996", .data =3D &qmp_hdmi_8996_cfg, + }, { + .compatible =3D "qcom,hdmi-phy-8998", .data =3D &qmp_hdmi_8998_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qmp_hdmi_of_match_table); + +DEFINE_RUNTIME_DEV_PM_OPS(qmp_hdmi_pm_ops, + qmp_hdmi_runtime_suspend, + qmp_hdmi_runtime_resume, + NULL); + +static struct platform_driver qmp_hdmi_driver =3D { + .probe =3D qmp_hdmi_probe, + .driver =3D { + .name =3D "qcom-qmp-hdmi-phy", + .of_match_table =3D qmp_hdmi_of_match_table, + .pm =3D &qmp_hdmi_pm_ops, + }, +}; + +module_platform_driver(qmp_hdmi_driver); + +MODULE_AUTHOR("Dmitry Baryshkov "); +MODULE_DESCRIPTION("Qualcomm QMP HDMI PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-msm8996.c b/drivers/phy= /qualcomm/phy-qcom-qmp-hdmi-msm8996.c new file mode 100644 index 000000000000..67be7aba6feb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-msm8996.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + */ + +#include +#include +#include + +#include "phy-qcom-qmp-hdmi.h" +#include "phy-qcom-qmp-qserdes-com.h" +#include "phy-qcom-qmp-qserdes-txrx.h" + +#define HDMI_VCO_MAX_FREQ 12000000000UL +#define HDMI_VCO_MIN_FREQ 8000000000UL + +#define HDMI_PCLK_MAX_FREQ 600000000UL +#define HDMI_PCLK_MIN_FREQ 25000000UL + +#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL +#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL +#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL +#define HDMI_DEFAULT_REF_CLOCK 19200000 +#define HDMI_PLL_CMP_CNT 1024 + +#define HDMI_PLL_POLL_MAX_READS 100 +#define HDMI_PLL_POLL_TIMEOUT_US 150 + +#define HDMI_8996_PHY_CFG 0x00000000 +#define HDMI_8996_PHY_PD_CTL 0x00000004 +#define HDMI_8996_PHY_MODE 0x00000008 +#define HDMI_8996_PHY_MISR_CLEAR 0x0000000c +#define HDMI_8996_PHY_TX0_TX1_BIST_CFG0 0x00000010 +#define HDMI_8996_PHY_TX0_TX1_BIST_CFG1 0x00000014 +#define HDMI_8996_PHY_TX0_TX1_PRBS_SEED_BYTE0 0x00000018 +#define HDMI_8996_PHY_TX0_TX1_PRBS_SEED_BYTE1 0x0000001c +#define HDMI_8996_PHY_TX0_TX1_BIST_PATTERN0 0x00000020 +#define HDMI_8996_PHY_TX0_TX1_BIST_PATTERN1 0x00000024 +#define HDMI_8996_PHY_TX2_TX3_BIST_CFG0 0x00000028 +#define HDMI_8996_PHY_TX2_TX3_BIST_CFG1 0x0000002c +#define HDMI_8996_PHY_TX2_TX3_PRBS_SEED_BYTE0 0x00000030 +#define HDMI_8996_PHY_TX2_TX3_PRBS_SEED_BYTE1 0x00000034 +#define HDMI_8996_PHY_TX2_TX3_BIST_PATTERN0 0x00000038 +#define HDMI_8996_PHY_TX2_TX3_BIST_PATTERN1 0x0000003c +#define HDMI_8996_PHY_DEBUG_BUS_SEL 0x00000040 +#define HDMI_8996_PHY_TXCAL_CFG0 0x00000044 +#define HDMI_8996_PHY_TXCAL_CFG1 0x00000048 +#define HDMI_8996_PHY_TX0_TX1_LANE_CTL 0x0000004c +#define HDMI_8996_PHY_TX2_TX3_LANE_CTL 0x00000050 +#define HDMI_8996_PHY_LANE_BIST_CONFIG 0x00000054 +#define HDMI_8996_PHY_CLOCK 0x00000058 +#define HDMI_8996_PHY_MISC1 0x0000005c +#define HDMI_8996_PHY_MISC2 0x00000060 +#define HDMI_8996_PHY_TX0_TX1_BIST_STATUS0 0x00000064 +#define HDMI_8996_PHY_TX0_TX1_BIST_STATUS1 0x00000068 +#define HDMI_8996_PHY_TX0_TX1_BIST_STATUS2 0x0000006c +#define HDMI_8996_PHY_TX2_TX3_BIST_STATUS0 0x00000070 +#define HDMI_8996_PHY_TX2_TX3_BIST_STATUS1 0x00000074 +#define HDMI_8996_PHY_TX2_TX3_BIST_STATUS2 0x00000078 +#define HDMI_8996_PHY_PRE_MISR_STATUS0 0x0000007c +#define HDMI_8996_PHY_PRE_MISR_STATUS1 0x00000080 +#define HDMI_8996_PHY_PRE_MISR_STATUS2 0x00000084 +#define HDMI_8996_PHY_PRE_MISR_STATUS3 0x00000088 +#define HDMI_8996_PHY_POST_MISR_STATUS0 0x0000008c +#define HDMI_8996_PHY_POST_MISR_STATUS1 0x00000090 +#define HDMI_8996_PHY_POST_MISR_STATUS2 0x00000094 +#define HDMI_8996_PHY_POST_MISR_STATUS3 0x00000098 +#define HDMI_8996_PHY_STATUS 0x0000009c +#define HDMI_8996_PHY_MISC3_STATUS 0x000000a0 +#define HDMI_8996_PHY_MISC4_STATUS 0x000000a4 +#define HDMI_8996_PHY_DEBUG_BUS0 0x000000a8 +#define HDMI_8996_PHY_DEBUG_BUS1 0x000000ac +#define HDMI_8996_PHY_DEBUG_BUS2 0x000000b0 +#define HDMI_8996_PHY_DEBUG_BUS3 0x000000b4 +#define HDMI_8996_PHY_PHY_REVISION_ID0 0x000000b8 +#define HDMI_8996_PHY_PHY_REVISION_ID1 0x000000bc +#define HDMI_8996_PHY_PHY_REVISION_ID2 0x000000c0 +#define HDMI_8996_PHY_PHY_REVISION_ID3 0x000000c4 + +struct qmp_hdmi_8996_post_divider { + u64 vco_freq; + int hsclk_divsel; + int vco_ratio; + int tx_band_sel; +}; + +static inline void write16(u16 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel(val >> 8, reg + 4); +} + +static inline void write24(u32 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel((val >> 8) & 0xff, reg + 4); + writel(val >> 16, reg + 8); +} + +static inline u32 read24(void __iomem *reg) +{ + u32 val =3D readl(reg); + + val |=3D readl(reg + 4) << 8; + val |=3D readl(reg + 8) << 16; + + return val; +} + +static inline u32 qmp_hdmi_8996_pll_get_pll_cmp(u64 fdata, unsigned long r= ef_clk) +{ + u64 dividend =3D HDMI_PLL_CMP_CNT * fdata; + u32 divisor =3D ref_clk * 10; + u32 rem; + + rem =3D do_div(dividend, divisor); + if (rem > (divisor >> 1)) + dividend++; + + return dividend - 1; +} + +static int qmp_hdmi_8996_pll_get_post_div(struct qmp_hdmi_8996_post_divide= r *pd, u64 bclk) +{ + int ratio[] =3D { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 }; + int hs_divsel[] =3D { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 }; + int tx_band_sel[] =3D { 0, 1, 2, 3 }; + u64 vco_freq[60]; + u64 vco, vco_optimal; + int half_rate_mode =3D 0; + int vco_optimal_index, vco_freq_index; + int i, j; + +retry: + vco_optimal =3D HDMI_VCO_MAX_FREQ; + vco_optimal_index =3D -1; + vco_freq_index =3D 0; + for (i =3D 0; i < 15; i++) { + for (j =3D 0; j < 4; j++) { + u32 ratio_mult =3D ratio[i] << tx_band_sel[j]; + + vco =3D bclk >> half_rate_mode; + vco *=3D ratio_mult; + vco_freq[vco_freq_index++] =3D vco; + } + } + + for (i =3D 0; i < 60; i++) { + u64 vco_tmp =3D vco_freq[i]; + + if ((vco_tmp >=3D HDMI_VCO_MIN_FREQ) && + (vco_tmp <=3D vco_optimal)) { + vco_optimal =3D vco_tmp; + vco_optimal_index =3D i; + } + } + + if (vco_optimal_index =3D=3D -1) { + if (!half_rate_mode) { + half_rate_mode =3D 1; + goto retry; + } + + return -EINVAL; + } + + pd->vco_freq =3D vco_optimal; + pd->tx_band_sel =3D tx_band_sel[vco_optimal_index % 4]; + pd->vco_ratio =3D ratio[vco_optimal_index / 4]; + pd->hsclk_divsel =3D hs_divsel[vco_optimal_index / 4]; + + return 0; +} + +static int qmp_hdmi_8996_phy_set_rate(struct qmp_hdmi_phy *hdmi_phy) +{ + unsigned long parent_rate =3D HDMI_DEFAULT_REF_CLOCK; + unsigned long rate =3D hdmi_phy->hdmi_opts.tmds_char_rate; + struct qmp_hdmi_8996_post_divider pd; + bool gen_ssc =3D false; + u64 bclk; + u64 dec_start; + u64 frac_start; + u64 fdata; + u32 pll_divisor; + u32 rem; + u32 integloop_gain; + u32 pll_cmp; + int i, ret; + + bclk =3D ((u64)rate) * 10; + ret =3D qmp_hdmi_8996_pll_get_post_div(&pd, bclk); + if (ret) { + dev_err(hdmi_phy->dev, "PLL calculation failed\n"); + return ret; + } + + dec_start =3D pd.vco_freq; + pll_divisor =3D 4 * parent_rate; + do_div(dec_start, pll_divisor); + + frac_start =3D pd.vco_freq * (1 << 20); + + rem =3D do_div(frac_start, pll_divisor); + frac_start -=3D dec_start * (1 << 20); + if (rem > (pll_divisor >> 1)) + frac_start++; + + fdata =3D pd.vco_freq; + do_div(fdata, pd.vco_ratio); + + pll_cmp =3D qmp_hdmi_8996_pll_get_pll_cmp(fdata, parent_rate); + + /* Initially shut down PHY */ + dev_dbg(hdmi_phy->dev, "Disabling PHY"); + writel(0x0, hdmi_phy->phy_reg + HDMI_8996_PHY_PD_CTL); + udelay(500); + + /* Power up sequence */ + writel(0x04, hdmi_phy->serdes + QSERDES_COM_BG_CTRL); + + writel(0x1, hdmi_phy->phy_reg + HDMI_8996_PHY_PD_CTL); + writel(0x20, hdmi_phy->serdes + QSERDES_COM_RESETSM_CNTRL); + writel(0x0f, hdmi_phy->phy_reg + HDMI_8996_PHY_TX0_TX1_LANE_CTL); + writel(0x0f, hdmi_phy->phy_reg + HDMI_8996_PHY_TX2_TX3_LANE_CTL); + + writel(0x43, hdmi_phy->tx[0] + QSERDES_TX_LANE_MODE); + writel(0x43, hdmi_phy->tx[2] + QSERDES_TX_LANE_MODE); + + writel(0x1e, hdmi_phy->serdes + QSERDES_COM_SYSCLK_BUF_ENABLE); + writel(0x07, hdmi_phy->serdes + QSERDES_COM_BIAS_EN_CLKBUFLR_EN); + writel(0x37, hdmi_phy->serdes + QSERDES_COM_SYSCLK_EN_SEL); + writel(0x02, hdmi_phy->serdes + QSERDES_COM_SYS_CLK_CTRL); + writel(0x0e, hdmi_phy->serdes + QSERDES_COM_CLK_ENABLE1); + + if (frac_start !=3D 0 || gen_ssc) { + writel(0x28, hdmi_phy->serdes + QSERDES_COM_PLL_CCTRL_MODE0); + writel(0x16, hdmi_phy->serdes + QSERDES_COM_PLL_RCTRL_MODE0); + writel(11000000 / (parent_rate / 20), + hdmi_phy->serdes + QSERDES_COM_CP_CTRL_MODE0); + integloop_gain =3D (64 * parent_rate) / HDMI_DEFAULT_REF_CLOCK; + } else { + writel(0x01, hdmi_phy->serdes + QSERDES_COM_PLL_CCTRL_MODE0); + writel(0x10, hdmi_phy->serdes + QSERDES_COM_PLL_RCTRL_MODE0); + writel(0x23, hdmi_phy->serdes + QSERDES_COM_CP_CTRL_MODE0); + integloop_gain =3D (1022 * parent_rate) / (100 * 1000 * 1000); + } + + /* Bypass VCO calibration */ + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) { + writel(1, hdmi_phy->serdes + QSERDES_COM_SVS_MODE_CLK_SEL); + integloop_gain <<=3D 1; + } else { + writel(2, hdmi_phy->serdes + QSERDES_COM_SVS_MODE_CLK_SEL); + integloop_gain <<=3D 2; + } + + integloop_gain =3D min_t(u32, integloop_gain, 2046); + + writel(0x0f, hdmi_phy->serdes + QSERDES_COM_BG_TRIM); + writel(0x0f, hdmi_phy->serdes + QSERDES_COM_PLL_IVCO); + writel(0, hdmi_phy->serdes + QSERDES_COM_VCO_TUNE_CTRL); + + writel(0x06, hdmi_phy->serdes + QSERDES_COM_BG_CTRL); + + writel(0x30, hdmi_phy->serdes + QSERDES_COM_CLK_SELECT); + writel(0x20 | pd.hsclk_divsel, hdmi_phy->serdes + QSERDES_COM_HSCLK_SEL); + writel(0x0, hdmi_phy->serdes + QSERDES_COM_LOCK_CMP_EN); + + writel(dec_start, hdmi_phy->serdes + QSERDES_COM_DEC_START_MODE0); + write24(frac_start, hdmi_phy->serdes + QSERDES_COM_DIV_FRAC_START1_MODE0); + + write16(integloop_gain, hdmi_phy->serdes + QSERDES_COM_INTEGLOOP_GAIN0_MO= DE0); + + write24(pll_cmp, hdmi_phy->serdes + QSERDES_COM_LOCK_CMP1_MODE0); + + writel(0x00, hdmi_phy->serdes + QSERDES_COM_VCO_TUNE_MAP); + writel(0x2c, hdmi_phy->serdes + QSERDES_COM_CORE_CLK_EN); + writel(5, hdmi_phy->serdes + QSERDES_COM_CORECLK_DIV); + writel(0x02, hdmi_phy->serdes + QSERDES_COM_CMN_CONFIG); + + writel(0x15, hdmi_phy->serdes + QSERDES_COM_RESCODE_DIV_NUM); + + /* TX lanes setup (TX 0/1/2/3) */ + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(0x03, hdmi_phy->tx[i] + QSERDES_TX_CLKBUF_ENABLE); + writel(pd.tx_band_sel + 4, hdmi_phy->tx[i] + QSERDES_TX_TX_BAND); + writel(0x03, hdmi_phy->tx[i] + QSERDES_TX_RESET_TSYNC_EN); + writel(0x00, hdmi_phy->tx[i] + QSERDES_TX_VMODE_CTRL1); + writel(0x00, hdmi_phy->tx[i] + QSERDES_TX_TX_DRV_LVL_OFFSET); + writel(0x00, hdmi_phy->tx[i] + QSERDES_TX_RES_CODE_LANE_OFFSET); + writel(0x03, hdmi_phy->tx[i] + QSERDES_TX_TRAN_DRVR_EMP_EN); + writel(0x40, hdmi_phy->tx[i] + QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN); + writel(i !=3D 3 ? 0xc : 0x3, hdmi_phy->tx[i] + QSERDES_TX_HP_PD_ENABLES); + } + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(i !=3D 3 ? 0x25 : 0x22, hdmi_phy->tx[i] + QSERDES_TX_TX_DRV_LVL); + writel(i !=3D 3 ? 0x23 : 0x27, hdmi_phy->tx[i] + QSERDES_TX_TX_EMP_POST= 1_LVL); + writel(i !=3D 3 ? 0x0d : 0x00, hdmi_phy->tx[i] + QSERDES_TX_VMODE_CTRL2= ); + } + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(0x25, hdmi_phy->tx[i] + QSERDES_TX_TX_DRV_LVL); + writel(0x23, hdmi_phy->tx[i] + QSERDES_TX_TX_EMP_POST1_LVL); + writel(i !=3D 3 ? 0x0d : 0x00, hdmi_phy->tx[i] + QSERDES_TX_VMODE_CTRL2= ); + } + } else { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(0x20, hdmi_phy->tx[i] + QSERDES_TX_TX_DRV_LVL); + writel(0x20, hdmi_phy->tx[i] + QSERDES_TX_TX_EMP_POST1_LVL); + writel(0x0e, hdmi_phy->tx[i] + QSERDES_TX_VMODE_CTRL2); + } + } + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + writel(0x10, hdmi_phy->phy_reg + HDMI_8996_PHY_MODE); + else + writel(0x00, hdmi_phy->phy_reg + HDMI_8996_PHY_MODE); + writel(0x1f, hdmi_phy->phy_reg + HDMI_8996_PHY_PD_CTL); + + return 0; +} + +static int qmp_hdmi_8996_phy_power_on(struct phy *phy) +{ + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + u32 status; + int i, ret =3D 0; + + ret =3D qmp_hdmi_8996_phy_set_rate(hdmi_phy); + if (ret) { + dev_err(hdmi_phy->dev, "Setting pixel clock rate failed\n"); + return ret; + } + + writel(0x1, hdmi_phy->phy_reg + HDMI_8996_PHY_CFG); + udelay(100); + + writel(0x19, hdmi_phy->phy_reg + HDMI_8996_PHY_CFG); + udelay(100); + + ret =3D readl_poll_timeout(hdmi_phy->serdes + QSERDES_COM_C_READY_STATUS, + status, status & BIT(0), + HDMI_PLL_POLL_TIMEOUT_US, + HDMI_PLL_POLL_MAX_READS * HDMI_PLL_POLL_TIMEOUT_US); + + if (ret) { + dev_warn(hdmi_phy->dev, "HDMI PLL is not locked\n"); + return ret; + } + + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) + writel(0x6f, hdmi_phy->tx[i] + QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_= EN); + + /* Disable SSC */ + writel(0x0, hdmi_phy->serdes + QSERDES_COM_SSC_PER1); + writel(0x0, hdmi_phy->serdes + QSERDES_COM_SSC_PER2); + writel(0x0, hdmi_phy->serdes + QSERDES_COM_SSC_STEP_SIZE1); + writel(0x0, hdmi_phy->serdes + QSERDES_COM_SSC_STEP_SIZE2); + writel(0x2, hdmi_phy->serdes + QSERDES_COM_SSC_EN_CENTER); + + ret =3D readl_poll_timeout(hdmi_phy->phy_reg + HDMI_8996_PHY_STATUS, + status, status & BIT(0), + HDMI_PLL_POLL_TIMEOUT_US, + HDMI_PLL_POLL_MAX_READS * HDMI_PLL_POLL_TIMEOUT_US); + if (ret) { + dev_warn(hdmi_phy->dev, "HDMI PLL is not locked\n"); + return ret; + } + + /* Restart the retiming buffer */ + writel(0x18, hdmi_phy->phy_reg + HDMI_8996_PHY_CFG); + udelay(1); + writel(0x19, hdmi_phy->phy_reg + HDMI_8996_PHY_CFG); + + return 0; +} + +static int qmp_hdmi_8996_phy_power_off(struct phy *phy) +{ + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + + writel(0x6, hdmi_phy->phy_reg + HDMI_8996_PHY_CFG); + usleep_range(100, 150); + + return 0; +} + +static int qmp_hdmi_8996_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + req->rate =3D clamp(req->rate, HDMI_PCLK_MIN_FREQ, HDMI_PCLK_MAX_FREQ); + + return 0; +} + +static unsigned long qmp_hdmi_8996_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct qmp_hdmi_phy *phy =3D hw_clk_to_pll(hw); + u32 pll_cmp =3D read24(phy->serdes + QSERDES_COM_LOCK_CMP1_MODE0); + + return mult_frac(pll_cmp + 1, parent_rate, HDMI_PLL_CMP_CNT); +} + +static int qmp_hdmi_8996_pll_is_enabled(struct clk_hw *hw) +{ + struct qmp_hdmi_phy *phy =3D hw_clk_to_pll(hw); + u32 status; + int pll_locked; + + status =3D readl(phy->serdes + QSERDES_COM_C_READY_STATUS); + pll_locked =3D status & BIT(0); + + return pll_locked; +} + +static const struct clk_ops qmp_hdmi_8996_pll_ops =3D { + .recalc_rate =3D qmp_hdmi_8996_pll_recalc_rate, + .determine_rate =3D qmp_hdmi_8996_pll_determine_rate, + .is_enabled =3D qmp_hdmi_8996_pll_is_enabled, +}; + +static const struct phy_ops qmp_hdmi_8996_phy_ops =3D { + .init =3D qmp_hdmi_phy_init, + .configure =3D qmp_hdmi_phy_configure, + .power_on =3D qmp_hdmi_8996_phy_power_on, + .power_off =3D qmp_hdmi_8996_phy_power_off, + .exit =3D qmp_hdmi_phy_exit, + .owner =3D THIS_MODULE, +}; + +const struct qmp_hdmi_cfg qmp_hdmi_8996_cfg =3D { + .pll_ops =3D &qmp_hdmi_8996_pll_ops, + .phy_ops =3D &qmp_hdmi_8996_phy_ops, +}; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-msm8998.c b/drivers/phy= /qualcomm/phy-qcom-qmp-hdmi-msm8998.c new file mode 100644 index 000000000000..495c5ee053d6 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-msm8998.c @@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Freebox SAS + * Copyright (c) 2025, Qualcomm Incorporated + */ + +#include +#include +#include +#include + +#include "phy-qcom-qmp-hdmi.h" +#include "phy-qcom-qmp-qserdes-com-v3.h" +#include "phy-qcom-qmp-qserdes-txrx.h" + +#define HDMI_VCO_MAX_FREQ 12000000000UL +#define HDMI_VCO_MIN_FREQ 8000000000UL + +#define HDMI_PCLK_MAX_FREQ 600000000UL +#define HDMI_PCLK_MIN_FREQ 25000000UL + +#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL +#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL +#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL +#define HDMI_DEFAULT_REF_CLOCK 19200000 +#define HDMI_PLL_CMP_CNT 1024 + +#define HDMI_PLL_POLL_MAX_READS 100 +#define HDMI_PLL_POLL_TIMEOUT_US 150 + +#define HDMI_8998_PHY_CFG 0x00000000 +#define HDMI_8998_PHY_PD_CTL 0x00000004 +#define HDMI_8998_PHY_MODE 0x00000010 +#define HDMI_8998_PHY_CLOCK 0x0000005c +#define HDMI_8998_PHY_CMN_CTRL 0x00000068 +#define HDMI_8998_PHY_STATUS 0x000000b4 + +#define HDMI_8998_PHY_TXn_EMP_POST1_LVL 0x00000000 +#define HDMI_8998_PHY_TXn_INTERFACE_SELECT_TX_BAND 0x00000008 +#define HDMI_8998_PHY_TXn_CLKBUF_TERM_ENABLE 0x0000000c +#define HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET 0x00000014 +#define HDMI_8998_PHY_TXn_DRV_LVL 0x00000018 +#define HDMI_8998_PHY_TXn_LANE_CONFIG 0x0000001c +#define HDMI_8998_PHY_TXn_PRE_DRIVER_1 0x00000024 +#define HDMI_8998_PHY_TXn_PRE_DRIVER_2 0x00000028 +#define HDMI_8998_PHY_TXn_LANE_MODE 0x0000002c + +struct qmp_hdmi_8998_post_divider { + u64 vco_freq; + int hsclk_divsel; + int vco_ratio; + int tx_band_sel; + int half_rate_mode; +}; + +static inline void write16(u16 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel(val >> 8, reg + 4); +} + +static inline void write24(u32 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel((val >> 8) & 0xff, reg + 4); + writel(val >> 16, reg + 8); +} + +static inline u32 read24(void __iomem *reg) +{ + u32 val =3D readl(reg); + + val |=3D readl(reg + 4) << 8; + val |=3D readl(reg + 8) << 16; + + return val; +} + +static inline u32 qmp_hdmi_8998_pll_get_pll_cmp(u64 fdata, unsigned long r= ef_clk) +{ + u64 dividend =3D HDMI_PLL_CMP_CNT * fdata; + u32 divisor =3D ref_clk * 10; + u32 rem; + + rem =3D do_div(dividend, divisor); + if (rem > (divisor >> 1)) + dividend++; + + return dividend - 1; +} + +static int qmp_hdmi_8998_pll_get_post_div(struct qmp_hdmi_8998_post_divide= r *pd, u64 bclk) +{ + u32 const ratio_list[] =3D {1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 25}; + u32 const band_list[] =3D {0, 1, 2, 3}; + u32 const sz_ratio =3D ARRAY_SIZE(ratio_list); + u32 const sz_band =3D ARRAY_SIZE(band_list); + u32 const cmp_cnt =3D 1024; + u32 const th_min =3D 500, th_max =3D 1000; + u32 half_rate_mode =3D 0; + u32 list_elements; + int optimal_index; + u32 i, j, k; + u32 found_hsclk_divsel =3D 0, found_vco_ratio; + u32 found_tx_band_sel; + u64 const min_freq =3D HDMI_VCO_MIN_FREQ, max_freq =3D HDMI_VCO_MAX_FREQ; + u64 freq_list[ARRAY_SIZE(ratio_list) * ARRAY_SIZE(band_list)]; + u64 found_vco_freq; + u64 freq_optimal; + +find_optimal_index: + freq_optimal =3D max_freq; + optimal_index =3D -1; + list_elements =3D 0; + + for (i =3D 0; i < sz_ratio; i++) { + for (j =3D 0; j < sz_band; j++) { + u64 freq =3D div_u64(bclk, (1 << half_rate_mode)); + + freq *=3D (ratio_list[i] * (1 << band_list[j])); + freq_list[list_elements++] =3D freq; + } + } + + for (k =3D 0; k < ARRAY_SIZE(freq_list); k++) { + u32 const clks_pll_div =3D 2, core_clk_div =3D 5; + u32 const rng1 =3D 16, rng2 =3D 8; + u32 th1, th2; + u64 core_clk, rvar1, rem; + + core_clk =3D div_u64(freq_list[k], + ratio_list[k / sz_band] * clks_pll_div * + core_clk_div); + + rvar1 =3D HDMI_DEFAULT_REF_CLOCK * rng1 * HZ_PER_MHZ; + rvar1 =3D div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem); + if (rem > ((cmp_cnt * core_clk) >> 1)) + rvar1++; + th1 =3D rvar1; + + rvar1 =3D HDMI_DEFAULT_REF_CLOCK * rng2 * HZ_PER_MHZ; + rvar1 =3D div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem); + if (rem > ((cmp_cnt * core_clk) >> 1)) + rvar1++; + th2 =3D rvar1; + + if (freq_list[k] >=3D min_freq && + freq_list[k] <=3D max_freq) { + if ((th1 >=3D th_min && th1 <=3D th_max) || + (th2 >=3D th_min && th2 <=3D th_max)) { + if (freq_list[k] <=3D freq_optimal) { + freq_optimal =3D freq_list[k]; + optimal_index =3D k; + } + } + } + } + + if (optimal_index =3D=3D -1) { + if (!half_rate_mode) { + half_rate_mode =3D 1; + goto find_optimal_index; + } else { + return -EINVAL; + } + } else { + found_vco_ratio =3D ratio_list[optimal_index / sz_band]; + found_tx_band_sel =3D band_list[optimal_index % sz_band]; + found_vco_freq =3D freq_optimal; + } + + switch (found_vco_ratio) { + case 1: + found_hsclk_divsel =3D 15; + break; + case 2: + found_hsclk_divsel =3D 0; + break; + case 3: + found_hsclk_divsel =3D 4; + break; + case 4: + found_hsclk_divsel =3D 8; + break; + case 5: + found_hsclk_divsel =3D 12; + break; + case 6: + found_hsclk_divsel =3D 1; + break; + case 9: + found_hsclk_divsel =3D 5; + break; + case 10: + found_hsclk_divsel =3D 2; + break; + case 12: + found_hsclk_divsel =3D 9; + break; + case 15: + found_hsclk_divsel =3D 13; + break; + case 25: + found_hsclk_divsel =3D 14; + break; + } + + pd->vco_freq =3D found_vco_freq; + pd->tx_band_sel =3D found_tx_band_sel; + pd->vco_ratio =3D found_vco_ratio; + pd->hsclk_divsel =3D found_hsclk_divsel; + + return 0; +} + +static int qmp_hdmi_8998_phy_set_rate(struct qmp_hdmi_phy *hdmi_phy) +{ + unsigned long parent_rate =3D HDMI_DEFAULT_REF_CLOCK; + unsigned long rate =3D hdmi_phy->hdmi_opts.tmds_char_rate; + struct qmp_hdmi_8998_post_divider pd; + bool gen_ssc =3D false; + u64 bclk; + u64 dec_start; + u64 frac_start; + u64 fdata; + u32 pll_divisor; + u32 rem; + u32 integloop_gain; + u32 pll_cmp; + int i, ret; + + bclk =3D ((u64)rate) * 10; + ret =3D qmp_hdmi_8998_pll_get_post_div(&pd, bclk); + if (ret) { + dev_err(hdmi_phy->dev, "PLL calculation failed\n"); + return ret; + } + + dec_start =3D pd.vco_freq; + pll_divisor =3D 4 * parent_rate; + do_div(dec_start, pll_divisor); + + frac_start =3D pd.vco_freq * (1 << 20); + + rem =3D do_div(frac_start, pll_divisor); + frac_start -=3D dec_start * (1 << 20); + if (rem > (pll_divisor >> 1)) + frac_start++; + + fdata =3D pd.vco_freq; + do_div(fdata, pd.vco_ratio); + + pll_cmp =3D qmp_hdmi_8998_pll_get_pll_cmp(fdata, parent_rate); + + /* Initially shut down PHY */ + dev_dbg(hdmi_phy->dev, "Disabling PHY"); + writel(0x0, hdmi_phy->phy_reg + HDMI_8998_PHY_PD_CTL); + udelay(500); + + /* Power up sequence */ + writel(0x1, hdmi_phy->phy_reg + HDMI_8998_PHY_PD_CTL); + writel(0x20, hdmi_phy->serdes + QSERDES_V3_COM_RESETSM_CNTRL); + writel(0x6, hdmi_phy->phy_reg + HDMI_8998_PHY_CMN_CTRL); + + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(pd.tx_band_sel, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_INTERFACE_SELECT_TX_BAND); + writel(0x1, hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_CLKBUF_TERM_ENABLE); + writel(0x20, hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_LANE_MODE); + } + + writel(0x02, hdmi_phy->serdes + QSERDES_V3_COM_SYSCLK_BUF_ENABLE); + writel(0x0B, hdmi_phy->serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); + writel(0x37, hdmi_phy->serdes + QSERDES_V3_COM_SYSCLK_EN_SEL); + writel(0x02, hdmi_phy->serdes + QSERDES_V3_COM_SYS_CLK_CTRL); + writel(0x0E, hdmi_phy->serdes + QSERDES_V3_COM_CLK_ENABLE1); + + if (frac_start !=3D 0 || gen_ssc) { + writel(0x34, hdmi_phy->serdes + QSERDES_V3_COM_PLL_CCTRL_MODE0); + writel(0x16, hdmi_phy->serdes + QSERDES_V3_COM_PLL_RCTRL_MODE0); + writel(0x08, hdmi_phy->serdes + QSERDES_V3_COM_CP_CTRL_MODE0); + integloop_gain =3D 0x3f; + } else { + writel(0x02, hdmi_phy->serdes + QSERDES_V3_COM_PLL_CCTRL_MODE0); + writel(0x18, hdmi_phy->serdes + QSERDES_V3_COM_PLL_RCTRL_MODE0); + writel(0x30, hdmi_phy->serdes + QSERDES_V3_COM_CP_CTRL_MODE0); + integloop_gain =3D 0xc4; + } + + /* Bypass VCO calibration */ + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) { + writel(1, hdmi_phy->serdes + QSERDES_V3_COM_SVS_MODE_CLK_SEL); + integloop_gain <<=3D 1; + } else { + writel(2, hdmi_phy->serdes + QSERDES_V3_COM_SVS_MODE_CLK_SEL); + integloop_gain <<=3D 2; + } + + writel(0x07, hdmi_phy->serdes + QSERDES_V3_COM_PLL_IVCO); + writel(0x00, hdmi_phy->serdes + QSERDES_V3_COM_VCO_TUNE_CTRL); + + writel(0x30, hdmi_phy->serdes + QSERDES_V3_COM_CLK_SELECT); + writel(0x20 | pd.hsclk_divsel, hdmi_phy->serdes + QSERDES_V3_COM_HSCLK_SE= L); + writel(0x0, hdmi_phy->serdes + QSERDES_V3_COM_LOCK_CMP_EN); + + writel(dec_start, hdmi_phy->serdes + QSERDES_V3_COM_DEC_START_MODE0); + write24(frac_start, hdmi_phy->serdes + QSERDES_V3_COM_DIV_FRAC_START1_MOD= E0); + + write16(integloop_gain, hdmi_phy->serdes + QSERDES_V3_COM_INTEGLOOP_GAIN0= _MODE0); + + write24(pll_cmp, hdmi_phy->serdes + QSERDES_V3_COM_LOCK_CMP1_MODE0); + + writel(0x00, hdmi_phy->serdes + QSERDES_V3_COM_VCO_TUNE_MAP); + writel(0x2c, hdmi_phy->serdes + QSERDES_V3_COM_CORE_CLK_EN); + writel(5, hdmi_phy->serdes + QSERDES_V3_COM_CORECLK_DIV_MODE0); + + /* TX lanes setup (TX 0/1/2/3) */ + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(0xf, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL); + writel(i =3D=3D 3 ? 0x00 : i =3D=3D 1 ? 0x02 : 0x03, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_EMP_POST1_LVL); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_1); + writel(i =3D=3D 3 ? 0x00 : 0x1c, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_2); + writel((i =3D=3D 0 || i =3D=3D 3) ? 0x03 : 0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET); + } + } else if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(0x0f, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL); + writel(i =3D=3D 3 ? 0x00 : 0x03, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_EMP_POST1_LVL); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_1); + writel(i =3D=3D 3 ? 0x18 : 0x16, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_2); + writel(i =3D=3D 0 ? 0x03 : 0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET); + } + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(0x0f, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL); + writel(i =3D=3D 3 ? 0x00 : 0x05, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_EMP_POST1_LVL); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_1); + writel(0x0e, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_2); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET); + } + } else { + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) { + writel(i =3D=3D 3 ? 0x00 : 0x01, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_EMP_POST1_LVL); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_1); + writel(i =3D=3D 3 ? 0x18 : 0x16, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_2); + writel(0x00, + hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL_RES_CODE_OFFSET); + } + } + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + writel(0x5, hdmi_phy->phy_reg + HDMI_8998_PHY_MODE); + else + writel(0x4, hdmi_phy->phy_reg + HDMI_8998_PHY_MODE); + + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) + writel(0x10, hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_LANE_CONFIG); + + return 0; +} + +static int qmp_hdmi_8998_phy_power_on(struct phy *phy) +{ + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + u32 status; + int i, ret =3D 0; + + ret =3D qmp_hdmi_8998_phy_set_rate(hdmi_phy); + if (ret) { + dev_err(hdmi_phy->dev, "Setting pixel clock rate failed\n"); + return ret; + } + + writel(0x1, hdmi_phy->phy_reg + HDMI_8998_PHY_CFG); + udelay(100); + + writel(0x59, hdmi_phy->phy_reg + HDMI_8998_PHY_CFG); + udelay(100); + + ret =3D readl_poll_timeout(hdmi_phy->serdes + QSERDES_V3_COM_C_READY_STAT= US, + status, status & BIT(0), + HDMI_PLL_POLL_TIMEOUT_US, + HDMI_PLL_POLL_MAX_READS * HDMI_PLL_POLL_TIMEOUT_US); + + if (ret) { + dev_warn(hdmi_phy->dev, "HDMI PLL is not locked\n"); + return ret; + } + + for (i =3D 0; i < HDMI_NUM_TX_CHANNEL; i++) + writel(0x1f, hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_LANE_CONFIG); + + ret =3D readl_poll_timeout(hdmi_phy->phy_reg + HDMI_8998_PHY_STATUS, + status, status & BIT(0), + HDMI_PLL_POLL_TIMEOUT_US, + HDMI_PLL_POLL_MAX_READS * HDMI_PLL_POLL_TIMEOUT_US); + if (ret) { + dev_warn(hdmi_phy->dev, "HDMI PLL is not locked\n"); + return ret; + } + + /* Restart the retiming buffer */ + writel(0x58, hdmi_phy->phy_reg + HDMI_8998_PHY_CFG); + udelay(1); + writel(0x59, hdmi_phy->phy_reg + HDMI_8998_PHY_CFG); + + return 0; +} + +static int qmp_hdmi_8998_phy_power_off(struct phy *phy) +{ + struct qmp_hdmi_phy *hdmi_phy =3D phy_get_drvdata(phy); + + writel(0, hdmi_phy->phy_reg + HDMI_8998_PHY_PD_CTL); + usleep_range(100, 150); + + return 0; +} + +static int qmp_hdmi_8998_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + req->rate =3D clamp(req->rate, HDMI_PCLK_MIN_FREQ, HDMI_PCLK_MAX_FREQ); + + return 0; +} + +static unsigned long qmp_hdmi_8998_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct qmp_hdmi_phy *phy =3D hw_clk_to_pll(hw); + u32 pll_cmp =3D read24(phy->serdes + QSERDES_V3_COM_LOCK_CMP1_MODE0); + + return mult_frac(pll_cmp + 1, parent_rate, HDMI_PLL_CMP_CNT); +} + +static int qmp_hdmi_8998_pll_is_enabled(struct clk_hw *hw) +{ + struct qmp_hdmi_phy *phy =3D hw_clk_to_pll(hw); + u32 status; + int pll_locked; + + status =3D readl(phy->serdes + QSERDES_V3_COM_C_READY_STATUS); + pll_locked =3D status & BIT(0); + + return pll_locked; +} + +static const struct clk_ops qmp_hdmi_8998_pll_ops =3D { + .recalc_rate =3D qmp_hdmi_8998_pll_recalc_rate, + .determine_rate =3D qmp_hdmi_8998_pll_determine_rate, + .is_enabled =3D qmp_hdmi_8998_pll_is_enabled, +}; + +static const struct phy_ops qmp_hdmi_8998_phy_ops =3D { + .init =3D qmp_hdmi_phy_init, + .configure =3D qmp_hdmi_phy_configure, + .power_on =3D qmp_hdmi_8998_phy_power_on, + .power_off =3D qmp_hdmi_8998_phy_power_off, + .exit =3D qmp_hdmi_phy_exit, + .owner =3D THIS_MODULE, +}; + +const struct qmp_hdmi_cfg qmp_hdmi_8998_cfg =3D { + .pll_ops =3D &qmp_hdmi_8998_pll_ops, + .phy_ops =3D &qmp_hdmi_8998_phy_ops, +}; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-hdmi.h b/drivers/phy/qualcom= m/phy-qcom-qmp-hdmi.h new file mode 100644 index 000000000000..7a92ac02cab5 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-hdmi.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + * Copyright (c) 2025, Qualcomm Incorporated + */ + +#ifndef PHY_QCOM_QMP_HDMI_H +#define PHY_QCOM_QMP_HDMI_H + +#include +#include +#include +#include + +#define MAX_CLKS 2 +#define MAX_SUPPLIES 2 + +#define HDMI_NUM_TX_CHANNEL 4 + +struct qmp_hdmi_phy { + struct device *dev; + struct phy *phy; + void __iomem *serdes; + void __iomem *tx[HDMI_NUM_TX_CHANNEL]; + void __iomem *phy_reg; + + struct phy_configure_opts_hdmi hdmi_opts; + + struct clk_hw pll_hw; + struct clk_bulk_data clks[MAX_CLKS]; + struct regulator_bulk_data supplies[MAX_SUPPLIES]; +}; + +struct qmp_hdmi_cfg { + const struct clk_ops *pll_ops; + const struct phy_ops *phy_ops; +}; + +#define hw_clk_to_pll(x) container_of(x, struct qmp_hdmi_phy, pll_hw) + +int qmp_hdmi_phy_init(struct phy *phy); +int qmp_hdmi_phy_configure(struct phy *phy, union phy_configure_opts *opts= ); +int qmp_hdmi_phy_exit(struct phy *phy); + +extern const struct qmp_hdmi_cfg qmp_hdmi_8996_cfg; +extern const struct qmp_hdmi_cfg qmp_hdmi_8998_cfg; + +#endif --=20 2.47.3 From nobody Wed Apr 1 08:38:30 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C261637F016 for ; Wed, 1 Apr 2026 03:39:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014747; cv=none; b=uSK0JZqlqXv1CDwOXNGAVrIE0QpP73cT1iC+Iz8IfRheL8HZWhJcvdvlCX19KeZyX2YEoTvMFEwqf1nB9x5SrVyQ4BkfpxSgjNB32/cm76/gonGIRYXSWN7aRB8lTRi2ysn+4NUjh9x8sXy/e+SYZra8msRJB4tZmS8gHmpOa5M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014747; c=relaxed/simple; bh=DvPJs4paEzKp90KWrdtN4xdrXL6yB+AnGKH2S8yofHg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hIw0rqopQ8jIvkpa0T3aIjpHa2IHXUhA9/vsC8U1MIdm/NfulgBsvnMNd5K0+iLjMqX9cBCspdFOGTVHYh9V/BnjombWyoPucyhz44j2JyWE7OCt2wfn7n5qTh6xYAwz1gDnLK2vU8K2HpD/13inGhRkxQrnmZAlT+wj4Rcl+bE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=pElYl/Hc; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=b6pCcuh5; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="pElYl/Hc"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="b6pCcuh5" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 62VMIj9c1831867 for ; Wed, 1 Apr 2026 03:39:05 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= omnRmyUQg5dIWgAiljjvcZxQHMNx7Y0vRQtYTvOpk/4=; b=pElYl/Hc3H1CCT8y mZ3LDc/acK820bG1wLb5+OAoRCygFovt+Nrpq5KmiMEGut/832L16uQDnOgYfut0 HpYS8Rk8+bpSAMWdtTXRU0s33DiBdWlGFPgMwnL0ima3tyL+yUyxATRlxHAIz/iT 7NNVtS7YEvoFfwmB5YTKDVHIdpxX0pdT1rPy0nlY5uFMf7kNJ8qWRk8CC0WQclrC V9RkLm8IUi5AD/vvwRcRmJoSLvuRtD22h7Yppc6B+a5woapNNrmISwth1KcfytXm drx9/E7eGX5Wcmm/JkjclzSxGt0rUbyRa86uhdPUulikphdiMFt/q3wFqfoXxNaU weofqg== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4d8equbpex-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 01 Apr 2026 03:39:04 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-509219f94b0so76737181cf.3 for ; Tue, 31 Mar 2026 20:39:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1775014744; x=1775619544; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=omnRmyUQg5dIWgAiljjvcZxQHMNx7Y0vRQtYTvOpk/4=; b=b6pCcuh5Vk5F0p0H1nGvaJq/FA3fUKeJ1+z0aH5sflKsGgR6wrV462EM5J94o9OnYy iuacUw7W8QmJWLMFcjTV8xKWnlfuR11u1be8jvprg6HbdQorNoypMJqVgq5v1ff9WWVZ /WxpWLuLCTlf85dH8qdipx5Qwf+YJT1kPUoNY9Gj5kpmo54Fc0KQm/U6zS5BjGOOFiT2 +RxU8sXtw0Oow/HjcQQlkfr3svMKdJB92amP4OWNhJhus/SWR6QJ1XUV+8hgWdPYwKRd SmdZl9iJWVpR1ALemvjZqk/L14suDoS3aOF3mOGkncnn0T1RgIbq+KW/cESEqJ1D6gxI ht2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775014744; x=1775619544; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=omnRmyUQg5dIWgAiljjvcZxQHMNx7Y0vRQtYTvOpk/4=; b=o4Dk2jGXfMuNAECdDrlxggWQ8nsthHAK2111Phc82dUM6sSoMUF6TOSqyy0M+bUUnP Ce+YSvKXYTqgykHmdFpu/9Q3VeVTP1Mxt7vRgWwFgol0T1Ua6/XVzCa7v3hPOfPywsEe hmF2FzmUHDu6jgVAXbhpBCaFl2Liw9ZMmDUCqokwu1/fPRwZCf8t8m9YbCoif26RyEwp om8/6KfSiAeyMFw0RpRTWYa593fYn7pS+2XIzWyXWvHvGx9SiKMfQH1m0JfI/l3hhBof XtEIFHAxvYjO0RffyPKrA6zbi15WzeTL7mgsS3VHNd7JtqsXa+t85L7nOPl9YICLeh9c 2SuQ== X-Gm-Message-State: AOJu0YwMLVwvA6jtpLaNbsnPZaTgQROp29RRqIqqVIobVdyjK4KOLr3a 6cKNqA7ovzVFL1fE8F/kvihKqCoOi9UHcqifR6pYEGHgD1kR+g3vFcHaHYLUEwLC4ogHejdGNs7 rnAs6HrRcIp04YM8ZGorrhcjJBSf1ELJn+poT1Tqih942hNxDesNiNKn5yLjf8PlMXks= X-Gm-Gg: ATEYQzx64/4PzzVSK2GTuV5fTQHA2uQYtQchf2VKyJ9XpYcCjLVZAlo8DGOxf2KSz28 DORYqE3Ej3Dh5UuUUQ47zzOLR0aIk6t4VVH57Xt6iElLeLQHmCkd46K4wcMB+4dGVmuLL+Nq3uu sgeRpVIjrZdJ9j1Y8k5N43vMEO8loZEQZcQ8MS6/gEDAXmaisEDylDEsjVegeMQbQrdU2e2QLoM mf7kHx6WreRW/fWyxjh1W2o2yEucKg8y+g1AR1A+6qqRsuALzQxZTHRIEr2u/b3A3WycfrbTXBb hpxTMchvXoMAwoLpj0NhlLXPHFL4FTlGLHS0ZqQqCOmiYOBpkwcnY7jRjixkCTvecAAhdb+8ISL Gto8qoQ8wfAXj15SOlZqR3l0vr9WV89e+1ncUtLJ5knt5FQoYs8QbT4teaoh5KIG1N0BxnBriS9 A65j5Aykht9aQ4/0HOgSilK6KUInyAEAXeFjw= X-Received: by 2002:a05:622a:208:b0:50b:3e64:9b2a with SMTP id d75a77b69052e-50d3bbba0e5mr30945631cf.20.1775014744009; Tue, 31 Mar 2026 20:39:04 -0700 (PDT) X-Received: by 2002:a05:622a:208:b0:50b:3e64:9b2a with SMTP id d75a77b69052e-50d3bbba0e5mr30945391cf.20.1775014743505; Tue, 31 Mar 2026 20:39:03 -0700 (PDT) Received: from umbar.lan (2001-14ba-a073-af00-264b-feff-fe8b-be8a.rev.dnainternet.fi. [2001:14ba:a073:af00:264b:feff:fe8b:be8a]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-38c83730f7csm24404851fa.16.2026.03.31.20.38.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 20:39:01 -0700 (PDT) From: Dmitry Baryshkov Date: Wed, 01 Apr 2026 06:38:47 +0300 Subject: [PATCH v8 2/4] phy: qcom: apq8064-sata: extract UNI PLL register defines Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260401-fd-hdmi-phy-v8-2-51b0e98edf6c@oss.qualcomm.com> References: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> In-Reply-To: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> To: Rob Clark , Dmitry Baryshkov , Abhinav Kumar , Jessica Zhang , Sean Paul , Marijn Suijten , David Airlie , Simona Vetter , Vinod Koul , Neil Armstrong Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, dri-devel@lists.freedesktop.org, freedreno@lists.freedesktop.org, linux-phy@lists.infradead.org, Dmitry Baryshkov X-Mailer: b4 0.15.1 X-Developer-Signature: v=1; a=openpgp-sha256; l=3165; i=dmitry.baryshkov@oss.qualcomm.com; h=from:subject:message-id; bh=ouscQfS7cqU8c/8/Fh7An6AeO5MKx3nAag0yIE0xM6c=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBpzJNLmOEeHDlMQsEi8WfV2M/FU+7jv6unYshVp BRBShnAhbeJATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCacyTSwAKCRCLPIo+Aiko 1WXBB/0YQwCQumGtJGpmrCgUNvTNsMRnD8+VpK4wTT4nxIRaWiJmMASu4yQFBjrJLDlFTRmeM1E FmF19X7t3V+1yLDgx4SQ8pBLXFRTzph7z1R7CUgKDizPxyowFBwNBpMivdXhuYCQgidC/ykiA8W PGyqv9YPnSwC5MCSij2RAADe8Z+jWFXLNJH7YsADl5ukdE1+QYZbPo+8i/XKWTPROuM8HYf1vyl iKla8x0FpYpg28/JCXnoOktQwMggiYPTk4tU/GU0ewQ2YTpBQ9uSU+ztMJkXGrqaQeTIEL/k+EE 4k+DNJmwRkT5lpkiDn+3qHtlJ13eDT8Pi5ZGsGCTSqsIw52O X-Developer-Key: i=dmitry.baryshkov@oss.qualcomm.com; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDAxMDAyNiBTYWx0ZWRfX5Vb0Vj5F2KGj unDqgFiJuhBwbVBkObHyTNzcqeHudU+caCrP+JP9eLhxZngX7Y3O+Zq7CdODIJGyW8zcGvOvDRg goVzsyCYydPgegSYQudRW2v49vgT9Z2quMKs4z/oF8CPDFQal1x1XKANims9eOarRkkHwqZMK1f 6nqykvqZg25EPLxCwUxVFb13uz/JFFyviIqYIlnNEumjxCZnkguA+wvAJ1/ubvl1bUng8wualF/ b5rAICvgWkUZZPKSJHY6XTEIpNmT1Bijx/EZGO0eqtAl0sluqMUPmE5HK+OwYLVkvsW485hRLBz 9dXwkiavWrMT0av5NQbOnjE2OnEj7OSKvhQlcJhjyj2Q0bAAdQB8oHJUnAYbWAMdfM9a0wSZRUK 0FnlZcYniLHa+fv8iJkogj6WsZb5PmPkbrKHnDTXOkYMxw2LBG4vxkVRyRsFDh5XLQ27fDDOQVm XXZrInn7122YrxT1kYw== X-Proofpoint-GUID: q6kxNFFd4-BvAJ00iEgib26bFOrMMdpq X-Proofpoint-ORIG-GUID: q6kxNFFd4-BvAJ00iEgib26bFOrMMdpq X-Authority-Analysis: v=2.4 cv=YMOSCBGx c=1 sm=1 tr=0 ts=69cc9359 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=A5OVakUREuEA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=KKAkSRfTAAAA:8 a=EUspDBNiAAAA:8 a=_xNQ4P_HJxdd5osA_owA:9 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 a=cvBusfyB2V15izCimMoJ:22 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-01_01,2026-03-31_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 clxscore=1015 spamscore=0 impostorscore=0 adultscore=0 lowpriorityscore=0 bulkscore=0 phishscore=0 malwarescore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2603050001 definitions=main-2604010026 From: Dmitry Baryshkov The "uni" PLL is shared between several PHYS: APQ8064's SATA, MSM8974/APQ8084 HDMI, MSM8916 DSI, MSM8974/APQ8084 DSI. Extract the common register names to a separate header with the hope of later having the common code to handle PLL in those PHYs. Signed-off-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Signed-off-by: Dmitry Baryshkov --- drivers/phy/qualcomm/phy-qcom-apq8064-sata.c | 23 +------------------- drivers/phy/qualcomm/phy-qcom-uniphy.h | 32 ++++++++++++++++++++++++= ++++ 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qua= lcomm/phy-qcom-apq8064-sata.c index cae290a6e19f..dd9929429f9a 100644 --- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -15,28 +15,7 @@ #include #include =20 -/* PHY registers */ -#define UNIPHY_PLL_REFCLK_CFG 0x000 -#define UNIPHY_PLL_PWRGEN_CFG 0x014 -#define UNIPHY_PLL_GLB_CFG 0x020 -#define UNIPHY_PLL_SDM_CFG0 0x038 -#define UNIPHY_PLL_SDM_CFG1 0x03C -#define UNIPHY_PLL_SDM_CFG2 0x040 -#define UNIPHY_PLL_SDM_CFG3 0x044 -#define UNIPHY_PLL_SDM_CFG4 0x048 -#define UNIPHY_PLL_SSC_CFG0 0x04C -#define UNIPHY_PLL_SSC_CFG1 0x050 -#define UNIPHY_PLL_SSC_CFG2 0x054 -#define UNIPHY_PLL_SSC_CFG3 0x058 -#define UNIPHY_PLL_LKDET_CFG0 0x05C -#define UNIPHY_PLL_LKDET_CFG1 0x060 -#define UNIPHY_PLL_LKDET_CFG2 0x064 -#define UNIPHY_PLL_CAL_CFG0 0x06C -#define UNIPHY_PLL_CAL_CFG8 0x08C -#define UNIPHY_PLL_CAL_CFG9 0x090 -#define UNIPHY_PLL_CAL_CFG10 0x094 -#define UNIPHY_PLL_CAL_CFG11 0x098 -#define UNIPHY_PLL_STATUS 0x0C0 +#include "phy-qcom-uniphy.h" =20 #define SATA_PHY_SER_CTRL 0x100 #define SATA_PHY_TX_DRIV_CTRL0 0x104 diff --git a/drivers/phy/qualcomm/phy-qcom-uniphy.h b/drivers/phy/qualcomm/= phy-qcom-uniphy.h new file mode 100644 index 000000000000..e5b79a4dc270 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-uniphy.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + */ + +#ifndef PHY_QCOM_UNIPHY_H +#define PHY_QCOM_UNIPHY_H + +/* PHY registers */ +#define UNIPHY_PLL_REFCLK_CFG 0x000 +#define UNIPHY_PLL_PWRGEN_CFG 0x014 +#define UNIPHY_PLL_GLB_CFG 0x020 +#define UNIPHY_PLL_SDM_CFG0 0x038 +#define UNIPHY_PLL_SDM_CFG1 0x03c +#define UNIPHY_PLL_SDM_CFG2 0x040 +#define UNIPHY_PLL_SDM_CFG3 0x044 +#define UNIPHY_PLL_SDM_CFG4 0x048 +#define UNIPHY_PLL_SSC_CFG0 0x04c +#define UNIPHY_PLL_SSC_CFG1 0x050 +#define UNIPHY_PLL_SSC_CFG2 0x054 +#define UNIPHY_PLL_SSC_CFG3 0x058 +#define UNIPHY_PLL_LKDET_CFG0 0x05c +#define UNIPHY_PLL_LKDET_CFG1 0x060 +#define UNIPHY_PLL_LKDET_CFG2 0x064 +#define UNIPHY_PLL_CAL_CFG0 0x06c +#define UNIPHY_PLL_CAL_CFG8 0x08c +#define UNIPHY_PLL_CAL_CFG9 0x090 +#define UNIPHY_PLL_CAL_CFG10 0x094 +#define UNIPHY_PLL_CAL_CFG11 0x098 +#define UNIPHY_PLL_STATUS 0x0c0 + +#endif --=20 2.47.3 From nobody Wed Apr 1 08:38:30 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76DBC37F744 for ; Wed, 1 Apr 2026 03:39:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014749; cv=none; b=Q3mgp8AcZ/SYOkIcZ4pv0GOl3ZwGbUxBD2X0Twc6wrGi+mnFJMpAUIKzhMbWtYWXfU2Rlu8EyZGZ5d1/ff8AABsFwu4g1YLfWp6AJgXmGCAdD42TVGORVi10z6gcTmrAd/yQO8As3pl1CW1u5KtSAmOQkoe45aJWLNJXdTWq2mo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014749; c=relaxed/simple; bh=RRdFiNjgnPzCRkH1+W1J54NjjzrWTLFzh8czz6UBnnY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=W+e9nIXaU7daG7Kc1DdA1uvsYYrsZKWYnNn57yftb9HjdBRLcXmYqmtLxJEcExCD0D2Y9h+z+WRRe9VRZeCW6tztruDWxyZUOydfUHZo0UGgPIUmtWbnunfHDWC2KF67m1TOtyDU0ze+I4A8z4eJx3wfFxk4GMdK8yOvnd1oSCk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=kWyMSHBX; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=RMX2eX1f; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="kWyMSHBX"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="RMX2eX1f" Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6313VTxP1459610 for ; Wed, 1 Apr 2026 03:39:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= c58/Sfeclx1N64rZvnt6VYgoyb1/qdvgxUAhY/kc9dc=; b=kWyMSHBXruavV91s r4KBfh3/2zQYc3tYcuA/aXXFlELxdSDQdKEPyzYpA2uWm9pg3DDpjNRnMLMZmUps Y3jv0h3proTQYSFpRQ8measxcesyFKqTqMXIE19XTGeD19mk/xfYL3wPp3aPlOXL UzQrC3ZCmZuLJAXWfVimoGLjAwTbKNV9lzzsWhaBPNHxSWy10Dp2aiQSAbhSAcY+ xckn5WR+lVZkoJTFgwTsWHJBKI9i4ooMkAMuqzdBEAc1Wl+HZ/P0xk1w6KSzQBL8 pG+tk6Xepa5aBRGvQwoDi5psr8nX/Rf2BOgU7LbPcxpOVZkuvtvszct8axI5JqZZ EA+BaQ== Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4d8uhg00uc-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 01 Apr 2026 03:39:07 +0000 (GMT) Received: by mail-qt1-f200.google.com with SMTP id d75a77b69052e-50b2cbe7223so209544361cf.2 for ; Tue, 31 Mar 2026 20:39:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1775014747; x=1775619547; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=c58/Sfeclx1N64rZvnt6VYgoyb1/qdvgxUAhY/kc9dc=; b=RMX2eX1fVh4FzaM6VLdi63heIt0lwnK3w46CgLgppgHOjFuie0y0QWutRQpFCn5LRp BDuUlmUaNvmunncNPw1wjORp2shTEvMd97IWJdvqh8Ni5DOkk1ZX08ZRSf3PeQJ5Q6Kz 2T4jORYRsW+mIkJaDCM3B10SAecDWH2qtlkyPUjvNv5Hk9gdLbG8o0ccnfE9ELdw2Vdj SrpaRXVenQokWtkU77hpHD/ZWR4mKfVNbyl0Vg0FojGcwsqOtcGlmtwfSN4bspXuRfxS GQSL/ZzJ+dBeII98qNxK3OKNXABlP7laGxMH+Qd8SrHuaPPVb7nIYObOs2RIgZuOnFTd 6xfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775014747; x=1775619547; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=c58/Sfeclx1N64rZvnt6VYgoyb1/qdvgxUAhY/kc9dc=; b=azQIMbYWtZL2CLW6WlKt0Rmue+MHGk5rlRR6ueSEyhVG60ugZrM/cRhsDsHUX20vmN hL6k1x3B2E/fGLT3Gf2NDcYdoJs2FAjt9KIo503DyEBr9mMQcJJgcdg+69+RsY6UCjxo 8pSGhTlt41tPndoXwDSEBpmvjZ3I+mCAnxaOawUZ8AkWtYkJyxy0Q95aOZTBi/glmK2v wK26yj/QGbRqghP4LQapjU/RR4vSyk3JQG3BPgulYE3rIslqDTXvjfxzbRPExX7hsxzL lR+5o1KA/kqCa6SfucH/XC/MM3BpYPr9ee8OBakRdTLYSEapdLaLR8Ugl+9p6VZVqY8v dSyg== X-Gm-Message-State: AOJu0YyxLhS1UCpDgCjuzVgbVpBVZsLnV/XzCrANoa5yGrfbIVNLYiiL LJpjMzEZ5Y3A4gxLv1QBAuhbtUCVIwsQLlSnSZQ7iG0+I72eKuxktj/LmdDc1n0UyJC5RFSsc6H vI3Ldo9Q4ExLNfVqbYqs+sHBu3i+oxp3vE39x+mLlOF0VdGQ2WLk69do+hvaomTqGhoY= X-Gm-Gg: ATEYQzwOL+hDWGACuvb2j5GuJbamtHB11dhI2vYW8ZkYo7Ty5HZYNedVOcoAzI+JtiB ky/m8xeYbHd6ARloVV+7X5LLPqtaZBgj3RA5mccZVCX8ZWd0UbE/u+5aqbaJY2DvWO54fDd1THX GoyY0ALaAAbk0i7xDpbaE0TPFBwODVdkhHtM5YJCfaMdGcTDAz+E/6i2epOJsxdzMY92VPVxYMT +Z+yxtCTbiQYPHBbTAIcAtDAJ9vOPVJE8ET5zBFG16IVlSTS/lJb4B71LUQHV2Vaw6Xkog9ko2w 6FpphnSqqoP12hwc36hCSNjaohkbYf1w99URDRO9Z3Jq6Nj5fgpKl5TQB5XLvbAafbpXUofGwE/ TGWm4q3rjgL6TnRNojsJv1od5rjUYwrKzQiJmmwiWr7+Sj3wbH5JRBXuyprUxLesemN5LFU81x/ IVW/UEbPDNVbB7+t/O7OlcfWtf5M3z6kxsniI= X-Received: by 2002:a05:622a:a13:b0:50b:3b54:d78c with SMTP id d75a77b69052e-50d3bd78c0amr32199621cf.52.1775014746739; Tue, 31 Mar 2026 20:39:06 -0700 (PDT) X-Received: by 2002:a05:622a:a13:b0:50b:3b54:d78c with SMTP id d75a77b69052e-50d3bd78c0amr32199311cf.52.1775014746284; Tue, 31 Mar 2026 20:39:06 -0700 (PDT) Received: from umbar.lan (2001-14ba-a073-af00-264b-feff-fe8b-be8a.rev.dnainternet.fi. [2001:14ba:a073:af00:264b:feff:fe8b:be8a]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-38c83730f7csm24404851fa.16.2026.03.31.20.39.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 20:39:05 -0700 (PDT) From: Dmitry Baryshkov Date: Wed, 01 Apr 2026 06:38:48 +0300 Subject: [PATCH v8 3/4] phy: qcom-uniphy: add more registers from display PHYs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260401-fd-hdmi-phy-v8-3-51b0e98edf6c@oss.qualcomm.com> References: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> In-Reply-To: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> To: Rob Clark , Dmitry Baryshkov , Abhinav Kumar , Jessica Zhang , Sean Paul , Marijn Suijten , David Airlie , Simona Vetter , Vinod Koul , Neil Armstrong Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, dri-devel@lists.freedesktop.org, freedreno@lists.freedesktop.org, linux-phy@lists.infradead.org, Dmitry Baryshkov X-Mailer: b4 0.15.1 X-Developer-Signature: v=1; a=openpgp-sha256; l=2885; i=dmitry.baryshkov@oss.qualcomm.com; h=from:subject:message-id; bh=A0KQeSlhmmB1eMUUohCWtmSPla+2ZySmmRe5s7HDmyc=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBpzJNL5iRXqp1OJirSlIM8YMCd0J2GckN/Wf64w 2wMeVZ3OgOJATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCacyTSwAKCRCLPIo+Aiko 1fJ7CACbeqEUhDFAb4uFedgJ/xy0En4l0uW2mMQAwEyXkpazQdH3Kwe1d4IjF66qerc3o+mv1vy JyMuGdrTEljp3Jz+MF9L6MWLIDlf/xO1BYyUl0k/g3v//fAXD/pJ8rUr+o3LfY1Sqx0w9bC85Ea FR/z+rMWgZNfZWHM+x0z6YUkilODx25BcC8PQc/B38yfXpAjQNJcmz1NoMxlO5xE/aYw7xKHfWT uKlCPpb9jBL3k3AysVZCmoSwBUXlEfkzQwv6x0gjd4cavJX+KrtaRVB4J7PgsQGyr/yIjTZutH6 qKD4wp5b/gg7PK3uMZZVD7Ige9qteLlOvz+HjGUVbO06fSnq X-Developer-Key: i=dmitry.baryshkov@oss.qualcomm.com; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDAxMDAyNiBTYWx0ZWRfX5bG9qiQJdaIy YxLQ+WpWMm4qnaL5UTuMRM6cqSpAsQV8PSaZi7JKbZgOGKjnKI+MhRsZzlI0munavsOoIG09fhK yerakSUTMpqbGVvfHAfhA12tQ3NMTtXC2/pU5CyIGntJoS3y+DimPZn5q4NTJEmcIL/iiQs9s6J 8hGpXU3t9KjoJaEnkFzVMjTm8uVqgbwViVDxQAb9B9FN+nWYjZ8whEBYRH9tVMPBRpi+/++nmrK 4DcWNek3UpY7+5PB4zviyjdqcn4IYMjTOv7ERUXW0O/8s/HIwkrbR51jLpB5tdA+JItmTBPw6Mi YJq6hLdPzU7GvUvKsiYjuw3+LC+m7mLiC821ohJlU+2iPsXRv62IMEibFHNQplJYBJWunL8c0Bv FnlpSrJgVIXCNLsOhnvf5U1r/E4DHPZlsVIE5cjMEXbLSjUAyztGU5zlXEuU4Sy2spkpUaapk4v mvGbFNmIZx44ryR2cMQ== X-Authority-Analysis: v=2.4 cv=YcawJgRf c=1 sm=1 tr=0 ts=69cc935b cx=c_pps a=JbAStetqSzwMeJznSMzCyw==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=A5OVakUREuEA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=KKAkSRfTAAAA:8 a=EUspDBNiAAAA:8 a=NWzoLV28igjY6Ppe7YgA:9 a=QEXdDO2ut3YA:10 a=uxP6HrT_eTzRwkO_Te1X:22 a=cvBusfyB2V15izCimMoJ:22 X-Proofpoint-GUID: 2V_KucFWAzHIogqoli9aMzZXYyFLK14t X-Proofpoint-ORIG-GUID: 2V_KucFWAzHIogqoli9aMzZXYyFLK14t X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-01_01,2026-03-31_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 priorityscore=1501 adultscore=0 clxscore=1015 suspectscore=0 malwarescore=0 impostorscore=0 spamscore=0 lowpriorityscore=0 bulkscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2603050001 definitions=main-2604010026 From: Dmitry Baryshkov Import register definitions from 28nm DSI and HDMI PHYs, adding more UNI PHY registers. Signed-off-by: Dmitry Baryshkov Reviewed-by: Neil Armstrong Signed-off-by: Dmitry Baryshkov --- drivers/phy/qualcomm/phy-qcom-uniphy.h | 42 ++++++++++++++++++++++++++++++= ++++ 1 file changed, 42 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-uniphy.h b/drivers/phy/qualcomm/= phy-qcom-uniphy.h index e5b79a4dc270..ba9d14aae682 100644 --- a/drivers/phy/qualcomm/phy-qcom-uniphy.h +++ b/drivers/phy/qualcomm/phy-qcom-uniphy.h @@ -8,10 +8,30 @@ =20 /* PHY registers */ #define UNIPHY_PLL_REFCLK_CFG 0x000 +#define UNIPHY_PLL_REFCLK_DBLR BIT(0) +#define UNIPHY_PLL_REFCLK_DIV GENMASK(3, 2) + +#define UNIPHY_PLL_POSTDIV1_CFG 0x004 +#define UNIPHY_PLL_CHGPUMP_CFG 0x008 +#define UNIPHY_PLL_VCOLPF_CFG 0x00c +#define UNIPHY_PLL_VREG_CFG 0x010 #define UNIPHY_PLL_PWRGEN_CFG 0x014 +#define UNIPHY_PLL_DMUX_CFG 0x018 +#define UNIPHY_PLL_AMUX_CFG 0x01c #define UNIPHY_PLL_GLB_CFG 0x020 +#define UNIPHY_PLL_POSTDIV2_CFG 0x024 +#define UNIPHY_PLL_POSTDIV3_CFG 0x028 +#define UNIPHY_PLL_LPFR_CFG 0x02c +#define UNIPHY_PLL_LPFC1_CFG 0x030 +#define UNIPHY_PLL_LPFC2_CFG 0x034 #define UNIPHY_PLL_SDM_CFG0 0x038 +#define UNIPHY_PLL_SDM_BYP BIT(6) +#define UNIPHY_PLL_SDM_BYP_DIV GENMASK(5, 0) + #define UNIPHY_PLL_SDM_CFG1 0x03c +#define UNIPHY_PLL_SDM_DITHER_EN BIT(6) +#define UNIPHY_PLL_SDM_DC_OFFSET GENMASK(5, 0) + #define UNIPHY_PLL_SDM_CFG2 0x040 #define UNIPHY_PLL_SDM_CFG3 0x044 #define UNIPHY_PLL_SDM_CFG4 0x048 @@ -22,11 +42,33 @@ #define UNIPHY_PLL_LKDET_CFG0 0x05c #define UNIPHY_PLL_LKDET_CFG1 0x060 #define UNIPHY_PLL_LKDET_CFG2 0x064 +#define UNIPHY_PLL_TEST_CFG 0x068 #define UNIPHY_PLL_CAL_CFG0 0x06c +#define UNIPHY_PLL_CAL_CFG1 0x070 +#define UNIPHY_PLL_CAL_CFG2 0x074 +#define UNIPHY_PLL_CAL_CFG3 0x078 +#define UNIPHY_PLL_CAL_CFG4 0x07c +#define UNIPHY_PLL_CAL_CFG5 0x080 +#define UNIPHY_PLL_CAL_CFG6 0x084 +#define UNIPHY_PLL_CAL_CFG7 0x088 #define UNIPHY_PLL_CAL_CFG8 0x08c #define UNIPHY_PLL_CAL_CFG9 0x090 #define UNIPHY_PLL_CAL_CFG10 0x094 #define UNIPHY_PLL_CAL_CFG11 0x098 +#define UNIPHY_PLL_EFUSE_CFG 0x09c +#define UNIPHY_PLL_DEBUG_BUS_SEL 0x0a0 +#define UNIPHY_PLL_CTRL_42 0x0a4 +#define UNIPHY_PLL_CTRL_43 0x0a8 +#define UNIPHY_PLL_CTRL_44 0x0ac +#define UNIPHY_PLL_CTRL_45 0x0b0 +#define UNIPHY_PLL_CTRL_46 0x0b4 +#define UNIPHY_PLL_CTRL_47 0x0b8 +#define UNIPHY_PLL_CTRL_48 0x0bc #define UNIPHY_PLL_STATUS 0x0c0 +#define UNIPHY_PLL_DEBUG_BUS0 0x0c4 +#define UNIPHY_PLL_DEBUG_BUS1 0x0c8 +#define UNIPHY_PLL_DEBUG_BUS2 0x0cc +#define UNIPHY_PLL_DEBUG_BUS3 0x0d0 +#define UNIPHY_PLL_CTRL_54 0x0d4 =20 #endif --=20 2.47.3 From nobody Wed Apr 1 08:38:30 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1646C37F006 for ; Wed, 1 Apr 2026 03:39:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014754; cv=none; b=DGdQG1lgJeQt9xUcIvelnIu7Ko/Q3Xl+5GV9j3i+vYWUYte6KUkq6nXqh38rtyDAWqi7j5nBx8qI1SD2V9hDhWZyjkEfxIxkAGn7nqdVREfnjQkIKU8ZBt7xHmI7lOG+wW3MB8QbcwZRchZwXXdIBUzwFB6ETldI8hpzPkHtGwY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775014754; c=relaxed/simple; bh=eGJGe8eovJ/oAHU5KO53a5jb8nImFIZo6uG/tUVMlCM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Aw9PsjKMSgnmaUyFw4l1TKimXBrxLdgN6SiQlre/o6zaFROrirP+VVNBH61YpH8ZLm/atXJ7gNSbCeDq173rXsiJ+Kyo+Dm8TqG72XagXzDDIX2u2xNc0LJxJDvezDbJZedqw8IRT8CPiIzhW9+Tn+CwWZ3V8BxsyXfpl2X6N2o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=aLkzGpkY; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=Lma7DY+O; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="aLkzGpkY"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="Lma7DY+O" Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 6313VSOw1459604 for ; Wed, 1 Apr 2026 03:39:12 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= HJ9ENS/CvssgrseD6ncEcwK29NBYxwaboFYVZIp4MUI=; b=aLkzGpkYuSMHIoty QRvFCTVvd9qCu78OEEa9UxPg0cnd1QlWnfWJi5CzRXQLp3EvOUR5qnzVA1JF85DN TnZUoSuBr5PYFYohHb7tWER1Mw8KB31Ls40wTZ+qEXPvWf/3O3xyzZLf1X02/apV 5Nr2iVkN9gJw/kxk5uvatt8167FFGNvf2t+8lGCeQ5M9c04p5rj+gOg/zb9KvKFI AA98LFKztwCPCuZAaGuwJuVJBkVWvlSG+RsUdjYTRqd5V9W+/XvOHv4XuzrbfWaK HqQGJxKile8HeyxwgUYf9Mh2x78cbFE9orHVf6H71lj1RecAe47+P8gOdinL2eMN Rd8e5Q== Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4d8uhg00up-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Wed, 01 Apr 2026 03:39:12 +0000 (GMT) Received: by mail-qt1-f197.google.com with SMTP id d75a77b69052e-50925fed647so72928341cf.3 for ; Tue, 31 Mar 2026 20:39:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1775014751; x=1775619551; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=HJ9ENS/CvssgrseD6ncEcwK29NBYxwaboFYVZIp4MUI=; b=Lma7DY+Ocwel68lXA+TgZLovuf2MkVkjGVnvif12ExKEq99r5lvN9f/NjNz89BDVqs TY4zs3NVsxqD5LcHFoVPRoZGrka/LOgEdKTYf8poZINJknkxcG4H4f758i//TUbjiFrT BFRI85ZQDFJBZZtMu4qRJhED692Nr50E6tiHRRsQv+QJkeA9gdJkXer4n+jXsHPNN1uo P/Hr6zDh4T4cQ8a/PI8QHUZoWNvXfafmyEvKAlat8QQWrJM6RqwisbPL84um1mLh1Lcz kSj4cRRPLA/IHrs0FmYAM0aTVRWUNnLC95IjkpYcYZv1IuB2F2vYWqvfQNkOoYky23ba 5AaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775014751; x=1775619551; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=HJ9ENS/CvssgrseD6ncEcwK29NBYxwaboFYVZIp4MUI=; b=J/SVSfy6IYev83lx0p4ZPFHFbijZdcOZnM9B02OKWe4WRdlJqCMAPGjygp6EGBe2bl 5kmpXKs8X6aWh4Vk5b53Aa2T+gL7NAAQ/q2/Tzs+E518p9sgvaj3ZdJMq86DJAmGBIy8 5bzL1BmMvlyaOtJ1drwRLGjF3nf1E95IgQ2Q0tFU0JH2fC4NMNNH3WzpH8WOSrmFvatO mbzbw5ws7aWLsnn3cHS6QmE00eZwm9tA0BCKxozF/AOo/OrQ3fnWvUGvY/F69k+et+Jl UNoaRHplCfppU2lA5EQMoR7dVy2C2TkbkjdJjf3bzTkewfWmyZ+uPpdm2VsKuBp73ZmO ZyOA== X-Gm-Message-State: AOJu0YyUsfa3Uaawz5xA+VAkZpXam8NYK1EERViOch4yImCp7lICCEi3 wp9qCX7ycRLXfJUTkp5TPQZtDEfJ/FLKSbQQ9S6AMwlQ0dGbPzOMxkN5j3UT4pIGHYDTJTA5P/9 rrExDnwunoGf6L1wim/UMwWgpWJNcwcNu/o6AxFYRE6JNPW/yp7E5zGesE0h7+8h2TD4= X-Gm-Gg: ATEYQzyT1ed+r36AnkTr2d+ikD2bqmKDPgpMBo3WNppZPOr1uaEYbMT1CA29QkQqGTM IyV0bxfmc3yKb/5HdCmylkJxQczSSfKFAdiMihrY3LR+3rud86VlAzw7aE4zDB1kpfTMJafMWQe GRvGYwyrVuP+NT2JIcV4gMGfJGFVN6QEe62E0GnhcfQnUcmZescCRR/Abjf1bOgMHW1tMQ41tSU F9jdr+eOoS6IUsDD2/MXxwlFfRFTwjVqi9aiv05/PTg2yU69QFNG8LKL9FmDng1VFf4r0jlkusm Gof/c2k8u2Gd2qMKh+IsUtaz7ocV1nnv9yF9y+3pNdvVAQsixjnCMaaQlnAUwLJSz248WwuicOZ ewIxhPZQdiTkPrYDzAOEGhZxaXHER/bRZuE/JU+M93VXKTprx4WV7T3QX7MRmb1ioAKExtNdvz8 /Z5OV+fQYySXujhBKES8v9P0DzxO565sa2ReY= X-Received: by 2002:a05:622a:514:b0:509:aa4:4a02 with SMTP id d75a77b69052e-50d3bcc3e29mr30479201cf.29.1775014751237; Tue, 31 Mar 2026 20:39:11 -0700 (PDT) X-Received: by 2002:a05:622a:514:b0:509:aa4:4a02 with SMTP id d75a77b69052e-50d3bcc3e29mr30478811cf.29.1775014750776; Tue, 31 Mar 2026 20:39:10 -0700 (PDT) Received: from umbar.lan (2001-14ba-a073-af00-264b-feff-fe8b-be8a.rev.dnainternet.fi. [2001:14ba:a073:af00:264b:feff:fe8b:be8a]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-38c83730f7csm24404851fa.16.2026.03.31.20.39.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 31 Mar 2026 20:39:08 -0700 (PDT) From: Dmitry Baryshkov Date: Wed, 01 Apr 2026 06:38:49 +0300 Subject: [PATCH v8 4/4] phy: qualcomm: add MSM8974 HDMI PHY support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260401-fd-hdmi-phy-v8-4-51b0e98edf6c@oss.qualcomm.com> References: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> In-Reply-To: <20260401-fd-hdmi-phy-v8-0-51b0e98edf6c@oss.qualcomm.com> To: Rob Clark , Dmitry Baryshkov , Abhinav Kumar , Jessica Zhang , Sean Paul , Marijn Suijten , David Airlie , Simona Vetter , Vinod Koul , Neil Armstrong Cc: linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, dri-devel@lists.freedesktop.org, freedreno@lists.freedesktop.org, linux-phy@lists.infradead.org, Dmitry Baryshkov , Konrad Dybcio X-Mailer: b4 0.15.1 X-Developer-Signature: v=1; a=openpgp-sha256; l=10290; i=dmitry.baryshkov@oss.qualcomm.com; h=from:subject:message-id; bh=eGJGe8eovJ/oAHU5KO53a5jb8nImFIZo6uG/tUVMlCM=; b=owEBbQGS/pANAwAKAYs8ij4CKSjVAcsmYgBpzJNL0jPH+Axh+uC1JfC2+H52qSqXBO8GmiMwe iW69JGMTYyJATMEAAEKAB0WIQRMcISVXLJjVvC4lX+LPIo+Aiko1QUCacyTSwAKCRCLPIo+Aiko 1Q2dB/4psose9Y/icznz3t/7IznjflEyGbw1FxyXI/iSSYwQP/qV20Mt2r1e6YjzUEJQkXFjbEf CufrmTlcGCTVnAceuWR/ALfpds8pNP1w2+HCyeHEkOCplCrOOLSA9Ur/XAbpouW43EeKt8GolNO LNMJvEqAo74WCVAf4+yl4Sso5pNhAmvAOk1yboSWQ+wGMkfxlTo+fHiA0H6/uvZX0dqlJ3nWpGY EPRNzoYQJvEMw+7pkFXQFhzrrl9iBXmS2XZbPROO/6dLmroJefMfOW9VM9BxuNy9TSntQNaPztk lT/ATu9GHDxVS7d3jPQQ2iLs7KFZ4CXRR0E4az/FkWlDn/rm X-Developer-Key: i=dmitry.baryshkov@oss.qualcomm.com; a=openpgp; fpr=8F88381DD5C873E4AE487DA5199BF1243632046A X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNDAxMDAyNiBTYWx0ZWRfXwAGiixjgBEIK yTK2FyiG+vy/8o75x+5Vg8//gpU51/SWtGYgvS1Q0Y+phgji7tTMBJmgGvNP1W2/aJcpMzWScym sn7X0BhcQaUvFhg77f6c4sfSRmQHDHAsRV/YWyqvcVgo1K4G2nxOHJaMUkpA6QB/9xw/ruMHP/+ rh9qj7esMicb16K9mFQWP3SWZ5qmZ3AJJXMDNPb3N0vfXuIQojr8J43vKePeXdl8TIYXZpiqBiY 9irNxeH0JSxMj7ELxdMddRv2qkvjGCCVfYOXrlnD0p3R5K6bz5/j0sVmrC2x1azo0Zwqp1Zhpi/ lV7O+Vnaf3VUqeO7RaBpwyhhEMUE4eOxBQL/KD0nZVoDWDh/Z/t5Es+lNVlDqzYcQchwpmGI+AK DGwLdqIXYPnraGgpI0LX+nZEJ6L5gwrvTp+Zy0UewD5OKK1VaLswRTyB5OkiVc2h0iNx0XEpaOb 5YWyfmtKR2af64PSYlA== X-Authority-Analysis: v=2.4 cv=YcawJgRf c=1 sm=1 tr=0 ts=69cc9360 cx=c_pps a=EVbN6Ke/fEF3bsl7X48z0g==:117 a=xqWC_Br6kY4A:10 a=IkcTkHD0fZMA:10 a=A5OVakUREuEA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=DJpcGTmdVt4CTyJn9g5Z:22 a=KKAkSRfTAAAA:8 a=EUspDBNiAAAA:8 a=pGLkceISAAAA:8 a=U0NZ41NzsMKYcVuMMSoA:9 a=QEXdDO2ut3YA:10 a=a_PwQJl-kcHnX1M80qC6:22 a=cvBusfyB2V15izCimMoJ:22 X-Proofpoint-GUID: weK6GvK0aLHBwEbV4eviM0PYWfT5jwfe X-Proofpoint-ORIG-GUID: weK6GvK0aLHBwEbV4eviM0PYWfT5jwfe X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-04-01_01,2026-03-31_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 priorityscore=1501 adultscore=0 clxscore=1015 suspectscore=0 malwarescore=0 impostorscore=0 spamscore=0 lowpriorityscore=0 bulkscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2603050001 definitions=main-2604010026 Add support for HDMI PHY on Qualcomm MSM8974 / APQ8074 platforms. Signed-off-by: Dmitry Baryshkov Acked-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov --- drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c | 282 +++++++++++++++++++++++++= ++++ 1 file changed, 282 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c b/drivers/phy/qualc= omm/phy-qcom-hdmi-28hpm.c index 720757f8f393..801e304801b3 100644 --- a/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c @@ -6,10 +6,13 @@ * Author: Rob Clark */ =20 +#include #include #include +#include =20 #include "phy-qcom-hdmi-preqmp.h" +#include "phy-qcom-uniphy.h" =20 #define REG_HDMI_8x74_ANA_CFG0 0x00000000 #define REG_HDMI_8x74_ANA_CFG1 0x00000004 @@ -31,8 +34,282 @@ #define REG_HDMI_8x74_BIST_PATN3 0x00000048 #define REG_HDMI_8x74_STATUS 0x0000005c =20 +#define HDMI_8974_VCO_MAX_FREQ 1800000000UL +#define HDMI_8974_VCO_MIN_FREQ 600000000UL + +#define HDMI_8974_COMMON_DIV 5 + +static inline void write16(u16 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel(val >> 8, reg + 4); +} + +static inline void write24(u32 val, void __iomem *reg) +{ + writel(val & 0xff, reg); + writel((val >> 8) & 0xff, reg + 4); + writel(val >> 16, reg + 8); +} + +static inline u32 read24(void __iomem *reg) +{ + u32 val =3D readl(reg); + + val |=3D readl(reg + 4) << 8; + val |=3D readl(reg + 8) << 16; + + return val; +} + +static void qcom_uniphy_setup(void __iomem *base, unsigned int ref_freq, + bool sdm_mode, + bool ref_freq_mult_2, + bool dither, + unsigned int refclk_div, + unsigned int vco_freq) +{ + unsigned int int_ref_freq =3D ref_freq * (ref_freq_mult_2 ? 2 : 1); + unsigned int div_in_freq =3D vco_freq / refclk_div; + unsigned int dc_offset =3D div_in_freq / int_ref_freq - 1; + unsigned int sdm_freq_seed; + unsigned int val; + u64 tmp =3D div_in_freq % int_ref_freq; + + tmp *=3D 0x10000; + sdm_freq_seed =3D div_u64(tmp, int_ref_freq); + + val =3D FIELD_PREP(UNIPHY_PLL_REFCLK_DBLR, ref_freq_mult_2) | + FIELD_PREP(UNIPHY_PLL_REFCLK_DIV, refclk_div - 1); + writel(val, base + UNIPHY_PLL_REFCLK_CFG); + + if (sdm_mode) { + writel(0, base + UNIPHY_PLL_SDM_CFG0); + writel(FIELD_PREP(UNIPHY_PLL_SDM_DITHER_EN, dither) | dc_offset, + base + UNIPHY_PLL_SDM_CFG1); + write24(sdm_freq_seed, base + UNIPHY_PLL_SDM_CFG2); + } else { + writel(UNIPHY_PLL_SDM_BYP | dc_offset, base + UNIPHY_PLL_SDM_CFG0); + writel(0, base + UNIPHY_PLL_SDM_CFG1); + write24(0, base + UNIPHY_PLL_SDM_CFG2); + } + + write16(mult_frac(ref_freq, 5, 1000000), base + UNIPHY_PLL_CAL_CFG8); + write16(vco_freq / 16, base + UNIPHY_PLL_CAL_CFG10); +} + +static unsigned long qcom_uniphy_recalc(void __iomem *base, unsigned long = parent_rate) +{ + unsigned long rate; + u32 refclk_cfg; + u32 dc_offset; + u64 fraq_n; + u32 val; + + refclk_cfg =3D readl(base + UNIPHY_PLL_REFCLK_CFG); + if (refclk_cfg & UNIPHY_PLL_REFCLK_DBLR) + parent_rate *=3D 2; + + val =3D readl(base + UNIPHY_PLL_SDM_CFG0); + if (FIELD_GET(UNIPHY_PLL_SDM_BYP, val)) { + dc_offset =3D FIELD_GET(UNIPHY_PLL_SDM_BYP_DIV, val); + fraq_n =3D 0; + } else { + dc_offset =3D FIELD_GET(UNIPHY_PLL_SDM_DC_OFFSET, + readl(base + UNIPHY_PLL_SDM_CFG1)); + fraq_n =3D read24(base + UNIPHY_PLL_SDM_CFG2); + } + + rate =3D (dc_offset + 1) * parent_rate; + rate +=3D div_u64(fraq_n * parent_rate, 0x10000); + + rate *=3D FIELD_GET(UNIPHY_PLL_REFCLK_DIV, refclk_cfg) + 1; + + return rate; +} + +static const unsigned int qcom_hdmi_8974_divs[] =3D {1, 2, 4, 6}; + +static unsigned long qcom_hdmi_8974_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct qcom_hdmi_preqmp_phy *hdmi_phy =3D hw_clk_to_phy(hw); + u32 div_idx =3D readl(hdmi_phy->pll_reg + UNIPHY_PLL_POSTDIV1_CFG); + unsigned long rate =3D qcom_uniphy_recalc(hdmi_phy->pll_reg, parent_rate); + + return rate / HDMI_8974_COMMON_DIV / qcom_hdmi_8974_divs[div_idx & 0x3]; +} + +static int qcom_hdmi_8974_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long min_freq =3D HDMI_8974_VCO_MIN_FREQ / HDMI_8974_COMMON_DIV; + unsigned long max_freq =3D HDMI_8974_VCO_MAX_FREQ / HDMI_8974_COMMON_DIV; + + req->rate =3D clamp(req->rate, min_freq / 6, max_freq); + + return 0; +} + +static const struct clk_ops qcom_hdmi_8974_pll_ops =3D { + .recalc_rate =3D qcom_hdmi_8974_pll_recalc_rate, + .determine_rate =3D qcom_hdmi_8974_pll_determine_rate, +}; + +static int qcom_hdmi_msm8974_phy_find_div(unsigned long long pixclk) +{ + unsigned long long min_freq =3D HDMI_8974_VCO_MIN_FREQ / HDMI_8974_COMMON= _DIV; + int i; + + if (pixclk > HDMI_8974_VCO_MAX_FREQ / HDMI_8974_COMMON_DIV) + return -EINVAL; + + for (i =3D 0; i < ARRAY_SIZE(qcom_hdmi_8974_divs); i++) { + if (pixclk >=3D min_freq / qcom_hdmi_8974_divs[i]) + return i; + } + + return -EINVAL; +} + +static int qcom_hdmi_msm8974_phy_pll_set_rate(struct qcom_hdmi_preqmp_phy = *hdmi_phy) +{ + unsigned long long pixclk =3D hdmi_phy->hdmi_opts.tmds_char_rate; + unsigned long vco_rate; + unsigned int div; + int div_idx =3D 0; + + div_idx =3D qcom_hdmi_msm8974_phy_find_div(pixclk); + if (WARN_ON(div_idx < 0)) + return div_idx; + + div =3D qcom_hdmi_8974_divs[div_idx]; + vco_rate =3D pixclk * HDMI_8974_COMMON_DIV * div; + + writel(0x81, hdmi_phy->phy_reg + REG_HDMI_8x74_GLB_CFG); + + writel(0x01, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + writel(0x19, hdmi_phy->pll_reg + UNIPHY_PLL_VCOLPF_CFG); + writel(0x0e, hdmi_phy->pll_reg + UNIPHY_PLL_LPFR_CFG); + writel(0x20, hdmi_phy->pll_reg + UNIPHY_PLL_LPFC1_CFG); + writel(0x0d, hdmi_phy->pll_reg + UNIPHY_PLL_LPFC2_CFG); + + qcom_uniphy_setup(hdmi_phy->pll_reg, 19200000, true, true, true, 1, vco_r= ate); + + writel(0x10, hdmi_phy->pll_reg + UNIPHY_PLL_LKDET_CFG0); + writel(0x1a, hdmi_phy->pll_reg + UNIPHY_PLL_LKDET_CFG1); + writel(0x05, hdmi_phy->pll_reg + UNIPHY_PLL_LKDET_CFG2); + + writel(div_idx, hdmi_phy->pll_reg + UNIPHY_PLL_POSTDIV1_CFG); + + writel(0x00, hdmi_phy->pll_reg + UNIPHY_PLL_POSTDIV2_CFG); + writel(0x00, hdmi_phy->pll_reg + UNIPHY_PLL_POSTDIV3_CFG); + writel(0x01, hdmi_phy->pll_reg + UNIPHY_PLL_CAL_CFG2); + + writel(0x1f, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL0); + udelay(50); + + writel(0x0f, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL1); + writel(0x10, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG2); + writel(0xdb, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG0); + writel(0x43, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG1); + if (pixclk =3D=3D 297000000) { + writel(0x06, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG2); + writel(0x03, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG3); + } else if (pixclk =3D=3D 268500000) { + writel(0x05, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG2); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG3); + } else { + writel(0x02, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG2); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG3); + } + + writel(0x04, hdmi_phy->pll_reg + UNIPHY_PLL_VREG_CFG); + + writel(0xd0, hdmi_phy->phy_reg + REG_HDMI_8x74_DCC_CFG0); + writel(0x1a, hdmi_phy->phy_reg + REG_HDMI_8x74_DCC_CFG1); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_TXCAL_CFG0); + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_TXCAL_CFG1); + + if (pixclk =3D=3D 268500000) + writel(0x11, hdmi_phy->phy_reg + REG_HDMI_8x74_TXCAL_CFG2); + else + writel(0x02, hdmi_phy->phy_reg + REG_HDMI_8x74_TXCAL_CFG2); + + writel(0x05, hdmi_phy->phy_reg + REG_HDMI_8x74_TXCAL_CFG3); + udelay(200); + + return 0; +} + +static int qcom_hdmi_msm8974_phy_pll_enable(struct qcom_hdmi_preqmp_phy *h= dmi_phy) +{ + int ret; + unsigned long status; + + /* Global enable */ + writel(0x81, hdmi_phy->phy_reg + REG_HDMI_8x74_GLB_CFG); + + /* Power up power gen */ + writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL0); + udelay(350); + + /* PLL power up */ + writel(0x01, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + udelay(5); + + /* Power up PLL LDO */ + writel(0x03, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + udelay(350); + + /* PLL power up */ + writel(0x0f, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + udelay(350); + + /* Poll for PLL ready status */ + ret =3D readl_poll_timeout(hdmi_phy->pll_reg + UNIPHY_PLL_STATUS, + status, status & BIT(0), + 100, 2000); + if (ret) { + dev_warn(hdmi_phy->dev, "HDMI PLL not ready\n"); + goto err; + } + + udelay(350); + + /* Poll for PHY ready status */ + ret =3D readl_poll_timeout(hdmi_phy->phy_reg + REG_HDMI_8x74_STATUS, + status, status & BIT(0), + 100, 2000); + if (ret) { + dev_warn(hdmi_phy->dev, "HDMI PHY not ready\n"); + goto err; + } + + return 0; + +err: + writel(0, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + udelay(5); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x74_GLB_CFG); + + return ret; +} + static int qcom_hdmi_msm8974_phy_power_on(struct qcom_hdmi_preqmp_phy *hdm= i_phy) { + int ret; + + ret =3D qcom_hdmi_msm8974_phy_pll_set_rate(hdmi_phy); + if (ret) + return ret; + + ret =3D qcom_hdmi_msm8974_phy_pll_enable(hdmi_phy); + if (ret) + return ret; + writel(0x1b, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG0); writel(0xf2, hdmi_phy->phy_reg + REG_HDMI_8x74_ANA_CFG1); writel(0x00, hdmi_phy->phy_reg + REG_HDMI_8x74_BIST_CFG0); @@ -49,6 +326,10 @@ static int qcom_hdmi_msm8974_phy_power_off(struct qcom_= hdmi_preqmp_phy *hdmi_phy { writel(0x7f, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL0); =20 + writel(0, hdmi_phy->pll_reg + UNIPHY_PLL_GLB_CFG); + udelay(5); + writel(0, hdmi_phy->phy_reg + REG_HDMI_8x74_GLB_CFG); + return 0; } =20 @@ -67,5 +348,6 @@ const struct qcom_hdmi_preqmp_cfg msm8974_hdmi_phy_cfg = =3D { .power_on =3D qcom_hdmi_msm8974_phy_power_on, .power_off =3D qcom_hdmi_msm8974_phy_power_off, =20 + .pll_ops =3D &qcom_hdmi_8974_pll_ops, .pll_parent =3D &msm8974_hdmi_pll_parent, }; --=20 2.47.3