At the moment the driver just sets the clock rate with clk_set_rate(),
and if the resulting rate is not the same as requested, prints a debug
print, but nothing else.
Add functionality to atomic_check(), in which the clk_round_rate() is
used to get the "rounded" rate, and set that to the adjusted_mode.
In practice, with the current K3 SoCs, the display PLL is capable of
producing very exact clocks, so most likely the rounded rate is the same
as the original one.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/tidss/tidss_crtc.c | 23 +++++++++++++++++++----
drivers/gpu/drm/tidss/tidss_dispc.c | 6 ++++++
drivers/gpu/drm/tidss/tidss_dispc.h | 2 ++
3 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
index 1604eca265ef..6c3967f70510 100644
--- a/drivers/gpu/drm/tidss/tidss_crtc.c
+++ b/drivers/gpu/drm/tidss/tidss_crtc.c
@@ -91,7 +91,7 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
struct dispc_device *dispc = tidss->dispc;
struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
u32 hw_videoport = tcrtc->hw_videoport;
- const struct drm_display_mode *mode;
+ struct drm_display_mode *adjusted_mode;
enum drm_mode_status ok;
dev_dbg(ddev->dev, "%s\n", __func__);
@@ -99,12 +99,27 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
if (!crtc_state->enable)
return 0;
- mode = &crtc_state->adjusted_mode;
+ adjusted_mode = &crtc_state->adjusted_mode;
- ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
+ if (drm_atomic_crtc_needs_modeset(crtc_state)) {
+ long rate;
+
+ rate = dispc_vp_round_clk_rate(tidss->dispc,
+ tcrtc->hw_videoport,
+ adjusted_mode->clock * 1000);
+ if (rate < 0)
+ return -EINVAL;
+
+ adjusted_mode->clock = rate / 1000;
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+ }
+
+ ok = dispc_vp_mode_valid(dispc, hw_videoport, adjusted_mode);
if (ok != MODE_OK) {
dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",
- __func__, mode->hdisplay, mode->vdisplay, mode->clock);
+ __func__, adjusted_mode->hdisplay,
+ adjusted_mode->vdisplay, adjusted_mode->clock);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index a5107f2732b1..3930fb7f03c2 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -1318,6 +1318,12 @@ unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
return (unsigned int)(abs(((rr - r) * 100) / r));
}
+long dispc_vp_round_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
+ unsigned long rate)
+{
+ return clk_round_rate(dispc->vp_clk[hw_videoport], rate);
+}
+
int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
unsigned long rate)
{
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
index c31b477a18b0..d4c335e918fb 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -120,6 +120,8 @@ enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
const struct drm_display_mode *mode);
int dispc_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport);
void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport);
+long dispc_vp_round_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
+ unsigned long rate);
int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
unsigned long rate);
void dispc_vp_setup(struct dispc_device *dispc, u32 hw_videoport,
--
2.43.0
Hi Tomi,
While testing Aardhya's OLDI support patches [1], I've noticed that
the resulting LVDS clock is wrong if this patch is applied.
> In practice, with the current K3 SoCs, the display PLL is capable of
> producing very exact clocks, so most likely the rounded rate is the same
> as the original one.
This is now what I'm seeing. Most SoCs have that fixed clock thingy
for (some?) VPs, e.g. [2]. And clk_round_rate() will return the
fixed clock rate for this clock, which will then result in an LVDS
clock which is way off.
I'm testing on an AM67A (J722S) and I've backported some of the
patches as well as dtsi fragmets from downstream. Thus, it might be
as well the case that the fixed-factor-clock node is wrong here.
OTOH other K3 SoCs do this in mainline as well.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
> drivers/gpu/drm/tidss/tidss_crtc.c | 23 +++++++++++++++++++----
> drivers/gpu/drm/tidss/tidss_dispc.c | 6 ++++++
> drivers/gpu/drm/tidss/tidss_dispc.h | 2 ++
> 3 files changed, 27 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
> index 1604eca265ef..6c3967f70510 100644
> --- a/drivers/gpu/drm/tidss/tidss_crtc.c
> +++ b/drivers/gpu/drm/tidss/tidss_crtc.c
> @@ -91,7 +91,7 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
> struct dispc_device *dispc = tidss->dispc;
> struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
> u32 hw_videoport = tcrtc->hw_videoport;
> - const struct drm_display_mode *mode;
> + struct drm_display_mode *adjusted_mode;
> enum drm_mode_status ok;
>
> dev_dbg(ddev->dev, "%s\n", __func__);
> @@ -99,12 +99,27 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
> if (!crtc_state->enable)
> return 0;
>
> - mode = &crtc_state->adjusted_mode;
> + adjusted_mode = &crtc_state->adjusted_mode;
Here, adjusted_mode->clock is still the correct pixel clock.
> - ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
> + if (drm_atomic_crtc_needs_modeset(crtc_state)) {
> + long rate;
> +
> + rate = dispc_vp_round_clk_rate(tidss->dispc,
> + tcrtc->hw_videoport,
> + adjusted_mode->clock * 1000);
> + if (rate < 0)
> + return -EINVAL;
> +
> + adjusted_mode->clock = rate / 1000;
While after this statement, adjusted_mode->clock is 300MHz in my
case (the VP1 clock seems to be 2.1GHz, divided by 7).
-michael
[1] https://lore.kernel.org/all/20250525151721.567042-1-aradhya.bhatia@linux.dev/
[2] https://elixir.bootlin.com/linux/v6.15/source/arch/arm64/boot/dts/ti/k3-am62.dtsi#L110
> +
> + drm_mode_set_crtcinfo(adjusted_mode, 0);
> + }
> +
> + ok = dispc_vp_mode_valid(dispc, hw_videoport, adjusted_mode);
> if (ok != MODE_OK) {
> dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",
> - __func__, mode->hdisplay, mode->vdisplay, mode->clock);
> + __func__, adjusted_mode->hdisplay,
> + adjusted_mode->vdisplay, adjusted_mode->clock); > return -EINVAL;
> }
>
> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> index a5107f2732b1..3930fb7f03c2 100644
> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
> @@ -1318,6 +1318,12 @@ unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
> return (unsigned int)(abs(((rr - r) * 100) / r));
> }
>
> +long dispc_vp_round_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
> + unsigned long rate)
> +{
> + return clk_round_rate(dispc->vp_clk[hw_videoport], rate);
> +}
> +
> int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
> unsigned long rate)
> {
> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
> index c31b477a18b0..d4c335e918fb 100644
> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
> @@ -120,6 +120,8 @@ enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
> const struct drm_display_mode *mode);
> int dispc_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport);
> void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport);
> +long dispc_vp_round_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
> + unsigned long rate);
> int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
> unsigned long rate);
> void dispc_vp_setup(struct dispc_device *dispc, u32 hw_videoport,
Hi Michael (and Aradhya, Devarsh),
On 27/05/2025 12:13, Michael Walle wrote:
> Hi Tomi,
>
> While testing Aardhya's OLDI support patches [1], I've noticed that
> the resulting LVDS clock is wrong if this patch is applied.
>
>> In practice, with the current K3 SoCs, the display PLL is capable of
>> producing very exact clocks, so most likely the rounded rate is the same
>> as the original one.
>
> This is now what I'm seeing. Most SoCs have that fixed clock thingy
> for (some?) VPs, e.g. [2]. And clk_round_rate() will return the
> fixed clock rate for this clock, which will then result in an LVDS
> clock which is way off.
>
> I'm testing on an AM67A (J722S) and I've backported some of the
> patches as well as dtsi fragmets from downstream. Thus, it might be
> as well the case that the fixed-factor-clock node is wrong here.
> OTOH other K3 SoCs do this in mainline as well.
Thanks for findings this (It's not a fixed clock, but a (fixed)
divider). I can reproduce on my AM62 SK's OLDI output.
I didn't see AM625 TRM explaining the DSS + OLDI clocking. I remember it
was a bit "interesting". Afaics from testing, the VP clock is derived
from the OLDI serial clock divided by 7. To change the VP clock, we need
to set the OLDI clock's rate. But the code we have at the moment is
using clk_round_rate/set_rate to the VP clock.
And we get the crtc atomic_check called before setting the OLDI clock
rate, so it doesn't even work by luck (i.e. if the OLDI clock was set
earlier, the VP clock would already have the right rate, and it would
seem that everything is ok). In the atomic_check we see the OLDI bypass
clock (25 MHz), which results in 3571428 Hz VP clock.
And with this patch, the code then decides that 3571428 Hz is what the
HW can do, and uses it as the pixel clock.
Aradhya, Devarsh, do you remember how the clocking goes here? Or if it's
in the TRM, please point me to it...
Tomi
>>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>> ---
>> drivers/gpu/drm/tidss/tidss_crtc.c | 23 +++++++++++++++++++----
>> drivers/gpu/drm/tidss/tidss_dispc.c | 6 ++++++
>> drivers/gpu/drm/tidss/tidss_dispc.h | 2 ++
>> 3 files changed, 27 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
>> index 1604eca265ef..6c3967f70510 100644
>> --- a/drivers/gpu/drm/tidss/tidss_crtc.c
>> +++ b/drivers/gpu/drm/tidss/tidss_crtc.c
>> @@ -91,7 +91,7 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
>> struct dispc_device *dispc = tidss->dispc;
>> struct tidss_crtc *tcrtc = to_tidss_crtc(crtc);
>> u32 hw_videoport = tcrtc->hw_videoport;
>> - const struct drm_display_mode *mode;
>> + struct drm_display_mode *adjusted_mode;
>> enum drm_mode_status ok;
>>
>> dev_dbg(ddev->dev, "%s\n", __func__);
>> @@ -99,12 +99,27 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
>> if (!crtc_state->enable)
>> return 0;
>>
>> - mode = &crtc_state->adjusted_mode;
>> + adjusted_mode = &crtc_state->adjusted_mode;
>
> Here, adjusted_mode->clock is still the correct pixel clock.
>
>> - ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
>> + if (drm_atomic_crtc_needs_modeset(crtc_state)) {
>> + long rate;
>> +
>> + rate = dispc_vp_round_clk_rate(tidss->dispc,
>> + tcrtc->hw_videoport,
>> + adjusted_mode->clock * 1000);
>> + if (rate < 0)
>> + return -EINVAL;
>> +
>> + adjusted_mode->clock = rate / 1000;
>
> While after this statement, adjusted_mode->clock is 300MHz in my
> case (the VP1 clock seems to be 2.1GHz, divided by 7).
>
> -michael
>
> [1] https://lore.kernel.org/all/20250525151721.567042-1-aradhya.bhatia@linux.dev/
> [2] https://elixir.bootlin.com/linux/v6.15/source/arch/arm64/boot/dts/ti/k3-am62.dtsi#L110
>
>> +
>> + drm_mode_set_crtcinfo(adjusted_mode, 0);
>> + }
>> +
>> + ok = dispc_vp_mode_valid(dispc, hw_videoport, adjusted_mode);
>> if (ok != MODE_OK) {
>> dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",
>> - __func__, mode->hdisplay, mode->vdisplay, mode->clock);
>> + __func__, adjusted_mode->hdisplay,
>> + adjusted_mode->vdisplay, adjusted_mode->clock); > return -EINVAL;
>> }
>>
>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>> index a5107f2732b1..3930fb7f03c2 100644
>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>> @@ -1318,6 +1318,12 @@ unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
>> return (unsigned int)(abs(((rr - r) * 100) / r));
>> }
>>
>> +long dispc_vp_round_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
>> + unsigned long rate)
>> +{
>> + return clk_round_rate(dispc->vp_clk[hw_videoport], rate);
>> +}
>> +
>> int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
>> unsigned long rate)
>> {
>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
>> index c31b477a18b0..d4c335e918fb 100644
>> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
>> @@ -120,6 +120,8 @@ enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
>> const struct drm_display_mode *mode);
>> int dispc_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport);
>> void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport);
>> +long dispc_vp_round_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
>> + unsigned long rate);
>> int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
>> unsigned long rate);
>> void dispc_vp_setup(struct dispc_device *dispc, u32 hw_videoport,
>
Hi Michael, Tomi, On 27/05/25 18:03, Tomi Valkeinen wrote: > Hi Michael (and Aradhya, Devarsh), > > On 27/05/2025 12:13, Michael Walle wrote: >> Hi Tomi, >> >> While testing Aardhya's OLDI support patches [1], I've noticed that >> the resulting LVDS clock is wrong if this patch is applied. >> >>> In practice, with the current K3 SoCs, the display PLL is capable of >>> producing very exact clocks, so most likely the rounded rate is the same >>> as the original one. >> Yes, display PLL is flexible and device manager should set exact frequency. Please note that there was a bug in device manager in earlier releases which would prevent setting it to exact clocks. You should try with latest SDK release firmware binaries (11.0...(10.1 should also work though)) if seeing any misbehaviour in that regard. >> This is now what I'm seeing. Most SoCs have that fixed clock thingy >> for (some?) VPs, e.g. [2]. And clk_round_rate() will return the >> fixed clock rate for this clock, which will then result in an LVDS >> clock which is way off. >> >> I'm testing on an AM67A (J722S) and I've backported some of the >> patches as well as dtsi fragmets from downstream. Thus, it might be >> as well the case that the fixed-factor-clock node is wrong here. >> OTOH other K3 SoCs do this in mainline as well. > > Thanks for findings this (It's not a fixed clock, but a (fixed) > divider). I can reproduce on my AM62 SK's OLDI output. > > I didn't see AM625 TRM explaining the DSS + OLDI clocking. I remember it > was a bit "interesting". Afaics from testing, the VP clock is derived > from the OLDI serial clock divided by 7. To change the VP clock, we need > to set the OLDI clock's rate. But the code we have at the moment is > using clk_round_rate/set_rate to the VP clock. > This is correct. The pixel clock is derived as OLDI clock/7 when OLDI is enabled. > And we get the crtc atomic_check called before setting the OLDI clock > rate, so it doesn't even work by luck (i.e. if the OLDI clock was set > earlier, the VP clock would already have the right rate, and it would > seem that everything is ok). In the atomic_check we see the OLDI bypass > clock (25 MHz), which results in 3571428 Hz VP clock. > > And with this patch, the code then decides that 3571428 Hz is what the > HW can do, and uses it as the pixel clock. > > Aradhya, Devarsh, do you remember how the clocking goes here? Or if it's > in the TRM, please point me to it... > I think what you described is correct, if any specific questions I can help check. But any misbehaviour you are seeing w.r.t clock setting (i.e. what driver is trying to set versus what actually is getting set) then please dump the dss clock tree along with relevant details of test done: k3conf dump clock <display_device_id> You can get the device ID via TISCI Doc [1] [1]: https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am62x/clocks.html#clock-for-am62x-device Regards Devarsh
Hi Devarsh, Hi Tomi, >>> While testing Aardhya's OLDI support patches [1], I've noticed that >>> the resulting LVDS clock is wrong if this patch is applied. >>> >>>> In practice, with the current K3 SoCs, the display PLL is capable of >>>> producing very exact clocks, so most likely the rounded rate is the >>>> same >>>> as the original one. >>> > > Yes, display PLL is flexible and device manager should set exact > frequency. > Please note that there was a bug in device manager in earlier releases > which > would prevent setting it to exact clocks. You should try with latest > SDK > release firmware binaries (11.0...(10.1 should also work though)) if > seeing > any misbehaviour in that regard. Yes, I don't doubt that. But it's the way the driver is handling the clock w.r.t. to the fixed-clock-divider in the LVDS case which is causing problems. >>> This is now what I'm seeing. Most SoCs have that fixed clock thingy >>> for (some?) VPs, e.g. [2]. And clk_round_rate() will return the >>> fixed clock rate for this clock, which will then result in an LVDS >>> clock which is way off. >>> >>> I'm testing on an AM67A (J722S) and I've backported some of the >>> patches as well as dtsi fragmets from downstream. Thus, it might be >>> as well the case that the fixed-factor-clock node is wrong here. >>> OTOH other K3 SoCs do this in mainline as well. >> >> Thanks for findings this (It's not a fixed clock, but a (fixed) >> divider). I can reproduce on my AM62 SK's OLDI output. >> >> I didn't see AM625 TRM explaining the DSS + OLDI clocking. I remember >> it >> was a bit "interesting". Afaics from testing, the VP clock is derived >> from the OLDI serial clock divided by 7. To change the VP clock, we >> need >> to set the OLDI clock's rate. But the code we have at the moment is >> using clk_round_rate/set_rate to the VP clock. >> > > This is correct. The pixel clock is derived as OLDI clock/7 when OLDI > is > enabled. What means "if OLDI is enabled"? Is there any other clock otherwise or none at all? Reading the clock ID desciption in the TISCI documentation, I presume there is no configurable clock for the first VP at all. I.e. if one compares it with DSS1 which has two muxed clocks, DSS0 only has one muxed clock (DEV_DSS0_DPI_1_IN_CLK) and DEV_DSS0_DPI_0_IN_CLK is fixed, presumely thats the fixed /7 clock. Also how is this handled for the DSS1 (on an AM67A)? The TI downstream kernel also has a fixed-clock-divider for the VP1 on DSS1, but I think that is wrong, because it's actually configurable (if OLDI is disabled?). >> And we get the crtc atomic_check called before setting the OLDI clock >> rate, so it doesn't even work by luck (i.e. if the OLDI clock was set >> earlier, the VP clock would already have the right rate, and it would >> seem that everything is ok). In the atomic_check we see the OLDI >> bypass >> clock (25 MHz), which results in 3571428 Hz VP clock. >> >> And with this patch, the code then decides that 3571428 Hz is what the >> HW can do, and uses it as the pixel clock. FWIW, I'm seeing exactly 300MHz on the AM67A (the FCLK is 2.1GHz according to k3conf). -michael >> >> Aradhya, Devarsh, do you remember how the clocking goes here? Or if >> it's >> in the TRM, please point me to it... >> > > I think what you described is correct, if any specific questions I can > help > check. But any misbehaviour you are seeing w.r.t clock setting (i.e. > what > driver is trying to set versus what actually is getting set) > then please dump the dss clock tree along with relevant details of test > done: > > k3conf dump clock <display_device_id> > > You can get the device ID via TISCI Doc [1] > > [1]: > https://downloads.ti.com/tisci/esd/latest/5_soc_doc/am62x/clocks.html#clock-for-am62x-device > > Regards > Devarsh
On 02/04/25 19:00, Tomi Valkeinen wrote: > At the moment the driver just sets the clock rate with clk_set_rate(), > and if the resulting rate is not the same as requested, prints a debug > print, but nothing else. > > Add functionality to atomic_check(), in which the clk_round_rate() is > used to get the "rounded" rate, and set that to the adjusted_mode. > > In practice, with the current K3 SoCs, the display PLL is capable of > producing very exact clocks, so most likely the rounded rate is the same > as the original one. > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> > --- > drivers/gpu/drm/tidss/tidss_crtc.c | 23 +++++++++++++++++++---- > drivers/gpu/drm/tidss/tidss_dispc.c | 6 ++++++ > drivers/gpu/drm/tidss/tidss_dispc.h | 2 ++ > 3 files changed, 27 insertions(+), 4 deletions(-) > Reviewed-by: Aradhya Bhatia <aradhya.bhatia@linux.dev> -- Regards Aradhya
© 2016 - 2026 Red Hat, Inc.