* Re: [PATCH] phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context
From: Pavel Machek @ 2026-05-14 21:18 UTC (permalink / raw)
To: Claudiu Beznea
Cc: yoshihiro.shimoda.uh, vkoul, neil.armstrong, geert+renesas,
magnus.damm, prabhakar.mahadev-lad.rj, linux-renesas-soc,
linux-phy, linux-kernel, Claudiu Beznea, stable, Pavel Machek,
Nobuhiro Iwamatsu
In-Reply-To: <20260514111300.2152386-1-claudiu.beznea@kernel.org>
[-- Attachment #1.1: Type: text/plain, Size: 2597 bytes --]
Hi!
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> The OTG PHY initialization sequence needs to wait for 20 ms at a specific
> step, as described in commit 72c0339c115b ("phy: renesas:
> rcar-gen3-usb2: follow the hardware manual procedure").
>
> Commit 55a387ebb921 ("phy: renesas: rcar-gen3-usb2: Lock around hardware
> registers and driver data") tried to address various problems in the
> rcar-gen3-usb2 driver and converted the mutex protecting HW register
> accesses to a spin lock, leaving, however, a long delay in the critical
> section protected by the spin lock. This may become a problem,
> especially on RT kernels.
>
> To address this, release the spin lock before sleeping for 20 ms as
> required by the HW manual and reacquire it afterwards. To avoid other
> threads entering the critical section and configuring the HW while the
> software is waiting for the OTG initialization to complete, introduce the
> otg_initializing variable alongside the otg_init_done completion. Any
> other thread trying to configure the HW while the OTG PHY initialization
> is in progress waits for the completion instead of immediately returning
> errors to PHY users. The IRQs were also disabled while waiting for the OTG
> PHY initialization to complete, as the interrupt handler may also apply HW
> settings.
Just... there has to be a better way.
> +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> +static int rcar_gen3_phy_wait_otg_init(struct rcar_gen3_chan *channel,
> + unsigned long *flags)
> +{
> + unsigned long timeout = msecs_to_jiffies(25);
> + unsigned long ret = 1;
> +
> + lockdep_assert_held(&channel->lock);
> +
> + /*
> + * The OTG can be initialized only once and needs to release the lock
> + * and wait for 20 ms due to hardware constraints. Wait for the OTG PHY
> + * initialization to complete if another PHY executes configuration
> + * code while the OTG PHY is waiting. This avoids returning failures to
> + * PHY users.
> + */
> + if (READ_ONCE(channel->otg_initializing)) {
> + spin_unlock_irqrestore(&channel->lock, *flags);
This is not nice, passing flags between functions like this is a red flag.
You are only accessing otg_initializing under the spinlock. That means
that READ_ONCE is reduntant.
But AFAICT spinlock is only held over this function to protect
channel->otg_initializing access. I suspect correct answer here is
getting rid of spinlock over this function, and using
test_bit(BIT_INITIALIZING, ...) or something similar.
Best regards,
Pavel
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 112 bytes --]
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context
From: sashiko-bot @ 2026-05-14 18:52 UTC (permalink / raw)
To: Claudiu Beznea; +Cc: vkoul, neil.armstrong, wsa+renesas, linux-phy, olteanv
In-Reply-To: <20260514111300.2152386-1-claudiu.beznea@kernel.org>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] PM runtime reference leak in interrupt handler due to goto mixing.
- [High] Unconditional calls to `disable_irq_nosync()` with IRQ 0.
- [High] Disabling a shared IRQ line globally starves other devices.
- [High] Time-of-Check to Time-of-Use (TOCTOU) race condition in PHY power control.
- [Medium] TOCTOU race condition in `role_store` causes spurious `-EIO` errors.
- [Medium] Tight completion timeout causes spurious PHY initialization failures.
- [Medium] Spurious kernel warnings generated by valid shared interrupts.
--
commit 856d28e68a5bcfa08fa03aa685cb15772197dedc
Author: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context
This commit releases the spin lock before sleeping for 20 ms during
OTG PHY initialization and introduces completion mechanisms to prevent
other threads from configuring the hardware concurrently. It also disables
IRQs while waiting to prevent interrupt handlers from applying settings.
Closes: https://lore.kernel.org/all/afhkX2Ys2BG1gnqy@duo.ucw.cz
Closes: https://lore.kernel.org/all/afhkX2Ys2BG1gnqy@duo.ucw.cz
> diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> index 79e820e2fe55d..5a18bbd87c3b9 100644
> --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
> +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
[ ... ]
> @@ -388,32 +392,68 @@ static bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch)
> return true;
> }
>
> +static int rcar_gen3_phy_wait_otg_init(struct rcar_gen3_chan *channel,
> + unsigned long *flags)
> +{
> + unsigned long timeout = msecs_to_jiffies(25);
Could this completion timeout be too tight?
The initializing thread uses fsleep(20000) which guarantees a minimum sleep
of 20ms. Due to scheduler jitter or timer wheel granularity (especially if
HZ=100, where 25ms rounds to just 2 or 3 jiffies), the 20ms sleep can easily
exceed 25ms of wall-clock time, causing this to spuriously return -ETIMEDOUT.
> + unsigned long ret = 1;
> +
> + lockdep_assert_held(&channel->lock);
[ ... ]
> @@ -427,7 +467,10 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
> const char *buf, size_t count)
> {
> struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
> bool is_b_device;
> enum phy_mode cur_mode, new_mode;
> + unsigned long flags;
> + int ret = -EIO;
>
> - guard(spinlock_irqsave)(&ch->lock);
> + spin_lock_irqsave(&ch->lock, flags);
>
> if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
> - return -EIO;
> + goto unlock;
> +
> + ret = rcar_gen3_phy_wait_otg_init(ch, &flags);
Does checking the initialized state before waiting for the lock to be
released introduce a race condition?
Because rcar_gen3_phy_usb2_init() only sets rphy->initialized = true after
it finishes its 20ms sleep and reacquires the lock, a concurrent sysfs write
to role_store() during this 20ms window will see initialized as false and
immediately return -EIO instead of waiting for initialization to complete.
> + if (ret)
> + goto unlock;
[ ... ]
> @@ -512,6 +556,11 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
> goto rpm_put;
>
> scoped_guard(spinlock, &ch->lock) {
> + if (READ_ONCE(ch->otg_initializing)) {
> + dev_warn(dev, "%s: Got IRQ while waiting for OTG init!\n", __func__);
> + return IRQ_NONE;
> + }
Does returning early here leak the PM runtime reference?
The interrupt handler calls pm_runtime_get_noresume(dev) at the beginning of
the function, and releases it at the rpm_put label. By using return IRQ_NONE
inside the scoped_guard, the code bypasses pm_runtime_put_noidle() completely,
preventing the device from ever entering runtime suspend.
Additionally, since this interrupt is requested with IRQF_SHARED, could
another device sharing the line trigger a valid interrupt during the 20ms
OTG initialization window? If so, this would print a spurious warning to
the kernel log for a valid shared interrupt event.
> +
> status = readl(usb2_base + USB2_OBINTSTA);
> if (status & ch->phy_data->obint_enable_bits) {
> dev_vdbg(dev, "%s: %08x\n", __func__, status);
[ ... ]
> @@ -550,8 +605,22 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
> }
>
> /* Initialize otg part (only if we initialize a PHY with IRQs). */
> - if (rphy->int_enable_bits)
> - rcar_gen3_init_otg(channel);
> + if (rphy->int_enable_bits && channel->is_otg_channel &&
> + !rcar_gen3_is_any_otg_rphy_initialized(channel)) {
> + rcar_gen3_init_otg_phase0(channel);
> + disable_irq_nosync(channel->irq);
Could channel->irq be 0 here?
In rcar_gen3_phy_usb2_probe(), platform_get_irq_optional() is used. If it
returns -ENXIO, channel->irq remains 0. Since rphy->int_enable_bits is a
statically defined non-zero value for OTG PHYs, this would pass 0 to
disable_irq_nosync(), which manipulates critical system interrupts.
Also, does calling disable_irq_nosync() on a shared interrupt line starve
other devices?
Since the interrupt is requested with IRQF_SHARED, masking it at the
interrupt controller level means all other unrelated devices on the same
physical line will have their interrupts disabled for the full 20ms
duration. Should shared interrupts be masked locally at the device level
instead?
> + reinit_completion(&channel->otg_init_done);
> + WRITE_ONCE(channel->otg_initializing, true);
> + spin_unlock_irqrestore(&channel->lock, flags);
[ ... ]
> @@ -598,6 +678,7 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
> struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
> struct rcar_gen3_chan *channel = rphy->ch;
> void __iomem *usb2_base = channel->base;
> + unsigned long flags;
> u32 val;
> int ret = 0;
>
> @@ -607,11 +688,15 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
> return ret;
> }
>
> - guard(spinlock_irqsave)(&channel->lock);
> + spin_lock_irqsave(&channel->lock, flags);
>
> if (!rcar_gen3_are_all_rphys_power_off(channel))
> goto out;
>
> + ret = rcar_gen3_phy_wait_otg_init(channel, &flags);
> + if (ret)
> + goto unlock;
> +
> val = readl(usb2_base + USB2_USBCTR);
> val |= USB2_USBCTR_PLL_RST;
Does dropping the lock in rcar_gen3_phy_wait_otg_init() introduce a race
condition with the !rcar_gen3_are_all_rphys_power_off() check?
If two threads concurrently power on different PHYs, both will evaluate the
condition as false because neither has reached rphy->powered = true at the
end of the function. Both threads might then sleep in
rcar_gen3_phy_wait_otg_init().
Upon waking and reacquiring the lock, both will unconditionally apply
USB2_USBCTR_PLL_RST, which could catastrophically disrupt the PHY that was
just initialized by the first thread.
Should the power off condition be re-evaluated after the lock is reacquired?
> writel(val, usb2_base + USB2_USBCTR);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260514111300.2152386-1-claudiu.beznea@kernel.org?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH phy-next 1/2] dt-bindings: phy: add PHY bindings for the TI DS125DF111 Retimer PHY
From: Conor Dooley @ 2026-05-14 18:09 UTC (permalink / raw)
To: Ioana Ciornei
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, johan, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260513185103.1371809-2-ioana.ciornei@nxp.com>
[-- Attachment #1.1: Type: text/plain, Size: 1963 bytes --]
On Wed, May 13, 2026 at 09:51:02PM +0300, Ioana Ciornei wrote:
> Add device tree binding for the TI DS125DF111 Retimer PHY.
>
> Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
> ---
> .../bindings/phy/ti,ds125df111.yaml | 46 +++++++++++++++++++
> 1 file changed, 46 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/ti,ds125df111.yaml
>
> diff --git a/Documentation/devicetree/bindings/phy/ti,ds125df111.yaml b/Documentation/devicetree/bindings/phy/ti,ds125df111.yaml
> new file mode 100644
> index 000000000000..47e9ba2a8990
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/ti,ds125df111.yaml
> @@ -0,0 +1,46 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/phy/ti,ds125df111.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: TI DS125DF111 Retimer PHY
> +
> +description:
> + This binding describes the TI DS125DF111 multi-protocol Retimer PHY.
> +
> +maintainers:
> + - Ioana Ciornei <ioana.ciornei@nxp.com>
> +
> +properties:
> + compatible:
> + const: ti,ds125df111
> +
> + reg:
> + maxItems: 1
> +
> + "#phy-cells":
> + const: 1
> + description: |
> + The phandle's argument in the PHY specifier selects one of the two
> + channels of the retimer
> +
> +required:
> + - compatible
> + - reg
> + - "#phy-cells"
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + ti_retimer: retimer@18 {
Drop the unused label here please. How come the node name isn't phy@
btw?
Otherwise,
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: changes-requested
Cheers,
Conor.
> + compatible = "ti,ds125df111";
> + reg = <0x18>;
> + #phy-cells = <1>;
> + };
> + };
> --
> 2.25.1
>
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #2: Type: text/plain, Size: 112 bytes --]
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: mediatek: xsphy: reduce main allocation
From: Vinod Koul @ 2026-05-14 16:18 UTC (permalink / raw)
To: linux-phy, Rosen Penev
Cc: Chunfeng Yun, Neil Armstrong, Matthias Brugger,
AngeloGioacchino Del Regno, Kees Cook, Gustavo A. R. Silva,
linux-arm-kernel, linux-mediatek, linux-kernel, linux-hardening
In-Reply-To: <20260304043420.14151-1-rosenp@gmail.com>
On Tue, 03 Mar 2026 20:34:20 -0800, Rosen Penev wrote:
> Instead of kzalloc and kcalloc, we can use a flex array to reduce to a
> single allocation.
>
> Also added __counted_by() for extra possible analysis.
>
>
Applied, thanks!
[1/1] phy: mediatek: xsphy: reduce main allocation
commit: fd6cd05ceabdf67635a4cef5145f79d1217bf11b
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: qcom-usb-hs: use flexible array member
From: Vinod Koul @ 2026-05-14 16:18 UTC (permalink / raw)
To: linux-phy, Rosen Penev; +Cc: Neil Armstrong, linux-arm-msm, linux-kernel
In-Reply-To: <20260315214930.4621-1-rosenp@gmail.com>
On Sun, 15 Mar 2026 14:49:30 -0700, Rosen Penev wrote:
> Simplify allocation by removing kmalloc_array and just doing kzalloc.
>
>
Applied, thanks!
[1/1] phy: qcom-usb-hs: use flexible array member
commit: deb281cec3cbef5f02c6bc29fa5cd51f6d5be10f
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3 0/2] phy: econet: Add PCIe PHY driver for EcoNet SoCs
From: Vinod Koul @ 2026-05-14 16:18 UTC (permalink / raw)
To: linux-phy, Caleb James DeLisle
Cc: naseefkm, neil.armstrong, robh, krzk+dt, conor+dt, linux-mips,
devicetree, linux-kernel
In-Reply-To: <20260425173642.406089-1-cjd@cjdns.fr>
On Sat, 25 Apr 2026 17:36:40 +0000, Caleb James DeLisle wrote:
> I just added explicit includes per the recommendation of
> vladimiroltean/sashiko
> See https://sashiko.dev/#/patchset/20260404184918.2184070-1-cjd@cjdns.fr
>
> Changes from v2:
> * Added includes per recommendation of vladimiroltean/sashiko
> * Rebased on commit 7080e32d3f09 ("Add linux-next specific files for 20260424")
> * v2: https://lore.kernel.org/linux-mips/20260404184918.2184070-1-cjd@cjdns.fr/
>
> [...]
Applied, thanks!
[1/2] dt-bindings: phy: Document PCIe PHY in EcoNet EN751221 and EN7528
commit: 2cc066dbd3a211628b40967949cff2790d0f62eb
[2/2] phy: econet: Add PCIe PHY driver for EcoNet EN751221 and EN7528 SoCs.
commit: da1af49257591190ad6521abb3198f1f40420407
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 0/5] rockchip: Add USB 2.0 support for RK3528
From: Vinod Koul @ 2026-05-14 16:17 UTC (permalink / raw)
To: Heiko Stuebner
Cc: neil.armstrong, robh, krzk+dt, conor+dt, linux-phy, devicetree,
linux-arm-kernel, linux-rockchip, linux-kernel, jonas
In-Reply-To: <20260505170410.3265305-1-heiko@sntech.de>
On Tue, 05 May 2026 19:04:05 +0200, Heiko Stuebner wrote:
> This series adds support for USB 2.0 on Rockchip RK3528 to the
> Innosilicon usbphy driver.
>
> The usb3 support has already been merge for the naneng combophy
> last year.
>
> Changes in v2:
> - rebase on top of v7.1-rc1
> - split off dwc3 compatible and dts changes
> - add error handling to regmap_write (Vinod)
> - v1 is here
> https://lore.kernel.org/linux-rockchip/20250723122323.2344916-1-jonas@kwiboo.se/
>
> [...]
Applied, thanks!
[1/5] dt-bindings: phy: rockchip,inno-usb2phy: Require GRF for RK3568/RV1108
commit: a896852613136e6babe6036567ab0989fb322a57
[2/5] phy: rockchip: inno-usb2: Simplify rockchip,usbgrf handling
commit: be29cd958f5393004a24cd7b5b1da88dd90a651b
[3/5] dt-bindings: phy: rockchip,inno-usb2phy: Add compatible for RK3528
commit: c430f042f4ba2c5abd592b6924f028d3bb09d4cf
[4/5] phy: rockchip: inno-usb2: Add clkout_ctl_phy support
commit: 2775541de0580ab1cd077dfef710e6316563d567
[5/5] phy: rockchip: inno-usb2: Add support for RK3528
commit: 864b3617df827865a95a06f06f09a8d57a795b91
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v5 0/6] phy: phy-can-transceiver: Ad-hoc cleanups and refactoring
From: Vinod Koul @ 2026-05-14 16:17 UTC (permalink / raw)
To: Peng Fan, linux-can, linux-phy, linux-kernel, Andy Shevchenko
Cc: Marc Kleine-Budde, Vincent Mailhol, Neil Armstrong, Josua Mayer,
Ulf Hansson
In-Reply-To: <20260513220336.369628-1-andriy.shevchenko@linux.intel.com>
On Thu, 14 May 2026 00:01:25 +0200, Andy Shevchenko wrote:
> The driver does two things that need to be addressed:
> - includes subject to remove gpio.h
> - checks for error code from device property APIs when it can be done in
> a robust way
>
> This series addresses the above and adds a couple of additional refactoring.
>
> [...]
Applied, thanks!
[1/6] phy: phy-can-transceiver: Check driver match and driver data against NULL
commit: ebee9004cc0200b2b708ebf7ac625d35c71c049f
[2/6] phy: phy-can-transceiver: use device_get_match_data()
commit: 23db9fd578ca3b446ceaa5c9a0157f0838f4df4e
[3/6] phy: phy-can-transceiver: Move OF ID table closer to their user
commit: 62455f6be1256084cfff8690df416f418b6f0dd2
[4/6] phy: phy-can-transceiver: Don't check for specific errors when parsing properties
commit: 79a5274fb39904f8a60bdd7bf7753ee1ba700210
[5/6] phy: phy-can-transceiver: Decouple assignment and definition in probe
commit: 05c72fbff4ac18e9bbb0e4b3884dad1f833807f4
[6/6] phy: phy-can-transceiver: Drop unused include
commit: 52ae64602394bc9a8e7b67f5e4e70c56e31699a7
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH phy-next 0/5] Lynx 28G SerDes: 25GbE support
From: Vinod Koul @ 2026-05-14 16:17 UTC (permalink / raw)
To: linux-phy, Vladimir Oltean
Cc: netdev, Ioana Ciornei, Neil Armstrong, Josua Mayer, linux-kernel
In-Reply-To: <20260511150023.1903577-1-vladimir.oltean@nxp.com>
On Mon, 11 May 2026 18:00:18 +0300, Vladimir Oltean wrote:
> This is the remainder of "Lynx 28G improvements part 2", previously
> submitted here:
> https://lore.kernel.org/linux-phy/176850672122.1082429.444623229961712368.robh@kernel.org/
>
> but split up into smaller portions (merged separately):
> - https://lore.kernel.org/linux-phy/20260226182853.1103616-1-vladimir.oltean@nxp.com/
> - https://lore.kernel.org/linux-phy/20260321011451.1557091-1-vladimir.oltean@nxp.com/
>
> [...]
Applied, thanks!
[1/5] dt-bindings: phy: lynx-28g: add compatible strings per SerDes and instantiation
commit: 1561f12db08b1da1fe47ce9a5beff11adfd90ac8
[2/5] dt-bindings: phy: lynx-28g: add constraint on LX2162A lane indices
commit: 37056ea140242681ccf706f63ad8c7366d89f5e3
[3/5] phy: lynx-28g: require an OF node to probe
commit: c9d80e861034e2f3618065dc02b1de828aed24f6
[4/5] phy: lynx-28g: probe on per-SoC and per-instance compatible strings
commit: 8c57247f5d9734d617862d320bd3894eec898298
[5/5] phy: lynx-28g: add support for 25GBASER
commit: 1cab8fba5073962712fae3b763417af95ccf476a
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 4/5] phy: rockchip: inno-usb2: Add clkout_ctl_phy support
From: Vinod Koul @ 2026-05-14 16:17 UTC (permalink / raw)
To: Heiko Stuebner
Cc: neil.armstrong, robh, krzk+dt, conor+dt, linux-phy, devicetree,
linux-arm-kernel, linux-rockchip, linux-kernel, jonas
In-Reply-To: <20260505170410.3265305-5-heiko@sntech.de>
On 05-05-26, 19:04, Heiko Stuebner wrote:
> From: Jonas Karlman <jonas@kwiboo.se>
>
> The 480m clk is controlled using regs in the PHY address space and not
> in the USB GRF address space on e.g. RK3528 and RK3506.
>
> Add a clkout_ctl_phy usb2phy_reg to handle enable/disable of the 480m
> clk on these SoCs.
>
> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
> drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 47 +++++++++++++++----
> 1 file changed, 38 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
> index 7cec45192393..d8879fcd4291 100644
> --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
> +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
> @@ -179,6 +179,7 @@ struct rockchip_usb2phy_cfg {
> unsigned int num_ports;
> int (*phy_tuning)(struct rockchip_usb2phy *rphy);
> struct usb2phy_reg clkout_ctl;
> + struct usb2phy_reg clkout_ctl_phy;
This gives a kernel-doc warning. I have applied please send fix for this
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v3] phy: apple: atc: Fix typec switch/mux leak on unbind
From: Vinod Koul @ 2026-05-14 16:08 UTC (permalink / raw)
To: sven, j, neal, neil.armstrong, marcan, p.zabel, David Carlier
Cc: olteanv, asahi, linux-arm-kernel, linux-phy, linux-kernel
In-Reply-To: <20260508201958.30060-1-devnexen@gmail.com>
On Fri, 08 May 2026 21:19:58 +0100, David Carlier wrote:
> atcphy_probe_switch() and atcphy_probe_mux() discard the pointers
> returned by typec_switch_register() and typec_mux_register(). The
> platform driver has no .remove callback, so when the driver unbinds
> (e.g. via sysfs unbind) neither typec_switch_unregister() nor
> typec_mux_unregister() is called. The framework reference taken in
> typec_switch_register() (device_initialize() + device_add() in
> drivers/usb/typec/mux.c) is therefore never dropped and the
> typec_switch_dev / typec_mux_dev objects stay live forever, with
> their sysfs entries under the typec_mux class also left behind. A
> subsequent rebind cannot recreate them with the same fwnode-derived
> name.
>
> [...]
Applied, thanks!
[1/1] phy: apple: atc: Fix typec switch/mux leak on unbind
commit: 1854082fe0ddb81bc93d1f8e8a00554217fd09d1
Best regards,
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH] phy: google-usb: Fix runtime PM cleanup on probe failure
From: Vinod Koul @ 2026-05-14 15:49 UTC (permalink / raw)
To: Felix Gu
Cc: Peter Griffin, André Draszik, Tudor Ambarus, Neil Armstrong,
Naveen Kumar, Joy Chakraborty, Roy Luo, linux-arm-kernel,
linux-samsung-soc, linux-phy, linux-kernel
In-Reply-To: <20260316-google-v1-1-30a034e87a2a@gmail.com>
On 16-03-26, 21:11, Felix Gu wrote:
> In google_usb_phy_probe(), if typec_switch_register() fails,
> pm_runtime_disable() is not called, leading to an unbalanced
> runtime PM state on probe failure.
>
> Switch to devm_pm_runtime_enable() to ensure that runtime PM is
> automatically disabled both on probe failure and during driver
> removal.
PLease see https://sashiko.dev/#/patchset/20260316-google-v1-1-30a034e87a2a%40gmail.com
>
> Fixes: cbce66669c82 ("phy: Add Google Tensor SoC USB PHY driver")
> Signed-off-by: Felix Gu <ustc.gu@gmail.com>
> ---
> drivers/phy/phy-google-usb.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/phy/phy-google-usb.c b/drivers/phy/phy-google-usb.c
> index ab20bc20f19e..79480f8b110e 100644
> --- a/drivers/phy/phy-google-usb.c
> +++ b/drivers/phy/phy-google-usb.c
> @@ -251,7 +251,9 @@ static int google_usb_phy_probe(struct platform_device *pdev)
> return dev_err_probe(dev, PTR_ERR(phy_provider),
> "failed to register phy provider\n");
>
> - pm_runtime_enable(dev);
> + ret = devm_pm_runtime_enable(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
>
> sw_desc.fwnode = dev_fwnode(dev);
> sw_desc.drvdata = gphy;
> @@ -271,7 +273,6 @@ static void google_usb_phy_remove(struct platform_device *pdev)
> struct google_usb_phy *gphy = dev_get_drvdata(&pdev->dev);
>
> typec_switch_unregister(gphy->sw);
> - pm_runtime_disable(&pdev->dev);
> }
>
> static const struct of_device_id google_usb_phy_of_match[] = {
>
> ---
> base-commit: b84a0ebe421ca56995ff78b66307667b62b3a900
> change-id: 20260316-google-3acfa2f3de9d
>
> Best regards,
> --
> Felix Gu <ustc.gu@gmail.com>
--
~Vinod
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH v9 1/5] drm/msm/hdmi: switch to generic PHY subsystem
From: Dmitry Baryshkov @ 2026-05-13 18:14 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter,
Vinod Koul, Neil Armstrong
Cc: linux-kernel, linux-arm-msm, dri-devel, freedreno, linux-phy,
Dmitry Baryshkov, Konrad Dybcio
In-Reply-To: <20260513-fd-hdmi-phy-v9-0-ca98c72f1f9f@oss.qualcomm.com>
From: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
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 <dmitry.baryshkov@linaro.org>
Acked-by: Konrad Dybcio <konrad.dybcio@linaro.org>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/Makefile | 7 -
drivers/gpu/drm/msm/hdmi/hdmi.c | 59 +-
drivers/gpu/drm/msm/hdmi/hdmi.h | 83 +--
drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 88 ++-
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/registers/display/hdmi.xml | 537 ---------------
drivers/phy/qualcomm/Kconfig | 24 +
drivers/phy/qualcomm/Makefile | 14 +
drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c | 71 ++
.../qualcomm/phy-qcom-hdmi-28lpm.c} | 430 ++++++++----
drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c | 186 +++++
drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c | 211 ++++++
drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h | 59 ++
drivers/phy/qualcomm/phy-qcom-qmp-hdmi-base.c | 185 +++++
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 ++
22 files changed, 2063 insertions(+), 2857 deletions(-)
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index ba45e99be05b..01b6ebb1086f 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -37,13 +37,6 @@ msm-display-$(CONFIG_DRM_MSM_HDMI) += \
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 \
msm-display-$(CONFIG_DRM_MSM_MDP4) += \
disp/mdp4/mdp4_crtc.o \
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index d9491aac1a89..fd71da9a8782 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -10,6 +10,7 @@
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_of.h>
@@ -76,44 +77,6 @@ static void msm_hdmi_destroy(struct hdmi *hdmi)
msm_hdmi_i2c_destroy(hdmi->i2c);
}
-static void msm_hdmi_put_phy(struct hdmi *hdmi)
-{
- if (hdmi->phy_dev) {
- put_device(hdmi->phy_dev);
- hdmi->phy = NULL;
- hdmi->phy_dev = NULL;
- }
-}
-
-static int msm_hdmi_get_phy(struct hdmi *hdmi)
-{
- struct platform_device *pdev = hdmi->pdev;
- struct platform_device *phy_pdev;
- struct device_node *phy_node;
-
- phy_node = 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 = 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 = 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 = &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
@@ -353,34 +316,30 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
if (hdmi->hpd_gpiod)
gpiod_set_consumer_name(hdmi->hpd_gpiod, "HDMI_HPD");
- ret = msm_hdmi_get_phy(hdmi);
- if (ret)
- return ret;
+ hdmi->phy = devm_phy_get(dev, NULL);
+ if (IS_ERR(hdmi->phy))
+ return dev_err_probe(dev, PTR_ERR(hdmi->phy),
+ "failed to get phy\n");
ret = devm_pm_runtime_enable(dev);
if (ret)
- goto err_put_phy;
+ goto err;
platform_set_drvdata(pdev, hdmi);
ret = component_add(dev, &msm_hdmi_ops);
if (ret)
- goto err_put_phy;
+ goto err;
return 0;
-err_put_phy:
- msm_hdmi_put_phy(hdmi);
+err:
return ret;
}
static void msm_hdmi_dev_remove(struct platform_device *pdev)
{
- struct hdmi *hdmi = dev_get_drvdata(&pdev->dev);
-
component_del(&pdev->dev, &msm_hdmi_ops);
-
- msm_hdmi_put_phy(hdmi);
}
static int msm_hdmi_runtime_suspend(struct device *dev)
@@ -449,12 +408,10 @@ static struct platform_driver msm_hdmi_driver = {
void __init msm_hdmi_register(void)
{
- msm_hdmi_phy_driver_register();
platform_driver_register(&msm_hdmi_driver);
}
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/hdmi.h
index 49433f7727c3..5857b5fa8ece 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"
-struct hdmi_phy;
struct hdmi_platform_config;
struct hdmi_audio {
@@ -41,8 +40,9 @@ struct hdmi {
/* video state: */
bool power_on;
+ bool phy_power_on;
bool hpd_enabled;
- struct mutex state_mutex; /* protects two booleans */
+ struct mutex state_mutex; /* protects the booleans */
unsigned long pixclock;
void __iomem *mmio;
@@ -55,8 +55,7 @@ struct hdmi {
struct gpio_desc *hpd_gpiod;
- struct hdmi_phy *phy;
- struct device *phy_dev;
+ struct phy *phy;
struct i2c_adapter *i2c;
struct drm_connector *connector;
@@ -117,82 +116,6 @@ static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg)
return readl(hdmi->qfprom_mmio + reg);
}
-/*
- * 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/hdmi/hdmi_bridge.c
index c4d5e0faf3b3..42eafc2bde00 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
@@ -5,6 +5,7 @@
*/
#include <linux/delay.h>
+#include <linux/phy/phy.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_edid.h>
#include <drm/display/drm_hdmi_helper.h>
@@ -13,43 +14,6 @@
#include "msm_kms.h"
#include "hdmi.h"
-static void msm_hdmi_power_on(struct drm_bridge *bridge)
-{
- struct drm_device *dev = bridge->dev;
- struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
- struct hdmi *hdmi = hdmi_bridge->hdmi;
- int ret;
-
- pm_runtime_resume_and_get(&hdmi->pdev->dev);
-
- if (hdmi->extp_clk) {
- DBG("pixclock: %lu", hdmi->pixclock);
- ret = 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 = 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 = to_hdmi_bridge(bridge);
- struct hdmi *hdmi = 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 = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
- struct hdmi_phy *phy = hdmi->phy;
struct drm_encoder *encoder = bridge->encoder;
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_crtc_state *crtc_state;
+ union phy_configure_opts phy_opts = {};
+ int ret;
DBG("power up");
@@ -305,18 +270,38 @@ static void msm_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
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 = true;
}
- mutex_unlock(&hdmi->state_mutex);
if (connector->display_info.is_hdmi)
msm_hdmi_audio_update(hdmi);
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
- msm_hdmi_phy_powerup(phy, hdmi->pixclock);
+ phy_opts.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+ phy_opts.hdmi.bpc = 8;
+ phy_configure(hdmi->phy, &phy_opts);
+
+ ret = phy_power_on(hdmi->phy);
+ if (WARN_ON(ret)) {
+ mutex_unlock(&hdmi->state_mutex);
+ return;
+ }
+
+ hdmi->phy_power_on = true;
+ mutex_unlock(&hdmi->state_mutex);
+
+ if (hdmi->extp_clk) {
+ ret = clk_set_rate(hdmi->extp_clk, hdmi->pixclock);
+ if (ret)
+ DRM_DEV_ERROR(bridge->dev->dev, "failed to set extp clk rate: %d\n", ret);
+
+ ret = clk_prepare_enable(hdmi->extp_clk);
+ if (ret)
+ DRM_DEV_ERROR(bridge->dev->dev, "failed to enable extp clk: %d\n", ret);
+ }
msm_hdmi_set_mode(hdmi, true);
@@ -329,7 +314,6 @@ static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge,
{
struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
struct hdmi *hdmi = hdmi_bridge->hdmi;
- struct hdmi_phy *phy = hdmi->phy;
if (hdmi->hdcp_ctrl)
msm_hdmi_hdcp_off(hdmi->hdcp_ctrl);
@@ -340,14 +324,26 @@ static void msm_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge,
mutex_lock(&hdmi->state_mutex);
msm_hdmi_set_mode(hdmi, hdmi->hpd_enabled);
- 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);
+
+ if (hdmi->phy_power_on)
+ phy_power_off(hdmi->phy);
+
+ hdmi->phy_power_on = false;
if (hdmi->power_on) {
- power_off(bridge);
+ pm_runtime_put_sync(&hdmi->pdev->dev);
+
hdmi->power_on = 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 <linux/of.h>
-#include <linux/platform_device.h>
-
-#include "hdmi.h"
-
-static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
-{
- struct hdmi_phy_cfg *cfg = phy->cfg;
- struct device *dev = &phy->pdev->dev;
- int i, ret;
-
- phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]),
- GFP_KERNEL);
- if (!phy->regs)
- return -ENOMEM;
-
- phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]),
- GFP_KERNEL);
- if (!phy->clks)
- return -ENOMEM;
-
- for (i = 0; i < cfg->num_regs; i++)
- phy->regs[i].supply = cfg->reg_names[i];
-
- ret = devm_regulator_bulk_get(dev, cfg->num_regs, phy->regs);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- DRM_DEV_ERROR(dev, "failed to get phy regulators: %d\n", ret);
-
- return ret;
- }
-
- for (i = 0; i < cfg->num_clks; i++) {
- struct clk *clk;
-
- clk = msm_clk_get(phy->pdev, cfg->clk_names[i]);
- if (IS_ERR(clk)) {
- ret = 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] = clk;
- }
-
- return 0;
-}
-
-int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
-{
- struct hdmi_phy_cfg *cfg = phy->cfg;
- struct device *dev = &phy->pdev->dev;
- int i, ret = 0;
-
- ret = pm_runtime_resume_and_get(dev);
- if (ret) {
- DRM_DEV_ERROR(dev, "runtime resume failed: %d\n", ret);
- return ret;
- }
-
- ret = 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 = 0; i < cfg->num_clks; i++) {
- ret = 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 = phy->cfg;
- struct device *dev = &phy->pdev->dev;
- int i;
-
- for (i = cfg->num_clks - 1; i >= 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 = msm_hdmi_pll_8960_init(pdev);
- break;
- case MSM_HDMI_PHY_8996:
- ret = msm_hdmi_pll_8996_init(pdev);
- break;
- case MSM_HDMI_PHY_8998:
- ret = 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 = 0;
- break;
- }
-
- return ret;
-}
-
-static int msm_hdmi_phy_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hdmi_phy *phy;
- int ret;
-
- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
- if (!phy)
- return -ENODEV;
-
- phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
- if (!phy->cfg)
- return -ENODEV;
-
- phy->mmio = 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 = pdev;
-
- ret = msm_hdmi_phy_resource_init(phy);
- if (ret)
- return ret;
-
- pm_runtime_enable(&pdev->dev);
-
- ret = msm_hdmi_phy_resource_enable(phy);
- if (ret)
- return ret;
-
- ret = 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[] = {
- { .compatible = "qcom,hdmi-phy-8660",
- .data = &msm_hdmi_phy_8x60_cfg },
- { .compatible = "qcom,hdmi-phy-8960",
- .data = &msm_hdmi_phy_8960_cfg },
- { .compatible = "qcom,hdmi-phy-8974",
- .data = &msm_hdmi_phy_8x74_cfg },
- { .compatible = "qcom,hdmi-phy-8084",
- .data = &msm_hdmi_phy_8x74_cfg },
- { .compatible = "qcom,hdmi-phy-8996",
- .data = &msm_hdmi_phy_8996_cfg },
- { .compatible = "qcom,hdmi-phy-8998",
- .data = &msm_hdmi_phy_8998_cfg },
- {}
-};
-MODULE_DEVICE_TABLE(of, msm_hdmi_phy_dt_match);
-
-static struct platform_driver msm_hdmi_phy_platform_driver = {
- .probe = msm_hdmi_phy_probe,
- .remove = msm_hdmi_phy_remove,
- .driver = {
- .name = "msm_hdmi_phy",
- .of_match_table = 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 <robdclark@gmail.com>
- */
-
-#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[] = {
- "core-vdda",
-};
-
-static const char * const hdmi_phy_8960_clk_names[] = {
- "slave_iface",
-};
-
-const struct hdmi_phy_cfg msm_hdmi_phy_8960_cfg = {
- .type = MSM_HDMI_PHY_8960,
- .powerup = hdmi_phy_8960_powerup,
- .powerdown = hdmi_phy_8960_powerdown,
- .reg_names = hdmi_phy_8960_reg_names,
- .num_regs = ARRAY_SIZE(hdmi_phy_8960_reg_names),
- .clk_names = hdmi_phy_8960_clk_names,
- .num_clks = 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 <linux/clk-provider.h>
-#include <linux/delay.h>
-
-#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 channel,
- 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 != 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 != 0) || gen_ssc)
- return 0x16;
-
- return 0x10;
-}
-
-static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc)
-{
- if ((frac_start != 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 = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;
- u64 base;
-
- if ((frac_start != 0) || gen_ssc)
- base = (64 * ref_clk) / HDMI_DEFAULT_REF_CLOCK;
- else
- base = (1022 * ref_clk) / 100;
-
- base <<= digclk_divsel;
-
- return (base <= 2046 ? base : 2046);
-}
-
-static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)
-{
- u64 dividend = HDMI_PLL_CMP_CNT * fdata;
- u32 divisor = ref_clk * 10;
- u32 rem;
-
- rem = 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 = ((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[] = { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 };
- int hs_divsel[] = { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 };
- int tx_band_sel[] = { 0, 1, 2, 3 };
- u64 vco_freq[60];
- u64 vco, vco_optimal;
- int half_rate_mode = 0;
- int vco_optimal_index, vco_freq_index;
- int i, j;
-
-retry:
- vco_optimal = HDMI_VCO_MAX_FREQ;
- vco_optimal_index = -1;
- vco_freq_index = 0;
- for (i = 0; i < 15; i++) {
- for (j = 0; j < 4; j++) {
- u32 ratio_mult = ratio[i] << tx_band_sel[j];
-
- vco = bclk >> half_rate_mode;
- vco *= ratio_mult;
- vco_freq[vco_freq_index++] = vco;
- }
- }
-
- for (i = 0; i < 60; i++) {
- u64 vco_tmp = vco_freq[i];
-
- if ((vco_tmp >= HDMI_VCO_MIN_FREQ) &&
- (vco_tmp <= vco_optimal)) {
- vco_optimal = vco_tmp;
- vco_optimal_index = i;
- }
- }
-
- if (vco_optimal_index == -1) {
- if (!half_rate_mode) {
- half_rate_mode = 1;
- goto retry;
- }
- } else {
- pd->vco_freq = vco_optimal;
- pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4];
- pd->vco_ratio = ratio[vco_optimal_index / 4];
- pd->hsclk_divsel = 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 = 10 * pix_clk */
- bclk = ((u64)pix_clk) * 10;
-
- if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD)
- tmds_clk = pix_clk >> 2;
- else
- tmds_clk = pix_clk;
-
- ret = pll_get_post_div(&pd, bclk);
- if (ret)
- return ret;
-
- dec_start = pd.vco_freq;
- pll_divisor = 4 * ref_clk;
- do_div(dec_start, pll_divisor);
-
- frac_start = pd.vco_freq * (1 << 20);
-
- rem = do_div(frac_start, pll_divisor);
- frac_start -= dec_start * (1 << 20);
- if (rem > (pll_divisor >> 1))
- frac_start++;
-
- cpctrl = pll_get_cpctrl(frac_start, ref_clk, false);
- rctrl = pll_get_rctrl(frac_start, false);
- cctrl = pll_get_cctrl(frac_start, false);
- integloop_gain = pll_get_integloop_gain(frac_start, bclk,
- ref_clk, false);
-
- fdata = pd.vco_freq;
- do_div(fdata, pd.vco_ratio);
-
- pll_cmp = 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 = 1;
- else
- cfg->com_svs_mode_clk_sel = 2;
-
- cfg->com_hsclk_sel = (0x20 | pd.hsclk_divsel);
- cfg->com_pll_cctrl_mode0 = cctrl;
- cfg->com_pll_rctrl_mode0 = rctrl;
- cfg->com_cp_ctrl_mode0 = cpctrl;
- cfg->com_dec_start_mode0 = dec_start;
- cfg->com_div_frac_start1_mode0 = (frac_start & 0xff);
- cfg->com_div_frac_start2_mode0 = ((frac_start & 0xff00) >> 8);
- cfg->com_div_frac_start3_mode0 = ((frac_start & 0xf0000) >> 16);
- cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xff);
- cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xf00) >> 8);
- cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xff);
- cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xff00) >> 8);
- cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
- cfg->com_lock_cmp_en = 0x0;
- cfg->com_core_clk_en = 0x2c;
- cfg->com_coreclk_div = HDMI_CORECLK_DIV;
- cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0;
- cfg->com_vco_tune_ctrl = 0x0;
-
- cfg->tx_lx_lane_mode[0] =
- cfg->tx_lx_lane_mode[2] = 0x43;
-
- cfg->tx_lx_hp_pd_enables[0] =
- cfg->tx_lx_hp_pd_enables[1] =
- cfg->tx_lx_hp_pd_enables[2] = 0x0c;
- cfg->tx_lx_hp_pd_enables[3] = 0x3;
-
- for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++)
- cfg->tx_lx_tx_band[i] = pd.tx_band_sel + 4;
-
- if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
- cfg->tx_lx_tx_drv_lvl[0] =
- cfg->tx_lx_tx_drv_lvl[1] =
- cfg->tx_lx_tx_drv_lvl[2] = 0x25;
- cfg->tx_lx_tx_drv_lvl[3] = 0x22;
-
- cfg->tx_lx_tx_emp_post1_lvl[0] =
- cfg->tx_lx_tx_emp_post1_lvl[1] =
- cfg->tx_lx_tx_emp_post1_lvl[2] = 0x23;
- cfg->tx_lx_tx_emp_post1_lvl[3] = 0x27;
-
- cfg->tx_lx_vmode_ctrl1[0] =
- cfg->tx_lx_vmode_ctrl1[1] =
- cfg->tx_lx_vmode_ctrl1[2] =
- cfg->tx_lx_vmode_ctrl1[3] = 0x00;
-
- cfg->tx_lx_vmode_ctrl2[0] =
- cfg->tx_lx_vmode_ctrl2[1] =
- cfg->tx_lx_vmode_ctrl2[2] = 0x0D;
-
- cfg->tx_lx_vmode_ctrl2[3] = 0x00;
- } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
- for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
- cfg->tx_lx_tx_drv_lvl[i] = 0x25;
- cfg->tx_lx_tx_emp_post1_lvl[i] = 0x23;
- cfg->tx_lx_vmode_ctrl1[i] = 0x00;
- }
-
- cfg->tx_lx_vmode_ctrl2[0] =
- cfg->tx_lx_vmode_ctrl2[1] =
- cfg->tx_lx_vmode_ctrl2[2] = 0x0D;
- cfg->tx_lx_vmode_ctrl2[3] = 0x00;
- } else {
- for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
- cfg->tx_lx_tx_drv_lvl[i] = 0x20;
- cfg->tx_lx_tx_emp_post1_lvl[i] = 0x20;
- cfg->tx_lx_vmode_ctrl1[i] = 0x00;
- cfg->tx_lx_vmode_ctrl2[i] = 0x0E;
- }
- }
-
- DBG("com_svs_mode_clk_sel = 0x%x", cfg->com_svs_mode_clk_sel);
- DBG("com_hsclk_sel = 0x%x", cfg->com_hsclk_sel);
- DBG("com_lock_cmp_en = 0x%x", cfg->com_lock_cmp_en);
- DBG("com_pll_cctrl_mode0 = 0x%x", cfg->com_pll_cctrl_mode0);
- DBG("com_pll_rctrl_mode0 = 0x%x", cfg->com_pll_rctrl_mode0);
- DBG("com_cp_ctrl_mode0 = 0x%x", cfg->com_cp_ctrl_mode0);
- DBG("com_dec_start_mode0 = 0x%x", cfg->com_dec_start_mode0);
- DBG("com_div_frac_start1_mode0 = 0x%x", cfg->com_div_frac_start1_mode0);
- DBG("com_div_frac_start2_mode0 = 0x%x", cfg->com_div_frac_start2_mode0);
- DBG("com_div_frac_start3_mode0 = 0x%x", cfg->com_div_frac_start3_mode0);
- DBG("com_integloop_gain0_mode0 = 0x%x", cfg->com_integloop_gain0_mode0);
- DBG("com_integloop_gain1_mode0 = 0x%x", cfg->com_integloop_gain1_mode0);
- DBG("com_lock_cmp1_mode0 = 0x%x", cfg->com_lock_cmp1_mode0);
- DBG("com_lock_cmp2_mode0 = 0x%x", cfg->com_lock_cmp2_mode0);
- DBG("com_lock_cmp3_mode0 = 0x%x", cfg->com_lock_cmp3_mode0);
- DBG("com_core_clk_en = 0x%x", cfg->com_core_clk_en);
- DBG("com_coreclk_div = 0x%x", cfg->com_coreclk_div);
- DBG("phy_mode = 0x%x", cfg->phy_mode);
-
- DBG("tx_l0_lane_mode = 0x%x", cfg->tx_lx_lane_mode[0]);
- DBG("tx_l2_lane_mode = 0x%x", cfg->tx_lx_lane_mode[2]);
-
- for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
- DBG("tx_l%d_tx_band = 0x%x", i, cfg->tx_lx_tx_band[i]);
- DBG("tx_l%d_tx_drv_lvl = 0x%x", i, cfg->tx_lx_tx_drv_lvl[i]);
- DBG("tx_l%d_tx_emp_post1_lvl = 0x%x", i,
- cfg->tx_lx_tx_emp_post1_lvl[i]);
- DBG("tx_l%d_vmode_ctrl1 = 0x%x", i, cfg->tx_lx_vmode_ctrl1[i]);
- DBG("tx_l%d_vmode_ctrl2 = 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 rate,
- unsigned long parent_rate)
-{
- struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = pll_get_phy(pll);
- struct hdmi_8996_phy_pll_reg_cfg cfg;
- int i, ret;
-
- memset(&cfg, 0x00, sizeof(cfg));
-
- ret = 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 = 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 = 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 = HDMI_PLL_POLL_MAX_READS;
- unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US;
- u32 status;
- int phy_ready = 0;
-
- DBG("Waiting for PHY ready");
-
- while (nb_tries--) {
- status = hdmi_phy_read(phy, REG_HDMI_8996_PHY_STATUS);
- phy_ready = 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 = HDMI_PLL_POLL_MAX_READS;
- unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US;
- int pll_locked = 0;
-
- DBG("Waiting for PLL lock");
-
- while (nb_tries--) {
- status = hdmi_pll_read(pll,
- REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS);
- pll_locked = 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 = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = pll_get_phy(pll);
- int i, ret = 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 = hdmi_8996_pll_lock_status(pll);
- if (!ret)
- return ret;
-
- for (i = 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 = 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 = 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 = hw_clk_to_pll(hw);
- u64 fdata;
- u32 cmp1, cmp2, cmp3, pll_cmp;
-
- cmp1 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0);
- cmp2 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0);
- cmp3 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0);
-
- pll_cmp = cmp1 | (cmp2 << 8) | (cmp3 << 16);
-
- fdata = 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 = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = 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 = hw_clk_to_pll(hw);
- u32 status;
- int pll_locked;
-
- status = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS);
- pll_locked = status & BIT(0);
-
- return pll_locked;
-}
-
-static const struct clk_ops hdmi_8996_pll_ops = {
- .set_rate = hdmi_8996_pll_set_clk_rate,
- .determine_rate = hdmi_8996_pll_determine_rate,
- .recalc_rate = hdmi_8996_pll_recalc_rate,
- .prepare = hdmi_8996_pll_prepare,
- .unprepare = hdmi_8996_pll_unprepare,
- .is_enabled = hdmi_8996_pll_is_enabled,
-};
-
-static const struct clk_init_data pll_init = {
- .name = "hdmipll",
- .ops = &hdmi_8996_pll_ops,
- .parent_data = (const struct clk_parent_data[]){
- { .fw_name = "xo", .name = "xo_board" },
- },
- .num_parents = 1,
- .flags = CLK_IGNORE_UNUSED,
-};
-
-int msm_hdmi_pll_8996_init(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hdmi_pll_8996 *pll;
- int i, ret;
-
- pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return -ENOMEM;
-
- pll->pdev = pdev;
-
- pll->mmio_qserdes_com = 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 = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
- char name[32];
-
- snprintf(name, sizeof(name), "hdmi_tx_l%d", i);
-
- pll->mmio_qserdes_tx[i] = 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 = &pll_init;
-
- ret = devm_clk_hw_register(dev, &pll->clk_hw);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to register pll clock\n");
- return ret;
- }
-
- ret = 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[] = {
- "vddio",
- "vcca",
-};
-
-static const char * const hdmi_phy_8996_clk_names[] = {
- "iface", "ref",
-};
-
-const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg = {
- .type = MSM_HDMI_PHY_8996,
- .reg_names = hdmi_phy_8996_reg_names,
- .num_regs = ARRAY_SIZE(hdmi_phy_8996_reg_names),
- .clk_names = hdmi_phy_8996_clk_names,
- .num_clks = 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 <linux/clk-provider.h>
-#include <linux/delay.h>
-
-#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 channel,
- 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 != 0) || gen_ssc)
- return 0x8;
-
- return 0x30;
-}
-
-static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc)
-{
- if ((frac_start != 0) || gen_ssc)
- return 0x16;
-
- return 0x18;
-}
-
-static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc)
-{
- if ((frac_start != 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 = bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2;
- u64 base;
-
- if ((frac_start != 0) || gen_ssc)
- base = 0x3F;
- else
- base = 0xC4;
-
- base <<= (digclk_divsel == 2 ? 1 : 0);
-
- return base;
-}
-
-static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)
-{
- u64 dividend = HDMI_PLL_CMP_CNT * fdata;
- u32 divisor = ref_clk * 10;
- u32 rem;
-
- rem = 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[] = {1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 25};
- static const u32 band_list[] = {0, 1, 2, 3};
- u32 const sz_ratio = ARRAY_SIZE(ratio_list);
- u32 const sz_band = ARRAY_SIZE(band_list);
- u32 const cmp_cnt = 1024;
- u32 const th_min = 500, th_max = 1000;
- u32 half_rate_mode = 0;
- u32 list_elements;
- int optimal_index;
- u32 i, j, k;
- u32 found_hsclk_divsel = 0, found_vco_ratio;
- u32 found_tx_band_sel;
- u64 const min_freq = HDMI_VCO_MIN_FREQ, max_freq = 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 = max_freq;
- optimal_index = -1;
- list_elements = 0;
-
- for (i = 0; i < sz_ratio; i++) {
- for (j = 0; j < sz_band; j++) {
- u64 freq = div_u64(bclk, (1 << half_rate_mode));
-
- freq *= (ratio_list[i] * (1 << band_list[j]));
- freq_list[list_elements++] = freq;
- }
- }
-
- for (k = 0; k < ARRAY_SIZE(freq_list); k++) {
- u32 const clks_pll_div = 2, core_clk_div = 5;
- u32 const rng1 = 16, rng2 = 8;
- u32 th1, th2;
- u64 core_clk, rvar1, rem;
-
- core_clk = div_u64(freq_list[k],
- ratio_list[k / sz_band] * clks_pll_div *
- core_clk_div);
-
- rvar1 = HDMI_REF_CLOCK_HZ * rng1 * HDMI_MHZ_TO_HZ;
- rvar1 = div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem);
- if (rem > ((cmp_cnt * core_clk) >> 1))
- rvar1++;
- th1 = rvar1;
-
- rvar1 = HDMI_REF_CLOCK_HZ * rng2 * HDMI_MHZ_TO_HZ;
- rvar1 = div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem);
- if (rem > ((cmp_cnt * core_clk) >> 1))
- rvar1++;
- th2 = rvar1;
-
- if (freq_list[k] >= min_freq &&
- freq_list[k] <= max_freq) {
- if ((th1 >= th_min && th1 <= th_max) ||
- (th2 >= th_min && th2 <= th_max)) {
- if (freq_list[k] <= freq_optimal) {
- freq_optimal = freq_list[k];
- optimal_index = k;
- }
- }
- }
- }
-
- if (optimal_index == -1) {
- if (!half_rate_mode) {
- half_rate_mode = 1;
- goto find_optimal_index;
- } else {
- return -EINVAL;
- }
- } else {
- found_vco_ratio = ratio_list[optimal_index / sz_band];
- found_tx_band_sel = band_list[optimal_index % sz_band];
- found_vco_freq = freq_optimal;
- }
-
- switch (found_vco_ratio) {
- case 1:
- found_hsclk_divsel = 15;
- break;
- case 2:
- found_hsclk_divsel = 0;
- break;
- case 3:
- found_hsclk_divsel = 4;
- break;
- case 4:
- found_hsclk_divsel = 8;
- break;
- case 5:
- found_hsclk_divsel = 12;
- break;
- case 6:
- found_hsclk_divsel = 1;
- break;
- case 9:
- found_hsclk_divsel = 5;
- break;
- case 10:
- found_hsclk_divsel = 2;
- break;
- case 12:
- found_hsclk_divsel = 9;
- break;
- case 15:
- found_hsclk_divsel = 13;
- break;
- case 25:
- found_hsclk_divsel = 14;
- break;
- }
-
- pd->vco_freq = found_vco_freq;
- pd->tx_band_sel = found_tx_band_sel;
- pd->vco_ratio = found_vco_ratio;
- pd->hsclk_divsel = 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 = 10 * pix_clk */
- bclk = ((u64)pix_clk) * 10;
-
- ret = pll_get_post_div(&pd, bclk);
- if (ret)
- return ret;
-
- dec_start = pd.vco_freq;
- pll_divisor = 4 * ref_clk;
- do_div(dec_start, pll_divisor);
-
- frac_start = pd.vco_freq * (1 << 20);
-
- rem = do_div(frac_start, pll_divisor);
- frac_start -= dec_start * (1 << 20);
- if (rem > (pll_divisor >> 1))
- frac_start++;
-
- cpctrl = pll_get_cpctrl(frac_start, ref_clk, false);
- rctrl = pll_get_rctrl(frac_start, false);
- cctrl = pll_get_cctrl(frac_start, false);
- integloop_gain = pll_get_integloop_gain(frac_start, bclk,
- ref_clk, false);
-
- fdata = pd.vco_freq;
- do_div(fdata, pd.vco_ratio);
-
- pll_cmp = 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 = 1;
- else
- cfg->com_svs_mode_clk_sel = 2;
-
- cfg->com_hsclk_sel = (0x20 | pd.hsclk_divsel);
- cfg->com_pll_cctrl_mode0 = cctrl;
- cfg->com_pll_rctrl_mode0 = rctrl;
- cfg->com_cp_ctrl_mode0 = cpctrl;
- cfg->com_dec_start_mode0 = dec_start;
- cfg->com_div_frac_start1_mode0 = (frac_start & 0xff);
- cfg->com_div_frac_start2_mode0 = ((frac_start & 0xff00) >> 8);
- cfg->com_div_frac_start3_mode0 = ((frac_start & 0xf0000) >> 16);
- cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xff);
- cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xf00) >> 8);
- cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xff);
- cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xff00) >> 8);
- cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16);
- cfg->com_lock_cmp_en = 0x0;
- cfg->com_core_clk_en = 0x2c;
- cfg->com_coreclk_div_mode0 = HDMI_CORECLK_DIV;
- cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x5 : 0x4;
-
- for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++)
- cfg->tx_lx_tx_band[i] = pd.tx_band_sel;
-
- if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
- cfg->tx_lx_tx_drv_lvl[0] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[1] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[2] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[3] = 0x0f;
- cfg->tx_lx_tx_emp_post1_lvl[0] = 0x03;
- cfg->tx_lx_tx_emp_post1_lvl[1] = 0x02;
- cfg->tx_lx_tx_emp_post1_lvl[2] = 0x03;
- cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;
- cfg->tx_lx_pre_driver_1[0] = 0x00;
- cfg->tx_lx_pre_driver_1[1] = 0x00;
- cfg->tx_lx_pre_driver_1[2] = 0x00;
- cfg->tx_lx_pre_driver_1[3] = 0x00;
- cfg->tx_lx_pre_driver_2[0] = 0x1C;
- cfg->tx_lx_pre_driver_2[1] = 0x1C;
- cfg->tx_lx_pre_driver_2[2] = 0x1C;
- cfg->tx_lx_pre_driver_2[3] = 0x00;
- cfg->tx_lx_res_code_offset[0] = 0x03;
- cfg->tx_lx_res_code_offset[1] = 0x00;
- cfg->tx_lx_res_code_offset[2] = 0x00;
- cfg->tx_lx_res_code_offset[3] = 0x03;
- } else if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) {
- cfg->tx_lx_tx_drv_lvl[0] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[1] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[2] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[3] = 0x0f;
- cfg->tx_lx_tx_emp_post1_lvl[0] = 0x03;
- cfg->tx_lx_tx_emp_post1_lvl[1] = 0x03;
- cfg->tx_lx_tx_emp_post1_lvl[2] = 0x03;
- cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;
- cfg->tx_lx_pre_driver_1[0] = 0x00;
- cfg->tx_lx_pre_driver_1[1] = 0x00;
- cfg->tx_lx_pre_driver_1[2] = 0x00;
- cfg->tx_lx_pre_driver_1[3] = 0x00;
- cfg->tx_lx_pre_driver_2[0] = 0x16;
- cfg->tx_lx_pre_driver_2[1] = 0x16;
- cfg->tx_lx_pre_driver_2[2] = 0x16;
- cfg->tx_lx_pre_driver_2[3] = 0x18;
- cfg->tx_lx_res_code_offset[0] = 0x03;
- cfg->tx_lx_res_code_offset[1] = 0x00;
- cfg->tx_lx_res_code_offset[2] = 0x00;
- cfg->tx_lx_res_code_offset[3] = 0x00;
- } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
- cfg->tx_lx_tx_drv_lvl[0] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[1] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[2] = 0x0f;
- cfg->tx_lx_tx_drv_lvl[3] = 0x0f;
- cfg->tx_lx_tx_emp_post1_lvl[0] = 0x05;
- cfg->tx_lx_tx_emp_post1_lvl[1] = 0x05;
- cfg->tx_lx_tx_emp_post1_lvl[2] = 0x05;
- cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;
- cfg->tx_lx_pre_driver_1[0] = 0x00;
- cfg->tx_lx_pre_driver_1[1] = 0x00;
- cfg->tx_lx_pre_driver_1[2] = 0x00;
- cfg->tx_lx_pre_driver_1[3] = 0x00;
- cfg->tx_lx_pre_driver_2[0] = 0x0E;
- cfg->tx_lx_pre_driver_2[1] = 0x0E;
- cfg->tx_lx_pre_driver_2[2] = 0x0E;
- cfg->tx_lx_pre_driver_2[3] = 0x0E;
- cfg->tx_lx_res_code_offset[0] = 0x00;
- cfg->tx_lx_res_code_offset[1] = 0x00;
- cfg->tx_lx_res_code_offset[2] = 0x00;
- cfg->tx_lx_res_code_offset[3] = 0x00;
- } else {
- cfg->tx_lx_tx_drv_lvl[0] = 0x01;
- cfg->tx_lx_tx_drv_lvl[1] = 0x01;
- cfg->tx_lx_tx_drv_lvl[2] = 0x01;
- cfg->tx_lx_tx_drv_lvl[3] = 0x00;
- cfg->tx_lx_tx_emp_post1_lvl[0] = 0x00;
- cfg->tx_lx_tx_emp_post1_lvl[1] = 0x00;
- cfg->tx_lx_tx_emp_post1_lvl[2] = 0x00;
- cfg->tx_lx_tx_emp_post1_lvl[3] = 0x00;
- cfg->tx_lx_pre_driver_1[0] = 0x00;
- cfg->tx_lx_pre_driver_1[1] = 0x00;
- cfg->tx_lx_pre_driver_1[2] = 0x00;
- cfg->tx_lx_pre_driver_1[3] = 0x00;
- cfg->tx_lx_pre_driver_2[0] = 0x16;
- cfg->tx_lx_pre_driver_2[1] = 0x16;
- cfg->tx_lx_pre_driver_2[2] = 0x16;
- cfg->tx_lx_pre_driver_2[3] = 0x18;
- cfg->tx_lx_res_code_offset[0] = 0x00;
- cfg->tx_lx_res_code_offset[1] = 0x00;
- cfg->tx_lx_res_code_offset[2] = 0x00;
- cfg->tx_lx_res_code_offset[3] = 0x00;
- }
-
- return 0;
-}
-
-static int hdmi_8998_pll_set_clk_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
-{
- struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = pll_get_phy(pll);
- struct hdmi_8998_phy_pll_reg_cfg cfg = {};
- int i, ret;
-
- ret = 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 = 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, 0x0B);
- 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 = 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 = 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 = rate;
-
- return 0;
-}
-
-static int hdmi_8998_phy_ready_status(struct hdmi_phy *phy)
-{
- u32 nb_tries = HDMI_PLL_POLL_MAX_READS;
- unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US;
- u32 status;
- int phy_ready = 0;
-
- while (nb_tries--) {
- status = hdmi_phy_read(phy, REG_HDMI_8998_PHY_STATUS);
- phy_ready = 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 = HDMI_PLL_POLL_MAX_READS;
- unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US;
- int pll_locked = 0;
-
- while (nb_tries--) {
- status = hdmi_pll_read(pll,
- REG_HDMI_8998_PHY_QSERDES_COM_C_READY_STATUS);
- pll_locked = 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 = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = pll_get_phy(pll);
- int i, ret = 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 = hdmi_8998_pll_lock_status(pll);
- if (!ret)
- return ret;
-
- for (i = 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 = 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 = 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 = hw_clk_to_pll(hw);
- return pll->rate;
-}
-
-static void hdmi_8998_pll_unprepare(struct clk_hw *hw)
-{
- struct hdmi_pll_8998 *pll = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = 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 = hw_clk_to_pll(hw);
- u32 status;
- int pll_locked;
-
- status = hdmi_pll_read(pll, REG_HDMI_8998_PHY_QSERDES_COM_C_READY_STATUS);
- pll_locked = status & BIT(0);
-
- return pll_locked;
-}
-
-static const struct clk_ops hdmi_8998_pll_ops = {
- .set_rate = hdmi_8998_pll_set_clk_rate,
- .determine_rate = hdmi_8998_pll_determine_rate,
- .recalc_rate = hdmi_8998_pll_recalc_rate,
- .prepare = hdmi_8998_pll_prepare,
- .unprepare = hdmi_8998_pll_unprepare,
- .is_enabled = hdmi_8998_pll_is_enabled,
-};
-
-static const struct clk_init_data pll_init = {
- .name = "hdmipll",
- .ops = &hdmi_8998_pll_ops,
- .parent_data = (const struct clk_parent_data[]){
- { .fw_name = "xo", .name = "xo_board" },
- },
- .num_parents = 1,
- .flags = CLK_IGNORE_UNUSED,
-};
-
-int msm_hdmi_pll_8998_init(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hdmi_pll_8998 *pll;
- int ret, i;
-
- pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return -ENOMEM;
-
- pll->pdev = pdev;
-
- pll->mmio_qserdes_com = 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 = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
- char name[32];
-
- snprintf(name, sizeof(name), "hdmi_tx_l%d", i);
-
- pll->mmio_qserdes_tx[i] = 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 = &pll_init;
-
- ret = devm_clk_hw_register(dev, &pll->clk_hw);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to register pll clock\n");
- return ret;
- }
-
- ret = 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[] = {
- "vddio",
- "vcca",
-};
-
-static const char * const hdmi_phy_8998_clk_names[] = {
- "iface", "ref", "xo",
-};
-
-const struct hdmi_phy_cfg msm_hdmi_phy_8998_cfg = {
- .type = MSM_HDMI_PHY_8998,
- .reg_names = hdmi_phy_8998_reg_names,
- .num_regs = ARRAY_SIZE(hdmi_phy_8998_reg_names),
- .clk_names = hdmi_phy_8998_clk_names,
- .num_clks = 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 <robdclark@gmail.com>
- */
-
-#include <linux/delay.h>
-
-#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 == 27000000) {
- /* video_format == 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 = 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[] = {
- "core-vdda",
-};
-
-static const char * const hdmi_phy_8x60_clk_names[] = {
- "slave_iface",
-};
-
-const struct hdmi_phy_cfg msm_hdmi_phy_8x60_cfg = {
- .type = MSM_HDMI_PHY_8x60,
- .powerup = hdmi_phy_8x60_powerup,
- .powerdown = hdmi_phy_8x60_powerdown,
- .reg_names = hdmi_phy_8x60_reg_names,
- .num_regs = ARRAY_SIZE(hdmi_phy_8x60_reg_names),
- .clk_names = hdmi_phy_8x60_clk_names,
- .num_clks = 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 <robdclark@gmail.com>
- */
-
-#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[] = {
- "core-vdda",
- "vddio",
-};
-
-static const char * const hdmi_phy_8x74_clk_names[] = {
- "iface", "alt_iface"
-};
-
-const struct hdmi_phy_cfg msm_hdmi_phy_8x74_cfg = {
- .type = MSM_HDMI_PHY_8x74,
- .powerup = hdmi_phy_8x74_powerup,
- .powerdown = hdmi_phy_8x74_powerdown,
- .reg_names = hdmi_phy_8x74_reg_names,
- .num_regs = ARRAY_SIZE(hdmi_phy_8x74_reg_names),
- .clk_names = hdmi_phy_8x74_clk_names,
- .num_clks = ARRAY_SIZE(hdmi_phy_8x74_clk_names),
-};
diff --git a/drivers/gpu/drm/msm/registers/display/hdmi.xml b/drivers/gpu/drm/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="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
</domain>
-<domain name="HDMI_8x60" width="32">
- <reg32 offset="0x00000" name="PHY_REG0">
- <bitfield name="DESER_DEL_CTRL" low="2" high="4" type="uint"/>
- </reg32>
- <reg32 offset="0x00004" name="PHY_REG1">
- <bitfield name="DTEST_MUX_SEL" low="4" high="7" type="uint"/>
- <bitfield name="OUTVOL_SWING_CTRL" low="0" high="3" type="uint"/>
- </reg32>
- <reg32 offset="0x00008" name="PHY_REG2">
- <bitfield name="PD_DESER" pos="0" type="boolean"/>
- <bitfield name="PD_DRIVE_1" pos="1" type="boolean"/>
- <bitfield name="PD_DRIVE_2" pos="2" type="boolean"/>
- <bitfield name="PD_DRIVE_3" pos="3" type="boolean"/>
- <bitfield name="PD_DRIVE_4" pos="4" type="boolean"/>
- <bitfield name="PD_PLL" pos="5" type="boolean"/>
- <bitfield name="PD_PWRGEN" pos="6" type="boolean"/>
- <bitfield name="RCV_SENSE_EN" pos="7" type="boolean"/>
- </reg32>
- <reg32 offset="0x0000c" name="PHY_REG3">
- <bitfield name="PLL_ENABLE" pos="0" type="boolean"/>
- </reg32>
- <reg32 offset="0x00010" name="PHY_REG4"/>
- <reg32 offset="0x00014" name="PHY_REG5"/>
- <reg32 offset="0x00018" name="PHY_REG6"/>
- <reg32 offset="0x0001c" name="PHY_REG7"/>
- <reg32 offset="0x00020" name="PHY_REG8"/>
- <reg32 offset="0x00024" name="PHY_REG9"/>
- <reg32 offset="0x00028" name="PHY_REG10"/>
- <reg32 offset="0x0002c" name="PHY_REG11"/>
- <reg32 offset="0x00030" name="PHY_REG12">
- <bitfield name="RETIMING_EN" pos="0" type="boolean"/>
- <bitfield name="PLL_LOCK_DETECT_EN" pos="1" type="boolean"/>
- <bitfield name="FORCE_LOCK" pos="4" type="boolean"/>
- </reg32>
-</domain>
-
-<domain name="HDMI_8960" width="32">
- <!--
- some of the bitfields may be same as 8x60.. but no helpful comments
- in msm_dss_io_8960.c
- -->
- <reg32 offset="0x00000" name="PHY_REG0"/>
- <reg32 offset="0x00004" name="PHY_REG1"/>
- <reg32 offset="0x00008" name="PHY_REG2"/>
- <reg32 offset="0x0000c" name="PHY_REG3"/>
- <reg32 offset="0x00010" name="PHY_REG4"/>
- <reg32 offset="0x00014" name="PHY_REG5"/>
- <reg32 offset="0x00018" name="PHY_REG6"/>
- <reg32 offset="0x0001c" name="PHY_REG7"/>
- <reg32 offset="0x00020" name="PHY_REG8"/>
- <reg32 offset="0x00024" name="PHY_REG9"/>
- <reg32 offset="0x00028" name="PHY_REG10"/>
- <reg32 offset="0x0002c" name="PHY_REG11"/>
- <reg32 offset="0x00030" name="PHY_REG12">
- <bitfield name="SW_RESET" pos="5" type="boolean"/>
- <bitfield name="PWRDN_B" pos="7" type="boolean"/>
- </reg32>
- <reg32 offset="0x00034" name="PHY_REG_BIST_CFG"/>
- <reg32 offset="0x00038" name="PHY_DEBUG_BUS_SEL"/>
- <reg32 offset="0x0003c" name="PHY_REG_MISC0"/>
- <reg32 offset="0x00040" name="PHY_REG13"/>
- <reg32 offset="0x00044" name="PHY_REG14"/>
- <reg32 offset="0x00048" name="PHY_REG15"/>
-</domain>
-
-<domain name="HDMI_8960_PHY_PLL" width="32">
- <reg32 offset="0x00000" name="REFCLK_CFG"/>
- <reg32 offset="0x00004" name="CHRG_PUMP_CFG"/>
- <reg32 offset="0x00008" name="LOOP_FLT_CFG0"/>
- <reg32 offset="0x0000c" name="LOOP_FLT_CFG1"/>
- <reg32 offset="0x00010" name="IDAC_ADJ_CFG"/>
- <reg32 offset="0x00014" name="I_VI_KVCO_CFG"/>
- <reg32 offset="0x00018" name="PWRDN_B">
- <bitfield name="PD_PLL" pos="1" type="boolean"/>
- <bitfield name="PLL_PWRDN_B" pos="3" type="boolean"/>
- </reg32>
- <reg32 offset="0x0001c" name="SDM_CFG0"/>
- <reg32 offset="0x00020" name="SDM_CFG1"/>
- <reg32 offset="0x00024" name="SDM_CFG2"/>
- <reg32 offset="0x00028" name="SDM_CFG3"/>
- <reg32 offset="0x0002c" name="SDM_CFG4"/>
- <reg32 offset="0x00030" name="SSC_CFG0"/>
- <reg32 offset="0x00034" name="SSC_CFG1"/>
- <reg32 offset="0x00038" name="SSC_CFG2"/>
- <reg32 offset="0x0003c" name="SSC_CFG3"/>
- <reg32 offset="0x00040" name="LOCKDET_CFG0"/>
- <reg32 offset="0x00044" name="LOCKDET_CFG1"/>
- <reg32 offset="0x00048" name="LOCKDET_CFG2"/>
- <reg32 offset="0x0004c" name="VCOCAL_CFG0"/>
- <reg32 offset="0x00050" name="VCOCAL_CFG1"/>
- <reg32 offset="0x00054" name="VCOCAL_CFG2"/>
- <reg32 offset="0x00058" name="VCOCAL_CFG3"/>
- <reg32 offset="0x0005c" name="VCOCAL_CFG4"/>
- <reg32 offset="0x00060" name="VCOCAL_CFG5"/>
- <reg32 offset="0x00064" name="VCOCAL_CFG6"/>
- <reg32 offset="0x00068" name="VCOCAL_CFG7"/>
- <reg32 offset="0x0006c" name="DEBUG_SEL"/>
- <reg32 offset="0x00070" name="MISC0"/>
- <reg32 offset="0x00074" name="MISC1"/>
- <reg32 offset="0x00078" name="MISC2"/>
- <reg32 offset="0x0007c" name="MISC3"/>
- <reg32 offset="0x00080" name="MISC4"/>
- <reg32 offset="0x00084" name="MISC5"/>
- <reg32 offset="0x00088" name="MISC6"/>
- <reg32 offset="0x0008c" name="DEBUG_BUS0"/>
- <reg32 offset="0x00090" name="DEBUG_BUS1"/>
- <reg32 offset="0x00094" name="DEBUG_BUS2"/>
- <reg32 offset="0x00098" name="STATUS0">
- <bitfield name="PLL_LOCK" pos="0" type="boolean"/>
- </reg32>
- <reg32 offset="0x0009c" name="STATUS1"/>
-</domain>
-
-<domain name="HDMI_8x74" width="32">
- <!--
- seems to be all mdp5+ have same?
- -->
- <reg32 offset="0x00000" name="ANA_CFG0"/>
- <reg32 offset="0x00004" name="ANA_CFG1"/>
- <reg32 offset="0x00008" name="ANA_CFG2"/>
- <reg32 offset="0x0000c" name="ANA_CFG3"/>
- <reg32 offset="0x00010" name="PD_CTRL0"/>
- <reg32 offset="0x00014" name="PD_CTRL1"/>
- <reg32 offset="0x00018" name="GLB_CFG"/>
- <reg32 offset="0x0001c" name="DCC_CFG0"/>
- <reg32 offset="0x00020" name="DCC_CFG1"/>
- <reg32 offset="0x00024" name="TXCAL_CFG0"/>
- <reg32 offset="0x00028" name="TXCAL_CFG1"/>
- <reg32 offset="0x0002c" name="TXCAL_CFG2"/>
- <reg32 offset="0x00030" name="TXCAL_CFG3"/>
- <reg32 offset="0x00034" name="BIST_CFG0"/>
- <reg32 offset="0x0003c" name="BIST_PATN0"/>
- <reg32 offset="0x00040" name="BIST_PATN1"/>
- <reg32 offset="0x00044" name="BIST_PATN2"/>
- <reg32 offset="0x00048" name="BIST_PATN3"/>
- <reg32 offset="0x0005c" name="STATUS"/>
-</domain>
-
-<domain name="HDMI_28nm_PHY_PLL" width="32">
- <reg32 offset="0x00000" name="REFCLK_CFG"/>
- <reg32 offset="0x00004" name="POSTDIV1_CFG"/>
- <reg32 offset="0x00008" name="CHGPUMP_CFG"/>
- <reg32 offset="0x0000C" name="VCOLPF_CFG"/>
- <reg32 offset="0x00010" name="VREG_CFG"/>
- <reg32 offset="0x00014" name="PWRGEN_CFG"/>
- <reg32 offset="0x00018" name="DMUX_CFG"/>
- <reg32 offset="0x0001C" name="AMUX_CFG"/>
- <reg32 offset="0x00020" name="GLB_CFG">
- <bitfield name="PLL_PWRDN_B" pos="0" type="boolean"/>
- <bitfield name="PLL_LDO_PWRDN_B" pos="1" type="boolean"/>
- <bitfield name="PLL_PWRGEN_PWRDN_B" pos="2" type="boolean"/>
- <bitfield name="PLL_ENABLE" pos="3" type="boolean"/>
- </reg32>
- <reg32 offset="0x00024" name="POSTDIV2_CFG"/>
- <reg32 offset="0x00028" name="POSTDIV3_CFG"/>
- <reg32 offset="0x0002C" name="LPFR_CFG"/>
- <reg32 offset="0x00030" name="LPFC1_CFG"/>
- <reg32 offset="0x00034" name="LPFC2_CFG"/>
- <reg32 offset="0x00038" name="SDM_CFG0"/>
- <reg32 offset="0x0003C" name="SDM_CFG1"/>
- <reg32 offset="0x00040" name="SDM_CFG2"/>
- <reg32 offset="0x00044" name="SDM_CFG3"/>
- <reg32 offset="0x00048" name="SDM_CFG4"/>
- <reg32 offset="0x0004C" name="SSC_CFG0"/>
- <reg32 offset="0x00050" name="SSC_CFG1"/>
- <reg32 offset="0x00054" name="SSC_CFG2"/>
- <reg32 offset="0x00058" name="SSC_CFG3"/>
- <reg32 offset="0x0005C" name="LKDET_CFG0"/>
- <reg32 offset="0x00060" name="LKDET_CFG1"/>
- <reg32 offset="0x00064" name="LKDET_CFG2"/>
- <reg32 offset="0x00068" name="TEST_CFG">
- <bitfield name="PLL_SW_RESET" pos="0" type="boolean"/>
- </reg32>
- <reg32 offset="0x0006C" name="CAL_CFG0"/>
- <reg32 offset="0x00070" name="CAL_CFG1"/>
- <reg32 offset="0x00074" name="CAL_CFG2"/>
- <reg32 offset="0x00078" name="CAL_CFG3"/>
- <reg32 offset="0x0007C" name="CAL_CFG4"/>
- <reg32 offset="0x00080" name="CAL_CFG5"/>
- <reg32 offset="0x00084" name="CAL_CFG6"/>
- <reg32 offset="0x00088" name="CAL_CFG7"/>
- <reg32 offset="0x0008C" name="CAL_CFG8"/>
- <reg32 offset="0x00090" name="CAL_CFG9"/>
- <reg32 offset="0x00094" name="CAL_CFG10"/>
- <reg32 offset="0x00098" name="CAL_CFG11"/>
- <reg32 offset="0x0009C" name="EFUSE_CFG"/>
- <reg32 offset="0x000A0" name="DEBUG_BUS_SEL"/>
- <reg32 offset="0x000C0" name="STATUS"/>
-</domain>
-
-<domain name="HDMI_8996_PHY" width="32">
- <reg32 offset="0x00000" name="CFG"/>
- <reg32 offset="0x00004" name="PD_CTL"/>
- <reg32 offset="0x00008" name="MODE"/>
- <reg32 offset="0x0000C" name="MISR_CLEAR"/>
- <reg32 offset="0x00010" name="TX0_TX1_BIST_CFG0"/>
- <reg32 offset="0x00014" name="TX0_TX1_BIST_CFG1"/>
- <reg32 offset="0x00018" name="TX0_TX1_PRBS_SEED_BYTE0"/>
- <reg32 offset="0x0001C" name="TX0_TX1_PRBS_SEED_BYTE1"/>
- <reg32 offset="0x00020" name="TX0_TX1_BIST_PATTERN0"/>
- <reg32 offset="0x00024" name="TX0_TX1_BIST_PATTERN1"/>
- <reg32 offset="0x00028" name="TX2_TX3_BIST_CFG0"/>
- <reg32 offset="0x0002C" name="TX2_TX3_BIST_CFG1"/>
- <reg32 offset="0x00030" name="TX2_TX3_PRBS_SEED_BYTE0"/>
- <reg32 offset="0x00034" name="TX2_TX3_PRBS_SEED_BYTE1"/>
- <reg32 offset="0x00038" name="TX2_TX3_BIST_PATTERN0"/>
- <reg32 offset="0x0003C" name="TX2_TX3_BIST_PATTERN1"/>
- <reg32 offset="0x00040" name="DEBUG_BUS_SEL"/>
- <reg32 offset="0x00044" name="TXCAL_CFG0"/>
- <reg32 offset="0x00048" name="TXCAL_CFG1"/>
- <reg32 offset="0x0004C" name="TX0_TX1_LANE_CTL"/>
- <reg32 offset="0x00050" name="TX2_TX3_LANE_CTL"/>
- <reg32 offset="0x00054" name="LANE_BIST_CONFIG"/>
- <reg32 offset="0x00058" name="CLOCK"/>
- <reg32 offset="0x0005C" name="MISC1"/>
- <reg32 offset="0x00060" name="MISC2"/>
- <reg32 offset="0x00064" name="TX0_TX1_BIST_STATUS0"/>
- <reg32 offset="0x00068" name="TX0_TX1_BIST_STATUS1"/>
- <reg32 offset="0x0006C" name="TX0_TX1_BIST_STATUS2"/>
- <reg32 offset="0x00070" name="TX2_TX3_BIST_STATUS0"/>
- <reg32 offset="0x00074" name="TX2_TX3_BIST_STATUS1"/>
- <reg32 offset="0x00078" name="TX2_TX3_BIST_STATUS2"/>
- <reg32 offset="0x0007C" name="PRE_MISR_STATUS0"/>
- <reg32 offset="0x00080" name="PRE_MISR_STATUS1"/>
- <reg32 offset="0x00084" name="PRE_MISR_STATUS2"/>
- <reg32 offset="0x00088" name="PRE_MISR_STATUS3"/>
- <reg32 offset="0x0008C" name="POST_MISR_STATUS0"/>
- <reg32 offset="0x00090" name="POST_MISR_STATUS1"/>
- <reg32 offset="0x00094" name="POST_MISR_STATUS2"/>
- <reg32 offset="0x00098" name="POST_MISR_STATUS3"/>
- <reg32 offset="0x0009C" name="STATUS"/>
- <reg32 offset="0x000A0" name="MISC3_STATUS"/>
- <reg32 offset="0x000A4" name="MISC4_STATUS"/>
- <reg32 offset="0x000A8" name="DEBUG_BUS0"/>
- <reg32 offset="0x000AC" name="DEBUG_BUS1"/>
- <reg32 offset="0x000B0" name="DEBUG_BUS2"/>
- <reg32 offset="0x000B4" name="DEBUG_BUS3"/>
- <reg32 offset="0x000B8" name="PHY_REVISION_ID0"/>
- <reg32 offset="0x000BC" name="PHY_REVISION_ID1"/>
- <reg32 offset="0x000C0" name="PHY_REVISION_ID2"/>
- <reg32 offset="0x000C4" name="PHY_REVISION_ID3"/>
-</domain>
-
-<domain name="HDMI_PHY_QSERDES_COM" width="32">
- <reg32 offset="0x00000" name="ATB_SEL1"/>
- <reg32 offset="0x00004" name="ATB_SEL2"/>
- <reg32 offset="0x00008" name="FREQ_UPDATE"/>
- <reg32 offset="0x0000C" name="BG_TIMER"/>
- <reg32 offset="0x00010" name="SSC_EN_CENTER"/>
- <reg32 offset="0x00014" name="SSC_ADJ_PER1"/>
- <reg32 offset="0x00018" name="SSC_ADJ_PER2"/>
- <reg32 offset="0x0001C" name="SSC_PER1"/>
- <reg32 offset="0x00020" name="SSC_PER2"/>
- <reg32 offset="0x00024" name="SSC_STEP_SIZE1"/>
- <reg32 offset="0x00028" name="SSC_STEP_SIZE2"/>
- <reg32 offset="0x0002C" name="POST_DIV"/>
- <reg32 offset="0x00030" name="POST_DIV_MUX"/>
- <reg32 offset="0x00034" name="BIAS_EN_CLKBUFLR_EN"/>
- <reg32 offset="0x00038" name="CLK_ENABLE1"/>
- <reg32 offset="0x0003C" name="SYS_CLK_CTRL"/>
- <reg32 offset="0x00040" name="SYSCLK_BUF_ENABLE"/>
- <reg32 offset="0x00044" name="PLL_EN"/>
- <reg32 offset="0x00048" name="PLL_IVCO"/>
- <reg32 offset="0x0004C" name="LOCK_CMP1_MODE0"/>
- <reg32 offset="0x00050" name="LOCK_CMP2_MODE0"/>
- <reg32 offset="0x00054" name="LOCK_CMP3_MODE0"/>
- <reg32 offset="0x00058" name="LOCK_CMP1_MODE1"/>
- <reg32 offset="0x0005C" name="LOCK_CMP2_MODE1"/>
- <reg32 offset="0x00060" name="LOCK_CMP3_MODE1"/>
- <reg32 offset="0x00064" name="LOCK_CMP1_MODE2"/>
- <reg32 offset="0x00064" name="CMN_RSVD0"/>
- <reg32 offset="0x00068" name="LOCK_CMP2_MODE2"/>
- <reg32 offset="0x00068" name="EP_CLOCK_DETECT_CTRL"/>
- <reg32 offset="0x0006C" name="LOCK_CMP3_MODE2"/>
- <reg32 offset="0x0006C" name="SYSCLK_DET_COMP_STATUS"/>
- <reg32 offset="0x00070" name="BG_TRIM"/>
- <reg32 offset="0x00074" name="CLK_EP_DIV"/>
- <reg32 offset="0x00078" name="CP_CTRL_MODE0"/>
- <reg32 offset="0x0007C" name="CP_CTRL_MODE1"/>
- <reg32 offset="0x00080" name="CP_CTRL_MODE2"/>
- <reg32 offset="0x00080" name="CMN_RSVD1"/>
- <reg32 offset="0x00084" name="PLL_RCTRL_MODE0"/>
- <reg32 offset="0x00088" name="PLL_RCTRL_MODE1"/>
- <reg32 offset="0x0008C" name="PLL_RCTRL_MODE2"/>
- <reg32 offset="0x0008C" name="CMN_RSVD2"/>
- <reg32 offset="0x00090" name="PLL_CCTRL_MODE0"/>
- <reg32 offset="0x00094" name="PLL_CCTRL_MODE1"/>
- <reg32 offset="0x00098" name="PLL_CCTRL_MODE2"/>
- <reg32 offset="0x00098" name="CMN_RSVD3"/>
- <reg32 offset="0x0009C" name="PLL_CNTRL"/>
- <reg32 offset="0x000A0" name="PHASE_SEL_CTRL"/>
- <reg32 offset="0x000A4" name="PHASE_SEL_DC"/>
- <reg32 offset="0x000A8" name="CORE_CLK_IN_SYNC_SEL"/>
- <reg32 offset="0x000A8" name="BIAS_EN_CTRL_BY_PSM"/>
- <reg32 offset="0x000AC" name="SYSCLK_EN_SEL"/>
- <reg32 offset="0x000B0" name="CML_SYSCLK_SEL"/>
- <reg32 offset="0x000B4" name="RESETSM_CNTRL"/>
- <reg32 offset="0x000B8" name="RESETSM_CNTRL2"/>
- <reg32 offset="0x000BC" name="RESTRIM_CTRL"/>
- <reg32 offset="0x000C0" name="RESTRIM_CTRL2"/>
- <reg32 offset="0x000C4" name="RESCODE_DIV_NUM"/>
- <reg32 offset="0x000C8" name="LOCK_CMP_EN"/>
- <reg32 offset="0x000CC" name="LOCK_CMP_CFG"/>
- <reg32 offset="0x000D0" name="DEC_START_MODE0"/>
- <reg32 offset="0x000D4" name="DEC_START_MODE1"/>
- <reg32 offset="0x000D8" name="DEC_START_MODE2"/>
- <reg32 offset="0x000D8" name="VCOCAL_DEADMAN_CTRL"/>
- <reg32 offset="0x000DC" name="DIV_FRAC_START1_MODE0"/>
- <reg32 offset="0x000E0" name="DIV_FRAC_START2_MODE0"/>
- <reg32 offset="0x000E4" name="DIV_FRAC_START3_MODE0"/>
- <reg32 offset="0x000E8" name="DIV_FRAC_START1_MODE1"/>
- <reg32 offset="0x000EC" name="DIV_FRAC_START2_MODE1"/>
- <reg32 offset="0x000F0" name="DIV_FRAC_START3_MODE1"/>
- <reg32 offset="0x000F4" name="DIV_FRAC_START1_MODE2"/>
- <reg32 offset="0x000F4" name="VCO_TUNE_MINVAL1"/>
- <reg32 offset="0x000F8" name="DIV_FRAC_START2_MODE2"/>
- <reg32 offset="0x000F8" name="VCO_TUNE_MINVAL2"/>
- <reg32 offset="0x000FC" name="DIV_FRAC_START3_MODE2"/>
- <reg32 offset="0x000FC" name="CMN_RSVD4"/>
- <reg32 offset="0x00100" name="INTEGLOOP_INITVAL"/>
- <reg32 offset="0x00104" name="INTEGLOOP_EN"/>
- <reg32 offset="0x00108" name="INTEGLOOP_GAIN0_MODE0"/>
- <reg32 offset="0x0010C" name="INTEGLOOP_GAIN1_MODE0"/>
- <reg32 offset="0x00110" name="INTEGLOOP_GAIN0_MODE1"/>
- <reg32 offset="0x00114" name="INTEGLOOP_GAIN1_MODE1"/>
- <reg32 offset="0x00118" name="INTEGLOOP_GAIN0_MODE2"/>
- <reg32 offset="0x00118" name="VCO_TUNE_MAXVAL1"/>
- <reg32 offset="0x0011C" name="INTEGLOOP_GAIN1_MODE2"/>
- <reg32 offset="0x0011C" name="VCO_TUNE_MAXVAL2"/>
- <reg32 offset="0x00120" name="RES_TRIM_CONTROL2"/>
- <reg32 offset="0x00124" name="VCO_TUNE_CTRL"/>
- <reg32 offset="0x00128" name="VCO_TUNE_MAP"/>
- <reg32 offset="0x0012C" name="VCO_TUNE1_MODE0"/>
- <reg32 offset="0x00130" name="VCO_TUNE2_MODE0"/>
- <reg32 offset="0x00134" name="VCO_TUNE1_MODE1"/>
- <reg32 offset="0x00138" name="VCO_TUNE2_MODE1"/>
- <reg32 offset="0x0013C" name="VCO_TUNE1_MODE2"/>
- <reg32 offset="0x0013C" name="VCO_TUNE_INITVAL1"/>
- <reg32 offset="0x00140" name="VCO_TUNE2_MODE2"/>
- <reg32 offset="0x00140" name="VCO_TUNE_INITVAL2"/>
- <reg32 offset="0x00144" name="VCO_TUNE_TIMER1"/>
- <reg32 offset="0x00148" name="VCO_TUNE_TIMER2"/>
- <reg32 offset="0x0014C" name="SAR"/>
- <reg32 offset="0x00150" name="SAR_CLK"/>
- <reg32 offset="0x00154" name="SAR_CODE_OUT_STATUS"/>
- <reg32 offset="0x00158" name="SAR_CODE_READY_STATUS"/>
- <reg32 offset="0x0015C" name="CMN_STATUS"/>
- <reg32 offset="0x00160" name="RESET_SM_STATUS"/>
- <reg32 offset="0x00164" name="RESTRIM_CODE_STATUS"/>
- <reg32 offset="0x00168" name="PLLCAL_CODE1_STATUS"/>
- <reg32 offset="0x0016C" name="PLLCAL_CODE2_STATUS"/>
- <reg32 offset="0x00170" name="BG_CTRL"/>
- <reg32 offset="0x00174" name="CLK_SELECT"/>
- <reg32 offset="0x00178" name="HSCLK_SEL"/>
- <reg32 offset="0x0017C" name="INTEGLOOP_BINCODE_STATUS"/>
- <reg32 offset="0x00180" name="PLL_ANALOG"/>
- <reg32 offset="0x00184" name="CORECLK_DIV"/>
- <reg32 offset="0x00188" name="SW_RESET"/>
- <reg32 offset="0x0018C" name="CORE_CLK_EN"/>
- <reg32 offset="0x00190" name="C_READY_STATUS"/>
- <reg32 offset="0x00194" name="CMN_CONFIG"/>
- <reg32 offset="0x00198" name="CMN_RATE_OVERRIDE"/>
- <reg32 offset="0x0019C" name="SVS_MODE_CLK_SEL"/>
- <reg32 offset="0x001A0" name="DEBUG_BUS0"/>
- <reg32 offset="0x001A4" name="DEBUG_BUS1"/>
- <reg32 offset="0x001A8" name="DEBUG_BUS2"/>
- <reg32 offset="0x001AC" name="DEBUG_BUS3"/>
- <reg32 offset="0x001B0" name="DEBUG_BUS_SEL"/>
- <reg32 offset="0x001B4" name="CMN_MISC1"/>
- <reg32 offset="0x001B8" name="CMN_MISC2"/>
- <reg32 offset="0x001BC" name="CORECLK_DIV_MODE1"/>
- <reg32 offset="0x001C0" name="CORECLK_DIV_MODE2"/>
- <reg32 offset="0x001C4" name="CMN_RSVD5"/>
-</domain>
-
-
-<domain name="HDMI_PHY_QSERDES_TX_LX" width="32">
- <reg32 offset="0x00000" name="BIST_MODE_LANENO"/>
- <reg32 offset="0x00004" name="BIST_INVERT"/>
- <reg32 offset="0x00008" name="CLKBUF_ENABLE"/>
- <reg32 offset="0x0000C" name="CMN_CONTROL_ONE"/>
- <reg32 offset="0x00010" name="CMN_CONTROL_TWO"/>
- <reg32 offset="0x00014" name="CMN_CONTROL_THREE"/>
- <reg32 offset="0x00018" name="TX_EMP_POST1_LVL"/>
- <reg32 offset="0x0001C" name="TX_POST2_EMPH"/>
- <reg32 offset="0x00020" name="TX_BOOST_LVL_UP_DN"/>
- <reg32 offset="0x00024" name="HP_PD_ENABLES"/>
- <reg32 offset="0x00028" name="TX_IDLE_LVL_LARGE_AMP"/>
- <reg32 offset="0x0002C" name="TX_DRV_LVL"/>
- <reg32 offset="0x00030" name="TX_DRV_LVL_OFFSET"/>
- <reg32 offset="0x00034" name="RESET_TSYNC_EN"/>
- <reg32 offset="0x00038" name="PRE_STALL_LDO_BOOST_EN"/>
- <reg32 offset="0x0003C" name="TX_BAND"/>
- <reg32 offset="0x00040" name="SLEW_CNTL"/>
- <reg32 offset="0x00044" name="INTERFACE_SELECT"/>
- <reg32 offset="0x00048" name="LPB_EN"/>
- <reg32 offset="0x0004C" name="RES_CODE_LANE_TX"/>
- <reg32 offset="0x00050" name="RES_CODE_LANE_RX"/>
- <reg32 offset="0x00054" name="RES_CODE_LANE_OFFSET"/>
- <reg32 offset="0x00058" name="PERL_LENGTH1"/>
- <reg32 offset="0x0005C" name="PERL_LENGTH2"/>
- <reg32 offset="0x00060" name="SERDES_BYP_EN_OUT"/>
- <reg32 offset="0x00064" name="DEBUG_BUS_SEL"/>
- <reg32 offset="0x00068" name="HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN"/>
- <reg32 offset="0x0006C" name="TX_POL_INV"/>
- <reg32 offset="0x00070" name="PARRATE_REC_DETECT_IDLE_EN"/>
- <reg32 offset="0x00074" name="BIST_PATTERN1"/>
- <reg32 offset="0x00078" name="BIST_PATTERN2"/>
- <reg32 offset="0x0007C" name="BIST_PATTERN3"/>
- <reg32 offset="0x00080" name="BIST_PATTERN4"/>
- <reg32 offset="0x00084" name="BIST_PATTERN5"/>
- <reg32 offset="0x00088" name="BIST_PATTERN6"/>
- <reg32 offset="0x0008C" name="BIST_PATTERN7"/>
- <reg32 offset="0x00090" name="BIST_PATTERN8"/>
- <reg32 offset="0x00094" name="LANE_MODE"/>
- <reg32 offset="0x00098" name="IDAC_CAL_LANE_MODE"/>
- <reg32 offset="0x0009C" name="IDAC_CAL_LANE_MODE_CONFIGURATION"/>
- <reg32 offset="0x000A0" name="ATB_SEL1"/>
- <reg32 offset="0x000A4" name="ATB_SEL2"/>
- <reg32 offset="0x000A8" name="RCV_DETECT_LVL"/>
- <reg32 offset="0x000AC" name="RCV_DETECT_LVL_2"/>
- <reg32 offset="0x000B0" name="PRBS_SEED1"/>
- <reg32 offset="0x000B4" name="PRBS_SEED2"/>
- <reg32 offset="0x000B8" name="PRBS_SEED3"/>
- <reg32 offset="0x000BC" name="PRBS_SEED4"/>
- <reg32 offset="0x000C0" name="RESET_GEN"/>
- <reg32 offset="0x000C4" name="RESET_GEN_MUXES"/>
- <reg32 offset="0x000C8" name="TRAN_DRVR_EMP_EN"/>
- <reg32 offset="0x000CC" name="TX_INTERFACE_MODE"/>
- <reg32 offset="0x000D0" name="PWM_CTRL"/>
- <reg32 offset="0x000D4" name="PWM_ENCODED_OR_DATA"/>
- <reg32 offset="0x000D8" name="PWM_GEAR_1_DIVIDER_BAND2"/>
- <reg32 offset="0x000DC" name="PWM_GEAR_2_DIVIDER_BAND2"/>
- <reg32 offset="0x000E0" name="PWM_GEAR_3_DIVIDER_BAND2"/>
- <reg32 offset="0x000E4" name="PWM_GEAR_4_DIVIDER_BAND2"/>
- <reg32 offset="0x000E8" name="PWM_GEAR_1_DIVIDER_BAND0_1"/>
- <reg32 offset="0x000EC" name="PWM_GEAR_2_DIVIDER_BAND0_1"/>
- <reg32 offset="0x000F0" name="PWM_GEAR_3_DIVIDER_BAND0_1"/>
- <reg32 offset="0x000F4" name="PWM_GEAR_4_DIVIDER_BAND0_1"/>
- <reg32 offset="0x000F8" name="VMODE_CTRL1"/>
- <reg32 offset="0x000FC" name="VMODE_CTRL2"/>
- <reg32 offset="0x00100" name="TX_ALOG_INTF_OBSV_CNTL"/>
- <reg32 offset="0x00104" name="BIST_STATUS"/>
- <reg32 offset="0x00108" name="BIST_ERROR_COUNT1"/>
- <reg32 offset="0x0010C" name="BIST_ERROR_COUNT2"/>
- <reg32 offset="0x00110" name="TX_ALOG_INTF_OBSV"/>
-</domain>
-
-<domain name="HDMI_8998_PHY" width="32">
- <reg32 offset="0x00000" name="CFG"/>
- <reg32 offset="0x00004" name="PD_CTL"/>
- <reg32 offset="0x00010" name="MODE"/>
- <reg32 offset="0x0005C" name="CLOCK"/>
- <reg32 offset="0x00068" name="CMN_CTRL"/>
- <reg32 offset="0x000B4" name="STATUS"/>
-</domain>
-
-<domain name="HDMI_8998_PHY_QSERDES_COM" width="32">
- <reg32 offset="0x0000" name="ATB_SEL1"/>
- <reg32 offset="0x0004" name="ATB_SEL2"/>
- <reg32 offset="0x0008" name="FREQ_UPDATE"/>
- <reg32 offset="0x000C" name="BG_TIMER"/>
- <reg32 offset="0x0010" name="SSC_EN_CENTER"/>
- <reg32 offset="0x0014" name="SSC_ADJ_PER1"/>
- <reg32 offset="0x0018" name="SSC_ADJ_PER2"/>
- <reg32 offset="0x001C" name="SSC_PER1"/>
- <reg32 offset="0x0020" name="SSC_PER2"/>
- <reg32 offset="0x0024" name="SSC_STEP_SIZE1"/>
- <reg32 offset="0x0028" name="SSC_STEP_SIZE2"/>
- <reg32 offset="0x002C" name="POST_DIV"/>
- <reg32 offset="0x0030" name="POST_DIV_MUX"/>
- <reg32 offset="0x0034" name="BIAS_EN_CLKBUFLR_EN"/>
- <reg32 offset="0x0038" name="CLK_ENABLE1"/>
- <reg32 offset="0x003C" name="SYS_CLK_CTRL"/>
- <reg32 offset="0x0040" name="SYSCLK_BUF_ENABLE"/>
- <reg32 offset="0x0044" name="PLL_EN"/>
- <reg32 offset="0x0048" name="PLL_IVCO"/>
- <reg32 offset="0x004C" name="CMN_IETRIM"/>
- <reg32 offset="0x0050" name="CMN_IPTRIM"/>
- <reg32 offset="0x0060" name="CP_CTRL_MODE0"/>
- <reg32 offset="0x0064" name="CP_CTRL_MODE1"/>
- <reg32 offset="0x0068" name="PLL_RCTRL_MODE0"/>
- <reg32 offset="0x006C" name="PLL_RCTRL_MODE1"/>
- <reg32 offset="0x0070" name="PLL_CCTRL_MODE0"/>
- <reg32 offset="0x0074" name="PLL_CCTRL_MODE1"/>
- <reg32 offset="0x0078" name="PLL_CNTRL"/>
- <reg32 offset="0x007C" name="BIAS_EN_CTRL_BY_PSM"/>
- <reg32 offset="0x0080" name="SYSCLK_EN_SEL"/>
- <reg32 offset="0x0084" name="CML_SYSCLK_SEL"/>
- <reg32 offset="0x0088" name="RESETSM_CNTRL"/>
- <reg32 offset="0x008C" name="RESETSM_CNTRL2"/>
- <reg32 offset="0x0090" name="LOCK_CMP_EN"/>
- <reg32 offset="0x0094" name="LOCK_CMP_CFG"/>
- <reg32 offset="0x0098" name="LOCK_CMP1_MODE0"/>
- <reg32 offset="0x009C" name="LOCK_CMP2_MODE0"/>
- <reg32 offset="0x00A0" name="LOCK_CMP3_MODE0"/>
- <reg32 offset="0x00B0" name="DEC_START_MODE0"/>
- <reg32 offset="0x00B4" name="DEC_START_MODE1"/>
- <reg32 offset="0x00B8" name="DIV_FRAC_START1_MODE0"/>
- <reg32 offset="0x00BC" name="DIV_FRAC_START2_MODE0"/>
- <reg32 offset="0x00C0" name="DIV_FRAC_START3_MODE0"/>
- <reg32 offset="0x00C4" name="DIV_FRAC_START1_MODE1"/>
- <reg32 offset="0x00C8" name="DIV_FRAC_START2_MODE1"/>
- <reg32 offset="0x00CC" name="DIV_FRAC_START3_MODE1"/>
- <reg32 offset="0x00D0" name="INTEGLOOP_INITVAL"/>
- <reg32 offset="0x00D4" name="INTEGLOOP_EN"/>
- <reg32 offset="0x00D8" name="INTEGLOOP_GAIN0_MODE0"/>
- <reg32 offset="0x00DC" name="INTEGLOOP_GAIN1_MODE0"/>
- <reg32 offset="0x00E0" name="INTEGLOOP_GAIN0_MODE1"/>
- <reg32 offset="0x00E4" name="INTEGLOOP_GAIN1_MODE1"/>
- <reg32 offset="0x00E8" name="VCOCAL_DEADMAN_CTRL"/>
- <reg32 offset="0x00EC" name="VCO_TUNE_CTRL"/>
- <reg32 offset="0x00F0" name="VCO_TUNE_MAP"/>
- <reg32 offset="0x0124" name="CMN_STATUS"/>
- <reg32 offset="0x0128" name="RESET_SM_STATUS"/>
- <reg32 offset="0x0138" name="CLK_SEL"/>
- <reg32 offset="0x013C" name="HSCLK_SEL"/>
- <reg32 offset="0x0148" name="CORECLK_DIV_MODE0"/>
- <reg32 offset="0x0150" name="SW_RESET"/>
- <reg32 offset="0x0154" name="CORE_CLK_EN"/>
- <reg32 offset="0x0158" name="C_READY_STATUS"/>
- <reg32 offset="0x015C" name="CMN_CONFIG"/>
- <reg32 offset="0x0164" name="SVS_MODE_CLK_SEL"/>
-</domain>
-
-<domain name="HDMI_8998_PHY_TXn" width="32">
- <reg32 offset="0x0000" name="EMP_POST1_LVL"/>
- <reg32 offset="0x0008" name="INTERFACE_SELECT_TX_BAND"/>
- <reg32 offset="0x000C" name="CLKBUF_TERM_ENABLE"/>
- <reg32 offset="0x0014" name="DRV_LVL_RES_CODE_OFFSET"/>
- <reg32 offset="0x0018" name="DRV_LVL"/>
- <reg32 offset="0x001C" name="LANE_CONFIG"/>
- <reg32 offset="0x0024" name="PRE_DRIVER_1"/>
- <reg32 offset="0x0028" name="PRE_DRIVER_2"/>
- <reg32 offset="0x002C" name="LANE_MODE"/>
-</domain>
-
</database>
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
+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 platforms:
+ MSM8260, MSM8660, MSM8960, MSM8974, APQ8060, APQ8064, APQ8074 and APQ8084.
+
+ 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.
+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) += phy-qcom-apq8064-sata.o
obj-$(CONFIG_PHY_QCOM_EDP) += phy-qcom-edp.o
obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
+obj-$(CONFIG_PHY_QCOM_HDMI) += phy-qcom-hdmi.o
+
+phy-qcom-hdmi-y := \
+ 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) += phy-qcom-m31.o
obj-$(CONFIG_PHY_QCOM_M31_EUSB) += phy-qcom-m31-eusb2.o
obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o
obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o phy-qcom-qmp-usbc.o
+obj-$(CONFIG_PHY_QCOM_QMP_HDMI) += phy-qcom-qmp-hdmi.o
obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o
obj-$(CONFIG_PHY_QCOM_QMP_PCIE_8996) += phy-qcom-qmp-pcie-msm8996.o
obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o
obj-$(CONFIG_PHY_QCOM_QMP_USB) += phy-qcom-qmp-usb.o
obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) += phy-qcom-qmp-usb-legacy.o
+phy-qcom-qmp-hdmi-y := \
+ phy-qcom-qmp-hdmi-base.o \
+ phy-qcom-qmp-hdmi-msm8996.o \
+ phy-qcom-qmp-hdmi-msm8998.o \
+
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o
obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) += phy-qcom-uniphy-pcie-28lp.o
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c b/drivers/phy/qualcomm/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 <robdclark@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#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 *hdmi_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 *hdmi_phy)
+{
+ writel(0x7f, hdmi_phy->phy_reg + REG_HDMI_8x74_PD_CTRL0);
+
+ return 0;
+}
+
+const struct clk_parent_data msm8974_hdmi_pll_parent = {
+ .fw_name = "xo", .name = "xo_board",
+};
+
+const struct qcom_hdmi_preqmp_cfg msm8974_hdmi_phy_cfg = {
+ .clk_names = { "iface", "alt_iface" },
+ .num_clks = 2,
+
+ .reg_names = { "vddio", "core-vdda" },
+ .reg_init_load = { 100000, 10000 },
+ .num_regs = 2,
+
+ .power_on = qcom_hdmi_msm8974_phy_power_on,
+ .power_off = qcom_hdmi_msm8974_phy_power_off,
+
+ .pll_parent = &msm8974_hdmi_pll_parent,
+};
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c b/drivers/phy/qualcomm/phy-qcom-hdmi-28lpm.c
similarity index 52%
rename from drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c
rename to drivers/phy/qualcomm/phy-qcom-hdmi-28lpm.c
index 6ba6bbdb7e05..f1e7113e10bd 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c
+++ b/drivers/phy/qualcomm/phy-qcom-hdmi-28lpm.c
@@ -1,31 +1,152 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
+ * Copyright (c) 2023, Linaro Ltd.
+ * Copyright (c) 2025, Qualcomm Incorporated
* Author: Rob Clark <robdclark@gmail.com>
*/
-#include <linux/clk-provider.h>
#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
-#include "hdmi.h"
+#include "phy-qcom-hdmi-preqmp.h"
-struct hdmi_pll_8960 {
- struct platform_device *pdev;
- struct clk_hw clk_hw;
- void __iomem *mmio;
+#define REG_HDMI_8960_PHY_REG0 0x00000000
- unsigned long pixclk;
-};
+#define REG_HDMI_8960_PHY_REG1 0x00000004
-#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw)
+#define REG_HDMI_8960_PHY_REG2 0x00000008
-/*
- * HDMI PLL:
- *
- * To get the parent clock setup properly, we need to plug in hdmi pll
- * configuration into common-clock-framework.
- */
+#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
struct pll_rate {
unsigned long rate;
@@ -234,34 +355,96 @@ static const struct pll_rate freqtbl[] = {
},
};
-static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data)
+static const struct pll_rate *qcom_hdmi_8960_pll_find_rate(unsigned long rate)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
+ if (rate > freqtbl[i].rate)
+ return &freqtbl[i - 1];
+
+ return &freqtbl[i - 1];
+}
+
+static inline u32 read24(void __iomem *reg)
{
- writel(data, pll->mmio + reg);
+ u32 val = readl(reg);
+
+ val |= readl(reg + 4) << 8;
+ val |= readl(reg + 8) << 16;
+
+ return val;
}
-static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg)
+/* This function is close to UNIPHY, but it has slighly different fields */
+static unsigned long qcom_28lpm_recalc(struct qcom_hdmi_preqmp_phy *hdmi_phy,
+ unsigned long parent_rate)
{
- return readl(pll->mmio + reg);
+ unsigned long rate;
+ u32 refclk_cfg;
+ u32 dc_offset;
+ u64 fraq_n;
+ u32 val;
+
+ refclk_cfg = readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_REFCLK_CFG);
+ parent_rate /= FIELD_GET(HDMI_8960_PHY_CLK0_DIV, refclk_cfg) + 1;
+ if (refclk_cfg & HDMI_8960_PHY_DBLR_EN)
+ parent_rate *= 2;
+
+ val = readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG0);
+ if (val & HDMI_8960_PHY_SDM_BYP) {
+ dc_offset = FIELD_GET(HDMI_8960_PHY_BYP_DIV, val);
+ fraq_n = 0;
+ } else {
+ val = readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG1);
+ dc_offset = FIELD_GET(HDMI_8960_PHY_DC_OFFSET, val);
+ fraq_n = read24(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_SDM_CFG2);
+ }
+
+ rate = (dc_offset + 1) * parent_rate;
+ rate += div_u64(fraq_n * parent_rate, 0x10000);
+
+ return rate;
}
-static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll)
+static const unsigned int qcom_hdmi_8960_divs[] = {1, 2, 4, 6};
+
+static unsigned long qcom_hdmi_8960_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- return platform_get_drvdata(pll->pdev);
+ struct qcom_hdmi_preqmp_phy *hdmi_phy = hw_clk_to_phy(hw);
+ u32 refclk_cfg = readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_REFCLK_CFG);
+ u32 div_idx = FIELD_GET(HDMI_8960_PHY_PLL_VCO_DIV, refclk_cfg);
+ unsigned long rate = qcom_28lpm_recalc(hdmi_phy, parent_rate);
+
+ return rate / HDMI_8960_COMMON_DIV / qcom_hdmi_8960_divs[div_idx];
}
-static int hdmi_pll_enable(struct clk_hw *hw)
+static int qcom_hdmi_8960_pll_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = pll_get_phy(pll);
- int timeout_count, pll_lock_retry = 10;
- unsigned int val;
+ const struct pll_rate *pll_rate = qcom_hdmi_8960_pll_find_rate(req->rate);
- DBG("");
+ req->rate = pll_rate->rate;
+
+ return 0;
+}
+
+static const struct clk_ops qcom_hdmi_8960_pll_ops = {
+ .recalc_rate = qcom_hdmi_8960_pll_recalc_rate,
+ .determine_rate = qcom_hdmi_8960_pll_determine_rate,
+};
+
+static int qcom_hdmi_msm8960_phy_pll_enable(struct qcom_hdmi_preqmp_phy *hdmi_phy)
+{
+ int pll_lock_retry = 10;
+ unsigned int val;
+ int ret;
/* 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);
+ 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.
@@ -271,12 +454,12 @@ static int hdmi_pll_enable(struct clk_hw *hw)
udelay(10);
/* De-assert PLL S/W reset */
- pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
+ writel(0x0d, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2);
- val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
+ val = readl(hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
val |= HDMI_8960_PHY_REG12_SW_RESET;
/* Assert PHY S/W reset */
- hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+ writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
val &= ~HDMI_8960_PHY_REG12_SW_RESET;
/*
* Wait for a short time before de-asserting to allow the hardware to
@@ -285,176 +468,127 @@ static int hdmi_pll_enable(struct clk_hw *hw)
*/
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);
+ writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
+ writel(0x3f, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2);
- val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
+ val = readl(hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
val |= HDMI_8960_PHY_REG12_PWRDN_B;
- hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+ writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
/* Wait 10 us for enabling global power for PHY */
mb();
udelay(10);
- val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
+ val = readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B);
val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
val &= ~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);
+ writel(val, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B);
+ writel(0x80, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2);
- timeout_count = 1000;
while (--pll_lock_retry > 0) {
- /* are we there yet? */
- val = pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0);
- if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK)
+ ret = readl_poll_timeout(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_STATUS0,
+ val, val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK,
+ 1, 1000);
+ if (!ret)
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);
+ writel(0x8d, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2);
udelay(10);
- pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
+ 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);
-
- timeout_count = 1000;
}
+ return ret;
+}
+
+static int qcom_hdmi_msm8960_phy_set_rate(struct qcom_hdmi_preqmp_phy *hdmi_phy)
+{
+ unsigned long long pixclk = hdmi_phy->hdmi_opts.tmds_char_rate;
+ const struct pll_rate *pll_rate = qcom_hdmi_8960_pll_find_rate(pixclk);
+ int i;
+
+ for (i = 0; i < pll_rate->num_reg; i++)
+ writel(pll_rate->conf[i].val, hdmi_phy->pll_reg + pll_rate->conf[i].reg);
+
return 0;
}
-static void hdmi_pll_disable(struct clk_hw *hw)
+static void qcom_hdmi_msm8960_phy_pll_disable(struct qcom_hdmi_preqmp_phy *hdmi_phy)
{
- struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
- struct hdmi_phy *phy = pll_get_phy(pll);
unsigned int val;
- DBG("");
-
- val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12);
+ val = readl(hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
- hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val);
+ writel(val, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG12);
- val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B);
+ val = readl(hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B);
val |= HDMI_8960_PHY_REG12_SW_RESET;
val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
- pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
+ writel(val, hdmi_phy->pll_reg + REG_HDMI_8960_PHY_PLL_PWRDN_B);
/* Make sure HDMI PHY/PLL are powered down */
mb();
}
-static const struct pll_rate *find_rate(unsigned long rate)
+static int qcom_hdmi_msm8960_phy_power_on(struct qcom_hdmi_preqmp_phy *hdmi_phy)
{
- int i;
-
- for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
- if (rate > freqtbl[i].rate)
- return &freqtbl[i - 1];
+ int ret;
- return &freqtbl[i - 1];
-}
-
-static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
-
- return pll->pixclk;
-}
+ ret = qcom_hdmi_msm8960_phy_set_rate(hdmi_phy);
+ if (ret)
+ return ret;
-static int hdmi_pll_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
-{
- const struct pll_rate *pll_rate = find_rate(req->rate);
+ ret = qcom_hdmi_msm8960_phy_pll_enable(hdmi_phy);
+ if (ret)
+ return ret;
- req->rate = pll_rate->rate;
+ 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 hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long parent_rate)
+static int qcom_hdmi_msm8960_phy_power_off(struct qcom_hdmi_preqmp_phy *hdmi_phy)
{
- struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw);
- const struct pll_rate *pll_rate = find_rate(rate);
- int i;
+ writel(0x7f, hdmi_phy->phy_reg + REG_HDMI_8960_PHY_REG2);
- DBG("rate=%lu", rate);
-
- for (i = 0; i < pll_rate->num_reg; i++)
- pll_write(pll, pll_rate->conf[i].reg, pll_rate->conf[i].val);
-
- pll->pixclk = rate;
+ qcom_hdmi_msm8960_phy_pll_disable(hdmi_phy);
return 0;
}
-static const struct clk_ops hdmi_pll_ops = {
- .enable = hdmi_pll_enable,
- .disable = hdmi_pll_disable,
- .recalc_rate = hdmi_pll_recalc_rate,
- .determine_rate = hdmi_pll_determine_rate,
- .set_rate = hdmi_pll_set_rate,
-};
-
-static const struct clk_parent_data hdmi_pll_parents[] = {
- { .fw_name = "pxo", .name = "pxo_board" },
+const struct clk_parent_data msm8960_hdmi_pll_parent = {
+ .fw_name = "pxo", .name = "pxo_board",
};
-static struct clk_init_data pll_init = {
- .name = "hdmi_pll",
- .ops = &hdmi_pll_ops,
- .parent_data = hdmi_pll_parents,
- .num_parents = ARRAY_SIZE(hdmi_pll_parents),
- .flags = CLK_IGNORE_UNUSED,
-};
-
-int msm_hdmi_pll_8960_init(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct hdmi_pll_8960 *pll;
- int i, ret;
-
- /* sanity check: */
- for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
- if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate))
- return -EINVAL;
-
- pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
- if (!pll)
- return -ENOMEM;
-
- pll->mmio = msm_ioremap(pdev, "hdmi_pll");
- if (IS_ERR(pll->mmio)) {
- DRM_DEV_ERROR(dev, "failed to map pll base\n");
- return -ENOMEM;
- }
-
- pll->pdev = pdev;
- pll->clk_hw.init = &pll_init;
+const struct qcom_hdmi_preqmp_cfg msm8960_hdmi_phy_cfg = {
+ .clk_names = { "slave_iface" },
+ .num_clks = 1,
- ret = devm_clk_hw_register(dev, &pll->clk_hw);
- if (ret < 0) {
- DRM_DEV_ERROR(dev, "failed to register pll clock\n");
- return ret;
- }
+ .reg_names = { "core-vdda" },
+ .num_regs = 1,
- ret = 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;
- }
+ .power_on = qcom_hdmi_msm8960_phy_power_on,
+ .power_off = qcom_hdmi_msm8960_phy_power_off,
- return 0;
-}
+ .pll_ops = &qcom_hdmi_8960_pll_ops,
+ .pll_parent = &msm8960_hdmi_pll_parent,
+};
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c b/drivers/phy/qualcomm/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 <robdclark@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+
+#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 *hdmi_phy)
+{
+ unsigned long pixclock = 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 == 27 * HZ_PER_MHZ) {
+ /* video_format == 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 = 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 *hdmi_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 = {
+ .clk_names = { "slave_iface" },
+ .num_clks = 1,
+
+ .reg_names = { "core-vdda" },
+ .num_regs = 1,
+
+ .power_on = qcom_hdmi_msm8x60_phy_power_on,
+ .power_off = qcom_hdmi_msm8x60_phy_power_off,
+
+ /* FIXME: no PLL support */
+};
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c
new file mode 100644
index 000000000000..7715e5c9b1e4
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 Red Hat
+ * Copyright (c) 2023, Linaro Ltd.
+ * Copyright (c) 2025, Qualcomm Incorporated
+ */
+
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#include "phy-qcom-hdmi-preqmp.h"
+
+static int qcom_hdmi_preqmp_phy_init(struct phy *phy)
+{
+ struct qcom_hdmi_preqmp_phy *hdmi_phy = 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 = phy_get_drvdata(phy);
+
+ return pm_runtime_put_sync(hdmi_phy->dev);
+}
+
+static int qcom_hdmi_preqmp_phy_power_on(struct phy *phy)
+{
+ struct qcom_hdmi_preqmp_phy *hdmi_phy = 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 = phy_get_drvdata(phy);
+
+ return hdmi_phy->power_off(hdmi_phy);
+};
+
+static int qcom_hdmi_preqmp_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ const struct phy_configure_opts_hdmi *hdmi_opts = &opts->hdmi;
+ struct qcom_hdmi_preqmp_phy *hdmi_phy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ memcpy(&hdmi_phy->hdmi_opts, hdmi_opts, sizeof(*hdmi_opts));
+
+ return ret;
+}
+
+static int __maybe_unused qcom_hdmi_preqmp_runtime_resume(struct device *dev)
+{
+ struct qcom_hdmi_preqmp_phy *hdmi_phy = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(hdmi_phy->num_regs, hdmi_phy->regs);
+ if (ret)
+ return ret;
+
+ ret = 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 = 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 = {
+ .init = qcom_hdmi_preqmp_phy_init,
+ .configure = qcom_hdmi_preqmp_phy_configure,
+ .power_on = qcom_hdmi_preqmp_phy_power_on,
+ .power_off = qcom_hdmi_preqmp_phy_power_off,
+ .exit = qcom_hdmi_preqmp_phy_exit,
+ .owner = 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 = &pdev->dev;
+ struct qcom_hdmi_preqmp_phy *hdmi_phy;
+ const struct qcom_hdmi_preqmp_cfg *cfg;
+ int i, ret;
+
+ cfg = of_device_get_match_data(dev);
+ if (!cfg)
+ return -EINVAL;
+
+ hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL);
+ if (!hdmi_phy)
+ return -ENOMEM;
+
+ hdmi_phy->dev = dev;
+
+ hdmi_phy->phy_reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmi_phy->phy_reg))
+ return PTR_ERR(hdmi_phy->phy_reg);
+
+ hdmi_phy->pll_reg = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(hdmi_phy->pll_reg))
+ return PTR_ERR(hdmi_phy->pll_reg);
+
+ hdmi_phy->num_clks = cfg->num_clks;
+ for (i = 0; i < cfg->num_clks; i++)
+ hdmi_phy->clks[i].id = cfg->clk_names[i];
+ ret = devm_clk_bulk_get(dev, hdmi_phy->num_clks, hdmi_phy->clks);
+ if (ret)
+ return ret;
+
+ hdmi_phy->num_regs = cfg->num_regs;
+ for (i = 0; i < cfg->num_regs; i++) {
+ hdmi_phy->regs[i].supply = cfg->reg_names[i];
+ hdmi_phy->regs[i].init_load_uA = cfg->reg_init_load[i];
+ }
+ ret = devm_regulator_bulk_get(dev, hdmi_phy->num_regs, hdmi_phy->regs);
+ if (ret)
+ return ret;
+
+ hdmi_phy->power_on = cfg->power_on;
+ hdmi_phy->power_off = cfg->power_off;
+
+ platform_set_drvdata(pdev, hdmi_phy);
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
+
+ ret = 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 = "hdmipll";
+ init.ops = cfg->pll_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_data = cfg->pll_parent;
+ init.num_parents = 1;
+
+ hdmi_phy->pll_hw.init = &init;
+ ret = devm_clk_hw_register(hdmi_phy->dev, &hdmi_phy->pll_hw);
+ if (ret)
+ goto err;
+
+ ret = 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 = devm_phy_create(dev, pdev->dev.of_node, &qcom_hdmi_preqmp_phy_ops);
+ if (IS_ERR(hdmi_phy->phy)) {
+ ret = PTR_ERR(hdmi_phy->phy);
+ goto err;
+ }
+
+ phy_set_drvdata(hdmi_phy->phy, hdmi_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ pm_runtime_put_sync(&pdev->dev);
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err:
+ pm_runtime_put_sync(&pdev->dev);
+ return ret;
+}
+
+static const struct of_device_id qcom_hdmi_preqmp_of_match_table[] = {
+ { .compatible = "qcom,hdmi-phy-8084", .data = &msm8974_hdmi_phy_cfg, },
+ { .compatible = "qcom,hdmi-phy-8660", .data = &msm8x60_hdmi_phy_cfg, },
+ { .compatible = "qcom,hdmi-phy-8960", .data = &msm8960_hdmi_phy_cfg, },
+ { .compatible = "qcom,hdmi-phy-8974", .data = &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 = {
+ .probe = qcom_hdmi_preqmp_probe,
+ .driver = {
+ .name = "qcom-preqmp-hdmi-phy",
+ .of_match_table = qcom_hdmi_preqmp_of_match_table,
+ .pm = &qcom_hdmi_preqmp_pm_ops,
+ },
+};
+
+module_platform_driver(qcom_hdmi_preqmp_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
+MODULE_DESCRIPTION("Qualcomm MSMpreqmp HDMI PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h b/drivers/phy/qualcomm/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 <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/phy/phy-hdmi.h>
+#include <linux/regulator/consumer.h>
+
+#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/qualcomm/phy-qcom-qmp-hdmi-base.c
new file mode 100644
index 000000000000..7ea8295f7f40
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-hdmi-base.c
@@ -0,0 +1,185 @@
+// 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 <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#include "phy-qcom-qmp-hdmi.h"
+
+int qmp_hdmi_phy_init(struct phy *phy)
+{
+ struct qmp_hdmi_phy *hdmi_phy = 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 = &opts->hdmi;
+ struct qmp_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
+ int ret = 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 = phy_get_drvdata(phy);
+
+ return pm_runtime_put_sync(hdmi_phy->dev);
+}
+
+static int __maybe_unused qmp_hdmi_runtime_resume(struct device *dev)
+{
+ struct qmp_hdmi_phy *hdmi_phy = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(hdmi_phy->supplies), hdmi_phy->supplies);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(hdmi_phy->clks), hdmi_phy->clks);
+ 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 = 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 = {
+ .name = "hdmipll",
+ .parent_data = (const struct clk_parent_data[]) {
+ { .fw_name = "xo", .name = "xo_board" },
+ },
+ .flags = CLK_GET_RATE_NOCACHE,
+ .num_parents = 1,
+ };
+ const struct qmp_hdmi_cfg *cfg = of_device_get_match_data(&pdev->dev);
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct qmp_hdmi_phy *hdmi_phy;
+ int ret, i;
+
+ hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL);
+ if (!hdmi_phy)
+ return -ENOMEM;
+
+ hdmi_phy->dev = dev;
+
+ hdmi_phy->serdes = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmi_phy->serdes))
+ return PTR_ERR(hdmi_phy->serdes);
+
+ for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
+ hdmi_phy->tx[i] = 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 = 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 = "iface";
+ hdmi_phy->clks[1].id = "ref";
+ ret = devm_clk_bulk_get(dev, ARRAY_SIZE(hdmi_phy->clks), hdmi_phy->clks);
+ if (ret)
+ return ret;
+
+ hdmi_phy->supplies[0].supply = "vddio";
+ hdmi_phy->supplies[0].init_load_uA = 100000;
+ hdmi_phy->supplies[1].supply = "vcca";
+ hdmi_phy->supplies[1].init_load_uA = 10000;
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hdmi_phy->supplies), hdmi_phy->supplies);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, hdmi_phy);
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret)
+ return ret;
+
+ init.ops = cfg->pll_ops;
+ hdmi_phy->pll_hw.init = &init;
+ ret = devm_clk_hw_register(hdmi_phy->dev, &hdmi_phy->pll_hw);
+ if (ret)
+ goto err;
+
+ ret = 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 = devm_phy_create(dev, pdev->dev.of_node, cfg->phy_ops);
+ if (IS_ERR(hdmi_phy->phy)) {
+ ret = PTR_ERR(hdmi_phy->phy);
+ goto err;
+ }
+
+ phy_set_drvdata(hdmi_phy->phy, hdmi_phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ pm_runtime_put_sync(&pdev->dev);
+ return PTR_ERR_OR_ZERO(phy_provider);
+
+err:
+ pm_runtime_put_sync(&pdev->dev);
+ return ret;
+}
+
+static const struct of_device_id qmp_hdmi_of_match_table[] = {
+ {
+ .compatible = "qcom,hdmi-phy-8996", .data = &qmp_hdmi_8996_cfg,
+ }, {
+ .compatible = "qcom,hdmi-phy-8998", .data = &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 = {
+ .probe = qmp_hdmi_probe,
+ .driver = {
+ .name = "qcom-qmp-hdmi-phy",
+ .of_match_table = qmp_hdmi_of_match_table,
+ .pm = &qmp_hdmi_pm_ops,
+ },
+};
+
+module_platform_driver(qmp_hdmi_driver);
+
+MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>");
+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 <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
+
+#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 = readl(reg);
+
+ val |= readl(reg + 4) << 8;
+ val |= readl(reg + 8) << 16;
+
+ return val;
+}
+
+static inline u32 qmp_hdmi_8996_pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)
+{
+ u64 dividend = HDMI_PLL_CMP_CNT * fdata;
+ u32 divisor = ref_clk * 10;
+ u32 rem;
+
+ rem = 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_divider *pd, u64 bclk)
+{
+ int ratio[] = { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 };
+ int hs_divsel[] = { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 };
+ int tx_band_sel[] = { 0, 1, 2, 3 };
+ u64 vco_freq[60];
+ u64 vco, vco_optimal;
+ int half_rate_mode = 0;
+ int vco_optimal_index, vco_freq_index;
+ int i, j;
+
+retry:
+ vco_optimal = HDMI_VCO_MAX_FREQ;
+ vco_optimal_index = -1;
+ vco_freq_index = 0;
+ for (i = 0; i < 15; i++) {
+ for (j = 0; j < 4; j++) {
+ u32 ratio_mult = ratio[i] << tx_band_sel[j];
+
+ vco = bclk >> half_rate_mode;
+ vco *= ratio_mult;
+ vco_freq[vco_freq_index++] = vco;
+ }
+ }
+
+ for (i = 0; i < 60; i++) {
+ u64 vco_tmp = vco_freq[i];
+
+ if ((vco_tmp >= HDMI_VCO_MIN_FREQ) &&
+ (vco_tmp <= vco_optimal)) {
+ vco_optimal = vco_tmp;
+ vco_optimal_index = i;
+ }
+ }
+
+ if (vco_optimal_index == -1) {
+ if (!half_rate_mode) {
+ half_rate_mode = 1;
+ goto retry;
+ }
+
+ return -EINVAL;
+ }
+
+ pd->vco_freq = vco_optimal;
+ pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4];
+ pd->vco_ratio = ratio[vco_optimal_index / 4];
+ pd->hsclk_divsel = 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 = HDMI_DEFAULT_REF_CLOCK;
+ unsigned long rate = hdmi_phy->hdmi_opts.tmds_char_rate;
+ struct qmp_hdmi_8996_post_divider pd;
+ bool gen_ssc = 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 = ((u64)rate) * 10;
+ ret = qmp_hdmi_8996_pll_get_post_div(&pd, bclk);
+ if (ret) {
+ dev_err(hdmi_phy->dev, "PLL calculation failed\n");
+ return ret;
+ }
+
+ dec_start = pd.vco_freq;
+ pll_divisor = 4 * parent_rate;
+ do_div(dec_start, pll_divisor);
+
+ frac_start = pd.vco_freq * (1 << 20);
+
+ rem = do_div(frac_start, pll_divisor);
+ frac_start -= dec_start * (1 << 20);
+ if (rem > (pll_divisor >> 1))
+ frac_start++;
+
+ fdata = pd.vco_freq;
+ do_div(fdata, pd.vco_ratio);
+
+ pll_cmp = 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 != 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 = (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 = (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 <<= 1;
+ } else {
+ writel(2, hdmi_phy->serdes + QSERDES_COM_SVS_MODE_CLK_SEL);
+ integloop_gain <<= 2;
+ }
+
+ integloop_gain = 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_MODE0);
+
+ 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 = 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 != 3 ? 0xc : 0x3, hdmi_phy->tx[i] + QSERDES_TX_HP_PD_ENABLES);
+ }
+
+ if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) {
+ for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
+ writel(i != 3 ? 0x25 : 0x22, hdmi_phy->tx[i] + QSERDES_TX_TX_DRV_LVL);
+ writel(i != 3 ? 0x23 : 0x27, hdmi_phy->tx[i] + QSERDES_TX_TX_EMP_POST1_LVL);
+ writel(i != 3 ? 0x0d : 0x00, hdmi_phy->tx[i] + QSERDES_TX_VMODE_CTRL2);
+ }
+ } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) {
+ for (i = 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 != 3 ? 0x0d : 0x00, hdmi_phy->tx[i] + QSERDES_TX_VMODE_CTRL2);
+ }
+ } else {
+ for (i = 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 = phy_get_drvdata(phy);
+ u32 status;
+ int i, ret = 0;
+
+ ret = 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 = 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 = 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 = 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 = 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 = 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 = hw_clk_to_pll(hw);
+ u32 pll_cmp = 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 = hw_clk_to_pll(hw);
+ u32 status;
+ int pll_locked;
+
+ status = readl(phy->serdes + QSERDES_COM_C_READY_STATUS);
+ pll_locked = status & BIT(0);
+
+ return pll_locked;
+}
+
+static const struct clk_ops qmp_hdmi_8996_pll_ops = {
+ .recalc_rate = qmp_hdmi_8996_pll_recalc_rate,
+ .determine_rate = qmp_hdmi_8996_pll_determine_rate,
+ .is_enabled = qmp_hdmi_8996_pll_is_enabled,
+};
+
+static const struct phy_ops qmp_hdmi_8996_phy_ops = {
+ .init = qmp_hdmi_phy_init,
+ .configure = qmp_hdmi_phy_configure,
+ .power_on = qmp_hdmi_8996_phy_power_on,
+ .power_off = qmp_hdmi_8996_phy_power_off,
+ .exit = qmp_hdmi_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+const struct qmp_hdmi_cfg qmp_hdmi_8996_cfg = {
+ .pll_ops = &qmp_hdmi_8996_pll_ops,
+ .phy_ops = &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 <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/phy/phy.h>
+#include <linux/units.h>
+
+#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 = readl(reg);
+
+ val |= readl(reg + 4) << 8;
+ val |= readl(reg + 8) << 16;
+
+ return val;
+}
+
+static inline u32 qmp_hdmi_8998_pll_get_pll_cmp(u64 fdata, unsigned long ref_clk)
+{
+ u64 dividend = HDMI_PLL_CMP_CNT * fdata;
+ u32 divisor = ref_clk * 10;
+ u32 rem;
+
+ rem = 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_divider *pd, u64 bclk)
+{
+ u32 const ratio_list[] = {1, 2, 3, 4, 5, 6, 9, 10, 12, 15, 25};
+ u32 const band_list[] = {0, 1, 2, 3};
+ u32 const sz_ratio = ARRAY_SIZE(ratio_list);
+ u32 const sz_band = ARRAY_SIZE(band_list);
+ u32 const cmp_cnt = 1024;
+ u32 const th_min = 500, th_max = 1000;
+ u32 half_rate_mode = 0;
+ u32 list_elements;
+ int optimal_index;
+ u32 i, j, k;
+ u32 found_hsclk_divsel = 0, found_vco_ratio;
+ u32 found_tx_band_sel;
+ u64 const min_freq = HDMI_VCO_MIN_FREQ, max_freq = 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 = max_freq;
+ optimal_index = -1;
+ list_elements = 0;
+
+ for (i = 0; i < sz_ratio; i++) {
+ for (j = 0; j < sz_band; j++) {
+ u64 freq = div_u64(bclk, (1 << half_rate_mode));
+
+ freq *= (ratio_list[i] * (1 << band_list[j]));
+ freq_list[list_elements++] = freq;
+ }
+ }
+
+ for (k = 0; k < ARRAY_SIZE(freq_list); k++) {
+ u32 const clks_pll_div = 2, core_clk_div = 5;
+ u32 const rng1 = 16, rng2 = 8;
+ u32 th1, th2;
+ u64 core_clk, rvar1, rem;
+
+ core_clk = div_u64(freq_list[k],
+ ratio_list[k / sz_band] * clks_pll_div *
+ core_clk_div);
+
+ rvar1 = HDMI_DEFAULT_REF_CLOCK * rng1 * HZ_PER_MHZ;
+ rvar1 = div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem);
+ if (rem > ((cmp_cnt * core_clk) >> 1))
+ rvar1++;
+ th1 = rvar1;
+
+ rvar1 = HDMI_DEFAULT_REF_CLOCK * rng2 * HZ_PER_MHZ;
+ rvar1 = div64_u64_rem(rvar1, (cmp_cnt * core_clk), &rem);
+ if (rem > ((cmp_cnt * core_clk) >> 1))
+ rvar1++;
+ th2 = rvar1;
+
+ if (freq_list[k] >= min_freq &&
+ freq_list[k] <= max_freq) {
+ if ((th1 >= th_min && th1 <= th_max) ||
+ (th2 >= th_min && th2 <= th_max)) {
+ if (freq_list[k] <= freq_optimal) {
+ freq_optimal = freq_list[k];
+ optimal_index = k;
+ }
+ }
+ }
+ }
+
+ if (optimal_index == -1) {
+ if (!half_rate_mode) {
+ half_rate_mode = 1;
+ goto find_optimal_index;
+ } else {
+ return -EINVAL;
+ }
+ } else {
+ found_vco_ratio = ratio_list[optimal_index / sz_band];
+ found_tx_band_sel = band_list[optimal_index % sz_band];
+ found_vco_freq = freq_optimal;
+ }
+
+ switch (found_vco_ratio) {
+ case 1:
+ found_hsclk_divsel = 15;
+ break;
+ case 2:
+ found_hsclk_divsel = 0;
+ break;
+ case 3:
+ found_hsclk_divsel = 4;
+ break;
+ case 4:
+ found_hsclk_divsel = 8;
+ break;
+ case 5:
+ found_hsclk_divsel = 12;
+ break;
+ case 6:
+ found_hsclk_divsel = 1;
+ break;
+ case 9:
+ found_hsclk_divsel = 5;
+ break;
+ case 10:
+ found_hsclk_divsel = 2;
+ break;
+ case 12:
+ found_hsclk_divsel = 9;
+ break;
+ case 15:
+ found_hsclk_divsel = 13;
+ break;
+ case 25:
+ found_hsclk_divsel = 14;
+ break;
+ }
+
+ pd->vco_freq = found_vco_freq;
+ pd->tx_band_sel = found_tx_band_sel;
+ pd->vco_ratio = found_vco_ratio;
+ pd->hsclk_divsel = found_hsclk_divsel;
+
+ return 0;
+}
+
+static int qmp_hdmi_8998_phy_set_rate(struct qmp_hdmi_phy *hdmi_phy)
+{
+ unsigned long parent_rate = HDMI_DEFAULT_REF_CLOCK;
+ unsigned long rate = hdmi_phy->hdmi_opts.tmds_char_rate;
+ struct qmp_hdmi_8998_post_divider pd;
+ bool gen_ssc = 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 = ((u64)rate) * 10;
+ ret = qmp_hdmi_8998_pll_get_post_div(&pd, bclk);
+ if (ret) {
+ dev_err(hdmi_phy->dev, "PLL calculation failed\n");
+ return ret;
+ }
+
+ dec_start = pd.vco_freq;
+ pll_divisor = 4 * parent_rate;
+ do_div(dec_start, pll_divisor);
+
+ frac_start = pd.vco_freq * (1 << 20);
+
+ rem = do_div(frac_start, pll_divisor);
+ frac_start -= dec_start * (1 << 20);
+ if (rem > (pll_divisor >> 1))
+ frac_start++;
+
+ fdata = pd.vco_freq;
+ do_div(fdata, pd.vco_ratio);
+
+ pll_cmp = 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 = 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 != 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 = 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 = 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 <<= 1;
+ } else {
+ writel(2, hdmi_phy->serdes + QSERDES_V3_COM_SVS_MODE_CLK_SEL);
+ integloop_gain <<= 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_SEL);
+ 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_MODE0);
+
+ 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 = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
+ writel(0xf,
+ hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL);
+ writel(i == 3 ? 0x00 : i == 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 == 3 ? 0x00 : 0x1c,
+ hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_2);
+ writel((i == 0 || i == 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 = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
+ writel(0x0f,
+ hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL);
+ writel(i == 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 == 3 ? 0x18 : 0x16,
+ hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_PRE_DRIVER_2);
+ writel(i == 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 = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
+ writel(0x0f,
+ hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_DRV_LVL);
+ writel(i == 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 = 0; i < HDMI_NUM_TX_CHANNEL; i++) {
+ writel(i == 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 == 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 = 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 = phy_get_drvdata(phy);
+ u32 status;
+ int i, ret = 0;
+
+ ret = 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 = readl_poll_timeout(hdmi_phy->serdes + QSERDES_V3_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 = 0; i < HDMI_NUM_TX_CHANNEL; i++)
+ writel(0x1f, hdmi_phy->tx[i] + HDMI_8998_PHY_TXn_LANE_CONFIG);
+
+ ret = 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 = 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 = 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 = hw_clk_to_pll(hw);
+ u32 pll_cmp = 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 = hw_clk_to_pll(hw);
+ u32 status;
+ int pll_locked;
+
+ status = readl(phy->serdes + QSERDES_V3_COM_C_READY_STATUS);
+ pll_locked = status & BIT(0);
+
+ return pll_locked;
+}
+
+static const struct clk_ops qmp_hdmi_8998_pll_ops = {
+ .recalc_rate = qmp_hdmi_8998_pll_recalc_rate,
+ .determine_rate = qmp_hdmi_8998_pll_determine_rate,
+ .is_enabled = qmp_hdmi_8998_pll_is_enabled,
+};
+
+static const struct phy_ops qmp_hdmi_8998_phy_ops = {
+ .init = qmp_hdmi_phy_init,
+ .configure = qmp_hdmi_phy_configure,
+ .power_on = qmp_hdmi_8998_phy_power_on,
+ .power_off = qmp_hdmi_8998_phy_power_off,
+ .exit = qmp_hdmi_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+const struct qmp_hdmi_cfg qmp_hdmi_8998_cfg = {
+ .pll_ops = &qmp_hdmi_8998_pll_ops,
+ .phy_ops = &qmp_hdmi_8998_phy_ops,
+};
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-hdmi.h b/drivers/phy/qualcomm/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 <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regulator/consumer.h>
+#include <linux/phy/phy-hdmi.h>
+
+#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
--
2.47.3
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH phy-next 5/5] phy: lynx-28g: add support for 25GBASER
From: Ioana Ciornei @ 2026-05-14 14:39 UTC (permalink / raw)
To: Josua Mayer, Vladimir Oltean
Cc: linux-phy@lists.infradead.org, netdev@vger.kernel.org, Vinod Koul,
Neil Armstrong, linux-kernel@vger.kernel.org, Rabeeh Khoury,
Yazan Shhady
In-Reply-To: <20260513151018.sfyowih53zistli3@skbuf>
On Wed, May 13, 2026 at 06:10:18PM +0300, Vladimir Oltean wrote:
> On Wed, May 13, 2026 at 02:50:38PM +0000, Josua Mayer wrote:
> > With updated compatible string it is working perfectly!
> >
> > I'll send my tested-by on the series, thanks!
>
> Thanks.
>
> > >
> > > You can look at my branch
> > > https://github.com/vladimiroltean/linux/commits/net-phy-upstreaming
> > > specifically commit "arm64: dts: lx2160a: transition to device-specific
> > > SerDes compatible strings".
> >
> > Thanks!
> >
> > I will probably have retimer comments i.e. one phy object ehach for rx, and tx,
> > instead of combined - as we can not rely on hardware designers making clean choices.
> > E.g. imagine two retimer chips, one handling rx direction of 2 ports and one tx of 2 ports.
> >
> > phys = <&serdes_1 0, &retimer_rx 0>, <&retimer_tx 0>;
> >
> > Not relevant to this patch.
>
> Correct. For the retimers we will upstream (Ioana is working on the TI
> DS125DF111), their channels will be split to separate Generic PHY
> objects, to cater specifically to that use case. Hoping that all other
> multi-channel retimer submissions where there is no meaningful
> distinction between RX and TX will do the same.
In case you didn't see, I send yesterday the series adding the TI
DS125DF111 retimer driver that Vladimir was mentioning above.
https://lore.kernel.org/all/20260513185103.1371809-1-ioana.ciornei@nxp.com/
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2 2/4] dt-bindings: phy: qcom,msm8998-qmp-usb3-phy: Add support for Shikra
From: Krzysztof Kozlowski @ 2026-05-14 14:37 UTC (permalink / raw)
To: Krishna Kurapati
Cc: Neil Armstrong, Vinod Koul, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Bjorn Andersson, Konrad Dybcio, Xiangxu Yin,
Johan Hovold, Loic Poulain, Kathiravan Thirumoorthy,
Dmitry Baryshkov, linux-arm-msm, linux-phy, devicetree,
linux-kernel
In-Reply-To: <42ba6ac7-306a-419f-96b6-3427c51ef567@oss.qualcomm.com>
On 14/05/2026 08:22, Krishna Kurapati wrote:
>
>
> On 5/14/2026 12:26 AM, Krzysztof Kozlowski wrote:
>> On 07/05/2026 13:37, Krishna Kurapati wrote:
>>>
>>>
>>> On 5/5/2026 7:30 PM, Krzysztof Kozlowski wrote:
>>>> On 05/05/2026 15:57, Krishna Kurapati wrote:
>>>>>
>>>>>
>>>>> On 5/5/2026 6:59 PM, Krzysztof Kozlowski wrote:
>>>>>> On 05/05/2026 15:27, Krishna Kurapati wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 5/5/2026 4:22 PM, Krzysztof Kozlowski wrote:
>>>>>>>> On 05/05/2026 12:49, Krzysztof Kozlowski wrote:
>>>>>>>>> On Mon, May 04, 2026 at 10:36:57PM +0530, Krishna Kurapati wrote:
>>>>>>>>>> Declare the USB-C QMP PHY present on the Qualcomm Shikra platform.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
>>>>>>>>>> ---
>>>>>>>>>> .../devicetree/bindings/phy/qcom,msm8998-qmp-usb3-phy.yaml | 2 ++
>>>>>>>>>> 1 file changed, 2 insertions(+)
>>>>>>>>>
>>>>>>>>> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
>>>>>>>>
>>>>>>>> ... and then I looked at the driver. So un-reviewed. Devices are clearly
>>>>>>>> compatible. If not, explain what is not compatible.
>>>>>>>>
>>>>>>> Talos uses GCC_USB3_PRIM_PHY_AUX_CLK.
>>>>>>>
>>>>>>> In Shikra, we are using GCC_USB3_PRIM_PHY_COM_AUX_CLK. We don't have
>>>>>>> GCC_USB3_PRIM_PHY_AUX_CLK.
>>>>>>>
>>>>>>> Hence, I didn't use a fallback compatible.
>>>>>>
>>>>>> This still explains nothing. How different clock makes interface for SW
>>>>>> incompatible exactly?
>>>>>>
>>>>> So I went by the naming. AUX vs COM_AUX.
>>>>
>>>> The naming does not matter. If the clock is called
>>>> "no_one_expects_spanish_inquisition", does that make software
>>>> incompatible? Why would the name itself matter?
>>>>
>>>>>
>>>>> Can I use a fallback compatible and in DT vote for "COM_AUX" clock with
>>>>> clock-names mentioning "aux" ?
>>>>
>>>> I don't know, I asked what is different in software interface.
>>>>
>>>
>>> Hi Krzysztof,
>>>
>>> I checked with the hw team here and found out two things.
>>>
>>> 1. Shikra is a spinoff of Agatti and its sw interface (clocks used and
>>> regulators used) is the same as agatti.
>>>
>>> 2. I thought we could use qcm2290 as a fallback since the phy register
>>> init sequence is the same for Talos/Shikra/Agatti. The difference
>>> between Talos and agatti when checked in the driver was the init load
>>> settings. I checked with the hw team and they suggested using the init
>>> load settings which talos was using.
>>>
>>> Hence both these compatibles (qcm2290 and qcs615) cannot be used as
>>> fallback for Shikra.
>>
>> Then I do not understand why you are using qcs615_usb3phy_cfg for
>> Shikra. You say that the initialization is different, but you use
>> exactly the same initialization. So in a meaning of compatibility
>> between hardware for Devicetree they are compatible.
>>
> Hi Krzysztof,
>
> There are 3 things:
>
> 1. Clocks used:
> -> Talos supports AUX Clock since it supports DP over USB.
> -> Agatti and Shikra use COM_AUX clock since they dont support DP over USB.
>
> 2. Phy register Init sequence - same for all 3 targets
>
> 3. Regulator init load:
> -> Different for both Talos and Agatti
> -> Recommendation is to use Talos regulator load values.
>
> SW interface wise, shikra is comaptible with agatti. If we use agatti as
> fallback, we would end up using the platform data of Agatti where the
> regulator init load is not suitable for Shikra. Hence not using Agatti
> as fallback.
>
> Coming to driver changes, I used qcs615_cfg because it has required phy
> register sequence and regulator init load as needed by shikra.
So is it compatible with QCS615? If not, then something is incomplete or
confusing. The driver uses the same software interface.
Best regards,
Krzysztof
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH phy-next 5/5] phy: lynx-28g: add support for 25GBASER
From: Vladimir Oltean @ 2026-05-14 14:32 UTC (permalink / raw)
To: Josua Mayer
Cc: linux-phy@lists.infradead.org, netdev@vger.kernel.org,
Ioana Ciornei, Vinod Koul, Neil Armstrong,
linux-kernel@vger.kernel.org
In-Reply-To: <7aa87d24-5501-4ca4-bb39-791beb7c95f2@solid-run.com>
Hi Josua,
On Wed, May 13, 2026 at 11:41:06AM +0000, Josua Mayer wrote:
> Am 13.05.26 um 13:22 schrieb Vladimir Oltean:
>
> > On Wed, May 13, 2026 at 11:00:32AM +0000, Josua Mayer wrote:
> >> Wouldn't it be more clear instead of indirect lane offset shift with
> >> lynx_28g_e25g_pcvt, to instead fix the E25G_CFG definition?:
> >>
> >> -#define E25G_CFG(id) (28 - (id) * 4) /* Offset into PCCD */
> >> +#define E25G_CFG(id) ((id) * 4) /* Offset into PCCD */
> >>
> >> This is equivalent when inserting (7 - lane) into E25G_CFG id:
> >>
> >> (28 - (id) * 4) = (28 - (7 - lane) * 4) = (28 - 7*4 + lane*4)
> > The 'indirect' lane offset shift is actually a two-step lookup:
> > lane -> protocol converter index
> > protocol converted index -> offset into PCCD
> >
> > LX2160ARM documents PCCD fields as:
> > E25GA_CFG, aka E25G_CFG(0) in code: 30:28
> > E25GB_CFG, aka E25G_CFG(1) in code: 26:24
> > ...
> > E25GH_CFG, aka E25G_CFG(7) in code: 2:0
> >
> > The odd bit is that lane 0 uses E25G protocol converter 7, unlike, say,
> > 1G and 10G where we have a lane:pcvt identity mapping.
> > lynx_28g_e25g_pcvt() performs that translation.
> >
> > Additionally, for locating E40GA_CFG, E40GB_CFG in PCCE, I've adopted
> > the same scheme downstream, where E40G_CFG() returns bits 30:28 for
> > argument 0 (pcvt A) and bits 26:24 for argument 1 (pcvt B), and it is
> > called with this lane->pcvt translation function:
> >
> > static int lynx_28g_e40g_pcvt(int lane)
> > {
> > return lane < 4 ? 1 : 0;
> > }
> >
> > Are you saying that merging the two lookups would be more clear because
> > as a reader you'd get to ask yourself less questions (the code would
> > flow more naturally) despite the non-trivial lane<->pcvt mapping,
> Exactly for this reason. Fewer look-up steps.
> RM dos not document this mapping, but now you do in source-code.
>
> Perhaps a short comment on the function will help, e.g.
> /* get protocol converter id for lane */
>
> > or why
> > exactly? For me it is the exact opposite. I can follow the RM
> > definitions and then I have a separate function that tells me how lanes
> > are mapped to the protocol converters.
> I think you give a good reason here!
So you would like me to respin this series? What change would address
your feedback here? Keep the E25G_CFG() macros as is, just add this?
+/* get protocol converter id for lane */
static int lynx_28g_e25g_pcvt(int lane)
{
return 7 - lane;
}
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 2/2] phy: qcom-qmp-pcie: Add support for ipq5210 PCIe phys
From: sashiko-bot @ 2026-05-14 12:52 UTC (permalink / raw)
To: Varadarajan Narayanan
Cc: olteanv, linux-phy, krzk+dt, devicetree, conor+dt, vkoul, robh,
neil.armstrong
In-Reply-To: <20260514-pci-phy-v1-2-482429192746@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Redundant array duplication and misleading naming for PHY init tables
--
commit 1a4fb65a84494415ea2c151ef7e1a6c4328d3d7f
Author: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
phy: qcom-qmp-pcie: Add support for ipq5210 PCIe phys
This commit adds support for the PCIe PHYs found on the Qualcomm
ipq5210 platform.
> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
> @@ -514,6 +514,105 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_gen3_pcs_misc_tbl[] = {
[ ... ]
> +static const struct qmp_phy_init_tbl ipq5210_gen3x1_pcie_ep_tx_tbl[] = {
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x06),
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
> +};
Does this new table duplicate the existing ipq6018_pcie_tx_tbl? Could we
reuse the existing table instead of creating a redundant copy?
[ ... ]
> +static const struct qmp_phy_init_tbl ipq5210_gen3x1_pcie_ep_pcs_tbl[] = {
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_FLL_CNTRL1, 0x01),
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x0d),
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB, 0x10),
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L, 0x01),
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_DCC_CAL_CONFIG, 0x01),
> + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x01),
> +};
Is it possible this is an exact duplicate of ipq6018_pcie_pcs_tbl?
[ ... ]
> @@ -654,6 +753,13 @@ static const struct qmp_phy_init_tbl ipq9574_gen3x2_pcie_serdes_tbl[] = {
[ ... ]
> +static const struct qmp_phy_init_tbl ipq9574_pcie_tx_tbl[] = {
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_HIGHZ_DRVR_EN, 0x10),
> + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x06),
> +};
Could this table be entirely replaced by ipq8074_pcie_gen3_tx_tbl?
Also, does the name ipq9574_pcie_tx_tbl accurately reflect its usage? It
seems to be used exclusively by the newly added ipq5210_gen3x1_pciephy_cfg,
while the actual IPQ9574 configurations retain their use of the ipq8074 table.
[ ... ]
> @@ -3613,6 +3719,49 @@ static const struct qmp_phy_cfg ipq6018_pciephy_cfg = {
[ ... ]
> +static const struct qmp_phy_cfg ipq5210_gen3x1_pciephy_cfg = {
> + .lanes = 1,
> +
> + .offsets = &qmp_pcie_offsets_v4x1,
> +
> + .tbls = {
> + .serdes = ipq9574_gen3x1_pcie_serdes_tbl,
> + .serdes_num = ARRAY_SIZE(ipq9574_gen3x1_pcie_serdes_tbl),
> + .tx = ipq9574_pcie_tx_tbl,
> + .tx_num = ARRAY_SIZE(ipq9574_pcie_tx_tbl),
If ipq9574_pcie_tx_tbl is identical to ipq8074_pcie_gen3_tx_tbl, should
this reference the existing ipq8074_pcie_gen3_tx_tbl instead?
> + .rx = ipq9574_pcie_rx_tbl,
> + .rx_num = ARRAY_SIZE(ipq9574_pcie_rx_tbl),
[ ... ]
> + .tbls_ep = &(const struct qmp_phy_cfg_tbls) {
> + .serdes = ipq5210_gen3x1_pcie_ep_serdes_tbl,
> + .serdes_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_serdes_tbl),
> + .tx = ipq5210_gen3x1_pcie_ep_tx_tbl,
> + .tx_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_tx_tbl),
> + .rx = ipq5210_gen3x1_pcie_ep_rx_tbl,
> + .rx_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_rx_tbl),
> + .pcs = ipq5210_gen3x1_pcie_ep_pcs_tbl,
> + .pcs_num = ARRAY_SIZE(ipq5210_gen3x1_pcie_ep_pcs_tbl),
Similarly, could these point to ipq6018_pcie_tx_tbl and
ipq6018_pcie_pcs_tbl respectively?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260514-pci-phy-v1-0-482429192746@oss.qualcomm.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: phy: qcom,ipq8074-qmp-pcie: Document the ipq5210 QMP PCIe PHY
From: sashiko-bot @ 2026-05-14 12:05 UTC (permalink / raw)
To: Varadarajan Narayanan
Cc: krzk+dt, linux-phy, conor+dt, robh, vkoul, neil.armstrong,
olteanv, devicetree
In-Reply-To: <20260514-pci-phy-v1-1-482429192746@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The compatible string `qcom,ipq5210-qmp-gen3x1-pcie-phy` is added to both the standalone enum and the fallback items list, allowing it to bypass the intended fallback requirement.
--
commit 55b201f9c0cef59e94dd05d524ed71d29f4e86c9
Author: Varadarajan Narayanan <varadarajan.narayanan@oss.qualcomm.com>
dt-bindings: phy: qcom,ipq8074-qmp-pcie: Document the ipq5210 QMP PCIe PHY
This commit documents the ipq5210 QMP PCIe PHY. It aims to use the ipq9574
bindings as a fallback, as the hardware is similar.
> diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml
> index f60804687412a..1ac31439c20a0 100644
> --- a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml
> +++ b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml
> @@ -17,6 +17,7 @@ properties:
> compatible:
> oneOf:
> - enum:
> + - qcom,ipq5210-qmp-gen3x1-pcie-phy
The commit message states the intent to document the IPQ5210 PHY "using the
ipq9574 bindings as a fallback". Does adding the compatible string to this
standalone enum allow device trees to omit the fallback and still pass
validation?
Since this is inside a oneOf block, a device tree could specify just this
string without the ipq9574 fallback, defeating the intended requirement.
> - qcom,ipq6018-qmp-pcie-phy
> - qcom,ipq8074-qmp-gen3-pcie-phy
[ ... ]
> @@ -24,10 +25,12 @@ properties:
> - qcom,ipq9574-qmp-gen3x2-pcie-phy
> - items:
> - enum:
> + - qcom,ipq5210-qmp-gen3x1-pcie-phy
> - qcom,ipq5424-qmp-gen3x1-pcie-phy
> - const: qcom,ipq9574-qmp-gen3x1-pcie-phy
> - items:
> - enum:
> + - qcom,ipq5210-qmp-gen3x2-pcie-phy
> - qcom,ipq5424-qmp-gen3x2-pcie-phy
> - const: qcom,ipq9574-qmp-gen3x2-pcie-phy
The gen3x2 variant added here correctly enforces the fallback because it
was not added to the standalone enum above.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260514-pci-phy-v1-0-482429192746@oss.qualcomm.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v2] phy: ti: pipe3: Fix clock resource leak on probe errors
From: sashiko-bot @ 2026-05-14 12:02 UTC (permalink / raw)
To: Hongling Zeng; +Cc: vkoul, neil.armstrong, linux-phy, olteanv
In-Reply-To: <20260514023424.23815-1-zenghongling@kylinos.cn>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Unchecked clk_prepare_enable() return value leads to unbalanced clock disable.
- [Medium] Inverted resource teardown order in the error path.
--
commit 34a06b14102bc841b336d48ee9651b777cd39eb7
Author: Hongling Zeng <zenghongling@kylinos.cn>
phy: ti: pipe3: Fix clock resource leak on probe errors
When devm_phy_create() or devm_of_phy_provider_register() fails,
this commit adds an error handling path to disable the refclk that
was enabled earlier, preventing a resource leak.
> diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c
> --- a/drivers/phy/ti/phy-ti-pipe3.c
> +++ b/drivers/phy/ti/phy-ti-pipe3.c
> @@ -837,15 +837,28 @@ static int ti_pipe3_probe(struct platform_device *pdev)
[ ... ]
> +err_clk_disable:
> + pm_runtime_disable(dev);
> + if (phy->sata_refclk_enabled)
> + clk_disable_unprepare(phy->refclk);
> + return ret;
> }
Is the teardown order in this new error path correct?
During probe, pm_runtime_enable() is called before clk_prepare_enable().
Should clk_disable_unprepare() be called before pm_runtime_disable() to
follow reverse-initialization order, similar to the existing logic in
ti_pipe3_remove()?
Additionally, looking at the clock initialization earlier in
ti_pipe3_probe():
if (phy->mode == PIPE3_MODE_SATA) {
if (!IS_ERR(phy->refclk)) {
clk_prepare_enable(phy->refclk);
phy->sata_refclk_enabled = true;
}
}
The return value of clk_prepare_enable() is not checked, and
phy->sata_refclk_enabled is set to true regardless of whether the clock
was successfully enabled.
If clk_prepare_enable() fails, and a subsequent step like
devm_phy_create() also fails, the code will jump out to err_clk_disable
and call clk_disable_unprepare() because phy->sata_refclk_enabled is
true.
Does this introduce a regression by causing an unbalanced clock disable?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260514023424.23815-1-zenghongling@kylinos.cn?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH v9 5/5] phy: qualcomm: add MSM8974 HDMI PHY support
From: sashiko-bot @ 2026-05-14 11:14 UTC (permalink / raw)
To: Dmitry Baryshkov; +Cc: linux-phy, neil.armstrong, vkoul, olteanv
In-Reply-To: <20260513-fd-hdmi-phy-v9-5-ca98c72f1f9f@oss.qualcomm.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Integer truncation when passing vco_freq to write16() writes garbage calibration data
- [High] Concurrent access to PLL registers between clock and PHY frameworks without a shared lock
- [High] User-controllable input triggers a WARN_ON(), enabling a kernel panic DoS
- [Medium] Error path in qcom_hdmi_msm8974_phy_pll_enable() leaks power by failing to restore PD_CTRL0
- [Medium] The read24() helper lacks bitmasks, leading to potential data corruption from upper register bits
--
commit d2fb8e86ab045b987f00fdf674f81b4bc2d4b950
Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
phy: qualcomm: add MSM8974 HDMI PHY support
Add support for HDMI PHY on Qualcomm MSM8974 / APQ8074 platforms.
> diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c b/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c
> index 720757f8f3931..7d398060b3a3c 100644
> --- a/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c
> +++ b/drivers/phy/qualcomm/phy-qcom-hdmi-28hpm.c
[ ... ]
> @@ -31,8 +34,282 @@
> #define REG_HDMI_8x74_BIST_PATN3 0x00000048
> #define REG_HDMI_8x74_STATUS 0x0000005c
[ ... ]
> +static inline u32 read24(void __iomem *reg)
> +{
> + u32 val = readl(reg);
> +
> + val |= readl(reg + 4) << 8;
> + val |= readl(reg + 8) << 16;
Can this result in data corruption if the hardware bus returns non-zero upper
bits for these registers?
Unlike write24() which correctly masks its values with & 0xff, the readl()
results are not masked before bit-shifting. If the hardware returns reserved
or uninitialized upper bits, they will bleed into and corrupt the assembled
24-bit value used in qcom_uniphy_recalc().
> +
> + 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)
> +{
[ ... ]
> + write16(mult_frac(ref_freq, 5, 1000000), base + UNIPHY_PLL_CAL_CFG8);
> + write16(vco_freq / 16, base + UNIPHY_PLL_CAL_CFG10);
Will this cause integer truncation and misconfigure the PLL calibration?
The vco_freq parameter can be up to 1.8 GHz, meaning vco_freq / 16 can yield
a value up to ~112.5 million, which requires 27 bits. Since write16() takes a
u16 parameter, the upper 11 bits are silently discarded, and only the lower 16
bits are written to the CFG10 and CFG11 registers.
> +}
> +
> +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 = readl(base + UNIPHY_PLL_REFCLK_CFG);
[ ... ]
> + val = readl(base + UNIPHY_PLL_SDM_CFG0);
> + if (FIELD_GET(UNIPHY_PLL_SDM_BYP, val)) {
> + dc_offset = FIELD_GET(UNIPHY_PLL_SDM_BYP_DIV, val);
> + fraq_n = 0;
> + } else {
> + dc_offset = FIELD_GET(UNIPHY_PLL_SDM_DC_OFFSET,
> + readl(base + UNIPHY_PLL_SDM_CFG1));
> + fraq_n = read24(base + UNIPHY_PLL_SDM_CFG2);
> + }
Does this code race with qcom_hdmi_msm8974_phy_power_on()?
The PHY driver exposes a clock provider, and this recalc_rate callback reads
shared PLL hardware registers protected by the clock framework's prepare_lock.
However, qcom_hdmi_msm8974_phy_power_on() writes to these exact same PLL
registers while holding an unrelated PHY mutex.
Without a shared lock synchronizing these paths, reading from the clk_summary
debugfs file concurrently with a PHY power-on could cause torn reads,
especially since the 24-bit fractional divider is written across three
separate writel() calls.
[ ... ]
> +static int qcom_hdmi_msm8974_phy_pll_set_rate(struct qcom_hdmi_preqmp_phy *hdmi_phy)
> +{
> + unsigned long long pixclk = hdmi_phy->hdmi_opts.tmds_char_rate;
> + unsigned long vco_rate;
> + unsigned int div;
> + int div_idx = 0;
> +
> + div_idx = qcom_hdmi_msm8974_phy_find_div(pixclk);
> + if (WARN_ON(div_idx < 0))
> + return div_idx;
Is it safe to use a WARN_ON() for user-controllable input?
The requested pixel clock (hdmi_opts.tmds_char_rate) is influenced by DRM
modesetting or an external monitor's EDID. An unprivileged user could trigger
this path by requesting an unsupported display mode, intentionally hitting
this WARN_ON().
On systems configured with panic_on_warn=1, this would crash the kernel,
potentially leading to a local denial of service.
[ ... ]
> +static int qcom_hdmi_msm8974_phy_pll_enable(struct qcom_hdmi_preqmp_phy *hdmi_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);
[ ... ]
> + /* Poll for PHY ready status */
> + ret = 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;
> +}
Does this error path leave the PHY partially powered up?
During the power-on sequence, the PHY is powered up by writing to
REG_HDMI_8x74_PD_CTRL0. If the subsequent polling fails, the code jumps to
the err label.
The error path clears the global configuration registers but fails to write
0x7f back to REG_HDMI_8x74_PD_CTRL0 to power down the generator. Because the
function returns an error, the PHY framework will not invoke the power_off
callback, leaking power indefinitely.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260513-fd-hdmi-phy-v9-0-ca98c72f1f9f@oss.qualcomm.com?part=5
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* [PATCH] phy: renesas: rcar-gen3-usb2: Avoid long delay in atomic context
From: Claudiu Beznea @ 2026-05-14 11:13 UTC (permalink / raw)
To: yoshihiro.shimoda.uh, vkoul, neil.armstrong, geert+renesas,
magnus.damm, prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, linux-renesas-soc, linux-phy, linux-kernel,
Claudiu Beznea, stable, Pavel Machek, Nobuhiro Iwamatsu
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
The OTG PHY initialization sequence needs to wait for 20 ms at a specific
step, as described in commit 72c0339c115b ("phy: renesas:
rcar-gen3-usb2: follow the hardware manual procedure").
Commit 55a387ebb921 ("phy: renesas: rcar-gen3-usb2: Lock around hardware
registers and driver data") tried to address various problems in the
rcar-gen3-usb2 driver and converted the mutex protecting HW register
accesses to a spin lock, leaving, however, a long delay in the critical
section protected by the spin lock. This may become a problem,
especially on RT kernels.
To address this, release the spin lock before sleeping for 20 ms as
required by the HW manual and reacquire it afterwards. To avoid other
threads entering the critical section and configuring the HW while the
software is waiting for the OTG initialization to complete, introduce the
otg_initializing variable alongside the otg_init_done completion. Any
other thread trying to configure the HW while the OTG PHY initialization
is in progress waits for the completion instead of immediately returning
errors to PHY users. The IRQs were also disabled while waiting for the OTG
PHY initialization to complete, as the interrupt handler may also apply HW
settings.
Fixes: 55a387ebb921 ("phy: renesas: rcar-gen3-usb2: Lock around hardware registers and driver data")
Cc: stable@vger.kernel.org
Reported-by: Pavel Machek <pavel@nabladev.com>
Closes: https://lore.kernel.org/all/afhkX2Ys2BG1gnqy@duo.ucw.cz
Reported-by: Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
Closes: https://lore.kernel.org/all/afhkX2Ys2BG1gnqy@duo.ucw.cz
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
drivers/phy/renesas/phy-rcar-gen3-usb2.c | 163 ++++++++++++++++++-----
1 file changed, 132 insertions(+), 31 deletions(-)
diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
index 9a45d840efeb..bdb64726f4cf 100644
--- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c
@@ -15,6 +15,7 @@
#include <linux/extcon-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mux/consumer.h>
@@ -138,12 +139,15 @@ struct rcar_gen3_chan {
struct rcar_gen3_phy rphys[NUM_OF_PHYS];
struct regulator *vbus;
struct work_struct work;
+ struct completion otg_init_done;
spinlock_t lock; /* protects access to hardware and driver data structure. */
enum usb_dr_mode dr_mode;
+ int irq;
bool extcon_host;
bool is_otg_channel;
bool uses_otg_pins;
bool otg_internal_reg;
+ bool otg_initializing;
};
struct rcar_gen3_phy_drv_data {
@@ -386,32 +390,68 @@ static bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch)
return true;
}
+static int rcar_gen3_phy_wait_otg_init(struct rcar_gen3_chan *channel,
+ unsigned long *flags)
+{
+ unsigned long timeout = msecs_to_jiffies(25);
+ unsigned long ret = 1;
+
+ lockdep_assert_held(&channel->lock);
+
+ /*
+ * The OTG can be initialized only once and needs to release the lock
+ * and wait for 20 ms due to hardware constraints. Wait for the OTG PHY
+ * initialization to complete if another PHY executes configuration
+ * code while the OTG PHY is waiting. This avoids returning failures to
+ * PHY users.
+ */
+ if (READ_ONCE(channel->otg_initializing)) {
+ spin_unlock_irqrestore(&channel->lock, *flags);
+
+ ret = wait_for_completion_timeout(&channel->otg_init_done, timeout);
+
+ spin_lock_irqsave(&channel->lock, *flags);
+ }
+
+ return !ret ? -ETIMEDOUT : 0;
+}
+
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
bool is_b_device;
enum phy_mode cur_mode, new_mode;
+ unsigned long flags;
+ int ret = -EIO;
- guard(spinlock_irqsave)(&ch->lock);
+ spin_lock_irqsave(&ch->lock, flags);
if (!ch->is_otg_channel || !rcar_gen3_is_any_otg_rphy_initialized(ch))
- return -EIO;
+ goto unlock;
+
+ ret = rcar_gen3_phy_wait_otg_init(ch, &flags);
+ if (ret)
+ goto unlock;
- if (sysfs_streq(buf, "host"))
+ if (sysfs_streq(buf, "host")) {
new_mode = PHY_MODE_USB_HOST;
- else if (sysfs_streq(buf, "peripheral"))
+ } else if (sysfs_streq(buf, "peripheral")) {
new_mode = PHY_MODE_USB_DEVICE;
- else
- return -EINVAL;
+ } else {
+ ret = -EINVAL;
+ goto unlock;
+ }
/* is_b_device: true is B-Device. false is A-Device. */
is_b_device = rcar_gen3_check_id(ch);
cur_mode = rcar_gen3_get_phy_mode(ch);
/* If current and new mode is the same, this returns the error */
- if (cur_mode == new_mode)
- return -EINVAL;
+ if (cur_mode == new_mode) {
+ ret = -EINVAL;
+ goto unlock;
+ }
if (new_mode == PHY_MODE_USB_HOST) { /* And is_host must be false */
if (!is_b_device) /* A-Peripheral */
@@ -425,7 +465,10 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
rcar_gen3_init_for_peri(ch);
}
- return count;
+unlock:
+ spin_unlock_irqrestore(&ch->lock, flags);
+
+ return ret ?: count;
}
static ssize_t role_show(struct device *dev, struct device_attribute *attr,
@@ -441,14 +484,11 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(role);
-static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
+static void rcar_gen3_init_otg_phase0(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->base;
u32 val;
- if (!ch->is_otg_channel || rcar_gen3_is_any_otg_rphy_initialized(ch))
- return;
-
/* Should not use functions of read-modify-write a register */
val = readl(usb2_base + USB2_LINECTRL1);
val = (val & ~USB2_LINECTRL1_DP_RPD) | USB2_LINECTRL1_DPRPD_EN |
@@ -471,7 +511,11 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
}
}
- mdelay(20);
+}
+
+static void rcar_gen3_init_otg_phase1(struct rcar_gen3_chan *ch)
+{
+ void __iomem *usb2_base = ch->base;
writel(0xffffffff, usb2_base + USB2_OBINTSTA);
writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTEN);
@@ -510,6 +554,11 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
goto rpm_put;
scoped_guard(spinlock, &ch->lock) {
+ if (READ_ONCE(ch->otg_initializing)) {
+ dev_warn(dev, "%s: Got IRQ while waiting for OTG init!\n", __func__);
+ return IRQ_NONE;
+ }
+
status = readl(usb2_base + USB2_OBINTSTA);
if (status & ch->phy_data->obint_enable_bits) {
dev_vdbg(dev, "%s: %08x\n", __func__, status);
@@ -533,9 +582,15 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
+ unsigned long flags;
u32 val;
+ int ret;
- guard(spinlock_irqsave)(&channel->lock);
+ spin_lock_irqsave(&channel->lock, flags);
+
+ ret = rcar_gen3_phy_wait_otg_init(channel, &flags);
+ if (ret)
+ goto unlock;
/* Initialize USB2 part */
val = readl(usb2_base + USB2_INT_ENABLE);
@@ -548,8 +603,22 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
}
/* Initialize otg part (only if we initialize a PHY with IRQs). */
- if (rphy->int_enable_bits)
- rcar_gen3_init_otg(channel);
+ if (rphy->int_enable_bits && channel->is_otg_channel &&
+ !rcar_gen3_is_any_otg_rphy_initialized(channel)) {
+ rcar_gen3_init_otg_phase0(channel);
+ disable_irq_nosync(channel->irq);
+ reinit_completion(&channel->otg_init_done);
+ WRITE_ONCE(channel->otg_initializing, true);
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ fsleep(20000);
+
+ spin_lock_irqsave(&channel->lock, flags);
+ WRITE_ONCE(channel->otg_initializing, false);
+ complete_all(&channel->otg_init_done);
+ enable_irq(channel->irq);
+ rcar_gen3_init_otg_phase1(channel);
+ }
if (channel->phy_data->vblvl_ctrl) {
/* SIDDQ mode release */
@@ -568,7 +637,10 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
rphy->initialized = true;
- return 0;
+unlock:
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ return ret;
}
static int rcar_gen3_phy_usb2_exit(struct phy *p)
@@ -576,9 +648,15 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
+ unsigned long flags;
u32 val;
+ int ret;
- guard(spinlock_irqsave)(&channel->lock);
+ spin_lock_irqsave(&channel->lock, flags);
+
+ ret = rcar_gen3_phy_wait_otg_init(channel, &flags);
+ if (ret)
+ goto unlock;
rphy->initialized = false;
@@ -588,7 +666,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p)
val &= ~USB2_INT_ENABLE_UCOM_INTEN;
writel(val, usb2_base + USB2_INT_ENABLE);
- return 0;
+unlock:
+ spin_unlock_irqrestore(&channel->lock, flags);
+ return ret;
}
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
@@ -596,6 +676,7 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
void __iomem *usb2_base = channel->base;
+ unsigned long flags;
u32 val;
int ret = 0;
@@ -605,11 +686,15 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
return ret;
}
- guard(spinlock_irqsave)(&channel->lock);
+ spin_lock_irqsave(&channel->lock, flags);
if (!rcar_gen3_are_all_rphys_power_off(channel))
goto out;
+ ret = rcar_gen3_phy_wait_otg_init(channel, &flags);
+ if (ret)
+ goto unlock;
+
val = readl(usb2_base + USB2_USBCTR);
val |= USB2_USBCTR_PLL_RST;
writel(val, usb2_base + USB2_USBCTR);
@@ -620,27 +705,41 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
/* The powered flag should be set for any other phys anyway */
rphy->powered = true;
- return 0;
+unlock:
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ if (ret && channel->vbus && !channel->otg_internal_reg)
+ regulator_disable(channel->vbus);
+
+ return ret;
}
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
{
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
struct rcar_gen3_chan *channel = rphy->ch;
- int ret = 0;
+ unsigned long flags;
+ int ret;
- scoped_guard(spinlock_irqsave, &channel->lock) {
- rphy->powered = false;
+ spin_lock_irqsave(&channel->lock, flags);
- if (rcar_gen3_are_all_rphys_power_off(channel)) {
- u32 val = readl(channel->base + USB2_USBCTR);
+ ret = rcar_gen3_phy_wait_otg_init(channel, &flags);
+ if (ret)
+ goto unlock;
- val |= USB2_USBCTR_PLL_RST;
- writel(val, channel->base + USB2_USBCTR);
- }
+ rphy->powered = false;
+
+ if (rcar_gen3_are_all_rphys_power_off(channel)) {
+ u32 val = readl(channel->base + USB2_USBCTR);
+
+ val |= USB2_USBCTR_PLL_RST;
+ writel(val, channel->base + USB2_USBCTR);
}
- if (channel->vbus && !channel->otg_internal_reg)
+unlock:
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ if (!ret && channel->vbus && !channel->otg_internal_reg)
ret = regulator_disable(channel->vbus);
return ret;
@@ -1007,6 +1106,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
return ret;
spin_lock_init(&channel->lock);
+ init_completion(&channel->otg_init_done);
for (i = 0; i < NUM_OF_PHYS; i++) {
channel->rphys[i].phy = devm_phy_create(dev, NULL,
channel->phy_data->phy_usb2_ops);
@@ -1048,6 +1148,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret,
"Failed to request irq (%d)\n",
irq);
+ channel->irq = irq;
}
provider = devm_of_phy_provider_register(dev, rcar_gen3_phy_usb2_xlate);
--
2.43.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related
* Re: [PATCH phy-next 0/2] phy: ti: add driver for TI DS125DF111 Dual-Channel Retimer
From: Vladimir Oltean @ 2026-05-14 10:52 UTC (permalink / raw)
To: Ioana Ciornei
Cc: vkoul, neil.armstrong, robh, krzk+dt, conor+dt, johan, linux-phy,
devicetree, linux-kernel
In-Reply-To: <20260513185103.1371809-1-ioana.ciornei@nxp.com>
On Wed, May 13, 2026 at 09:51:01PM +0300, Ioana Ciornei wrote:
> This patch set adds a generic PHY driver and the corresponding DT
> binding for the TI DS125DF111 Dual-Channel retimer. The datasheet on
> which this driver was based on can be found at -
> https://www.ti.com/lit/gpn/DS125DF111.
>
> A separate generic PHY is registered for each of the two channels of the
> retimer, so consumers can drive each channel independently. This allows
> for independent control of the channels, which is especially important
> since each channel can be routed to different SerDes lanes and it is not
> guaranteed that the same retimer will do both directions of SerDes lane.
>
> This was tested on a LS1088ARDB board with the Lynx10G SerDes PHY driver
> yet to be submitted.
>
> Ioana Ciornei (2):
> dt-bindings: phy: add PHY bindings for the TI DS125DF111 Retimer PHY
> phy: ti: add PHY driver for TI DS125DF111 Dual-Channel Retimer
>
> .../bindings/phy/ti,ds125df111.yaml | 46 ++++
> drivers/phy/ti/Kconfig | 10 +
> drivers/phy/ti/Makefile | 1 +
> drivers/phy/ti/phy-ds125df111.c | 245 ++++++++++++++++++
> 4 files changed, 302 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/ti,ds125df111.yaml
> create mode 100644 drivers/phy/ti/phy-ds125df111.c
>
> --
> 2.25.1
When you resend, please add a MAINTAINERS entry for this driver.
Thanks!
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH phy-next 2/2] phy: ti: add PHY driver for TI DS125DF111 Dual-Channel Retimer
From: Ioana Ciornei @ 2026-05-14 10:47 UTC (permalink / raw)
To: sashiko-reviews
Cc: robh, krzk+dt, conor+dt, neil.armstrong, olteanv, devicetree,
vkoul, linux-phy
In-Reply-To: <20260514103143.4B33BC2BCB8@smtp.kernel.org>
On Thu, May 14, 2026 at 10:31:42AM +0000, sashiko-bot@kernel.org wrote:
> [You don't often get email from sashiko-bot@kernel.org. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Medium] Uninitialized memory read in `ds125df111_xlate` due to missing `args_count` validation.
> - [Low] Missing explicit header inclusions for macros and memory allocation functions.
> --
>
> commit a5dcf247e2e2a6b752d667ac46227925aa476def
> Author: Ioana Ciornei <ioana.ciornei@nxp.com>
>
> phy: ti: add PHY driver for TI DS125DF111 Dual-Channel Retimer
>
> This commit adds a generic PHY driver for the TI DS125DF111 Multi-Protocol
> Dual-Channel Retimer over I2C. It currently supports 10G and 1G link speeds,
> and registers a separate PHY device for each of the two channels to allow
> independent configuration.
>
> > diff --git a/drivers/phy/ti/phy-ds125df111.c b/drivers/phy/ti/phy-ds125df111.c
> > new file mode 100644
> > index 0000000000000..8ac10c603bf70
> > --- /dev/null
> > +++ b/drivers/phy/ti/phy-ds125df111.c
> > @@ -0,0 +1,245 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright 2026 NXP */
> > +
> > +#include <linux/i2c.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/phy.h>
> > +#include <linux/phy/phy.h>
>
> This isn't a bug, but should this include linux/bitfield.h, linux/bits.h,
> and linux/slab.h?
>
> The driver uses FIELD_PREP(), GENMASK(), and devm_kzalloc(), but relies on
> implicit inclusions for these headers. Explicitly including them can help
> prevent build breakages if the include chains are refactored in the future.
Ok, sure. Will fix in v2.
>
> [ ... ]
>
> > +static struct phy *ds125df111_xlate(struct device *dev,
> > + const struct of_phandle_args *args)
> > +{
> > + struct ds125df111_priv *priv = dev_get_drvdata(dev);
> > + u32 idx = args->args[0];
>
> Is it possible to read uninitialized memory here?
>
> If a consumer's Device Tree reference incorrectly configures #phy-cells = <0>,
> of_parse_phandle_with_args() sets args->args_count to 0 and leaves the
> args array unmodified.
>
> Since the of_phandle_args structure is often allocated on the stack, for
> example in _of_phy_get(), evaluating args->args[0] without verifying that
> args->args_count >= 1 could result in evaluating stack garbage against the
> maximum channel count.
Good point. Will verify the args_count.
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH 3/4] arm64: dts: qcom: Add Shikra CQM SoM platform
From: Kamal Wadhwa @ 2026-05-14 10:39 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Rakesh Kota, linux-arm-msm, sashiko-reviews, Komal Bajaj, robh,
linux-phy, neil.armstrong, vkoul, olteanv, krzk+dt, conor+dt,
devicetree
In-Reply-To: <0ac665bb-1a21-4077-aa9e-97162f7f4784@oss.qualcomm.com>
On Wed, May 13, 2026 at 06:14:20PM +0300, Dmitry Baryshkov wrote:
> On 13/05/2026 17:29, Rakesh Kota wrote:
> > On Wed, May 13, 2026 at 03:01:47PM +0300, Dmitry Baryshkov wrote:
> > > On Wed, May 13, 2026 at 04:28:35AM +0000, sashiko-bot@kernel.org wrote:
> > > > Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> > > > - [High] The PMIC regulator definitions omit their required input supply dependencies (e.g., `vdd_s2-supply`, `vdd_l3-supply`), breaking the power hierarchy.
> > > > - [Medium] The device tree inaccurately hardcodes the `compatible` string to a different PMIC model (`qcom,rpm-pm2250-regulators`) instead of explicitly identifying the actual hardware (PM4125).
> > > > --
> > > >
> > > > commit 457abf6e7ac410430c866842e08e602ec3daaa51
> > > > Author: Komal Bajaj <komal.bajaj@oss.qualcomm.com>
> > > >
> > > > arm64: dts: qcom: Add Shikra CQM SoM platform
> > > >
> > > > Add device tree include for the CQM variant of the Shikra System-on-Module.
> > > > This variant integrates the Shikra SoC and PM4125 PMIC. The DTSI includes
> > > > the common shikra.dtsi and adds PM4125 PMIC regulator definitions.
> > > >
> > > > > diff --git a/arch/arm64/boot/dts/qcom/shikra-cqm-som.dtsi b/arch/arm64/boot/dts/qcom/shikra-cqm-som.dtsi
> > > > > new file mode 100644
> > > > > index 0000000000000..401e71720519d
> > > > > --- /dev/null
> > > > > +++ b/arch/arm64/boot/dts/qcom/shikra-cqm-som.dtsi
> > > > [ ... ]
> > > > > +&rpm_requests {
> > > > > + regulators {
> > > > > + compatible = "qcom,rpm-pm2250-regulators";
> > > >
> > > > Since the commit message indicates this is a PM4125 PMIC, should the
> > > > compatible string include a specific identifier for the actual hardware?
> > > >
> > > > Using a specific identifier like "qcom,rpm-pm4125-regulators" alongside
> > > > the fallback allows the kernel to apply specific quirks if PM4125 errata
> > > > are discovered later.
> > >
> > > This can be ignored. The compat is a leftover from the historically
> > > incorrect naming.
> > >
> > > >
> > > > > +
> > > > > + pm4125_s2: s2 {
> > > > > + regulator-min-microvolt = <1000000>;
> > > > > + regulator-max-microvolt = <1200000>;
> > > > > + };
> > > >
> > > > Do these regulators need to explicitly define their input supply dependencies
> > > > such as vdd_s2-supply?
> > > >
> > > > Without these properties, the regulator framework might be unaware that the
> > > > PMIC regulators draw power from upstream supplies.
> > > >
> > > > If the kernel dynamically manages the upstream supply and its reference count
> > > > drops to zero, could it be disabled, causing an unexpected power loss for
> > > > downstream components?
> > >
> > > And this is a correct comment. Please provide missing supplies.
> > >
> > As per the Qualcomm system design, the parent-child supply relationship
> > is managed by the RPM firmware, not the Linux regulator framework. The
> > RPM ensures the parent supply is never disabled until all subsystem
> > votes are cleared.
>
> How is this different from other, previous platforms?
This is not different. In the previous platforms too this is taken care from the
RPM/RPMH firmware side, the only case where we may need explicit vote to parent
is for non-rpmh/rpm regulator rails (like i2c based regulator pm8008), which
may have a RPM/RPMH regulator as a parent.
Even on those previous targets the parent rail of all RPM/RPMH regulators are
internally voted by RPM/RPMH FW at proper voltage with required headroom
calculated based on the active child rails. This was done for all the
subsystems (including APPS) regulators.
So no explicit handling from the APPS is required for parent supply.
>
> --
> With best wishes
> Dmitry
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
* Re: [PATCH phy-next 2/2] phy: ti: add PHY driver for TI DS125DF111 Dual-Channel Retimer
From: sashiko-bot @ 2026-05-14 10:31 UTC (permalink / raw)
To: Ioana Ciornei
Cc: robh, krzk+dt, conor+dt, neil.armstrong, olteanv, devicetree,
vkoul, linux-phy
In-Reply-To: <20260513185103.1371809-3-ioana.ciornei@nxp.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Uninitialized memory read in `ds125df111_xlate` due to missing `args_count` validation.
- [Low] Missing explicit header inclusions for macros and memory allocation functions.
--
commit a5dcf247e2e2a6b752d667ac46227925aa476def
Author: Ioana Ciornei <ioana.ciornei@nxp.com>
phy: ti: add PHY driver for TI DS125DF111 Dual-Channel Retimer
This commit adds a generic PHY driver for the TI DS125DF111 Multi-Protocol
Dual-Channel Retimer over I2C. It currently supports 10G and 1G link speeds,
and registers a separate PHY device for each of the two channels to allow
independent configuration.
> diff --git a/drivers/phy/ti/phy-ds125df111.c b/drivers/phy/ti/phy-ds125df111.c
> new file mode 100644
> index 0000000000000..8ac10c603bf70
> --- /dev/null
> +++ b/drivers/phy/ti/phy-ds125df111.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright 2026 NXP */
> +
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/phy.h>
> +#include <linux/phy/phy.h>
This isn't a bug, but should this include linux/bitfield.h, linux/bits.h,
and linux/slab.h?
The driver uses FIELD_PREP(), GENMASK(), and devm_kzalloc(), but relies on
implicit inclusions for these headers. Explicitly including them can help
prevent build breakages if the include chains are refactored in the future.
[ ... ]
> +static struct phy *ds125df111_xlate(struct device *dev,
> + const struct of_phandle_args *args)
> +{
> + struct ds125df111_priv *priv = dev_get_drvdata(dev);
> + u32 idx = args->args[0];
Is it possible to read uninitialized memory here?
If a consumer's Device Tree reference incorrectly configures #phy-cells = <0>,
of_parse_phandle_with_args() sets args->args_count to 0 and leaves the
args array unmodified.
Since the of_phandle_args structure is often allocated on the stack, for
example in _of_phy_get(), evaluating args->args[0] without verifying that
args->args_count >= 1 could result in evaluating stack garbage against the
maximum channel count.
> +
> + if (idx >= DS125DF111_NUM_CH) {
> + dev_err(dev, "Maximum number of channels is %d\n",
> + DS125DF111_NUM_CH);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return priv->ch[idx].phy;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260513185103.1371809-1-ioana.ciornei@nxp.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox