* [PATCH v12 23/25] drm/tests: bridge: Add test for HDMI output bus formats helper
From: Nicolas Frattaroli @ 2026-04-09 15:45 UTC (permalink / raw)
To: Harry Wentland, Leo Li, Rodrigo Siqueira, Alex Deucher,
Christian König, David Airlie, Simona Vetter,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Sandy Huang, Heiko Stübner,
Andy Yan, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
Tvrtko Ursulin, Dmitry Baryshkov, Sascha Hauer, Rob Herring,
Jonathan Corbet, Shuah Khan
Cc: kernel, amd-gfx, dri-devel, linux-kernel, linux-arm-kernel,
linux-rockchip, intel-gfx, intel-xe, linux-doc,
Nicolas Frattaroli
In-Reply-To: <20260409-color-format-v12-0-ce84e1817a27@collabora.com>
The common atomic_get_output_bus_fmts helper for HDMI bridge connectors,
called drm_atomic_helper_bridge_get_hdmi_output_bus_fmts, should return
an array of output bus formats depending on the supported formats of the
connector, and the current output BPC.
Add a test to exercise some of this helper.
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
drivers/gpu/drm/tests/drm_bridge_test.c | 184 ++++++++++++++++++++++++++++++++
1 file changed, 184 insertions(+)
diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
index cb821c606070..d9bd930b1197 100644
--- a/drivers/gpu/drm/tests/drm_bridge_test.c
+++ b/drivers/gpu/drm/tests/drm_bridge_test.c
@@ -5,6 +5,7 @@
#include <linux/cleanup.h>
#include <linux/media-bus-format.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_bridge.h>
@@ -118,6 +119,28 @@ static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
};
+static int dummy_clear_infoframe(struct drm_bridge *bridge)
+{
+ return 0;
+}
+
+static int dummy_write_infoframe(struct drm_bridge *bridge, const u8 *buffer,
+ size_t len)
+{
+ return 0;
+}
+
+static const struct drm_bridge_funcs drm_test_bridge_bus_fmts_funcs = {
+ .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .hdmi_write_avi_infoframe = dummy_write_infoframe,
+ .hdmi_write_hdmi_infoframe = dummy_write_infoframe,
+ .hdmi_clear_avi_infoframe = dummy_clear_infoframe,
+ .hdmi_clear_hdmi_infoframe = dummy_clear_infoframe,
+};
+
/**
* struct fmt_tuple - a tuple of input/output MEDIA_BUS_FMT_*
*/
@@ -539,6 +562,83 @@ drm_test_bridge_chain_init(struct kunit *test, unsigned int num_bridges,
return priv;
}
+static struct drm_bridge_init_priv *
+drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *funcs,
+ unsigned int supported_formats, int max_bpc)
+{
+ struct drm_bridge_init_priv *priv;
+ struct drm_encoder *enc;
+ struct drm_bridge *bridge;
+ struct drm_device *drm;
+ struct device *dev;
+ int ret;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ if (IS_ERR(dev))
+ return ERR_CAST(dev);
+
+ priv = drm_kunit_helper_alloc_drm_device(test, dev,
+ struct drm_bridge_init_priv, drm,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ if (IS_ERR(priv))
+ return ERR_CAST(priv);
+
+ priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs);
+ if (IS_ERR(priv->test_bridge))
+ return ERR_CAST(priv->test_bridge);
+
+ priv->test_bridge->data = priv;
+
+ drm = &priv->drm;
+ priv->plane = drm_kunit_helper_create_primary_plane(test, drm,
+ NULL,
+ NULL,
+ NULL, 0,
+ NULL);
+ if (IS_ERR(priv->plane))
+ return ERR_CAST(priv->plane);
+
+ priv->crtc = drm_kunit_helper_create_crtc(test, drm,
+ priv->plane, NULL,
+ NULL,
+ NULL);
+ if (IS_ERR(priv->crtc))
+ return ERR_CAST(priv->crtc);
+
+ enc = &priv->encoder;
+ ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ERR_PTR(ret);
+
+ enc->possible_crtcs = drm_crtc_mask(priv->crtc);
+
+ bridge = &priv->test_bridge->bridge;
+ bridge->type = DRM_MODE_CONNECTOR_HDMIA;
+ bridge->supported_formats = supported_formats;
+ bridge->max_bpc = max_bpc;
+ bridge->ops |= DRM_BRIDGE_OP_HDMI;
+ bridge->vendor = "LNX";
+ bridge->product = "KUnit";
+
+ ret = drm_kunit_bridge_add(test, bridge);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = drm_bridge_attach(enc, bridge, NULL, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ priv->connector = drm_bridge_connector_init(drm, enc);
+ if (IS_ERR(priv->connector))
+ return ERR_CAST(priv->connector);
+
+ drm_connector_attach_encoder(priv->connector, enc);
+
+ drm_mode_config_reset(drm);
+
+ return priv;
+}
+
/*
* Test that drm_bridge_get_current_state() returns the last committed
* state for an atomic bridge.
@@ -786,10 +886,94 @@ static void drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit *test)
KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
}
+/*
+ * Test that a bridge using the drm_atomic_helper_bridge_get_hdmi_output_bus_fmts()
+ * function for &drm_bridge_funcs.atomic_get_output_bus_fmts behaves as expected
+ * for an HDMI connector bridge. Does so by creating an HDMI bridge connector
+ * with RGB444, YCBCR444, and YCBCR420 (but not YCBCR422) as supported formats,
+ * sets the output depth to 8 bits per component, and then validates the returned
+ * list of bus formats.
+ */
+static void drm_test_drm_bridge_helper_hdmi_output_bus_fmts(struct kunit *test)
+{
+ struct drm_connector_state *conn_state;
+ struct drm_bridge_state *bridge_state;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_bridge_init_priv *priv;
+ struct drm_crtc_state *crtc_state;
+ struct drm_atomic_state *state;
+ struct drm_display_mode *mode;
+ unsigned int num_output_fmts;
+ struct drm_bridge *bridge;
+ u32 *out_bus_fmts;
+ int ret;
+
+ priv = drm_test_bridge_hdmi_init(test, &drm_test_bridge_bus_fmts_funcs,
+ BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
+ BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420),
+ 12);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ bridge = &priv->test_bridge->bridge;
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+retry_commit:
+ conn_state = drm_atomic_get_connector_state(state, priv->connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ conn_state->hdmi.output_bpc = 8;
+
+ mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, priv->crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry_commit;
+ }
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ crtc_state->enable = true;
+ crtc_state->active = true;
+
+ bridge_state = drm_atomic_get_bridge_state(state, bridge);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state);
+
+ out_bus_fmts = bridge->funcs->atomic_get_output_bus_fmts(
+ bridge, bridge_state, crtc_state, conn_state, &num_output_fmts);
+ KUNIT_EXPECT_NOT_NULL(test, out_bus_fmts);
+ KUNIT_EXPECT_EQ(test, num_output_fmts, 3);
+
+ KUNIT_EXPECT_EQ(test, out_bus_fmts[0], MEDIA_BUS_FMT_RGB888_1X24);
+ KUNIT_EXPECT_EQ(test, out_bus_fmts[1], MEDIA_BUS_FMT_YUV8_1X24);
+ KUNIT_EXPECT_EQ(test, out_bus_fmts[2], MEDIA_BUS_FMT_UYYVYY8_0_5X24);
+
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+
+ kfree(out_bus_fmts);
+}
+
static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = {
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic),
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled),
KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy),
+ KUNIT_CASE(drm_test_drm_bridge_helper_hdmi_output_bus_fmts),
{ }
};
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v10 17/20] coresight: trbe: Save and restore state across CPU low power state
From: Leo Yan @ 2026-04-09 15:54 UTC (permalink / raw)
To: James Clark
Cc: coresight, linux-arm-kernel, Suzuki K Poulose, Mike Leach,
Yeoreum Yun, Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
In-Reply-To: <e2241b98-92a6-45e0-a9d3-061f1ed69ae1@linaro.org>
On Thu, Apr 09, 2026 at 11:52:06AM +0100, James Clark wrote:
> On 05/04/2026 4:02 pm, Leo Yan wrote:
> > From: Yabin Cui <yabinc@google.com>
> >
> > TRBE context can be lost when a CPU enters low power states. If a trace
> > source is restored while TRBE is not, tracing may run without an active
> > sink, which can lead to hangs on some devices (e.g., Pixel 9).
>
> Can't this still happen if saving the source times out on "wait for
> TRCSTATR.IDLE to go up"?
>
> That would make coresight_pm_save() exit early, not saving the active TRBE
> state. Then when coresight_pm_restore() is called it restores a stale
> inactive sink, then enables the source again which is the state that can
> hang.
Hmm... I don't expect this to happen anyway.
If CPU PM enter fails and returns an error, it will bail out early of
the idle flow and the CPU will not proceed entering low power states;
therefore, the restore flow does not exist.
Thanks,
Leo
^ permalink raw reply
* Re: [PATCH] mm/arm: pgtable: remove young bit check for pte_valid_user
From: Russell King (Oracle) @ 2026-04-09 16:00 UTC (permalink / raw)
To: Brian Ruley; +Cc: Will Deacon, Steve Capper, linux-arm-kernel, linux-kernel
In-Reply-To: <adfDEMK8EbIjPu3J@zoo11.fihel.lab.ge-healthcare.net>
On Thu, Apr 09, 2026 at 06:17:36PM +0300, Brian Ruley wrote:
> However, in the case I describe, if VA_B is mapped immediately to pfn_q
> after it been has unmapped and freed for VA_A, then it's quite possible
> that the page is still indexed in the cache.
True.
> The hypothesis is that if
> VA_A and VA_B land in the same I-cache set and VA_A old cache entry
> still exists (tagged with pfn_q), then the CPU can fetch stale
> instructions because the tag will match. That's one reason why we need
> to invalidate the cache, but that will be skipped in the path:
>
> migrate_pages
> migrate_pages_batch
> migrate_folio_move
> remove_migration_ptes
> remove_migration_pte
> set_pte_at
> set_ptes
> __sync_icache_dcache (skipped if !young)
> set_pte_ext
In this case, if the old PTE was marked !young, then the new PTE will
have:
pte = pte_mkold(pte);
on it, which marks it !young. As you say, __sync_icache_dcache() will
be skipped. While a PTE entry will be set for the kernel, the code in
set_pte_ext() will *not* establish a hardware PTE entry. For the
2-level pte code:
tst r1, #L_PTE_YOUNG @ <- results in Z being set
tstne r1, #L_PTE_VALID @ <- not executed
eorne r1, r1, #L_PTE_NONE @ <- not executed
tstne r1, #L_PTE_NONE @ <- not executed
moveq r3, #0 @ <- hardware PTE value
ARM( str r3, [r0, #2048]! ) @ <- writes hardware PTE
So, for a !young PTE, the hardware PTE entry is written as zero,
which means accesses should fault, which will then cause the PTE to
be marked young.
For the 3-level case, the L_PTE_YOUNG bit corresponds with the AF bit
in the PTE, and there aren't split Linux / hardware PTE entries. AF
being clear should result in a page fault being generated for the
kernel to handle making the PTE young.
In both of these cases, set_ptes() will need to be called with the
updated PTE which will now be marked young, and that will result in
the I-cache being flushed.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH v7 7/7] KVM: arm64: Normalize cache configuration
From: David Woodhouse @ 2026-04-09 16:10 UTC (permalink / raw)
To: Marc Zyngier
Cc: Gutierrez Cantu, Bernardo, alexandru.elisei, alyssa, asahi,
broonie, catalin.marinas, james.morse, kvmarm, linux-arm-kernel,
linux-kernel, marcan, mathieu.poirier, oliver.upton,
suzuki.poulose, sven, will
In-Reply-To: <86tstk2l4w.wl-maz@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2531 bytes --]
On Thu, 2026-04-09 at 16:45 +0100, Marc Zyngier wrote:
> On Thu, 09 Apr 2026 15:51:59 +0100, David Woodhouse <dwmw2@infradead.org> wrote:
> > On Thu, 2026-04-09 at 14:36 +0100, Marc Zyngier wrote:
>
> > > We have never guaranteed host downgrade. It almost never works.
> >
> > Huh? Host downgrade absolutely *does* work; KVM on Arm isn't a toy.
> > We'd never be able to ship a new kernel if we didn't know we could roll
> > it *back* if we needed to.
>
> You have clearly been lucky so far, because I'd never such claim.
It's not luck. We do a *lot* of testing, both within the guest checking
that what it sees is identical between old and new environments, and
repeated live upgrade/downgrade cycles between versions as well as
migration back and forth, and hibernate/resume from one to the other.
> >
> > I *know* that you know perfectly well that we actively test host
> > downgrades prior to every rollout of a new kernel. So I'm a little
> > confused about what you're trying to say here, Marc.
>
> I said exactly this: we make no effort to ensure that a guest started
> on a version of the kernel can be migrated to an older version -- the
> state space might be different.
You said "it almost never works". If that were the case, then I would
suggest that KVM on arm64 would be entirely unsuitable as a production
platform for virtual hosting. But thankfully it isn't my experience at
all.
Sure, we've occasionally found breakage, such as this commit and the
vGIC 'IMP_REV_1' thing being discussed in a separate thread, but those
are the exceptions rather than the norm.
(And yes, we need to do better than just reverting the offending
commits when we find them, and fix the issues properly upstream. Which
is what I'm doing now, belatedly).
> >
> > Now that the values are writable, but userspace can't easily see *what*
> > they would have been in the previous kernel. So having the capability
> > to ask KVM to set them to those values seems like it might be useful.
>
> Well, it's only a patch away.
Indeed so. When I asked what I was missing, I was *kind* of hoping
someone would tell me I'm wrong and that I can get the original values
from userspace either through sysfs or some other means, but in the
absence of that:
https://lore.kernel.org/lkml/7fb7b823c68e04321eb532a5b8ae21a818d4926d.camel@infradead.org/
(Lore is being insanely slow today but it *will* be there, and the
marc.info first alternative it offers does already have the message).
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Andrea della Porta @ 2026-04-09 16:16 UTC (permalink / raw)
To: Uwe Kleine-König
Cc: Andrea della Porta, linux-pwm, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, devicetree,
linux-rpi-kernel, linux-arm-kernel, linux-kernel, Naushir Patuck,
Stanimir Varbanov
In-Reply-To: <adLTwOTbkJ0VQXy6@monoceros>
Hi Uwe,
On 23:45 Sun 05 Apr , Uwe Kleine-König wrote:
> Hello Andrea,
>
> On Fri, Apr 03, 2026 at 04:31:55PM +0200, Andrea della Porta wrote:
> > From: Naushir Patuck <naush@raspberrypi.com>
> >
> > The Raspberry Pi RP1 southbridge features an embedded PWM
> > controller with 4 output channels, alongside an RPM interface
> > to read the fan speed on the Raspberry Pi 5.
> >
> > Add the supporting driver.
> >
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
> > Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
> > Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
> > ---
> > drivers/pwm/Kconfig | 10 ++
> > drivers/pwm/Makefile | 1 +
> > drivers/pwm/pwm-rp1.c | 244 ++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 255 insertions(+)
> > create mode 100644 drivers/pwm/pwm-rp1.c
> >
> > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> > index 6f3147518376a..22e4fc6385da2 100644
> > --- a/drivers/pwm/Kconfig
> > +++ b/drivers/pwm/Kconfig
> > @@ -625,6 +625,16 @@ config PWM_ROCKCHIP
> > Generic PWM framework driver for the PWM controller found on
> > Rockchip SoCs.
> >
> > +config PWM_RP1
>
> I prefer PWM_RASPBERRYPI1, or PWM_RASPBERRYPI_RP1 here.
Ack.
>
> > + tristate "RP1 PWM support"
> > + depends on MISC_RP1 || COMPILE_TEST
> > + depends on HWMON
> > + help
> > + PWM framework driver for Raspberry Pi RP1 controller
> > +
> > + To compile this driver as a module, choose M here: the module
> > + will be called pwm-rp1.
> > +
> > config PWM_SAMSUNG
> > tristate "Samsung PWM support"
> > depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
> > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> > index 0dc0d2b69025d..895a7c42fe9c0 100644
> > --- a/drivers/pwm/Makefile
> > +++ b/drivers/pwm/Makefile
> > @@ -56,6 +56,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT) += pwm-rzg2l-gpt.o
> > obj-$(CONFIG_PWM_RENESAS_RZ_MTU3) += pwm-rz-mtu3.o
> > obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
> > obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
> > +obj-$(CONFIG_PWM_RP1) += pwm-rp1.o
> > obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
> > obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
> > obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
> > diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c
> > new file mode 100644
> > index 0000000000000..0a1c1c1dd27e9
> > --- /dev/null
> > +++ b/drivers/pwm/pwm-rp1.c
> > @@ -0,0 +1,244 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * pwm-rp1.c
> > + *
> > + * Raspberry Pi RP1 PWM.
> > + *
> > + * Copyright © 2026 Raspberry Pi Ltd.
> > + *
> > + * Author: Naushir Patuck (naush@raspberrypi.com)
> > + *
> > + * Based on the pwm-bcm2835 driver by:
> > + * Bart Tanghe <bart.tanghe@thomasmore.be>
> > + */
>
> Please add a paragraph here named "Limitations" in the same format as
> several other drivers describing how the driver behaves on disable and
> configuration changes (can glitches occur? Is the currently running
> period completed or aborted?)
Ack.
>
> > +#include <linux/bitops.h>
> > +#include <linux/clk.h>
> > +#include <linux/err.h>
> > +#include <linux/hwmon.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pwm.h>
> > +
> > +#define PWM_GLOBAL_CTRL 0x000
> > +#define PWM_CHANNEL_CTRL(x) (0x014 + ((x) * 0x10))
> > +#define PWM_RANGE(x) (0x018 + ((x) * 0x10))
> > +#define PWM_PHASE(x) (0x01C + ((x) * 0x10))
> > +#define PWM_DUTY(x) (0x020 + ((x) * 0x10))
> > +
> > +/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */
> > +#define PWM_CHANNEL_DEFAULT (BIT(8) + BIT(0))
> > +#define PWM_CHANNEL_ENABLE(x) BIT(x)
> > +#define PWM_POLARITY BIT(3)
> > +#define SET_UPDATE BIT(31)
> > +#define PWM_MODE_MASK GENMASK(1, 0)
> > +
> > +#define NUM_PWMS 4
>
> Please prefix all #defines by something driver specific (e.g. RP1_PWM_).
Ack.
>
> > +
> > +struct rp1_pwm {
> > + void __iomem *base;
> > + struct clk *clk;
> > +};
> > +
> > +static const struct hwmon_channel_info * const rp1_fan_hwmon_info[] = {
> > + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
> > + NULL
> > +};
> > +
> > +static umode_t rp1_fan_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
> > + u32 attr, int channel)
> > +{
> > + umode_t mode = 0;
> > +
> > + if (type == hwmon_fan && attr == hwmon_fan_input)
> > + mode = 0444;
> > +
> > + return mode;
> > +}
> > +
> > +static int rp1_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
> > + u32 attr, int channel, long *val)
> > +{
> > + struct rp1_pwm *rp1 = dev_get_drvdata(dev);
> > +
> > + if (type != hwmon_fan || attr != hwmon_fan_input)
> > + return -EOPNOTSUPP;
> > +
> > + *val = readl(rp1->base + PWM_PHASE(2));
> > +
> > + return 0;
> > +}
>
> I don't like having hwmon bits in pwm drivers. Is the PWM only usable
> for a fan? I guess the hwmon parts should be dropped and a pwm-fan
> defined in dt.
The pwm-fan generic driver expects an interrupt to count the RPM, while
on RP1 this data is passed via a register filled by the RP1 fw running
on the internal core. Instead of changing the generic pwm-fan driver,
I'll add a syscon to export this register which will be read by a new
device/driver registering an hwmon device.
>
> > +static const struct hwmon_ops rp1_fan_hwmon_ops = {
> > + .is_visible = rp1_fan_hwmon_is_visible,
> > + .read = rp1_fan_hwmon_read,
> > +};
> > +
> > +static const struct hwmon_chip_info rp1_fan_hwmon_chip_info = {
> > + .ops = &rp1_fan_hwmon_ops,
> > + .info = rp1_fan_hwmon_info,
> > +};
> > +
> > +static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
> > +{
> > + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> > + u32 value;
> > +
> > + value = readl(rp1->base + PWM_GLOBAL_CTRL);
> > + value |= SET_UPDATE;
> > + writel(value, rp1->base + PWM_GLOBAL_CTRL);
> > +}
> > +
> > +static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
> > +{
> > + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> > +
> > + writel(PWM_CHANNEL_DEFAULT, rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
>
> Please add a comment about what this does.
Ack.
>
> > + return 0;
> > +}
> > +
> > +static void rp1_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
> > +{
> > + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> > + u32 value;
> > +
> > + value = readl(rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
> > + value &= ~PWM_MODE_MASK;
> > + writel(value, rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
> > +
> > + rp1_pwm_apply_config(chip, pwm);
>
> What is the purpose of this call?
To update the configuration on the next PWM strobe in order to avoid
glitches. I'll add a short comment in the code.
>
> > +}
> > +
> > +static int rp1_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
> > + const struct pwm_state *state)
> > +{
> > + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> > + unsigned long clk_rate = clk_get_rate(rp1->clk);
> > + unsigned long clk_period;
> > + u32 value;
> > +
> > + if (!clk_rate) {
> > + dev_err(&chip->dev, "failed to get clock rate\n");
> > + return -EINVAL;
> > + }
> > +
> > + /* set period and duty cycle */
> > + clk_period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, clk_rate);
>
> DIV_ROUND_CLOSEST is wrong here. (I don't go into details as .apply()
> should be dropped.)
Ack.
>
> > + writel(DIV_ROUND_CLOSEST(state->duty_cycle, clk_period),
>
> Dividing by the result of a division loses precision.
>
> > + rp1->base + PWM_DUTY(pwm->hwpwm));
> > +
> > + writel(DIV_ROUND_CLOSEST(state->period, clk_period),
> > + rp1->base + PWM_RANGE(pwm->hwpwm));
> > +
> > + /* set polarity */
> > + value = readl(rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
> > + if (state->polarity == PWM_POLARITY_NORMAL)
> > + value &= ~PWM_POLARITY;
> > + else
> > + value |= PWM_POLARITY;
> > + writel(value, rp1->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
> > +
> > + /* enable/disable */
> > + value = readl(rp1->base + PWM_GLOBAL_CTRL);
> > + if (state->enabled)
> > + value |= PWM_CHANNEL_ENABLE(pwm->hwpwm);
> > + else
> > + value &= ~PWM_CHANNEL_ENABLE(pwm->hwpwm);
> > + writel(value, rp1->base + PWM_GLOBAL_CTRL);
> > +
> > + rp1_pwm_apply_config(chip, pwm);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct pwm_ops rp1_pwm_ops = {
> > + .request = rp1_pwm_request,
> > + .free = rp1_pwm_free,
> > + .apply = rp1_pwm_apply,
>
> Please implement the waveform callbacks instead of .apply().
Ack.
>
> > +};
> > +
> > +static int rp1_pwm_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct device *hwmon_dev;
> > + struct pwm_chip *chip;
> > + struct rp1_pwm *rp1;
> > + int ret;
> > +
> > + chip = devm_pwmchip_alloc(dev, NUM_PWMS, sizeof(*rp1));
> > + if (IS_ERR(chip))
> > + return PTR_ERR(chip);
> > +
> > + rp1 = pwmchip_get_drvdata(chip);
> > +
> > + rp1->base = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(rp1->base))
> > + return PTR_ERR(rp1->base);
> > +
> > + rp1->clk = devm_clk_get_enabled(dev, NULL);
> > + if (IS_ERR(rp1->clk))
> > + return dev_err_probe(dev, PTR_ERR(rp1->clk), "clock not found\n");
>
> Please start error messages with a capital letter.
Ack.
>
> > +
> > + ret = devm_clk_rate_exclusive_get(dev, rp1->clk);
>
> After this call you can determine the rate just once and fail if it's ==
> 0.
Ack.
>
> > + if (ret)
> > + return dev_err_probe(dev, ret, "fail to get exclusive rate\n");
> > +
> > + chip->ops = &rp1_pwm_ops;
> > +
> > + platform_set_drvdata(pdev, chip);
> > +
> > + ret = devm_pwmchip_add(dev, chip);
> > + if (ret)
> > + return dev_err_probe(dev, ret, "failed to register PWM chip\n");
> > +
> > + hwmon_dev = devm_hwmon_device_register_with_info(dev, "rp1_fan_tach", rp1,
> > + &rp1_fan_hwmon_chip_info,
> > + NULL);
> > +
> > + if (IS_ERR(hwmon_dev))
> > + return dev_err_probe(dev, PTR_ERR(hwmon_dev),
> > + "failed to register hwmon fan device\n");
> > +
> > + return 0;
> > +}
> > +
> > +static int rp1_pwm_suspend(struct device *dev)
> > +{
> > + struct rp1_pwm *rp1 = dev_get_drvdata(dev);
> > +
> > + clk_disable_unprepare(rp1->clk);
> > +
> > + return 0;
> > +}
> > +
> > +static int rp1_pwm_resume(struct device *dev)
> > +{
> > + struct rp1_pwm *rp1 = dev_get_drvdata(dev);
> > +
> > + return clk_prepare_enable(rp1->clk);
>
> Hmm, if this fails and then the driver is unbound, the clk operations
> are not balanced.
I'll add some flags to check if the clock is really enabled or not.
Regards,
Andrea
>
> > +}
> > +
> > +static DEFINE_SIMPLE_DEV_PM_OPS(rp1_pwm_pm_ops, rp1_pwm_suspend, rp1_pwm_resume);
> > +
> > +static const struct of_device_id rp1_pwm_of_match[] = {
> > + { .compatible = "raspberrypi,rp1-pwm" },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
> > +
> > +static struct platform_driver rp1_pwm_driver = {
> > + .probe = rp1_pwm_probe,
> > + .driver = {
> > + .name = "rp1-pwm",
> > + .of_match_table = rp1_pwm_of_match,
> > + .pm = pm_ptr(&rp1_pwm_pm_ops),
> > + },
> > +};
> > +module_platform_driver(rp1_pwm_driver);
> > +
> > +MODULE_DESCRIPTION("RP1 PWM driver");
> > +MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.35.3
> >
>
> Best regards
> Uwe
^ permalink raw reply
* Re: [PATCH v14 00/10] arm64: entry: Convert to Generic Entry
From: Kees Cook @ 2026-04-09 16:14 UTC (permalink / raw)
To: Jinjie Ruan
Cc: mark.rutland, peterz, catalin.marinas, ldv, edumazet, will, mingo,
thuth, ryan.roberts, arnd, anshuman.khandual, kevin.brodsky,
pengcan, broonie, mathieu.desnoyers, luto, linux-arm-kernel, wad,
song, linusw, oleg, linux-kernel, tglx, liqiang01, yeoreum.yun
In-Reply-To: <174e6d08-4922-f42f-2899-4c5b0df13469@huawei.com>
On Thu, Apr 09, 2026 at 02:29:04PM +0800, Jinjie Ruan wrote:
> On 2026/3/20 18:26, Jinjie Ruan wrote:
> > Currently, x86, Riscv, Loongarch use the Generic Entry which makes
> > maintainers' work easier and codes more elegant. arm64 has already
> > successfully switched to the Generic IRQ Entry in commit
> > b3cf07851b6c ("arm64: entry: Switch to generic IRQ entry"), it is
> > time to completely convert arm64 to Generic Entry.
> >
> > The goal is to bring arm64 in line with other architectures that already
> > use the generic entry infrastructure, reducing duplicated code and
> > making it easier to share future changes in entry/exit paths, such as
> > "Syscall User Dispatch" and RSEQ optimizations.
>
> Just a quick ping to see if this series is good to go. Do I need to
> provide a new version rebased on the latest arm64 for-next/generic-entry
> branches, or is the current version acceptable?
One thing I see is Sashiko's comments on seccomp:
https://sashiko.dev/#/patchset/20260320102620.1336796-1-ruanjinjie%40huawei.com
where "ret", when not 0 or -1, will override the syscall number. While
that's not currently possible, it'd be better to catch that, or rather,
avoid the "ret ? : syscall" logic which isn't useful here. "ret" should
probably be local to the "if (flags & _TIF_SECCOMP)" scope.
--
Kees Cook
^ permalink raw reply
* Re: [PATCH] arm64: syscall: use cntvct_el0 for kstack offset randomization
From: Kees Cook @ 2026-04-09 16:17 UTC (permalink / raw)
To: Xu Laiguang, Ryan Roberts
Cc: catalin.marinas, will, gustavoars, linux-arm-kernel,
linux-hardening
In-Reply-To: <20260409095322.1774250-1-xulaiguang@lixiang.com>
On Thu, Apr 09, 2026 at 09:53:22AM +0000, Xu Laiguang wrote:
> On PREEMPT_RT kernels, get_random_u16() can suffer significant lock
> contention in the syscall hot path. The batched entropy layer uses
> local_lock, which on RT maps to a real spinlock. When a batch refill
> is in progress, other tasks on the same CPU block on the lock. Under
> heavy syscall load on a 24-core RT system, worst-case latencies of
> 16.65ms have been observed.
>
> contended total wait max wait caller
> 307 86.18 ms 16.65 ms get_random_u16+0x64
>
> The kstack offset randomization only needs 6 bits of entropy (the
> value is masked by KSTACK_OFFSET_MAX to bits [9:4] on 64-bit). This
> does not require cryptographic-strength randomness -- the goal is to
> make the kernel stack offset unpredictable enough to frustrate stack
> layout attacks.
>
> Other architectures already use lightweight hardware counters for this
> purpose:
> - x86: rdtsc() (arch/x86/include/asm/entry-common.h)
> - powerpc: mftb() (arch/powerpc/kernel/syscall.c)
> - loongarch: drdtime() (arch/loongarch/kernel/syscall.c)
> - s390: get_tod_clock_fast() (arch/s390/include/asm/entry-common.h)
>
> Replace get_random_u16() with a bare read of cntvct_el0 (the ARM
> generic timer virtual count register). The read is performed without
> ISB or arch_counter_enforce_ordering because kstack randomization does
> not require timing accuracy -- only unpredictability.
>
> Signed-off-by: Xu Laiguang <xulaiguang@lixiang.com>
Everything here has been replaced recently. Can you check again with the
-next tree?
https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=for-next/hardening
See commits:
randomize_kstack: Maintain kstack_offset per task
randomize_kstack: Unify random source across arches
-Kees
> ---
> arch/arm64/kernel/syscall.c | 17 ++++++++++++++++-
> 1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
> index c062badd1a56..c0ee9fa529ca 100644
> --- a/arch/arm64/kernel/syscall.c
> +++ b/arch/arm64/kernel/syscall.c
> @@ -35,6 +35,21 @@ static long __invoke_syscall(struct pt_regs *regs, syscall_fn_t syscall_fn)
> return syscall_fn(regs);
> }
>
> +/*
> + * Read the virtual counter without ISB or other ordering barriers.
> + * This is intentional: kstack randomization only needs unpredictability,
> + * not timing accuracy. Speculative execution of the read, if it occurs,
> + * actually helps by making the precise value less predictable to an
> + * attacker.
> + */
> +static inline u64 kstack_entropy_cntvct(void)
> +{
> + u64 cnt;
> +
> + asm volatile("mrs %0, cntvct_el0" : "=r"(cnt));
> + return cnt;
> +}
> +
> static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
> unsigned int sc_nr,
> const syscall_fn_t syscall_table[])
> @@ -62,7 +77,7 @@ static void invoke_syscall(struct pt_regs *regs, unsigned int scno,
> *
> * The resulting 6 bits of entropy is seen in SP[9:4].
> */
> - choose_random_kstack_offset(get_random_u16());
> + choose_random_kstack_offset(kstack_entropy_cntvct());
> }
>
> static inline bool has_syscall_work(unsigned long flags)
> --
> 2.43.0
>
> 声明:这封邮件只允许文件接收者阅读,有很高的机密性要求。禁止其他人使用、打开、复制或转发里面的任何内容。如果本邮件错误地发给了你,请联系邮件发出者并删除这个文件。机密及法律的特权并不因为误发邮件而放弃或丧失。任何提出的观点或意见只属于作者的个人见解,并不一定代表本公司。
> Disclaimer: This email is intended to be read only by the designated recipient of the document and has high confidentiality requirements. Anyone else is prohibited from using, opening, copying or forwarding any of the contents inside. If this email was sent to you by mistake, please contact the sender of the email and delete this file immediately. Confidentiality and legal privileges are not waived or lost by misdirected emails. Any views or opinions expressed in the email are those of the author and do not necessarily represent those of the Company.
>
--
Kees Cook
^ permalink raw reply
* Re: BUG: net-next (7.0-rc6 based and later) fails to boot on Jetson Xavier NX
From: Russell King (Oracle) @ 2026-04-09 16:16 UTC (permalink / raw)
To: Linus Torvalds
Cc: Will Deacon, Robin Murphy, netdev, linux-arm-kernel, linux-kernel,
iommu, linux-ext4, dmaengine, Marek Szyprowski, Theodore Ts'o,
Andreas Dilger, Vinod Koul, Frank Li
In-Reply-To: <CAHk-=whO3F1u+nme4cnYMy5baYmb7CH=wE63dcNaPLWD0vKaew@mail.gmail.com>
On Thu, Apr 09, 2026 at 08:37:53AM -0700, Linus Torvalds wrote:
> On Thu, 9 Apr 2026 at 05:24, Will Deacon <will@kernel.org> wrote:
> >
> > On Wed, Apr 08, 2026 at 08:52:32PM +0100, Russell King (Oracle) wrote:
> > > What's the status on the iommu fix? Is it merged into mainline yet?
> > > If it isn't already, that means net-next remains unbootable going
> > > into the merge window without manually carrying the fix locally.
> >
> > I'll pick it up for 7.0 in the iommu tree.
>
> ... and now it's in my tree.
Thanks, I see you merged it prior to the net tree, which should mean
the fix finds its way into net-next! Yay! Double thanks for that!
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* [PATCH] arm64: Kconfig: fix duplicate word in CMDLINE help text
From: Michael Ugrin @ 2026-04-09 16:24 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon
Cc: linux-arm-kernel, linux-kernel, Michael Ugrin
Remove duplicate 'the' in the CMDLINE config help text.
Signed-off-by: Michael Ugrin <mugrinphoto@gmail.com>
---
arch/arm64/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 9ea19b74b..85d952740 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -2373,7 +2373,7 @@ config CMDLINE
default ""
help
Provide a set of default command-line options at build time by
- entering them here. As a minimum, you should specify the the
+ entering them here. As a minimum, you should specify the
root device (e.g. root=/dev/nfs).
choice
--
2.51.2.windows.1
^ permalink raw reply related
* Re: [PATCH] arm64: mte: Skip TFSR_EL1 checks and barriers in synchronous tag check mode
From: Catalin Marinas @ 2026-04-09 16:27 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: Muhammad Usama Anjum, Will Deacon, Matthew Wilcox (Oracle),
Thomas Huth, Andrew Morton, Lance Yang, Yeoreum Yun,
linux-arm-kernel, linux-kernel
In-Reply-To: <c347fdc9-90b4-43ac-8c2e-559248777ba8@kernel.org>
On Wed, Mar 25, 2026 at 12:46:40PM +0100, David Hildenbrand wrote:
> On 3/11/26 18:50, Muhammad Usama Anjum wrote:
> > In MTE synchronous mode, tag check faults are reported as immediate
> > Data Abort exceptions. The TFSR_EL1.TF1 bit is never set, since faults
> > never go through the asynchronous path. Therefore, reading TFSR_EL1
> > and executing data and instruction barriers on kernel entry, exit,
> > context switch, and suspend is unnecessary overhead in sync mode.
> >
> > The exit path (mte_check_tfsr_exit) and the assembly paths
> > (check_mte_async_tcf / clear_mte_async_tcf in entry.S) already had this
> > check.
>
> Right, that's for user space (TFSR_EL1.TF0 IIUC). What you are adding is
> for KASAN. Maybe make that clearer.
Yeah, I'll tweak the commit message a bit. Even
system_uses_mte_async_or_asymm_mode() should be renamed to something
resembling kasan but I'll leave the function name as is for now.
--
Catalin
^ permalink raw reply
* Re: [PATCH v2 2/4] perf: Fix uninitialized bitfields in perf_clear_branch_entry_bitfields()
From: Leo Yan @ 2026-04-09 16:28 UTC (permalink / raw)
To: Puranjay Mohan
Cc: bpf, Puranjay Mohan, Alexei Starovoitov, Andrii Nakryiko,
Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
Kumar Kartikeya Dwivedi, Will Deacon, Mark Rutland,
Catalin Marinas, Rob Herring, Breno Leitao, linux-arm-kernel,
linux-perf-users, kernel-team
In-Reply-To: <20260318171706.2840512-3-puranjay@kernel.org>
On Wed, Mar 18, 2026 at 10:16:56AM -0700, Puranjay Mohan wrote:
[...]
> diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
> index 48d851fbd8ea..d7f39b7e9cda 100644
> --- a/include/linux/perf_event.h
> +++ b/include/linux/perf_event.h
BTW, I found you didn't include correct maintainers for perf core
changes. You might need to loop them in the new version.
^ permalink raw reply
* Re: [PATCH v2 1/3] arm64: mm: Fix rodata=full block mapping support for realm guests
From: Yang Shi @ 2026-04-09 16:48 UTC (permalink / raw)
To: Catalin Marinas, Kevin Brodsky
Cc: Ryan Roberts, Will Deacon, David Hildenbrand (Arm), Dev Jain,
Suzuki K Poulose, Jinjiang Tu, linux-arm-kernel, linux-kernel,
stable
In-Reply-To: <adfDoatH8hj6zN7_@arm.com>
On 4/9/26 8:20 AM, Catalin Marinas wrote:
> On Thu, Apr 09, 2026 at 11:53:41AM +0200, Kevin Brodsky wrote:
>> On 07/04/2026 12:52, Catalin Marinas wrote:
>>>> if we have forced pte mapping then the value of
>>>> can_set_direct_map() is irrelevant - we will never need to split because we are
>>>> already pte-mapped.
>>> can_set_direct_map() is used in other places, so its value is
>>> relevant, e.g. sys_memfd_secret() is rejected if this function returns
>>> false.
>> Indeed, I have noticed this before: currently set_direct_map_*_noflush()
>> and other functions will either fail or do nothing if none of the
>> features (rodata=full, etc.) is enabled, even if we would be able to
>> split the linear map using BBML2-noabort.
> That's what I have been trying to say to Ryan ;), can_set_direct_map()
> has different meanings depending on the caller: hint that it might split
> or asking whether splitting is permitted. The latter is not captured.
> Ignoring realms, if we have BBML2_NOABORT the kernel won't force pte
> mappings under the assumption that split_kernel_leaf_mapping() is safe.
> However set_direct_map_*_noflush() won't even reach the split function
> because the "can" part says "no, you can't".
>
>> What would make more sense to me is to enable the use of BBML2-noabort
>> unconditionally if !force_pte_mapping(). We can then have
>> can_set_direct_map() return true if we have BBML2-noabort, and we no
>> longer need to check it in map_mem().
> Indeed.
I'm trying to wrap up my head for this discussion. IIUC, if none of the
features is enabled, it means we don't need do anything because the
direct map is not changed. For example, if vmalloc doesn't change direct
map permission when rodata != full, there is no need to call
set_direct_map_*_noflush(). So unconditionally checking BBML2_NOABORT
will change the behavior unnecessarily. Did I miss something?
I think the only exception is secretmem if I don't miss something.
Currently, secretmem is actually not supported if none of the features
is enabled. But BBML2_NOABORT allows to lift the restriction.
Thanks,
Yang
>
>> This is a functional change that doesn't have anything to do with realms
>> so it should probably be a separate series - happy to take care of it
>> once the dust settles on the realm handling.
> I think it can be done in parallel, it shouldn't interfere with realms.
> The realm part should just affect force_pte_mapping() and
> can_set_direct_map() should return just what's possible, not what may
> need to set the direct map.
>
^ permalink raw reply
* Re: [RFC net PATCH v1] net: pcs: pcs-mtk-lynxi: fix bpi-r3 serdes configuration
From: Vladimir Oltean @ 2026-04-09 16:49 UTC (permalink / raw)
To: Frank Wunderlich, Chester A. Unal, Felix Fietkau
Cc: Alexander Couzens, Daniel Golle, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Matthias Brugger, AngeloGioacchino Del Regno,
Frank Wunderlich, netdev, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <20260409133344.129620-1-linux@fw-web.de>
On Thu, Apr 09, 2026 at 03:33:42PM +0200, Frank Wunderlich wrote:
> From: Frank Wunderlich <frank-w@public-files.de>
>
> Commit 8871389da151 introduces common pcs dts properties which writes
> rx=normal,tx=normal polarity to register SGMSYS_QPHY_WRAP_CTRL of switch.
> This is initialized with tx-bit set and so change inverts polarity
> compared to before.
>
> It looks like mt7531 has tx polarity inverted in hardware and set tx-bit
> by default to restore the normal polarity.
>
> Till this patch the register write was only called when mediatek,pnswap
> property was set which cannot be done for switch because the fw-node param
> was always NULL from switch driver in the mtk_pcs_lynxi_create call.
>
> Do not configure switch side like it's done before.
>
> Fixes: 8871389da151 ("net: pcs: pcs-mtk-lynxi: deprecate "mediatek,pnswap"")
> Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
> ---
> drivers/net/pcs/pcs-mtk-lynxi.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/net/pcs/pcs-mtk-lynxi.c b/drivers/net/pcs/pcs-mtk-lynxi.c
> index c12f8087af9b..a753bd88cbc2 100644
> --- a/drivers/net/pcs/pcs-mtk-lynxi.c
> +++ b/drivers/net/pcs/pcs-mtk-lynxi.c
> @@ -129,6 +129,9 @@ static int mtk_pcs_config_polarity(struct mtk_pcs_lynxi *mpcs,
> unsigned int val = 0;
> int ret;
>
> + if (!fwnode)
> + return 0;
> +
> if (fwnode_property_read_bool(fwnode, "mediatek,pnswap"))
> default_pol = PHY_POL_INVERT;
>
> --
> 2.43.0
>
I notice Arınc, listed by ./scripts/get_maintainer.pl drivers/net/dsa/mt7530.c,
and Felix, listed by ./scripts/get_maintainer.pl drivers/net/ethernet/mediatek/mtk_eth_soc.c,
are not on CC. Maybe they have more info.
Only the switch port has a chance of having a non-zero default polarity
setting? (coming from the efuse, if I understood this discussion properly)
https://lore.kernel.org/netdev/C59EED96-3973-4074-A4D8-C264949D447E@linux.dev/
The GMAC doesn't?
^ permalink raw reply
* Re: [PATCH V11 02/12] PCI: host-generic: Add common helpers for parsing Root Port properties
From: Manivannan Sadhasivam @ 2026-04-09 16:58 UTC (permalink / raw)
To: Sherry Sun
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
Frank Li, s.hauer@pengutronix.de, kernel@pengutronix.de,
festevam@gmail.com, lpieralisi@kernel.org, kwilczynski@kernel.org,
bhelgaas@google.com, Hongxing Zhu, l.stach@pengutronix.de,
imx@lists.linux.dev, linux-pci@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <VI0PR04MB1211419BB996B4790AE04170F92582@VI0PR04MB12114.eurprd04.prod.outlook.com>
On Thu, Apr 09, 2026 at 02:58:21AM +0000, Sherry Sun wrote:
> > On Wed, Apr 08, 2026 at 06:34:02AM +0000, Sherry Sun wrote:
> >
> > [...]
> >
> > > > > +/**
> > > > > + * pci_host_common_parse_port - Parse a single Root Port node
> > > > > + * @dev: Device pointer
> > > > > + * @bridge: PCI host bridge
> > > > > + * @node: Device tree node of the Root Port
> > > > > + *
> > > > > + * Returns: 0 on success, negative error code on failure */
> > > > > +static int pci_host_common_parse_port(struct device *dev,
> > > > > + struct pci_host_bridge *bridge,
> > > > > + struct device_node *node) {
> > > > > + struct pci_host_port *port;
> > > > > + struct gpio_desc *reset;
> > > > > +
> > > > > + reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node),
> > > > > + "reset", GPIOD_ASIS, "PERST#");
> > > >
> > > > Sorry, I missed this earlier.
> > > >
> > > > Since PERST# is optional, you cannot reliably detect whether the
> > > > Root Port binding intentionally skipped the PERST# GPIO or legacy
> > > > binding is used, just by checking for PERST# in Root Port node.
> > > >
> > > > So this helper should do 3 things:
> > > >
> > > > 1. If PERST# is found in Root Port node, use it.
> > > > 2. If not, check the RC node and if present, return -ENOENT to
> > > > fallback to the legacy binding.
> > > > 3. If not found in both nodes, assume that the PERST# is not present
> > > > in the design, and proceed with parsing Root Port binding further.
> > >
> > > Hi Mani, understand, does the following code looks ok for above three
> > cases?
> > >
> > > /* Check if PERST# is present in Root Port node */
> > > reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node),
> > > "reset", GPIOD_ASIS, "PERST#");
> > > if (IS_ERR(reset)) {
> > > /* If error is not -ENOENT, it's a real error */
> > > if (PTR_ERR(reset) != -ENOENT)
> > > return PTR_ERR(reset);
> > >
> > > /* PERST# not found in Root Port node, check RC node */
> > > rc_has_reset = of_property_read_bool(dev->of_node, "reset-gpios") ||
> > > of_property_read_bool(dev->of_node, "reset-gpio");
> >
> > Just:
> > if (of_property_read_bool(dev->of_node, "reset-gpios") ||
> > of_property_read_bool(dev->of_node, "reset-gpio")) {
> > return -ENOENT;
> > }
>
> Ok, will do.
>
> >
> > > if (rc_has_reset)
> > > return -ENOENT;
> > >
> > > /* No PERST# in either node, assume not present in design */
> > > reset = NULL;
> > > }
> > >
> > > port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > > if (!port)
> > > return -ENOMEM;
> > > ...
> > >
> > > >
> > > > But there is one more important limitation here. Right now, this API
> > > > only handles PERST#. But if another vendor tries to use it and if
> > > > they need other properties such as PHY, clocks etc... those
> > > > resources should be fetched optionally only by this helper. But if
> > > > the controller has a hard dependency on those resources, the driver will
> > fail to operate.
> > > >
> > > > I don't think we can fix this limitation though and those platforms
> > > > should ensure that the resource dependency is correctly modeled in
> > > > DT binding and the DTS is validated properly. It'd be good to
> > > > mention this in the kernel doc of this API.
> > >
> > > Ok, I will add a NOTE for this in this API description.
> > >
> > > >
> > > > > + if (IS_ERR(reset))
> > > > > + return PTR_ERR(reset);
> > > > > +
> > > > > + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> > > > > + if (!port)
> > > > > + return -ENOMEM;
> > > > > +
> > > > > + port->reset = reset;
> > > > > + INIT_LIST_HEAD(&port->list);
> > > > > + list_add_tail(&port->list, &bridge->ports);
> > > > > +
> > > > > + return 0;
> > > > > +}
> > > > > +
> > > > > +/**
> > > > > + * pci_host_common_parse_ports - Parse Root Port nodes from
> > > > > +device tree
> > > > > + * @dev: Device pointer
> > > > > + * @bridge: PCI host bridge
> > > > > + *
> > > > > + * This function iterates through child nodes of the host bridge
> > > > > +and parses
> > > > > + * Root Port properties (currently only reset GPIO).
> > > > > + *
> > > > > + * Returns: 0 on success, -ENOENT if no ports found, other
> > > > > +negative error codes
> > > > > + * on failure
> > > > > + */
> > > > > +int pci_host_common_parse_ports(struct device *dev, struct
> > > > > +pci_host_bridge *bridge) {
> > > > > + int ret = -ENOENT;
> > > > > +
> > > > > + for_each_available_child_of_node_scoped(dev->of_node, of_port) {
> > > > > + if (!of_node_is_type(of_port, "pci"))
> > > > > + continue;
> > > > > + ret = pci_host_common_parse_port(dev, bridge, of_port);
> > > > > + if (ret)
> > > > > + return ret;
> > > >
> > > > As Sashiko flagged, you need to make sure that
> > > > devm_add_action_or_reset() is added even during the error path:
> > >
> > > Yes, it needs to be fixed. We can handle it with the following two methods, I
> > am not sure which method is better or more preferable?
> > >
> > > #1: register cleanup action after first successful port parse and use
> > cleanup_registered flag to avoid duplicate register.
> > > int ret = -ENOENT;
> > > bool cleanup_registered = false;
> > >
> > > for_each_available_child_of_node_scoped(dev->of_node, of_port) {
> > > if (!of_node_is_type(of_port, "pci"))
> > > continue;
> > > ret = pci_host_common_parse_port(dev, bridge, of_port);
> > > if (ret)
> > > return ret;
> > >
> > > /* Register cleanup action after first successful port parse */
> > > if (!cleanup_registered) {
> > > ret = devm_add_action_or_reset(dev,
> > > pci_host_common_delete_ports,
> > > &bridge->ports);
> >
> > Even if you register devm_add_action_or_reset(), it won't be called when
> > pci_host_common_parse_port() fails since the legacy fallback will be used.
> >
> > So you need to manually call pci_host_common_delete_ports() in the error
> > path.
>
> Get your point, so seems I should just add the err_cleanup handle path like this, right?
>
> for_each_available_child_of_node_scoped(dev->of_node, of_port) {
> if (!of_node_is_type(of_port, "pci"))
> continue;
> ret = pci_host_common_parse_port(dev, bridge, of_port);
> if (ret)
> goto err_cleanup;
> }
>
> if (ret)
> return ret;
>
> return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
> &bridge->ports);
>
> err_cleanup:
> pci_host_common_delete_ports(&bridge->ports);
> return ret;
>
Yes!
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH] KVM: arm64: Add KVM_CAP_ARM_NATIVE_CACHE_CONFIG vcpu capability
From: Marc Zyngier @ 2026-04-09 17:07 UTC (permalink / raw)
To: David Woodhouse
Cc: Gutierrez Cantu, Bernardo, alexandru.elisei, alyssa, asahi,
broonie, catalin.marinas, james.morse, kvmarm, linux-arm-kernel,
linux-kernel, marcan, mathieu.poirier, oliver.upton,
suzuki.poulose, sven, will
In-Reply-To: <7fb7b823c68e04321eb532a5b8ae21a818d4926d.camel@infradead.org>
On Thu, 09 Apr 2026 16:29:06 +0100,
David Woodhouse <dwmw2@infradead.org> wrote:
>
> [1 <text/plain; UTF-8 (quoted-printable)>]
> From: David Woodhouse <dwmw@amazon.co.uk>
>
> Commit 7af0c2534f4c5 ("KVM: arm64: Normalize cache configuration")
> fabricates CLIDR_EL1 and CCSIDR_EL1 values instead of using the real
> hardware values. While this provides consistent values across
> heterogeneous CPUs, it does cause visible changes in the CPU model
> exposed to guests.
>
> The commit claims that userspace can restore the original values, but
> there is no way for userspace to obtain the real CLIDR_EL1 register
> value — it is not fully reconstructible from sysfs, which lacks the
> LoC, LoUU, and LoUIS fields.
>
> Add a per-vcpu KVM_CAP_ARM_NATIVE_CACHE_CONFIG capability that reads
> the real CLIDR_EL1 and all CCSIDR_EL1 values from the current physical
> CPU and sets them on the vcpu.
>
> This allows hypervisors to present the real hardware cache configuration
> to guests, which is important for consistency of the environment across
> kernel versions and for migration compatibility with hosts running
> older kernels that exposed the real values.
>
> Fixes: 7af0c2534f4c ("KVM: arm64: Normalize cache configuration")
> Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
> ---
> Documentation/virt/kvm/api.rst | 23 ++++++++
> arch/arm64/include/asm/kvm_host.h | 1 +
> arch/arm64/kvm/arm.c | 17 ++++++
> arch/arm64/kvm/sys_regs.c | 26 ++++++++++
> include/uapi/linux/kvm.h | 1 +
> tools/testing/selftests/kvm/Makefile.kvm | 1 +
> .../selftests/kvm/arm64/native_cache_config.c | 52 +++++++++++++++++++
> 7 files changed, 121 insertions(+)
> create mode 100644 tools/testing/selftests/kvm/arm64/native_cache_config.c
>
> diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
> index e3b3bd9edeec..ee47dc07ceac 100644
> --- a/Documentation/virt/kvm/api.rst
> +++ b/Documentation/virt/kvm/api.rst
> @@ -8930,6 +8930,29 @@ no-op.
>
> ``KVM_CHECK_EXTENSION`` returns the bitmask of exits that can be disabled.
>
> +7.48 KVM_CAP_ARM_NATIVE_CACHE_CONFIG
> +-------------------------------------
> +
> +:Architecture: arm64
> +:Target: vcpu
> +:Parameters: none
> +:Returns: 0 on success, -ENOMEM on allocation failure, -EINVAL if
> + args[0] or flags are non-zero.
> +
> +This per-vcpu capability reads the real CLIDR_EL1 and CCSIDR_EL1 values
> +from the physical CPU on which the ioctl is executed, and sets them on
> +the vcpu. This replaces the fabricated cache configuration that KVM
> +provides by default.
> +
> +The caller should ensure the vcpu thread is pinned to the desired
> +physical CPU before invoking this capability, so that the correct cache
> +topology is captured. On heterogeneous systems, different physical CPUs
> +may have different cache configurations.
> +
> +After this capability is enabled, the vcpu's CLIDR_EL1 and CCSIDR_EL1
> +values can still be overridden individually via ``KVM_SET_ONE_REG`` and
> +the ``KVM_REG_ARM_DEMUX`` interface.
> +
> 8. Other capabilities.
> ======================
>
> diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
> index a1bb025c641f..c9713a472c47 100644
> --- a/arch/arm64/include/asm/kvm_host.h
> +++ b/arch/arm64/include/asm/kvm_host.h
> @@ -1296,6 +1296,7 @@ void kvm_sys_regs_create_debugfs(struct kvm *kvm);
> void kvm_reset_sys_regs(struct kvm_vcpu *vcpu);
>
> int __init kvm_sys_reg_table_init(void);
> +int kvm_vcpu_set_native_cache_config(struct kvm_vcpu *vcpu);
> struct sys_reg_desc;
> int __init populate_sysreg_config(const struct sys_reg_desc *sr,
> unsigned int idx);
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index 326a99fea753..579583e8dc5c 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -393,6 +393,10 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
> case KVM_CAP_ARM_DISABLE_EXITS:
> r = KVM_ARM_DISABLE_VALID_EXITS;
> break;
> + case KVM_CAP_ARM_NATIVE_CACHE_CONFIG:
> + case KVM_CAP_ENABLE_CAP:
> + r = 1;
> + break;
> case KVM_CAP_SET_GUEST_DEBUG2:
> return KVM_GUESTDBG_VALID_MASK;
> case KVM_CAP_ARM_SET_DEVICE_ADDR:
> @@ -1793,6 +1797,19 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
> r = kvm_arch_vcpu_ioctl_vcpu_init(vcpu, &init);
> break;
> }
> + case KVM_ENABLE_CAP: {
> + struct kvm_enable_cap cap;
> +
> + r = -EFAULT;
> + if (copy_from_user(&cap, argp, sizeof(cap)))
> + break;
> +
> + r = -EINVAL;
> + if (cap.cap == KVM_CAP_ARM_NATIVE_CACHE_CONFIG &&
> + !cap.args[0] && !cap.flags)
> + r = kvm_vcpu_set_native_cache_config(vcpu);
> + break;
> + }
> case KVM_SET_ONE_REG:
> case KVM_GET_ONE_REG: {
> struct kvm_one_reg reg;
> diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
> index 1b4cacb6e918..c19d84e48f8b 100644
> --- a/arch/arm64/kvm/sys_regs.c
> +++ b/arch/arm64/kvm/sys_regs.c
> @@ -484,6 +484,32 @@ static int set_ccsidr(struct kvm_vcpu *vcpu, u32 csselr, u32 val)
> return 0;
> }
>
> +int kvm_vcpu_set_native_cache_config(struct kvm_vcpu *vcpu)
> +{
> + u32 csselr;
> +
> + if (!vcpu->arch.ccsidr) {
> + vcpu->arch.ccsidr = kmalloc_array(CSSELR_MAX, sizeof(u32),
> + GFP_KERNEL_ACCOUNT);
> + if (!vcpu->arch.ccsidr)
> + return -ENOMEM;
> + }
Well, no.
The moment you decide to expose all of the host's crap, you really
need to put everything on the table. It means fully handling
FEAT_CCIDX, which we were careful not to expose anywhere because it is
a terrible idea.
So CCSIDR_EL1 becomes a 64bit value, is complemented with CCSIDR2_EL1,
and needs to be advertised as such through the idregs. CCSIDR2_EL1
must be exposed to userspace and made writable, but only if the
feature exists. You also need to conditionally undef CCSIDR2_EL1
depending on the VM configuration.
The "amusing" thing is that, before we introduced the cache hierarchy
sanitisation, we would happily report something completely senseless
on CCIDX hardware. It didn't matter, because nobody can make any use
of that information, apart from EL3 firmware.
But if you want this to be a reflection of the underlying HW, then so
be it.
> + for (csselr = 0; csselr < CSSELR_MAX; csselr++) {
> + write_sysreg(csselr, csselr_el1);
> + isb();
> + vcpu->arch.ccsidr[csselr] = read_sysreg(ccsidr_el1);
That's not how the selection register works. CLIDR_EL1 tells you what
each cache level is (Instructions, Data, Unified, Tags), and that must
be combined with the index (which doesn't start at bit 0).
I also wonder how you reconcile not exposing MTE when the cache
hierarchy indicate support for tags. That clearly contradicts "report
what the HW has".
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* [PATCH RFC 01/12] drm/i915/display/intel_sdvo: Fix double connector destroy in error paths
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
intel_sdvo_connector_funcs registers intel_connector_destroy() as the
.destroy callback. Once drm_connector_init_with_ddc() succeeds inside
intel_sdvo_connector_init(), the DRM core takes ownership of the
connector object and will call .destroy on teardown.
The error labels in intel_sdvo_tv_init() and intel_sdvo_lvds_init()
call intel_connector_destroy() explicitly before returning false,
causing it to be invoked twice: once in the error path and again by
the DRM core through the registered .destroy callback.
Remove the manual intel_connector_destroy() calls from the error labels
and return false directly instead.
Fixes: 32aad86fe88e7 ("drm/i915/sdvo: Propagate errors from reading/writing control bus.")
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
Not tested as I don't have such hardware.
---
drivers/gpu/drm/i915/display/intel_sdvo.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
index 2e1af9e869ded..6eb2b4b45a9b4 100644
--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
@@ -2873,16 +2873,12 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, u16 type)
}
if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
- goto err;
+ return false;
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
- goto err;
+ return false;
return true;
-
-err:
- intel_connector_destroy(connector);
- return false;
}
static bool
@@ -2945,7 +2941,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, u16 type)
}
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
- goto err;
+ return false;
intel_bios_init_panel_late(display, &intel_connector->panel, NULL, NULL);
@@ -2967,13 +2963,9 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, u16 type)
intel_panel_init(intel_connector, NULL);
if (!intel_panel_preferred_fixed_mode(intel_connector))
- goto err;
+ return false;
return true;
-
-err:
- intel_connector_destroy(connector);
- return false;
}
static u16 intel_sdvo_filter_output_flags(u16 flags)
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 00/12] Add support for DisplayPort link training information report
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
DisplayPort link training negotiates the physical-layer parameters needed
for a reliable connection: lane count, link rate, voltage swing,
pre-emphasis, and optionally Display Stream Compression (DSC). Currently,
each driver exposes this state in its own way, often through
driver-specific debugfs entries, with no standard interface for userspace
diagnostic and monitoring tools.
This series introduces a generic, DRM-managed framework for exposing DP
link training state as standard connector properties, modeled after the
existing HDMI helper drmm_connector_hdmi_init().
The new drmm_connector_dp_init() helper initializes a DP connector and
registers the following connector properties to expose the negotiated link
state to userspace:
- num_lanes: negotiated lane count (1, 2 or 4)
- link_rate: negotiated link rate
- dsc_en: whether Display Stream Compression is active
- voltage_swingN: per-lane voltage swing level (lanes 0-3)
- pre_emphasisN: per-lane pre-emphasis level (lanes 0-3)
Two runtime helpers update and clear these properties when link training
completes or the link goes down:
- drm_connector_dp_set_link_train_properties()
- drm_connector_dp_reset_link_train_properties()
Two drivers are updated as reference implementations: i915 (direct
connector path) and MediaTek (via the bridge connector framework using a
new DRM_BRIDGE_OP_DP flag). The i915 patches are preceded by a series of
conversions to DRM managed resources, which are required before adopting
drmm_connector_dp_init().
The MST case in i915 driver is not supported yet.
Patches 1-3: Fix two error-path cleanup bugs in i915 sdvo and lvds
[Will probably be sent standalone]
Patches 4-8: Convert i915 display resources to DRM managed lifetime
Patch 9: Introduce the core drmm_connector_dp_init() framework
Patch 10: Wire the i915 DP connector to use the new helpers
Patch 11: Introduce DRM_BRIDGE_OP_DP and wire bridge connectors
Patch 12: Wire the MediaTek DP bridge to the new helpers [untested]
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
Kory Maincent (12):
drm/i915/display/intel_sdvo: Fix double connector destroy in error paths
drm/i915/display/intel_lvds: Drop redundant manual cleanup on init failure
drm/i915/display/intel_dp: Drop redundant intel_dp_aux_fini() on init failure
drm/i915/display: Switch to drmm_mode_config_init() and drop manual cleanup
drm/i915/display: Switch to managed for crtc
drm/i915/display: Switch to managed for plane
drm/i915/display: Switch to managed for encoder
drm/i915/display: Switch to managed for connector
drm: Introduce drmm_connector_dp_init() with link training state properties
drm/i915/display/dp: Adopt dp_connector helpers to expose link training state
drm/bridge: Wire drmm_connector_dp_init() via new DRM_BRIDGE_OP_DP flag
drm/mediatek: Use dp_connector helpers to report link training state
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/display/drm_bridge_connector.c | 26 +-
drivers/gpu/drm/drm_dp_connector.c | 344 +++++++++++++++++++++
drivers/gpu/drm/i915/display/g4x_dp.c | 39 +--
drivers/gpu/drm/i915/display/g4x_hdmi.c | 27 +-
drivers/gpu/drm/i915/display/i9xx_plane.c | 97 +++---
drivers/gpu/drm/i915/display/icl_dsi.c | 50 ++-
drivers/gpu/drm/i915/display/intel_connector.c | 26 +-
drivers/gpu/drm/i915/display/intel_connector.h | 5 +-
drivers/gpu/drm/i915/display/intel_crt.c | 28 +-
drivers/gpu/drm/i915/display/intel_crtc.c | 102 +++---
drivers/gpu/drm/i915/display/intel_cursor.c | 41 ++-
drivers/gpu/drm/i915/display/intel_ddi.c | 64 ++--
drivers/gpu/drm/i915/display/intel_display.c | 8 -
drivers/gpu/drm/i915/display/intel_display.h | 1 -
.../gpu/drm/i915/display/intel_display_driver.c | 37 ++-
drivers/gpu/drm/i915/display/intel_dp.c | 43 ++-
.../gpu/drm/i915/display/intel_dp_link_training.c | 25 ++
drivers/gpu/drm/i915/display/intel_dp_mst.c | 33 +-
drivers/gpu/drm/i915/display/intel_dvo.c | 43 +--
drivers/gpu/drm/i915/display/intel_encoder.c | 6 +-
drivers/gpu/drm/i915/display/intel_encoder.h | 3 +-
drivers/gpu/drm/i915/display/intel_hdmi.c | 15 +-
drivers/gpu/drm/i915/display/intel_lvds.c | 45 ++-
drivers/gpu/drm/i915/display/intel_plane.c | 45 +--
drivers/gpu/drm/i915/display/intel_plane.h | 5 +-
drivers/gpu/drm/i915/display/intel_sdvo.c | 134 +++-----
drivers/gpu/drm/i915/display/intel_sprite.c | 119 ++++---
drivers/gpu/drm/i915/display/intel_tv.c | 26 +-
drivers/gpu/drm/i915/display/skl_universal_plane.c | 102 +++---
drivers/gpu/drm/i915/display/vlv_dsi.c | 42 +--
drivers/gpu/drm/mediatek/mtk_dp.c | 34 +-
include/drm/drm_bridge.h | 13 +
include/drm/drm_connector.h | 38 +++
include/drm/drm_dp_connector.h | 109 +++++++
35 files changed, 1125 insertions(+), 651 deletions(-)
---
base-commit: db5a75cfd29766536be62aece9f19c6e7a858fa6
change-id: 20260226-feat_link_cap-20cbb6f31d40
Best regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH RFC 02/12] drm/i915/display/intel_lvds: Drop redundant manual cleanup on init failure
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
intel_lvds_init() had a goto-based error path that manually called
drm_connector_cleanup(), drm_encoder_cleanup(), kfree() and
intel_connector_free() when no LVDS panel mode could be found.
Once drm_connector_init_with_ddc() and drm_encoder_init() have been
called, the DRM core takes ownership of these objects and will invoke
their .destroy callbacks (intel_connector_destroy and
intel_encoder_destroy) during device teardown. The manual cleanup in
the failed: label is therefore redundant.
Remove it and replace the goto with a simple early return.
Fixes: 79e539453b34e ("DRM: i915: add mode setting support")
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
Not tested as I don't have such hardware.
---
drivers/gpu/drm/i915/display/intel_lvds.c | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index cc6d4bfcff102..e78a41e2b268c 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -991,8 +991,10 @@ void intel_lvds_init(struct intel_display *display)
mutex_unlock(&display->drm->mode_config.mutex);
/* If we still don't have a mode after all that, give up. */
- if (!intel_panel_preferred_fixed_mode(connector))
- goto failed;
+ if (!intel_panel_preferred_fixed_mode(connector)) {
+ drm_dbg_kms(display->drm, "No LVDS modes found, disabling.\n");
+ return;
+ }
intel_panel_init(connector, drm_edid);
@@ -1005,12 +1007,4 @@ void intel_lvds_init(struct intel_display *display)
lvds_encoder->a3_power = lvds & LVDS_A3_POWER_MASK;
return;
-
-failed:
- drm_dbg_kms(display->drm, "No LVDS modes found, disabling.\n");
- drm_connector_cleanup(&connector->base);
- drm_encoder_cleanup(&encoder->base);
- kfree(lvds_encoder);
- intel_connector_free(connector);
- return;
}
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 03/12] drm/i915/display/intel_dp: Drop redundant intel_dp_aux_fini() on init failure
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
intel_dp_aux_fini() is already invoked via intel_dp_encoder_flush_work()
in the encoder destroy path (intel_dp_encoder_destroy() and
intel_ddi_encoder_destroy()). Calling it explicitly when
intel_edp_init_connector() fails before jumping to the fail label
therefore results in a double invocation. Drop the redundant call.
Fixes: c191eca110a37 ("drm/i915: Move intel_connector->unregister to connector->early_unregister")
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/gpu/drm/i915/display/intel_dp.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 4955bd8b11d7a..71f206adbebd3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -7232,10 +7232,8 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
connector->get_hw_state = intel_connector_get_hw_state;
connector->sync_state = intel_dp_connector_sync_state;
- if (!intel_edp_init_connector(intel_dp, connector)) {
- intel_dp_aux_fini(intel_dp);
+ if (!intel_edp_init_connector(intel_dp, connector))
goto fail;
- }
intel_dp_set_source_rates(intel_dp);
intel_dp_set_common_rates(intel_dp);
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 04/12] drm/i915/display: Switch to drmm_mode_config_init() and drop manual cleanup
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
Replace drm_mode_config_init() with drmm_mode_config_init() in
intel_mode_config_init(). The managed variant automatically registers
drm_mode_config_cleanup() with devres, so drivers must no longer call it
directly.
Since intel_mode_config_cleanup() was solely a wrapper combining
intel_atomic_global_obj_cleanup() and drm_mode_config_cleanup(), and the
latter is now handled by DRM core, remove it entirely. Instead, register
intel_atomic_global_obj_cleanup() as a devres action so it still runs just
before drm_mode_config_cleanup() during teardown, preserving the correct
cleanup ordering.
Change intel_mode_config_init() to return int to propagate any error from
drmm_mode_config_init().
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
.../gpu/drm/i915/display/intel_display_driver.c | 37 ++++++++++++++--------
1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index 23bfecc983e8d..d02393053cef4 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -12,6 +12,7 @@
#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_client_event.h>
+#include <drm/drm_managed.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_privacy_screen_consumer.h>
#include <drm/drm_print.h>
@@ -48,6 +49,7 @@
#include "intel_fbdev.h"
#include "intel_fdi.h"
#include "intel_flipq.h"
+#include "intel_global_state.h"
#include "intel_gmbus.h"
#include "intel_hdcp.h"
#include "intel_hotplug.h"
@@ -111,13 +113,28 @@ static const struct drm_mode_config_helper_funcs intel_mode_config_funcs = {
.atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
};
-static void intel_mode_config_init(struct intel_display *display)
+static void intel_atomic_global_obj_cleanup_action(struct drm_device *drm, void *data)
+{
+ intel_atomic_global_obj_cleanup((struct intel_display *)data);
+}
+
+static int intel_mode_config_init(struct intel_display *display)
{
struct drm_mode_config *mode_config = &display->drm->mode_config;
+ int ret;
+
+ ret = drmm_mode_config_init(display->drm);
+ if (ret)
+ return ret;
- drm_mode_config_init(display->drm);
INIT_LIST_HEAD(&display->global.obj_list);
+ ret = drmm_add_action_or_reset(display->drm,
+ intel_atomic_global_obj_cleanup_action,
+ display);
+ if (ret)
+ return ret;
+
mode_config->min_width = 0;
mode_config->min_height = 0;
@@ -148,12 +165,8 @@ static void intel_mode_config_init(struct intel_display *display)
}
intel_cursor_mode_config_init(display);
-}
-static void intel_mode_config_cleanup(struct intel_display *display)
-{
- intel_atomic_global_obj_cleanup(display);
- drm_mode_config_cleanup(display->drm);
+ return 0;
}
static void intel_plane_possible_crtcs_init(struct intel_display *display)
@@ -255,7 +268,9 @@ int intel_display_driver_probe_noirq(struct intel_display *display)
intel_dmc_init(display);
- intel_mode_config_init(display);
+ ret = intel_mode_config_init(display);
+ if (ret)
+ goto cleanup_wq_unordered;
ret = intel_cdclk_init(display);
if (ret)
@@ -456,7 +471,7 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
ret = intel_crtc_init(display);
if (ret)
- goto err_mode_config;
+ return ret;
intel_plane_possible_crtcs_init(display);
intel_dpll_init(display);
@@ -497,8 +512,6 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
err_hdcp:
intel_hdcp_component_fini(display);
-err_mode_config:
- intel_mode_config_cleanup(display);
return ret;
}
@@ -618,8 +631,6 @@ void intel_display_driver_remove_noirq(struct intel_display *display)
intel_hdcp_component_fini(display);
- intel_mode_config_cleanup(display);
-
intel_dp_tunnel_mgr_cleanup(display);
intel_overlay_cleanup(display);
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 05/12] drm/i915/display: Switch to managed for crtc
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
The current i915 driver uses non-managed function to create crtc. It
is not an issue yet, but in order to comply with the latest DRM
requirement, convert this code to use drm and device managed helpers.
Assisted-by: Claude Code:2.1.90
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/gpu/drm/i915/display/intel_crtc.c | 102 ++++++++++--------------------
1 file changed, 35 insertions(+), 67 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c
index b8189cd5d864a..e2f995313acf2 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_managed.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
@@ -191,41 +192,11 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state,
crtc_state->max_link_bpp_x16 = INT_MAX;
}
-static struct intel_crtc *intel_crtc_alloc(void)
+static void intel_crtc_vblank_pm_qos_cleanup(struct drm_device *drm, void *data)
{
- struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- crtc = kzalloc_obj(*crtc);
- if (!crtc)
- return ERR_PTR(-ENOMEM);
-
- crtc_state = intel_crtc_state_alloc(crtc);
- if (!crtc_state) {
- kfree(crtc);
- return ERR_PTR(-ENOMEM);
- }
-
- crtc->base.state = &crtc_state->uapi;
- crtc->config = crtc_state;
-
- return crtc;
-}
-
-static void intel_crtc_free(struct intel_crtc *crtc)
-{
- intel_crtc_destroy_state(&crtc->base, crtc->base.state);
- kfree(crtc);
-}
-
-static void intel_crtc_destroy(struct drm_crtc *_crtc)
-{
- struct intel_crtc *crtc = to_intel_crtc(_crtc);
+ struct intel_crtc *crtc = data;
cpu_latency_qos_remove_request(&crtc->vblank_pm_qos);
-
- drm_crtc_cleanup(&crtc->base);
- kfree(crtc);
}
static int intel_crtc_late_register(struct drm_crtc *crtc)
@@ -236,7 +207,6 @@ static int intel_crtc_late_register(struct drm_crtc *crtc)
#define INTEL_CRTC_FUNCS \
.set_config = drm_atomic_helper_set_config, \
- .destroy = intel_crtc_destroy, \
.page_flip = drm_atomic_helper_page_flip, \
.atomic_duplicate_state = intel_crtc_duplicate_state, \
.atomic_destroy_state = intel_crtc_destroy_state, \
@@ -311,28 +281,19 @@ static const struct drm_crtc_funcs i8xx_crtc_funcs = {
static int __intel_crtc_init(struct intel_display *display, enum pipe pipe)
{
struct intel_plane *primary, *cursor;
+ struct intel_crtc_state *crtc_state;
const struct drm_crtc_funcs *funcs;
struct intel_crtc *crtc;
+ u32 plane_ids_mask = 0;
int sprite, ret;
- crtc = intel_crtc_alloc();
- if (IS_ERR(crtc))
- return PTR_ERR(crtc);
-
- crtc->pipe = pipe;
- crtc->num_scalers = DISPLAY_RUNTIME_INFO(display)->num_scalers[pipe];
-
if (DISPLAY_VER(display) >= 9)
primary = skl_universal_plane_create(display, pipe, PLANE_1);
else
primary = intel_primary_plane_create(display, pipe);
- if (IS_ERR(primary)) {
- ret = PTR_ERR(primary);
- goto fail;
- }
- crtc->plane_ids_mask |= BIT(primary->id);
-
- intel_init_fifo_underrun_reporting(display, crtc, false);
+ if (IS_ERR(primary))
+ return PTR_ERR(primary);
+ plane_ids_mask |= BIT(primary->id);
for_each_sprite(display, pipe, sprite) {
struct intel_plane *plane;
@@ -341,19 +302,15 @@ static int __intel_crtc_init(struct intel_display *display, enum pipe pipe)
plane = skl_universal_plane_create(display, pipe, PLANE_2 + sprite);
else
plane = intel_sprite_plane_create(display, pipe, sprite);
- if (IS_ERR(plane)) {
- ret = PTR_ERR(plane);
- goto fail;
- }
- crtc->plane_ids_mask |= BIT(plane->id);
+ if (IS_ERR(plane))
+ return PTR_ERR(plane);
+ plane_ids_mask |= BIT(plane->id);
}
cursor = intel_cursor_plane_create(display, pipe);
- if (IS_ERR(cursor)) {
- ret = PTR_ERR(cursor);
- goto fail;
- }
- crtc->plane_ids_mask |= BIT(cursor->id);
+ if (IS_ERR(cursor))
+ return PTR_ERR(cursor);
+ plane_ids_mask |= BIT(cursor->id);
if (HAS_GMCH(display)) {
if (display->platform.cherryview ||
@@ -376,11 +333,23 @@ static int __intel_crtc_init(struct intel_display *display, enum pipe pipe)
funcs = &ilk_crtc_funcs;
}
- ret = drm_crtc_init_with_planes(display->drm, &crtc->base,
- &primary->base, &cursor->base,
- funcs, "pipe %c", pipe_name(pipe));
- if (ret)
- goto fail;
+ crtc = drmm_crtc_alloc_with_planes(display->drm, struct intel_crtc, base,
+ &primary->base, &cursor->base,
+ funcs, "pipe %c", pipe_name(pipe));
+ if (IS_ERR(crtc))
+ return PTR_ERR(crtc);
+
+ crtc->pipe = pipe;
+ crtc->num_scalers = DISPLAY_RUNTIME_INFO(display)->num_scalers[pipe];
+ crtc->plane_ids_mask = plane_ids_mask;
+
+ crtc_state = intel_crtc_state_alloc(crtc);
+ if (!crtc_state)
+ return -ENOMEM;
+ crtc->base.state = &crtc_state->uapi;
+ crtc->config = crtc_state;
+
+ intel_init_fifo_underrun_reporting(display, crtc, false);
if (DISPLAY_VER(display) >= 11)
drm_crtc_create_scaling_filter_property(&crtc->base,
@@ -393,17 +362,16 @@ static int __intel_crtc_init(struct intel_display *display, enum pipe pipe)
cpu_latency_qos_add_request(&crtc->vblank_pm_qos, PM_QOS_DEFAULT_VALUE);
+ ret = drmm_add_action_or_reset(display->drm, intel_crtc_vblank_pm_qos_cleanup, crtc);
+ if (ret)
+ return ret;
+
drm_WARN_ON(display->drm, drm_crtc_index(&crtc->base) != crtc->pipe);
if (HAS_CASF(display) && crtc->num_scalers >= 2)
drm_crtc_create_sharpness_strength_property(&crtc->base);
return 0;
-
-fail:
- intel_crtc_free(crtc);
-
- return ret;
}
int intel_crtc_init(struct intel_display *display)
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 06/12] drm/i915/display: Switch to managed for plane
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
The current i915 driver uses non-managed function to create plane. It
is not an issue yet, but in order to comply with the latest DRM
requirement, convert this code to use drm and device managed helpers.
Assisted-by: Claude Code:2.1.90
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/gpu/drm/i915/display/i9xx_plane.c | 97 ++++++++---------
drivers/gpu/drm/i915/display/intel_cursor.c | 41 ++++---
drivers/gpu/drm/i915/display/intel_plane.c | 45 +-------
drivers/gpu/drm/i915/display/intel_plane.h | 5 +-
drivers/gpu/drm/i915/display/intel_sprite.c | 119 ++++++++++-----------
drivers/gpu/drm/i915/display/skl_universal_plane.c | 102 +++++++++---------
6 files changed, 181 insertions(+), 228 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c
index 9c16753a1f3ba..032c56b478dfc 100644
--- a/drivers/gpu/drm/i915/display/i9xx_plane.c
+++ b/drivers/gpu/drm/i915/display/i9xx_plane.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include "i9xx_plane.h"
@@ -882,7 +883,6 @@ static unsigned int i9xx_plane_min_alignment(struct intel_plane *plane,
static const struct drm_plane_funcs i965_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = i965_plane_format_mod_supported,
@@ -892,7 +892,6 @@ static const struct drm_plane_funcs i965_plane_funcs = {
static const struct drm_plane_funcs i8xx_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = i8xx_plane_format_mod_supported,
@@ -923,32 +922,15 @@ static void i9xx_disable_tiling(struct intel_plane *plane)
struct intel_plane *
intel_primary_plane_create(struct intel_display *display, enum pipe pipe)
{
+ struct intel_plane_state *plane_state;
struct intel_plane *plane;
const struct drm_plane_funcs *plane_funcs;
unsigned int supported_rotations;
const u64 *modifiers;
const u32 *formats;
int num_formats;
- int ret, zpos;
-
- plane = intel_plane_alloc();
- if (IS_ERR(plane))
- return plane;
-
- plane->pipe = pipe;
- /*
- * On gen2/3 only plane A can do FBC, but the panel fitter and LVDS
- * port is hooked to pipe B. Hence we want plane A feeding pipe B.
- */
- if (HAS_FBC(display) && DISPLAY_VER(display) < 4 &&
- INTEL_NUM_PIPES(display) == 2)
- plane->i9xx_plane = (enum i9xx_plane_id) !pipe;
- else
- plane->i9xx_plane = (enum i9xx_plane_id) pipe;
- plane->id = PLANE_PRIMARY;
- plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane->id);
-
- intel_fbc_add_plane(i9xx_plane_fbc(display, plane->i9xx_plane), plane);
+ enum i9xx_plane_id i9xx_plane;
+ int zpos;
if (display->platform.valleyview || display->platform.cherryview) {
formats = vlv_primary_formats;
@@ -984,6 +966,46 @@ intel_primary_plane_create(struct intel_display *display, enum pipe pipe)
else
plane_funcs = &i8xx_plane_funcs;
+ /*
+ * On gen2/3 only plane A can do FBC, but the panel fitter and LVDS
+ * port is hooked to pipe B. Hence we want plane A feeding pipe B.
+ */
+ if (HAS_FBC(display) && DISPLAY_VER(display) < 4 &&
+ INTEL_NUM_PIPES(display) == 2)
+ i9xx_plane = (enum i9xx_plane_id)!pipe;
+ else
+ i9xx_plane = (enum i9xx_plane_id)pipe;
+
+ modifiers = intel_fb_plane_get_modifiers(display, INTEL_PLANE_CAP_TILING_X);
+
+ if (DISPLAY_VER(display) >= 5 || display->platform.g4x)
+ plane = drmm_universal_plane_alloc(display->drm, struct intel_plane, base,
+ 0, plane_funcs,
+ formats, num_formats,
+ modifiers,
+ DRM_PLANE_TYPE_PRIMARY,
+ "primary %c", pipe_name(pipe));
+ else
+ plane = drmm_universal_plane_alloc(display->drm, struct intel_plane, base,
+ 0, plane_funcs,
+ formats, num_formats,
+ modifiers,
+ DRM_PLANE_TYPE_PRIMARY,
+ "plane %c",
+ plane_name(i9xx_plane));
+
+ kfree(modifiers);
+
+ if (IS_ERR(plane))
+ return plane;
+
+ plane->pipe = pipe;
+ plane->i9xx_plane = i9xx_plane;
+ plane->id = PLANE_PRIMARY;
+ plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane->id);
+
+ intel_fbc_add_plane(i9xx_plane_fbc(display, plane->i9xx_plane), plane);
+
if (display->platform.valleyview || display->platform.cherryview)
plane->min_cdclk = vlv_plane_min_cdclk;
else if (display->platform.broadwell || display->platform.haswell)
@@ -1069,28 +1091,12 @@ intel_primary_plane_create(struct intel_display *display, enum pipe pipe)
plane->disable_tiling = i9xx_disable_tiling;
- modifiers = intel_fb_plane_get_modifiers(display, INTEL_PLANE_CAP_TILING_X);
-
- if (DISPLAY_VER(display) >= 5 || display->platform.g4x)
- ret = drm_universal_plane_init(display->drm, &plane->base,
- 0, plane_funcs,
- formats, num_formats,
- modifiers,
- DRM_PLANE_TYPE_PRIMARY,
- "primary %c", pipe_name(pipe));
- else
- ret = drm_universal_plane_init(display->drm, &plane->base,
- 0, plane_funcs,
- formats, num_formats,
- modifiers,
- DRM_PLANE_TYPE_PRIMARY,
- "plane %c",
- plane_name(plane->i9xx_plane));
-
- kfree(modifiers);
+ plane_state = kzalloc_obj(*plane_state);
+ if (!plane_state)
+ return ERR_PTR(-ENOMEM);
- if (ret)
- goto fail;
+ intel_plane_state_reset(plane_state, plane);
+ plane->base.state = &plane_state->uapi;
if (display->platform.cherryview && pipe == PIPE_B) {
supported_rotations =
@@ -1114,11 +1120,6 @@ intel_primary_plane_create(struct intel_display *display, enum pipe pipe)
intel_plane_helper_add(plane);
return plane;
-
-fail:
- intel_plane_free(plane);
-
- return ERR_PTR(ret);
}
static int i9xx_format_to_fourcc(int format)
diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c
index 18d1014de3613..2493baf25fbe2 100644
--- a/drivers/gpu/drm/i915/display/intel_cursor.c
+++ b/drivers/gpu/drm/i915/display/intel_cursor.c
@@ -9,6 +9,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
@@ -971,7 +972,6 @@ intel_legacy_cursor_update(struct drm_plane *_plane,
static const struct drm_plane_funcs intel_cursor_plane_funcs = {
.update_plane = intel_legacy_cursor_update,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = intel_cursor_format_mod_supported,
@@ -1004,11 +1004,23 @@ struct intel_plane *
intel_cursor_plane_create(struct intel_display *display,
enum pipe pipe)
{
+ struct intel_plane_state *plane_state;
struct intel_plane *cursor;
- int ret, zpos;
+ int zpos;
u64 *modifiers;
- cursor = intel_plane_alloc();
+ modifiers = intel_fb_plane_get_modifiers(display, INTEL_PLANE_CAP_NONE);
+
+ cursor = drmm_universal_plane_alloc(display->drm, struct intel_plane, base,
+ 0, &intel_cursor_plane_funcs,
+ intel_cursor_formats,
+ ARRAY_SIZE(intel_cursor_formats),
+ modifiers,
+ DRM_PLANE_TYPE_CURSOR,
+ "cursor %c", pipe_name(pipe));
+
+ kfree(modifiers);
+
if (IS_ERR(cursor))
return cursor;
@@ -1056,20 +1068,12 @@ intel_cursor_plane_create(struct intel_display *display,
if (display->platform.i845g || display->platform.i865g || HAS_CUR_FBC(display))
cursor->cursor.size = ~0;
- modifiers = intel_fb_plane_get_modifiers(display, INTEL_PLANE_CAP_NONE);
-
- ret = drm_universal_plane_init(display->drm, &cursor->base,
- 0, &intel_cursor_plane_funcs,
- intel_cursor_formats,
- ARRAY_SIZE(intel_cursor_formats),
- modifiers,
- DRM_PLANE_TYPE_CURSOR,
- "cursor %c", pipe_name(pipe));
-
- kfree(modifiers);
+ plane_state = kzalloc_obj(*plane_state);
+ if (!plane_state)
+ return ERR_PTR(-ENOMEM);
- if (ret)
- goto fail;
+ intel_plane_state_reset(plane_state, cursor);
+ cursor->base.state = &plane_state->uapi;
if (DISPLAY_VER(display) >= 4)
drm_plane_create_rotation_property(&cursor->base,
@@ -1088,11 +1092,6 @@ intel_cursor_plane_create(struct intel_display *display,
intel_plane_helper_add(cursor);
return cursor;
-
-fail:
- intel_plane_free(cursor);
-
- return ERR_PTR(ret);
}
void intel_cursor_mode_config_init(struct intel_display *display)
diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c
index 5390ceb21ca42..05c2dc0902f5c 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_plane.c
@@ -62,8 +62,8 @@
#include "skl_universal_plane.h"
#include "skl_watermark.h"
-static void intel_plane_state_reset(struct intel_plane_state *plane_state,
- struct intel_plane *plane)
+void intel_plane_state_reset(struct intel_plane_state *plane_state,
+ struct intel_plane *plane)
{
memset(plane_state, 0, sizeof(*plane_state));
@@ -72,47 +72,6 @@ static void intel_plane_state_reset(struct intel_plane_state *plane_state,
plane_state->scaler_id = -1;
}
-struct intel_plane *intel_plane_alloc(void)
-{
- struct intel_plane_state *plane_state;
- struct intel_plane *plane;
-
- plane = kzalloc_obj(*plane);
- if (!plane)
- return ERR_PTR(-ENOMEM);
-
- plane_state = kzalloc_obj(*plane_state);
- if (!plane_state) {
- kfree(plane);
- return ERR_PTR(-ENOMEM);
- }
-
- intel_plane_state_reset(plane_state, plane);
-
- plane->base.state = &plane_state->uapi;
-
- return plane;
-}
-
-void intel_plane_free(struct intel_plane *plane)
-{
- intel_plane_destroy_state(&plane->base, plane->base.state);
- kfree(plane);
-}
-
-/**
- * intel_plane_destroy - destroy a plane
- * @plane: plane to destroy
- *
- * Common destruction function for all types of planes (primary, cursor,
- * sprite).
- */
-void intel_plane_destroy(struct drm_plane *plane)
-{
- drm_plane_cleanup(plane);
- kfree(to_intel_plane(plane));
-}
-
/**
* intel_plane_duplicate_state - duplicate plane state
* @plane: drm plane
diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h
index 5a8f2f3baab5f..56221619a2b29 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.h
+++ b/drivers/gpu/drm/i915/display/intel_plane.h
@@ -55,9 +55,8 @@ void intel_plane_update_arm(struct intel_dsb *dsb,
void intel_plane_disable_arm(struct intel_dsb *dsb,
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state);
-struct intel_plane *intel_plane_alloc(void);
-void intel_plane_free(struct intel_plane *plane);
-void intel_plane_destroy(struct drm_plane *plane);
+void intel_plane_state_reset(struct intel_plane_state *plane_state,
+ struct intel_plane *plane);
struct drm_plane_state *intel_plane_duplicate_state(struct drm_plane *plane);
void intel_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state);
diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c
index 6a65f92e8a031..f285d15734ee5 100644
--- a/drivers/gpu/drm/i915/display/intel_sprite.c
+++ b/drivers/gpu/drm/i915/display/intel_sprite.c
@@ -36,6 +36,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_rect.h>
@@ -1563,7 +1564,6 @@ static bool vlv_sprite_format_mod_supported(struct drm_plane *_plane,
static const struct drm_plane_funcs g4x_sprite_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = g4x_sprite_format_mod_supported,
@@ -1573,7 +1573,6 @@ static const struct drm_plane_funcs g4x_sprite_funcs = {
static const struct drm_plane_funcs snb_sprite_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = snb_sprite_format_mod_supported,
@@ -1583,7 +1582,6 @@ static const struct drm_plane_funcs snb_sprite_funcs = {
static const struct drm_plane_funcs vlv_sprite_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = vlv_sprite_format_mod_supported,
@@ -1594,18 +1592,69 @@ struct intel_plane *
intel_sprite_plane_create(struct intel_display *display,
enum pipe pipe, int sprite)
{
+ struct intel_plane_state *plane_state;
struct intel_plane *plane;
const struct drm_plane_funcs *plane_funcs;
unsigned int supported_rotations;
const u64 *modifiers;
const u32 *formats;
int num_formats;
- int ret, zpos;
+ int zpos;
+
+ if (display->platform.valleyview || display->platform.cherryview) {
+ if (display->platform.cherryview && pipe == PIPE_B) {
+ formats = chv_pipe_b_sprite_formats;
+ num_formats = ARRAY_SIZE(chv_pipe_b_sprite_formats);
+ } else {
+ formats = vlv_sprite_formats;
+ num_formats = ARRAY_SIZE(vlv_sprite_formats);
+ }
+
+ plane_funcs = &vlv_sprite_funcs;
+ } else if (DISPLAY_VER(display) >= 7) {
+ formats = snb_sprite_formats;
+ num_formats = ARRAY_SIZE(snb_sprite_formats);
+
+ plane_funcs = &snb_sprite_funcs;
+ } else {
+ if (display->platform.sandybridge) {
+ formats = snb_sprite_formats;
+ num_formats = ARRAY_SIZE(snb_sprite_formats);
+
+ plane_funcs = &snb_sprite_funcs;
+ } else {
+ formats = g4x_sprite_formats;
+ num_formats = ARRAY_SIZE(g4x_sprite_formats);
+
+ plane_funcs = &g4x_sprite_funcs;
+ }
+ }
+
+ if (display->platform.cherryview && pipe == PIPE_B) {
+ supported_rotations =
+ DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
+ DRM_MODE_REFLECT_X;
+ } else {
+ supported_rotations =
+ DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180;
+ }
+
+ modifiers = intel_fb_plane_get_modifiers(display, INTEL_PLANE_CAP_TILING_X);
+
+ plane = drmm_universal_plane_alloc(display->drm, struct intel_plane, base,
+ 0, plane_funcs,
+ formats, num_formats, modifiers,
+ DRM_PLANE_TYPE_OVERLAY,
+ "sprite %c", sprite_name(display, pipe, sprite));
+ kfree(modifiers);
- plane = intel_plane_alloc();
if (IS_ERR(plane))
return plane;
+ plane->pipe = pipe;
+ plane->id = PLANE_SPRITE0 + sprite;
+ plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane->id);
+
if (display->platform.valleyview || display->platform.cherryview) {
plane->update_noarm = vlv_sprite_update_noarm;
plane->update_arm = vlv_sprite_update_arm;
@@ -1621,16 +1670,6 @@ intel_sprite_plane_create(struct intel_display *display,
/* FIXME undocumented for VLV/CHV so not sure what's actually needed */
if (intel_scanout_needs_vtd_wa(display))
plane->vtd_guard = 128;
-
- if (display->platform.cherryview && pipe == PIPE_B) {
- formats = chv_pipe_b_sprite_formats;
- num_formats = ARRAY_SIZE(chv_pipe_b_sprite_formats);
- } else {
- formats = vlv_sprite_formats;
- num_formats = ARRAY_SIZE(vlv_sprite_formats);
- }
-
- plane_funcs = &vlv_sprite_funcs;
} else if (DISPLAY_VER(display) >= 7) {
plane->update_noarm = ivb_sprite_update_noarm;
plane->update_arm = ivb_sprite_update_arm;
@@ -1652,11 +1691,6 @@ intel_sprite_plane_create(struct intel_display *display,
if (intel_scanout_needs_vtd_wa(display))
plane->vtd_guard = 64;
-
- formats = snb_sprite_formats;
- num_formats = ARRAY_SIZE(snb_sprite_formats);
-
- plane_funcs = &snb_sprite_funcs;
} else {
plane->update_noarm = g4x_sprite_update_noarm;
plane->update_arm = g4x_sprite_update_arm;
@@ -1671,44 +1705,14 @@ intel_sprite_plane_create(struct intel_display *display,
if (intel_scanout_needs_vtd_wa(display))
plane->vtd_guard = 64;
-
- if (display->platform.sandybridge) {
- formats = snb_sprite_formats;
- num_formats = ARRAY_SIZE(snb_sprite_formats);
-
- plane_funcs = &snb_sprite_funcs;
- } else {
- formats = g4x_sprite_formats;
- num_formats = ARRAY_SIZE(g4x_sprite_formats);
-
- plane_funcs = &g4x_sprite_funcs;
- }
}
- if (display->platform.cherryview && pipe == PIPE_B) {
- supported_rotations =
- DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 |
- DRM_MODE_REFLECT_X;
- } else {
- supported_rotations =
- DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180;
- }
+ plane_state = kzalloc_obj(*plane_state);
+ if (!plane_state)
+ return ERR_PTR(-ENOMEM);
- plane->pipe = pipe;
- plane->id = PLANE_SPRITE0 + sprite;
- plane->frontbuffer_bit = INTEL_FRONTBUFFER(pipe, plane->id);
-
- modifiers = intel_fb_plane_get_modifiers(display, INTEL_PLANE_CAP_TILING_X);
-
- ret = drm_universal_plane_init(display->drm, &plane->base,
- 0, plane_funcs,
- formats, num_formats, modifiers,
- DRM_PLANE_TYPE_OVERLAY,
- "sprite %c", sprite_name(display, pipe, sprite));
- kfree(modifiers);
-
- if (ret)
- goto fail;
+ intel_plane_state_reset(plane_state, plane);
+ plane->base.state = &plane_state->uapi;
drm_plane_create_rotation_property(&plane->base,
DRM_MODE_ROTATE_0,
@@ -1728,9 +1732,4 @@ intel_sprite_plane_create(struct intel_display *display,
intel_plane_helper_add(plane);
return plane;
-
-fail:
- intel_plane_free(plane);
-
- return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index 11ba42c67e3ed..6d6b108bf7e46 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -7,6 +7,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include "intel_bo.h"
@@ -2690,7 +2691,6 @@ static bool tgl_plane_format_mod_supported(struct drm_plane *_plane,
static const struct drm_plane_funcs skl_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = skl_plane_format_mod_supported,
@@ -2700,7 +2700,6 @@ static const struct drm_plane_funcs skl_plane_funcs = {
static const struct drm_plane_funcs icl_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = icl_plane_format_mod_supported,
@@ -2710,7 +2709,6 @@ static const struct drm_plane_funcs icl_plane_funcs = {
static const struct drm_plane_funcs tgl_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
- .destroy = intel_plane_destroy,
.atomic_duplicate_state = intel_plane_duplicate_state,
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = tgl_plane_format_mod_supported,
@@ -2850,6 +2848,7 @@ struct intel_plane *
skl_universal_plane_create(struct intel_display *display,
enum pipe pipe, enum plane_id plane_id)
{
+ struct intel_plane_state *plane_state;
const struct drm_plane_funcs *plane_funcs;
struct intel_plane *plane;
enum drm_plane_type plane_type;
@@ -2858,10 +2857,50 @@ skl_universal_plane_create(struct intel_display *display,
const u64 *modifiers;
const u32 *formats;
int num_formats;
- int ret;
u8 caps;
- plane = intel_plane_alloc();
+ if (DISPLAY_VER(display) >= 11)
+ formats = icl_get_plane_formats(display, pipe,
+ plane_id, &num_formats);
+ else if (DISPLAY_VER(display) >= 10)
+ formats = glk_get_plane_formats(display, pipe,
+ plane_id, &num_formats);
+ else
+ formats = skl_get_plane_formats(display, pipe,
+ plane_id, &num_formats);
+
+ if (DISPLAY_VER(display) >= 12)
+ plane_funcs = &tgl_plane_funcs;
+ else if (DISPLAY_VER(display) == 11)
+ plane_funcs = &icl_plane_funcs;
+ else
+ plane_funcs = &skl_plane_funcs;
+
+ if (plane_id == PLANE_1)
+ plane_type = DRM_PLANE_TYPE_PRIMARY;
+ else
+ plane_type = DRM_PLANE_TYPE_OVERLAY;
+
+ if (DISPLAY_VER(display) >= 12)
+ caps = tgl_plane_caps(display, pipe, plane_id);
+ else if (DISPLAY_VER(display) == 11)
+ caps = icl_plane_caps(display, pipe, plane_id);
+ else if (DISPLAY_VER(display) == 10)
+ caps = glk_plane_caps(display, pipe, plane_id);
+ else
+ caps = skl_plane_caps(display, pipe, plane_id);
+
+ modifiers = intel_fb_plane_get_modifiers(display, caps);
+
+ plane = drmm_universal_plane_alloc(display->drm, struct intel_plane, base,
+ 0, plane_funcs,
+ formats, num_formats, modifiers,
+ plane_type,
+ "plane %d%c", plane_id + 1,
+ pipe_name(pipe));
+
+ kfree(modifiers);
+
if (IS_ERR(plane))
return plane;
@@ -2940,50 +2979,12 @@ skl_universal_plane_create(struct intel_display *display,
plane->can_async_flip = skl_plane_can_async_flip;
}
- if (DISPLAY_VER(display) >= 11)
- formats = icl_get_plane_formats(display, pipe,
- plane_id, &num_formats);
- else if (DISPLAY_VER(display) >= 10)
- formats = glk_get_plane_formats(display, pipe,
- plane_id, &num_formats);
- else
- formats = skl_get_plane_formats(display, pipe,
- plane_id, &num_formats);
+ plane_state = kzalloc_obj(*plane_state);
+ if (!plane_state)
+ return ERR_PTR(-ENOMEM);
- if (DISPLAY_VER(display) >= 12)
- plane_funcs = &tgl_plane_funcs;
- else if (DISPLAY_VER(display) == 11)
- plane_funcs = &icl_plane_funcs;
- else
- plane_funcs = &skl_plane_funcs;
-
- if (plane_id == PLANE_1)
- plane_type = DRM_PLANE_TYPE_PRIMARY;
- else
- plane_type = DRM_PLANE_TYPE_OVERLAY;
-
- if (DISPLAY_VER(display) >= 12)
- caps = tgl_plane_caps(display, pipe, plane_id);
- else if (DISPLAY_VER(display) == 11)
- caps = icl_plane_caps(display, pipe, plane_id);
- else if (DISPLAY_VER(display) == 10)
- caps = glk_plane_caps(display, pipe, plane_id);
- else
- caps = skl_plane_caps(display, pipe, plane_id);
-
- modifiers = intel_fb_plane_get_modifiers(display, caps);
-
- ret = drm_universal_plane_init(display->drm, &plane->base,
- 0, plane_funcs,
- formats, num_formats, modifiers,
- plane_type,
- "plane %d%c", plane_id + 1,
- pipe_name(pipe));
-
- kfree(modifiers);
-
- if (ret)
- goto fail;
+ intel_plane_state_reset(plane_state, plane);
+ plane->base.state = &plane_state->uapi;
if (DISPLAY_VER(display) >= 13)
supported_rotations = DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180;
@@ -3033,11 +3034,6 @@ skl_universal_plane_create(struct intel_display *display,
intel_plane_helper_add(plane);
return plane;
-
-fail:
- intel_plane_free(plane);
-
- return ERR_PTR(ret);
}
void
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 07/12] drm/i915/display: Switch to managed for encoder
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
The current i915 driver uses non-managed function to create encoder. It
is not an issue yet, but in order to comply with the latest DRM
requirement, convert this code to use drm and device managed helpers.
Assisted-by: Claude Code:2.1.90
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/gpu/drm/i915/display/g4x_dp.c | 31 ++++++++---------
drivers/gpu/drm/i915/display/g4x_hdmi.c | 19 ++++-------
drivers/gpu/drm/i915/display/icl_dsi.c | 20 +++--------
drivers/gpu/drm/i915/display/intel_crt.c | 9 +++--
drivers/gpu/drm/i915/display/intel_ddi.c | 50 +++++++++++++++-------------
drivers/gpu/drm/i915/display/intel_display.c | 8 -----
drivers/gpu/drm/i915/display/intel_display.h | 1 -
drivers/gpu/drm/i915/display/intel_dp_mst.c | 20 +++--------
drivers/gpu/drm/i915/display/intel_dvo.c | 22 ++++++------
drivers/gpu/drm/i915/display/intel_encoder.c | 6 ++--
drivers/gpu/drm/i915/display/intel_encoder.h | 3 +-
drivers/gpu/drm/i915/display/intel_lvds.c | 13 +++-----
drivers/gpu/drm/i915/display/intel_sdvo.c | 45 +++++++++++--------------
drivers/gpu/drm/i915/display/intel_tv.c | 11 +++---
drivers/gpu/drm/i915/display/vlv_dsi.c | 22 +++++-------
15 files changed, 114 insertions(+), 166 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
index 5e74d8a3ba5c8..99b6c5ce39b2a 100644
--- a/drivers/gpu/drm/i915/display/g4x_dp.c
+++ b/drivers/gpu/drm/i915/display/g4x_dp.c
@@ -7,6 +7,7 @@
#include <linux/string_helpers.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include "g4x_dp.h"
@@ -1250,12 +1251,9 @@ static void g4x_dp_suspend_complete(struct intel_encoder *encoder)
intel_encoder_link_check_flush_work(encoder);
}
-static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
+static void intel_dp_encoder_flush_work_cleanup(struct drm_device *drm, void *data)
{
- intel_dp_encoder_flush_work(encoder);
-
- drm_encoder_cleanup(encoder);
- kfree(enc_to_dig_port(to_intel_encoder(encoder)));
+ intel_dp_encoder_flush_work(data);
}
static void intel_dp_encoder_reset(struct drm_encoder *encoder)
@@ -1276,7 +1274,6 @@ static void intel_dp_encoder_reset(struct drm_encoder *encoder)
static const struct drm_encoder_funcs intel_dp_enc_funcs = {
.reset = intel_dp_encoder_reset,
- .destroy = intel_dp_encoder_destroy,
};
bool g4x_dp_init(struct intel_display *display,
@@ -1298,22 +1295,26 @@ bool g4x_dp_init(struct intel_display *display,
drm_dbg_kms(display->drm, "No VBT child device for DP-%c\n",
port_name(port));
- dig_port = intel_dig_port_alloc();
+ dig_port = intel_dig_port_alloc(display->drm);
if (!dig_port)
return false;
intel_connector = intel_connector_alloc();
if (!intel_connector)
- goto err_connector_alloc;
+ return false;
intel_encoder = &dig_port->base;
encoder = &intel_encoder->base;
intel_encoder->devdata = devdata;
- if (drm_encoder_init(display->drm, &intel_encoder->base,
- &intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS,
- "DP %c", port_name(port)))
+ if (drmm_encoder_init(display->drm, &intel_encoder->base,
+ &intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS,
+ "DP %c", port_name(port)))
+ goto err_encoder_init;
+
+ if (drmm_add_action_or_reset(display->drm,
+ intel_dp_encoder_flush_work_cleanup, encoder))
goto err_encoder_init;
intel_encoder_link_check_init(intel_encoder, intel_dp_link_check);
@@ -1411,18 +1412,14 @@ bool g4x_dp_init(struct intel_display *display,
dig_port->aux_ch = intel_dp_aux_ch(intel_encoder);
if (dig_port->aux_ch == AUX_CH_NONE)
- goto err_init_connector;
+ goto err_encoder_init;
if (!intel_dp_init_connector(dig_port, intel_connector))
- goto err_init_connector;
+ goto err_encoder_init;
return true;
-err_init_connector:
- drm_encoder_cleanup(encoder);
err_encoder_init:
kfree(intel_connector);
-err_connector_alloc:
- kfree(dig_port);
return false;
}
diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c
index 5fe5067c4237c..45ee1ad504bc1 100644
--- a/drivers/gpu/drm/i915/display/g4x_hdmi.c
+++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c
@@ -5,6 +5,7 @@
* HDMI support for G4x,ILK,SNB,IVB,VLV,CHV (HSW+ handled by the DDI code).
*/
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include "g4x_hdmi.h"
@@ -562,7 +563,6 @@ static void chv_hdmi_pre_enable(struct intel_atomic_state *state,
}
static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
- .destroy = intel_encoder_destroy,
};
static enum intel_hotplug_state
@@ -686,21 +686,21 @@ bool g4x_hdmi_init(struct intel_display *display,
drm_dbg_kms(display->drm, "No VBT child device for HDMI-%c\n",
port_name(port));
- dig_port = intel_dig_port_alloc();
+ dig_port = intel_dig_port_alloc(display->drm);
if (!dig_port)
return false;
intel_connector = intel_connector_alloc();
if (!intel_connector)
- goto err_connector_alloc;
+ return false;
intel_encoder = &dig_port->base;
intel_encoder->devdata = devdata;
- if (drm_encoder_init(display->drm, &intel_encoder->base,
- &intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS,
- "HDMI %c", port_name(port)))
+ if (drmm_encoder_init(display->drm, &intel_encoder->base,
+ &intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS,
+ "HDMI %c", port_name(port)))
goto err_encoder_init;
intel_encoder->hotplug = intel_hdmi_hotplug;
@@ -763,16 +763,11 @@ bool g4x_hdmi_init(struct intel_display *display,
intel_infoframe_init(dig_port);
if (!intel_hdmi_init_connector(dig_port, intel_connector))
- goto err_init_connector;
+ goto err_encoder_init;
return true;
-
-err_init_connector:
- drm_encoder_cleanup(&intel_encoder->base);
err_encoder_init:
kfree(intel_connector);
-err_connector_alloc:
- kfree(dig_port);
return false;
}
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index afbaa0465842a..cfee173a2367a 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -30,6 +30,7 @@
#include <drm/display/drm_dsc_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fixed.h>
+#include <drm/drm_managed.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -1775,13 +1776,7 @@ static bool gen11_dsi_initial_fastset_check(struct intel_encoder *encoder,
return true;
}
-static void gen11_dsi_encoder_destroy(struct drm_encoder *encoder)
-{
- intel_encoder_destroy(encoder);
-}
-
static const struct drm_encoder_funcs gen11_dsi_encoder_funcs = {
- .destroy = gen11_dsi_encoder_destroy,
};
static const struct drm_connector_funcs gen11_dsi_connector_funcs = {
@@ -1934,8 +1929,10 @@ void icl_dsi_init(struct intel_display *display,
if (port == PORT_NONE)
return;
- intel_dsi = kzalloc_obj(*intel_dsi);
- if (!intel_dsi)
+ intel_dsi = drmm_encoder_alloc(display->drm, struct intel_dsi, base.base,
+ &gen11_dsi_encoder_funcs,
+ DRM_MODE_ENCODER_DSI, "DSI %c", port_name(port));
+ if (IS_ERR(intel_dsi))
return;
intel_connector = intel_connector_alloc();
@@ -1950,11 +1947,6 @@ void icl_dsi_init(struct intel_display *display,
encoder->devdata = devdata;
- /* register DSI encoder with DRM subsystem */
- drm_encoder_init(display->drm, &encoder->base,
- &gen11_dsi_encoder_funcs,
- DRM_MODE_ENCODER_DSI, "DSI %c", port_name(port));
-
encoder->pre_pll_enable = gen11_dsi_pre_pll_enable;
encoder->pre_enable = gen11_dsi_pre_enable;
encoder->enable = gen11_dsi_enable;
@@ -2037,7 +2029,5 @@ void icl_dsi_init(struct intel_display *display,
err:
drm_connector_cleanup(connector);
- drm_encoder_cleanup(&encoder->base);
- kfree(intel_dsi);
kfree(intel_connector);
}
diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
index 6aa6a1dd6e1b0..39bb115955f5a 100644
--- a/drivers/gpu/drm/i915/display/intel_crt.c
+++ b/drivers/gpu/drm/i915/display/intel_crt.c
@@ -31,6 +31,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <video/vga.h>
@@ -1004,7 +1005,6 @@ static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs
static const struct drm_encoder_funcs intel_crt_enc_funcs = {
.reset = intel_crt_reset,
- .destroy = intel_encoder_destroy,
};
void intel_crt_init(struct intel_display *display)
@@ -1041,8 +1041,9 @@ void intel_crt_init(struct intel_display *display)
intel_de_write(display, adpa_reg, adpa);
}
- crt = kzalloc_obj(struct intel_crt);
- if (!crt)
+ crt = drmm_encoder_alloc(display->drm, struct intel_crt, base.base,
+ &intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC, "CRT");
+ if (IS_ERR(crt))
return;
connector = intel_connector_alloc();
@@ -1058,8 +1059,6 @@ void intel_crt_init(struct intel_display *display)
DRM_MODE_CONNECTOR_VGA,
intel_gmbus_get_adapter(display, ddc_pin));
- drm_encoder_init(display->drm, &crt->base.base, &intel_crt_enc_funcs,
- DRM_MODE_ENCODER_DAC, "CRT");
intel_connector_attach_encoder(connector, &crt->base);
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index ebefa889bc8c5..003287ad5bc59 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -31,6 +31,7 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/display/drm_scdc_helper.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_privacy_screen_consumer.h>
@@ -4644,19 +4645,19 @@ static int intel_ddi_compute_config_late(struct intel_encoder *encoder,
return 0;
}
-static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
+static void intel_ddi_encoder_cleanup(struct drm_device *drm, void *data)
{
- struct intel_display *display = to_intel_display(encoder->dev);
- struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder));
+ struct intel_digital_port *dig_port = data;
+ struct intel_display *display = to_intel_display(&dig_port->base);
- intel_dp_encoder_flush_work(encoder);
- if (intel_encoder_is_tc(&dig_port->base))
- intel_tc_port_cleanup(dig_port);
+ intel_dp_encoder_flush_work(&dig_port->base.base);
intel_display_power_flush_work(display);
-
- drm_encoder_cleanup(encoder);
kfree(dig_port->hdcp.port_data.streams);
- kfree(dig_port);
+}
+
+static void intel_tc_port_cleanup_action(struct drm_device *drm, void *data)
+{
+ intel_tc_port_cleanup(data);
}
static void intel_ddi_encoder_reset(struct drm_encoder *encoder)
@@ -4684,7 +4685,6 @@ static int intel_ddi_encoder_late_register(struct drm_encoder *_encoder)
static const struct drm_encoder_funcs intel_ddi_funcs = {
.reset = intel_ddi_encoder_reset,
- .destroy = intel_ddi_encoder_destroy,
.late_register = intel_ddi_encoder_late_register,
};
@@ -5243,16 +5243,20 @@ void intel_ddi_init(struct intel_display *display,
phy_name(phy));
}
- dig_port = intel_dig_port_alloc();
+ dig_port = intel_dig_port_alloc(display->drm);
if (!dig_port)
return;
encoder = &dig_port->base;
encoder->devdata = devdata;
- drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
- DRM_MODE_ENCODER_TMDS, "%s",
- intel_ddi_encoder_name(display, port, phy, &encoder_name));
+ if (drmm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs,
+ DRM_MODE_ENCODER_TMDS, "%s",
+ intel_ddi_encoder_name(display, port, phy, &encoder_name)))
+ return;
+
+ if (drmm_add_action_or_reset(display->drm, intel_ddi_encoder_cleanup, dig_port))
+ return;
intel_encoder_link_check_init(encoder, intel_ddi_link_check);
@@ -5411,7 +5415,7 @@ void intel_ddi_init(struct intel_display *display,
if (need_aux_ch(encoder, init_dp)) {
dig_port->aux_ch = intel_dp_aux_ch(encoder);
if (dig_port->aux_ch == AUX_CH_NONE)
- goto err;
+ return;
}
/*
@@ -5447,7 +5451,11 @@ void intel_ddi_init(struct intel_display *display,
dig_port->unlock = intel_tc_port_unlock;
if (intel_tc_port_init(dig_port, is_legacy) < 0)
- goto err;
+ return;
+
+ if (drmm_add_action_or_reset(display->drm,
+ intel_tc_port_cleanup_action, dig_port))
+ return;
}
drm_WARN_ON(display->drm, port > PORT_I);
@@ -5478,7 +5486,7 @@ void intel_ddi_init(struct intel_display *display,
if (init_dp) {
if (intel_ddi_init_dp_connector(dig_port))
- goto err;
+ return;
dig_port->hpd_pulse = intel_dp_hpd_pulse;
@@ -5492,12 +5500,6 @@ void intel_ddi_init(struct intel_display *display,
*/
if (encoder->type != INTEL_OUTPUT_EDP && init_hdmi) {
if (intel_ddi_init_hdmi_connector(dig_port))
- goto err;
+ return;
}
-
- return;
-
-err:
- drm_encoder_cleanup(&encoder->base);
- kfree(dig_port);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 10b6c6fcb03f6..049a796da4c38 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -2192,14 +2192,6 @@ static void i9xx_crtc_disable(struct intel_atomic_state *state,
i830_enable_pipe(display, pipe);
}
-void intel_encoder_destroy(struct drm_encoder *encoder)
-{
- struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
-
- drm_encoder_cleanup(encoder);
- kfree(intel_encoder);
-}
-
static bool intel_crtc_supports_double_wide(const struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(crtc);
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 552a59d19e0f6..6c2681738e6b2 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -416,7 +416,6 @@ void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state);
void i830_enable_pipe(struct intel_display *display, enum pipe pipe);
void i830_disable_pipe(struct intel_display *display, enum pipe pipe);
bool intel_has_pending_fb_unpin(struct intel_display *display);
-void intel_encoder_destroy(struct drm_encoder *encoder);
struct drm_display_mode *
intel_encoder_current_mode(struct intel_encoder *encoder);
void intel_encoder_get_config(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 887b6de14e467..5fe6fbcaf9371 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -30,6 +30,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -1645,16 +1646,7 @@ static const struct drm_connector_helper_funcs mst_connector_helper_funcs = {
.detect_ctx = mst_connector_detect_ctx,
};
-static void mst_stream_encoder_destroy(struct drm_encoder *encoder)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(to_intel_encoder(encoder));
-
- drm_encoder_cleanup(encoder);
- kfree(intel_mst);
-}
-
static const struct drm_encoder_funcs mst_stream_encoder_funcs = {
- .destroy = mst_stream_encoder_destroy,
};
static bool mst_connector_get_hw_state(struct intel_connector *connector)
@@ -1850,18 +1842,16 @@ mst_stream_encoder_create(struct intel_digital_port *dig_port, enum pipe pipe)
struct intel_dp_mst_encoder *intel_mst;
struct intel_encoder *encoder;
- intel_mst = kzalloc_obj(*intel_mst);
-
- if (!intel_mst)
+ intel_mst = drmm_encoder_alloc(display->drm, struct intel_dp_mst_encoder,
+ base.base, &mst_stream_encoder_funcs,
+ DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
+ if (IS_ERR(intel_mst))
return NULL;
intel_mst->pipe = pipe;
encoder = &intel_mst->base;
intel_mst->primary = dig_port;
- drm_encoder_init(display->drm, &encoder->base, &mst_stream_encoder_funcs,
- DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
-
encoder->type = INTEL_OUTPUT_DP_MST;
encoder->power_domain = primary_encoder->power_domain;
encoder->port = primary_encoder->port;
diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c
index 405b33aca9dde..ea2f6426fd77d 100644
--- a/drivers/gpu/drm/i915/display/intel_dvo.c
+++ b/drivers/gpu/drm/i915/display/intel_dvo.c
@@ -31,6 +31,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -372,18 +373,15 @@ static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs
.get_modes = intel_dvo_get_modes,
};
-static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
+static void intel_dvo_dev_destroy(struct drm_device *drm, void *data)
{
- struct intel_dvo *intel_dvo = enc_to_dvo(to_intel_encoder(encoder));
+ struct intel_dvo *intel_dvo = data;
if (intel_dvo->dev.dev_ops->destroy)
intel_dvo->dev.dev_ops->destroy(&intel_dvo->dev);
-
- intel_encoder_destroy(encoder);
}
static const struct drm_encoder_funcs intel_dvo_enc_funcs = {
- .destroy = intel_dvo_enc_destroy,
};
static int intel_dvo_encoder_type(const struct intel_dvo_device *dvo)
@@ -494,7 +492,7 @@ void intel_dvo_init(struct intel_display *display)
struct intel_encoder *encoder;
struct intel_dvo *intel_dvo;
- intel_dvo = kzalloc_obj(*intel_dvo);
+ intel_dvo = drmm_kzalloc(display->drm, sizeof(*intel_dvo), GFP_KERNEL);
if (!intel_dvo)
return;
@@ -517,13 +515,15 @@ void intel_dvo_init(struct intel_display *display)
connector->get_hw_state = intel_dvo_connector_get_hw_state;
if (!intel_dvo_probe(display, intel_dvo)) {
- kfree(intel_dvo);
intel_connector_free(connector);
return;
}
assert_port_valid(display, intel_dvo->dev.port);
+ if (drmm_add_action_or_reset(display->drm, intel_dvo_dev_destroy, intel_dvo))
+ return;
+
encoder->type = INTEL_OUTPUT_DVO;
encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
encoder->port = intel_dvo->dev.port;
@@ -533,10 +533,10 @@ void intel_dvo_init(struct intel_display *display)
encoder->cloneable = BIT(INTEL_OUTPUT_ANALOG) |
BIT(INTEL_OUTPUT_DVO);
- drm_encoder_init(display->drm, &encoder->base,
- &intel_dvo_enc_funcs,
- intel_dvo_encoder_type(&intel_dvo->dev),
- "DVO %c", port_name(encoder->port));
+ drmm_encoder_init(display->drm, &encoder->base,
+ &intel_dvo_enc_funcs,
+ intel_dvo_encoder_type(&intel_dvo->dev),
+ "DVO %c", port_name(encoder->port));
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] detected %s\n",
encoder->base.base.id, encoder->base.name,
diff --git a/drivers/gpu/drm/i915/display/intel_encoder.c b/drivers/gpu/drm/i915/display/intel_encoder.c
index d01b081a60740..2ebd1e4457482 100644
--- a/drivers/gpu/drm/i915/display/intel_encoder.c
+++ b/drivers/gpu/drm/i915/display/intel_encoder.c
@@ -5,6 +5,8 @@
#include <linux/workqueue.h>
+#include <drm/drm_managed.h>
+
#include "intel_display_core.h"
#include "intel_display_types.h"
#include "intel_encoder.h"
@@ -104,11 +106,11 @@ void intel_encoder_shutdown_all(struct intel_display *display)
encoder->shutdown_complete(encoder);
}
-struct intel_digital_port *intel_dig_port_alloc(void)
+struct intel_digital_port *intel_dig_port_alloc(struct drm_device *drm)
{
struct intel_digital_port *dig_port;
- dig_port = kzalloc_obj(*dig_port);
+ dig_port = drmm_kzalloc(drm, sizeof(*dig_port), GFP_KERNEL);
if (!dig_port)
return NULL;
diff --git a/drivers/gpu/drm/i915/display/intel_encoder.h b/drivers/gpu/drm/i915/display/intel_encoder.h
index ace0fe1a8f27a..42f3982970065 100644
--- a/drivers/gpu/drm/i915/display/intel_encoder.h
+++ b/drivers/gpu/drm/i915/display/intel_encoder.h
@@ -6,6 +6,7 @@
#ifndef __INTEL_ENCODER_H__
#define __INTEL_ENCODER_H__
+struct drm_device;
struct intel_digital_port;
struct intel_display;
struct intel_encoder;
@@ -21,6 +22,6 @@ void intel_encoder_shutdown_all(struct intel_display *display);
void intel_encoder_block_all_hpds(struct intel_display *display);
void intel_encoder_unblock_all_hpds(struct intel_display *display);
-struct intel_digital_port *intel_dig_port_alloc(void);
+struct intel_digital_port *intel_dig_port_alloc(struct drm_device *drm);
#endif /* __INTEL_ENCODER_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index e78a41e2b268c..7bb99eb44a5a4 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -37,6 +37,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -517,7 +518,6 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = {
};
static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
- .destroy = intel_encoder_destroy,
};
static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
@@ -886,15 +886,14 @@ void intel_lvds_init(struct intel_display *display)
"LVDS is not present in VBT, but enabled anyway\n");
}
- lvds_encoder = kzalloc_obj(*lvds_encoder);
- if (!lvds_encoder)
+ lvds_encoder = drmm_encoder_alloc(display->drm, struct intel_lvds_encoder, base.base,
+ &intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS, "LVDS");
+ if (IS_ERR(lvds_encoder))
return;
connector = intel_connector_alloc();
- if (!connector) {
- kfree(lvds_encoder);
+ if (!connector)
return;
- }
lvds_encoder->attached_connector = connector;
encoder = &lvds_encoder->base;
@@ -904,8 +903,6 @@ void intel_lvds_init(struct intel_display *display)
DRM_MODE_CONNECTOR_LVDS,
intel_gmbus_get_adapter(display, ddc_pin));
- drm_encoder_init(display->drm, &encoder->base, &intel_lvds_enc_funcs,
- DRM_MODE_ENCODER_LVDS, "LVDS");
encoder->enable = intel_enable_lvds;
encoder->pre_enable = intel_pre_enable_lvds;
diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
index 6eb2b4b45a9b4..f5aaa38002674 100644
--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
@@ -36,6 +36,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_eld.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -2542,24 +2543,19 @@ static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs
.atomic_check = intel_sdvo_atomic_check,
};
-static void intel_sdvo_encoder_destroy(struct drm_encoder *_encoder)
+static void intel_sdvo_unselect_i2c_bus(struct intel_sdvo *sdvo);
+
+static void intel_sdvo_cleanup(struct drm_device *drm, void *data)
{
- struct intel_encoder *encoder = to_intel_encoder(_encoder);
- struct intel_sdvo *sdvo = to_sdvo(encoder);
+ struct intel_sdvo *intel_sdvo = data;
int i;
- for (i = 0; i < ARRAY_SIZE(sdvo->ddc); i++) {
- if (sdvo->ddc[i].ddc_bus)
- i2c_del_adapter(&sdvo->ddc[i].ddc);
+ for (i = 0; i < ARRAY_SIZE(intel_sdvo->ddc); i++) {
+ if (intel_sdvo->ddc[i].ddc_bus)
+ i2c_del_adapter(&intel_sdvo->ddc[i].ddc);
}
-
- drm_encoder_cleanup(&encoder->base);
- kfree(sdvo);
-};
-
-static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
- .destroy = intel_sdvo_encoder_destroy,
-};
+ intel_sdvo_unselect_i2c_bus(intel_sdvo);
+}
static int
intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo,
@@ -3381,8 +3377,9 @@ bool intel_sdvo_init(struct intel_display *display,
if (!assert_sdvo_port_valid(display, port))
return false;
- intel_sdvo = kzalloc_obj(*intel_sdvo);
- if (!intel_sdvo)
+ intel_sdvo = drmm_encoder_alloc(display->drm, struct intel_sdvo, base.base,
+ NULL, 0, "SDVO %c", port_name(port));
+ if (IS_ERR(intel_sdvo))
return false;
/* encoder type will be decided later */
@@ -3391,15 +3388,14 @@ bool intel_sdvo_init(struct intel_display *display,
intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
intel_encoder->port = port;
- drm_encoder_init(display->drm, &intel_encoder->base,
- &intel_sdvo_enc_funcs, 0,
- "SDVO %c", port_name(port));
-
intel_sdvo->sdvo_reg = sdvo_reg;
intel_sdvo->target_addr = intel_sdvo_get_target_addr(intel_sdvo) >> 1;
intel_sdvo_select_i2c_bus(intel_sdvo);
+ if (drmm_add_action_or_reset(display->drm, intel_sdvo_cleanup, intel_sdvo))
+ return false;
+
/* Read the regs to test if we can talk to the device */
for (i = 0; i < 0x40; i++) {
u8 byte;
@@ -3408,7 +3404,7 @@ bool intel_sdvo_init(struct intel_display *display,
drm_dbg_kms(display->drm,
"No SDVO device found on %s\n",
SDVO_NAME(intel_sdvo));
- goto err;
+ return false;
}
}
@@ -3428,7 +3424,7 @@ bool intel_sdvo_init(struct intel_display *display,
/* In default case sdvo lvds is false */
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
- goto err;
+ return false;
intel_sdvo->colorimetry_cap =
intel_sdvo_get_colorimetry_cap(intel_sdvo);
@@ -3439,7 +3435,7 @@ bool intel_sdvo_init(struct intel_display *display,
ret = intel_sdvo_init_ddc_proxy(&intel_sdvo->ddc[i],
intel_sdvo, i + 1);
if (ret)
- goto err;
+ return false;
}
if (!intel_sdvo_output_setup(intel_sdvo)) {
@@ -3502,9 +3498,6 @@ bool intel_sdvo_init(struct intel_display *display,
err_output:
intel_sdvo_output_cleanup(intel_sdvo);
-err:
- intel_sdvo_unselect_i2c_bus(intel_sdvo);
- intel_sdvo_encoder_destroy(&intel_encoder->base);
return false;
}
diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c
index 8fbf0adb56992..05df0acc6dc3e 100644
--- a/drivers/gpu/drm/i915/display/intel_tv.c
+++ b/drivers/gpu/drm/i915/display/intel_tv.c
@@ -33,6 +33,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -1881,7 +1882,6 @@ static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs =
};
static const struct drm_encoder_funcs intel_tv_enc_funcs = {
- .destroy = intel_encoder_destroy,
};
static void intel_tv_add_properties(struct drm_connector *connector)
@@ -1966,10 +1966,10 @@ intel_tv_init(struct intel_display *display)
(tv_dac_off & TVDAC_STATE_CHG_EN) != 0)
return;
- intel_tv = kzalloc_obj(*intel_tv);
- if (!intel_tv) {
+ intel_tv = drmm_encoder_alloc(display->drm, struct intel_tv, base.base,
+ &intel_tv_enc_funcs, DRM_MODE_ENCODER_TVDAC, "TV");
+ if (IS_ERR(intel_tv))
return;
- }
intel_connector = intel_connector_alloc();
if (!intel_connector) {
@@ -1996,9 +1996,6 @@ intel_tv_init(struct intel_display *display)
drm_connector_init(display->drm, connector, &intel_tv_connector_funcs,
DRM_MODE_CONNECTOR_SVIDEO);
- drm_encoder_init(display->drm, &intel_encoder->base,
- &intel_tv_enc_funcs,
- DRM_MODE_ENCODER_TVDAC, "TV");
intel_encoder->compute_config = intel_tv_compute_config;
intel_encoder->get_config = intel_tv_get_config;
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
index d4db73c184e5c..e84edb6242716 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
@@ -30,6 +30,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -1531,7 +1532,6 @@ static void intel_dsi_unprepare(struct intel_encoder *encoder)
}
static const struct drm_encoder_funcs intel_dsi_funcs = {
- .destroy = intel_encoder_destroy,
};
static enum drm_mode_status vlv_dsi_mode_valid(struct drm_connector *connector,
@@ -1918,22 +1918,19 @@ void vlv_dsi_init(struct intel_display *display)
else
display->dsi.mmio_base = VLV_MIPI_BASE;
- intel_dsi = kzalloc_obj(*intel_dsi);
- if (!intel_dsi)
+ intel_dsi = drmm_encoder_alloc(display->drm, struct intel_dsi, base.base,
+ &intel_dsi_funcs, DRM_MODE_ENCODER_DSI,
+ "DSI %c", port_name(port));
+ if (IS_ERR(intel_dsi))
return;
connector = intel_connector_alloc();
- if (!connector) {
- kfree(intel_dsi);
+ if (!connector)
return;
- }
encoder = &intel_dsi->base;
intel_dsi->attached_connector = connector;
- drm_encoder_init(display->drm, &encoder->base, &intel_dsi_funcs,
- DRM_MODE_ENCODER_DSI, "DSI %c", port_name(port));
-
encoder->compute_config = intel_dsi_compute_config;
encoder->pre_enable = intel_dsi_pre_enable;
if (display->platform.geminilake || display->platform.broxton)
@@ -1985,14 +1982,14 @@ void vlv_dsi_init(struct intel_display *display)
host = intel_dsi_host_init(intel_dsi, &intel_dsi_host_ops,
port);
if (!host)
- goto err;
+ return;
intel_dsi->dsi_hosts[port] = host;
}
if (!intel_dsi_vbt_init(intel_dsi, MIPI_DSI_GENERIC_PANEL_ID)) {
drm_dbg_kms(display->drm, "no device found\n");
- goto err;
+ return;
}
/* Use clock read-back from current hw-state for fastboot */
@@ -2050,8 +2047,5 @@ void vlv_dsi_init(struct intel_display *display)
err_cleanup_connector:
drm_connector_cleanup(&connector->base);
-err:
- drm_encoder_cleanup(&encoder->base);
- kfree(intel_dsi);
kfree(connector);
}
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 08/12] drm/i915/display: Switch to managed for connector
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
The current i915 driver uses non-managed function to create connector. It
is not an issue yet, but in order to comply with the latest DRM
requirement, convert this code to use drm and device managed helpers.
Assisted-by: Claude Code:2.1.90
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/gpu/drm/i915/display/g4x_dp.c | 14 ++---
drivers/gpu/drm/i915/display/g4x_hdmi.c | 10 ++--
drivers/gpu/drm/i915/display/icl_dsi.c | 30 +++++------
drivers/gpu/drm/i915/display/intel_connector.c | 26 ++++++---
drivers/gpu/drm/i915/display/intel_connector.h | 5 +-
drivers/gpu/drm/i915/display/intel_crt.c | 19 +++----
drivers/gpu/drm/i915/display/intel_ddi.c | 14 +++--
drivers/gpu/drm/i915/display/intel_dp.c | 12 +++--
drivers/gpu/drm/i915/display/intel_dp_mst.c | 13 ++++-
drivers/gpu/drm/i915/display/intel_dvo.c | 21 ++++----
drivers/gpu/drm/i915/display/intel_hdmi.c | 15 ++++--
drivers/gpu/drm/i915/display/intel_lvds.c | 18 ++++---
drivers/gpu/drm/i915/display/intel_sdvo.c | 73 +++++++++-----------------
drivers/gpu/drm/i915/display/intel_tv.c | 15 +++---
drivers/gpu/drm/i915/display/vlv_dsi.c | 20 ++++---
15 files changed, 153 insertions(+), 152 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
index 99b6c5ce39b2a..e7c3d72d223ee 100644
--- a/drivers/gpu/drm/i915/display/g4x_dp.c
+++ b/drivers/gpu/drm/i915/display/g4x_dp.c
@@ -1299,7 +1299,7 @@ bool g4x_dp_init(struct intel_display *display,
if (!dig_port)
return false;
- intel_connector = intel_connector_alloc();
+ intel_connector = intel_connector_alloc(display->drm);
if (!intel_connector)
return false;
@@ -1311,11 +1311,11 @@ bool g4x_dp_init(struct intel_display *display,
if (drmm_encoder_init(display->drm, &intel_encoder->base,
&intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS,
"DP %c", port_name(port)))
- goto err_encoder_init;
+ return false;
if (drmm_add_action_or_reset(display->drm,
intel_dp_encoder_flush_work_cleanup, encoder))
- goto err_encoder_init;
+ return false;
intel_encoder_link_check_init(intel_encoder, intel_dp_link_check);
@@ -1412,14 +1412,10 @@ bool g4x_dp_init(struct intel_display *display,
dig_port->aux_ch = intel_dp_aux_ch(intel_encoder);
if (dig_port->aux_ch == AUX_CH_NONE)
- goto err_encoder_init;
+ return false;
if (!intel_dp_init_connector(dig_port, intel_connector))
- goto err_encoder_init;
+ return false;
return true;
-
-err_encoder_init:
- kfree(intel_connector);
- return false;
}
diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c
index 45ee1ad504bc1..ab964194e63d0 100644
--- a/drivers/gpu/drm/i915/display/g4x_hdmi.c
+++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c
@@ -690,7 +690,7 @@ bool g4x_hdmi_init(struct intel_display *display,
if (!dig_port)
return false;
- intel_connector = intel_connector_alloc();
+ intel_connector = intel_connector_alloc(display->drm);
if (!intel_connector)
return false;
@@ -701,7 +701,7 @@ bool g4x_hdmi_init(struct intel_display *display,
if (drmm_encoder_init(display->drm, &intel_encoder->base,
&intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS,
"HDMI %c", port_name(port)))
- goto err_encoder_init;
+ return false;
intel_encoder->hotplug = intel_hdmi_hotplug;
intel_encoder->compute_config = g4x_hdmi_compute_config;
@@ -763,11 +763,7 @@ bool g4x_hdmi_init(struct intel_display *display,
intel_infoframe_init(dig_port);
if (!intel_hdmi_init_connector(dig_port, intel_connector))
- goto err_encoder_init;
+ return false;
return true;
-err_encoder_init:
- kfree(intel_connector);
-
- return false;
}
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index cfee173a2367a..3d7372f473d95 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -1783,7 +1783,6 @@ static const struct drm_connector_funcs gen11_dsi_connector_funcs = {
.detect = intel_panel_detect,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_digital_connector_atomic_get_property,
.atomic_set_property = intel_digital_connector_atomic_set_property,
@@ -1935,11 +1934,9 @@ void icl_dsi_init(struct intel_display *display,
if (IS_ERR(intel_dsi))
return;
- intel_connector = intel_connector_alloc();
- if (!intel_connector) {
- kfree(intel_dsi);
+ intel_connector = intel_connector_alloc(display->drm);
+ if (!intel_connector)
return;
- }
encoder = &intel_dsi->base;
intel_dsi->attached_connector = intel_connector;
@@ -1969,10 +1966,16 @@ void icl_dsi_init(struct intel_display *display,
encoder->shutdown = intel_dsi_shutdown;
/* register DSI connector with DRM subsystem */
- drm_connector_init(display->drm, connector,
- &gen11_dsi_connector_funcs,
- DRM_MODE_CONNECTOR_DSI);
+ drmm_connector_init(display->drm, connector,
+ &gen11_dsi_connector_funcs,
+ DRM_MODE_CONNECTOR_DSI, NULL);
drm_connector_helper_add(connector, &gen11_dsi_connector_helper_funcs);
+
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, intel_connector)) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return;
+ }
+
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
intel_connector->get_hw_state = intel_connector_get_hw_state;
@@ -1989,7 +1992,7 @@ void icl_dsi_init(struct intel_display *display,
if (!intel_panel_preferred_fixed_mode(intel_connector)) {
drm_err(display->drm, "DSI fixed mode info missing\n");
- goto err;
+ return;
}
intel_panel_init(intel_connector, NULL);
@@ -2012,22 +2015,17 @@ void icl_dsi_init(struct intel_display *display,
host = intel_dsi_host_init(intel_dsi, &gen11_dsi_host_ops, port);
if (!host)
- goto err;
+ return;
intel_dsi->dsi_hosts[port] = host;
}
if (!intel_dsi_vbt_init(intel_dsi, MIPI_DSI_GENERIC_PANEL_ID)) {
drm_dbg_kms(display->drm, "no device found\n");
- goto err;
+ return;
}
icl_dphy_param_init(intel_dsi);
icl_dsi_add_properties(intel_connector);
- return;
-
-err:
- drm_connector_cleanup(connector);
- kfree(intel_connector);
}
diff --git a/drivers/gpu/drm/i915/display/intel_connector.c b/drivers/gpu/drm/i915/display/intel_connector.c
index 7ef9338d67abf..6fb64c6f4c9e4 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.c
+++ b/drivers/gpu/drm/i915/display/intel_connector.c
@@ -28,6 +28,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -101,7 +102,21 @@ static int intel_connector_init(struct intel_connector *connector)
return 0;
}
-struct intel_connector *intel_connector_alloc(void)
+struct intel_connector *intel_connector_alloc(struct drm_device *dev)
+{
+ struct intel_connector *connector;
+
+ connector = drmm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
+ if (!connector)
+ return NULL;
+
+ if (intel_connector_init(connector) < 0)
+ return NULL;
+
+ return connector;
+}
+
+struct intel_connector *intel_subconnector_alloc(void)
{
struct intel_connector *connector;
@@ -127,15 +142,14 @@ struct intel_connector *intel_connector_alloc(void)
void intel_connector_free(struct intel_connector *connector)
{
kfree(to_intel_digital_connector_state(connector->base.state));
- kfree(connector);
}
/*
* Connector type independent destroy hook for drm_connector_funcs.
*/
-void intel_connector_destroy(struct drm_connector *connector)
+void intel_connector_destroy(struct drm_device *dev, void *data)
{
- struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_connector *intel_connector = (struct intel_connector *)data;
drm_edid_free(intel_connector->detect_edid);
@@ -143,12 +157,8 @@ void intel_connector_destroy(struct drm_connector *connector)
intel_panel_fini(intel_connector);
- drm_connector_cleanup(connector);
-
if (intel_connector->mst.port)
drm_dp_mst_put_port_malloc(intel_connector->mst.port);
-
- kfree(connector);
}
int intel_connector_register(struct drm_connector *_connector)
diff --git a/drivers/gpu/drm/i915/display/intel_connector.h b/drivers/gpu/drm/i915/display/intel_connector.h
index 0aa86626e6463..6c47ca46c1d18 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.h
+++ b/drivers/gpu/drm/i915/display/intel_connector.h
@@ -14,9 +14,10 @@ struct i2c_adapter;
struct intel_connector;
struct intel_encoder;
-struct intel_connector *intel_connector_alloc(void);
+struct intel_connector *intel_connector_alloc(struct drm_device *dev);
+struct intel_connector *intel_subconnector_alloc(void);
void intel_connector_free(struct intel_connector *connector);
-void intel_connector_destroy(struct drm_connector *connector);
+void intel_connector_destroy(struct drm_device *dev, void *data);
int intel_connector_register(struct drm_connector *connector);
void intel_connector_unregister(struct drm_connector *connector);
void intel_connector_attach_encoder(struct intel_connector *connector,
diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
index 39bb115955f5a..8af76d141888b 100644
--- a/drivers/gpu/drm/i915/display/intel_crt.c
+++ b/drivers/gpu/drm/i915/display/intel_crt.c
@@ -992,7 +992,6 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
};
@@ -1046,19 +1045,21 @@ void intel_crt_init(struct intel_display *display)
if (IS_ERR(crt))
return;
- connector = intel_connector_alloc();
- if (!connector) {
- kfree(crt);
+ connector = intel_connector_alloc(display->drm);
+ if (!connector)
return;
- }
ddc_pin = display->vbt.crt_ddc_pin;
- drm_connector_init_with_ddc(display->drm, &connector->base,
- &intel_crt_connector_funcs,
- DRM_MODE_CONNECTOR_VGA,
- intel_gmbus_get_adapter(display, ddc_pin));
+ drmm_connector_init(display->drm, &connector->base,
+ &intel_crt_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA,
+ intel_gmbus_get_adapter(display, ddc_pin));
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, connector)) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return;
+ }
intel_connector_attach_encoder(connector, &crt->base);
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index 003287ad5bc59..bb87a7361aa92 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -4694,7 +4694,7 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
struct intel_connector *connector;
enum port port = dig_port->base.port;
- connector = intel_connector_alloc();
+ connector = intel_connector_alloc(display->drm);
if (!connector)
return -ENOMEM;
@@ -4709,10 +4709,8 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
dig_port->dp.voltage_max = intel_ddi_dp_voltage_max;
dig_port->dp.preemph_max = intel_ddi_dp_preemph_max;
- if (!intel_dp_init_connector(dig_port, connector)) {
- kfree(connector);
+ if (!intel_dp_init_connector(dig_port, connector))
return -EINVAL;
- }
if (dig_port->base.type == INTEL_OUTPUT_EDP) {
struct drm_privacy_screen *privacy_screen;
@@ -4892,12 +4890,13 @@ static bool bdw_digital_port_connected(struct intel_encoder *encoder)
return intel_de_read(display, GEN8_DE_PORT_ISR) & bit;
}
-static int intel_ddi_init_hdmi_connector(struct intel_digital_port *dig_port)
+static int intel_ddi_init_hdmi_connector(struct drm_device *dev,
+ struct intel_digital_port *dig_port)
{
struct intel_connector *connector;
enum port port = dig_port->base.port;
- connector = intel_connector_alloc();
+ connector = intel_connector_alloc(dev);
if (!connector)
return -ENOMEM;
@@ -4910,7 +4909,6 @@ static int intel_ddi_init_hdmi_connector(struct intel_digital_port *dig_port)
* don't fail the entire DDI init.
*/
dig_port->hdmi.hdmi_reg = INVALID_MMIO_REG;
- kfree(connector);
}
return 0;
@@ -5499,7 +5497,7 @@ void intel_ddi_init(struct intel_display *display,
* but leave it just in case we have some really bad VBTs...
*/
if (encoder->type != INTEL_OUTPUT_EDP && init_hdmi) {
- if (intel_ddi_init_hdmi_connector(dig_port))
+ if (intel_ddi_init_hdmi_connector(display->drm, dig_port))
return;
}
}
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 71f206adbebd3..2af64de9c81de 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -47,6 +47,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -6810,7 +6811,6 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
.atomic_set_property = intel_digital_connector_atomic_set_property,
.late_register = intel_dp_connector_register,
.early_unregister = intel_dp_connector_unregister,
- .destroy = intel_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = intel_digital_connector_duplicate_state,
.oob_hotplug_event = intel_dp_oob_hotplug_event,
@@ -7213,10 +7213,15 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
type == DRM_MODE_CONNECTOR_eDP ? "eDP" : "DP",
encoder->base.base.id, encoder->base.name);
- drm_connector_init_with_ddc(dev, &connector->base, &intel_dp_connector_funcs,
- type, &intel_dp->aux.ddc);
+ drmm_connector_init(dev, &connector->base, &intel_dp_connector_funcs,
+ type, &intel_dp->aux.ddc);
drm_connector_helper_add(&connector->base, &intel_dp_connector_helper_funcs);
+ if (drmm_add_action_or_reset(dev, intel_connector_destroy, connector)) {
+ drm_err(dev, "Failed to register intel_connector_destroy clean-up.\n");
+ goto fail;
+ }
+
if (!HAS_GMCH(display) && DISPLAY_VER(display) < 12)
connector->base.interlace_allowed = true;
@@ -7260,7 +7265,6 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
fail:
intel_display_power_flush_work(display);
- drm_connector_cleanup(&connector->base);
return false;
}
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 5fe6fbcaf9371..4b0cdb61c33d6 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -1438,13 +1438,22 @@ mst_connector_early_unregister(struct drm_connector *_connector)
drm_dp_mst_connector_early_unregister(&connector->base, connector->mst.port);
}
+static void mst_connector_destroy(struct drm_connector *connector)
+{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
+
+ intel_connector_destroy(connector->dev, intel_connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
static const struct drm_connector_funcs mst_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_digital_connector_atomic_get_property,
.atomic_set_property = intel_digital_connector_atomic_set_property,
.late_register = mst_connector_late_register,
.early_unregister = mst_connector_early_unregister,
- .destroy = intel_connector_destroy,
+ .destroy = mst_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = intel_digital_connector_duplicate_state,
};
@@ -1769,7 +1778,7 @@ mst_topology_add_connector(struct drm_dp_mst_topology_mgr *mgr,
enum pipe pipe;
int ret;
- connector = intel_connector_alloc();
+ connector = intel_subconnector_alloc();
if (!connector)
return NULL;
diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c
index ea2f6426fd77d..1f84cc44598bf 100644
--- a/drivers/gpu/drm/i915/display/intel_dvo.c
+++ b/drivers/gpu/drm/i915/display/intel_dvo.c
@@ -362,7 +362,6 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.detect = intel_dvo_detect,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -496,11 +495,9 @@ void intel_dvo_init(struct intel_display *display)
if (!intel_dvo)
return;
- connector = intel_connector_alloc();
- if (!connector) {
- kfree(intel_dvo);
+ connector = intel_connector_alloc(display->drm);
+ if (!connector)
return;
- }
intel_dvo->attached_connector = connector;
@@ -547,13 +544,19 @@ void intel_dvo_init(struct intel_display *display)
DRM_CONNECTOR_POLL_DISCONNECT;
connector->base.polled = connector->polled;
- drm_connector_init_with_ddc(display->drm, &connector->base,
- &intel_dvo_connector_funcs,
- intel_dvo_connector_type(&intel_dvo->dev),
- intel_gmbus_get_adapter(display, GMBUS_PIN_DPC));
+ drmm_connector_init(display->drm, &connector->base,
+ &intel_dvo_connector_funcs,
+ intel_dvo_connector_type(&intel_dvo->dev),
+ intel_gmbus_get_adapter(display, GMBUS_PIN_DPC));
drm_connector_helper_add(&connector->base,
&intel_dvo_connector_helper_funcs);
+
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, connector)) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return;
+ }
+
connector->base.display_info.subpixel_order = SubPixelHorizontalRGB;
intel_connector_attach_encoder(connector, encoder);
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 05e898d10a2be..f744bae14eaf2 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -39,6 +39,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
+#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/intel/intel_lpe_audio.h>
@@ -2650,7 +2651,6 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.atomic_set_property = intel_digital_connector_atomic_set_property,
.late_register = intel_hdmi_connector_register,
.early_unregister = intel_hdmi_connector_unregister,
- .destroy = intel_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = intel_digital_connector_duplicate_state,
};
@@ -3083,13 +3083,18 @@ bool intel_hdmi_init_connector(struct intel_digital_port *dig_port,
if (!ddc_pin)
return false;
- drm_connector_init_with_ddc(dev, connector,
- &intel_hdmi_connector_funcs,
- DRM_MODE_CONNECTOR_HDMIA,
- intel_gmbus_get_adapter(display, ddc_pin));
+ drmm_connector_init(dev, connector,
+ &intel_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ intel_gmbus_get_adapter(display, ddc_pin));
drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, intel_connector)) {
+ drm_err(dev, "Failed to register intel_connector_destroy clean-up.\n");
+ return false;
+ }
+
if (DISPLAY_VER(display) < 12)
connector->interlace_allowed = true;
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index 7bb99eb44a5a4..567eda1720261 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -512,7 +512,6 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.atomic_set_property = intel_digital_connector_atomic_set_property,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = intel_digital_connector_duplicate_state,
};
@@ -891,18 +890,23 @@ void intel_lvds_init(struct intel_display *display)
if (IS_ERR(lvds_encoder))
return;
- connector = intel_connector_alloc();
+ encoder = &lvds_encoder->base;
+
+ connector = intel_connector_alloc(display->drm);
if (!connector)
return;
lvds_encoder->attached_connector = connector;
- encoder = &lvds_encoder->base;
- drm_connector_init_with_ddc(display->drm, &connector->base,
- &intel_lvds_connector_funcs,
- DRM_MODE_CONNECTOR_LVDS,
- intel_gmbus_get_adapter(display, ddc_pin));
+ drmm_connector_init(display->drm, &connector->base,
+ &intel_lvds_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS,
+ intel_gmbus_get_adapter(display, ddc_pin));
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, connector)) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return;
+ }
encoder->enable = intel_enable_lvds;
encoder->pre_enable = intel_pre_enable_lvds;
diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c
index f5aaa38002674..af3a250f637e1 100644
--- a/drivers/gpu/drm/i915/display/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/display/intel_sdvo.c
@@ -2507,7 +2507,6 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
.atomic_set_property = intel_sdvo_connector_atomic_set_property,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = intel_sdvo_connector_duplicate_state,
};
@@ -2730,17 +2729,23 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
if (HAS_DDC(connector))
ddc = intel_sdvo_select_ddc_bus(encoder, connector);
- ret = drm_connector_init_with_ddc(encoder->base.base.dev,
- &connector->base.base,
- &intel_sdvo_connector_funcs,
- connector->base.base.connector_type,
- ddc ? &ddc->ddc : NULL);
+ ret = drmm_connector_init(encoder->base.base.dev,
+ &connector->base.base,
+ &intel_sdvo_connector_funcs,
+ connector->base.base.connector_type,
+ ddc ? &ddc->ddc : NULL);
if (ret < 0)
return ret;
drm_connector_helper_add(&connector->base.base,
&intel_sdvo_connector_helper_funcs);
+ ret = drmm_add_action_or_reset(display->drm, intel_connector_destroy, &connector->base);
+ if (ret) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return ret;
+ }
+
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
connector->base.base.interlace_allowed = true;
connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
@@ -2765,20 +2770,18 @@ intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
intel_attach_aspect_ratio_property(&connector->base.base);
}
-static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)
+static struct intel_sdvo_connector *intel_sdvo_connector_alloc(struct drm_device *dev)
{
struct intel_sdvo_connector *sdvo_connector;
struct intel_sdvo_connector_state *conn_state;
- sdvo_connector = kzalloc_obj(*sdvo_connector);
+ sdvo_connector = drmm_kzalloc(dev, sizeof(*sdvo_connector), GFP_KERNEL);
if (!sdvo_connector)
return NULL;
conn_state = kzalloc_obj(*conn_state);
- if (!conn_state) {
- kfree(sdvo_connector);
+ if (!conn_state)
return NULL;
- }
__drm_atomic_helper_connector_reset(&sdvo_connector->base.base,
&conn_state->base.base);
@@ -2800,7 +2803,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, u16 type)
drm_dbg_kms(display->drm, "initialising DVI type 0x%x\n", type);
- intel_sdvo_connector = intel_sdvo_connector_alloc();
+ intel_sdvo_connector = intel_sdvo_connector_alloc(display->drm);
if (!intel_sdvo_connector)
return false;
@@ -2830,10 +2833,8 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, u16 type)
intel_sdvo_connector->is_hdmi = true;
}
- if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
- kfree(intel_sdvo_connector);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0)
return false;
- }
if (intel_sdvo_connector->is_hdmi)
intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector);
@@ -2852,7 +2853,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, u16 type)
drm_dbg_kms(display->drm, "initialising TV type 0x%x\n", type);
- intel_sdvo_connector = intel_sdvo_connector_alloc();
+ intel_sdvo_connector = intel_sdvo_connector_alloc(display->drm);
if (!intel_sdvo_connector)
return false;
@@ -2863,10 +2864,8 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, u16 type)
intel_sdvo_connector->output_flag = type;
- if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
- kfree(intel_sdvo_connector);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0)
return false;
- }
if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
return false;
@@ -2888,7 +2887,7 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, u16 type)
drm_dbg_kms(display->drm, "initialising analog type 0x%x\n", type);
- intel_sdvo_connector = intel_sdvo_connector_alloc();
+ intel_sdvo_connector = intel_sdvo_connector_alloc(display->drm);
if (!intel_sdvo_connector)
return false;
@@ -2901,10 +2900,8 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, u16 type)
intel_sdvo_connector->output_flag = type;
- if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
- kfree(intel_sdvo_connector);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0)
return false;
- }
return true;
}
@@ -2920,7 +2917,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, u16 type)
drm_dbg_kms(display->drm, "initialising LVDS type 0x%x\n", type);
- intel_sdvo_connector = intel_sdvo_connector_alloc();
+ intel_sdvo_connector = intel_sdvo_connector_alloc(display->drm);
if (!intel_sdvo_connector)
return false;
@@ -2931,10 +2928,8 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, u16 type)
intel_sdvo_connector->output_flag = type;
- if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0) {
- kfree(intel_sdvo_connector);
+ if (intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo) < 0)
return false;
- }
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
return false;
@@ -3038,19 +3033,6 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo)
return true;
}
-static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo)
-{
- struct intel_display *display = to_intel_display(&intel_sdvo->base);
- struct drm_connector *connector, *tmp;
-
- list_for_each_entry_safe(connector, tmp,
- &display->drm->mode_config.connector_list, head) {
- if (intel_attached_encoder(to_intel_connector(connector)) == &intel_sdvo->base) {
- drm_connector_unregister(connector);
- intel_connector_destroy(connector);
- }
- }
-}
static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
@@ -3443,7 +3425,7 @@ bool intel_sdvo_init(struct intel_display *display,
"SDVO output failed to setup on %s\n",
SDVO_NAME(intel_sdvo));
/* Output_setup can leave behind connectors! */
- goto err_output;
+ return false;
}
/*
@@ -3469,12 +3451,12 @@ bool intel_sdvo_init(struct intel_display *display,
/* Set the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo))
- goto err_output;
+ return false;
if (!intel_sdvo_get_input_pixel_clock_range(intel_sdvo,
&intel_sdvo->pixel_clock_min,
&intel_sdvo->pixel_clock_max))
- goto err_output;
+ return false;
drm_dbg_kms(display->drm, "%s device VID/DID: %02X:%02X.%02X, "
"clock range %dMHz - %dMHz, "
@@ -3495,9 +3477,4 @@ bool intel_sdvo_init(struct intel_display *display,
(SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1 |
SDVO_OUTPUT_LVDS1) ? 'Y' : 'N');
return true;
-
-err_output:
- intel_sdvo_output_cleanup(intel_sdvo);
-
- return false;
}
diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c
index 05df0acc6dc3e..d473d915909e0 100644
--- a/drivers/gpu/drm/i915/display/intel_tv.c
+++ b/drivers/gpu/drm/i915/display/intel_tv.c
@@ -1841,7 +1841,6 @@ intel_tv_get_modes(struct drm_connector *connector)
static const struct drm_connector_funcs intel_tv_connector_funcs = {
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
.atomic_duplicate_state = intel_tv_connector_duplicate_state,
@@ -1971,11 +1970,9 @@ intel_tv_init(struct intel_display *display)
if (IS_ERR(intel_tv))
return;
- intel_connector = intel_connector_alloc();
- if (!intel_connector) {
- kfree(intel_tv);
+ intel_connector = intel_connector_alloc(display->drm);
+ if (!intel_connector)
return;
- }
intel_encoder = &intel_tv->base;
connector = &intel_connector->base;
@@ -1993,9 +1990,13 @@ intel_tv_init(struct intel_display *display)
intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
intel_connector->base.polled = intel_connector->polled;
- drm_connector_init(display->drm, connector, &intel_tv_connector_funcs,
- DRM_MODE_CONNECTOR_SVIDEO);
+ drmm_connector_init(display->drm, connector, &intel_tv_connector_funcs,
+ DRM_MODE_CONNECTOR_SVIDEO, NULL);
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, intel_connector)) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return;
+ }
intel_encoder->compute_config = intel_tv_compute_config;
intel_encoder->get_config = intel_tv_get_config;
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
index e84edb6242716..726f4190a8398 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
@@ -1560,7 +1560,6 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = {
.detect = intel_panel_detect,
.late_register = intel_connector_register,
.early_unregister = intel_connector_unregister,
- .destroy = intel_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_get_property = intel_digital_connector_atomic_get_property,
.atomic_set_property = intel_digital_connector_atomic_set_property,
@@ -1924,7 +1923,7 @@ void vlv_dsi_init(struct intel_display *display)
if (IS_ERR(intel_dsi))
return;
- connector = intel_connector_alloc();
+ connector = intel_connector_alloc(display->drm);
if (!connector)
return;
@@ -2011,11 +2010,16 @@ void vlv_dsi_init(struct intel_display *display)
intel_dsi_vbt_gpio_init(intel_dsi,
intel_dsi_get_hw_state(encoder, &pipe));
- drm_connector_init(display->drm, &connector->base, &intel_dsi_connector_funcs,
- DRM_MODE_CONNECTOR_DSI);
+ drmm_connector_init(display->drm, &connector->base, &intel_dsi_connector_funcs,
+ DRM_MODE_CONNECTOR_DSI, NULL);
drm_connector_helper_add(&connector->base, &intel_dsi_connector_helper_funcs);
+ if (drmm_add_action_or_reset(display->drm, intel_connector_destroy, connector)) {
+ drm_err(display->drm, "Failed to register intel_connector_destroy clean-up.\n");
+ return;
+ }
+
connector->base.display_info.subpixel_order = SubPixelHorizontalRGB; /*XXX*/
intel_connector_attach_encoder(connector, encoder);
@@ -2026,7 +2030,7 @@ void vlv_dsi_init(struct intel_display *display)
if (!intel_panel_preferred_fixed_mode(connector)) {
drm_dbg_kms(display->drm, "no fixed mode\n");
- goto err_cleanup_connector;
+ return;
}
dmi_id = dmi_first_match(vlv_dsi_dmi_quirk_table);
@@ -2042,10 +2046,4 @@ void vlv_dsi_init(struct intel_display *display)
intel_backlight_setup(connector, INVALID_PIPE);
vlv_dsi_add_properties(connector);
-
- return;
-
-err_cleanup_connector:
- drm_connector_cleanup(&connector->base);
- kfree(connector);
}
--
2.43.0
^ permalink raw reply related
* [PATCH RFC 09/12] drm: Introduce drmm_connector_dp_init() with link training state properties
From: Kory Maincent @ 2026-04-09 17:08 UTC (permalink / raw)
To: Jani Nikula, Rodrigo Vivi, Joonas Lahtinen, Tvrtko Ursulin,
David Airlie, Simona Vetter, Dave Airlie, Jesse Barnes,
Eric Anholt, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann,
Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Chun-Kuang Hu, Philipp Zabel,
Matthias Brugger, AngeloGioacchino Del Regno, Chris Wilson
Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Louis Chauvet,
intel-gfx, intel-xe, dri-devel, linux-kernel, linux-mediatek,
linux-arm-kernel, Simona Vetter, Kory Maincent
In-Reply-To: <20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com>
Add a managed DisplayPort connector initialization helper,
drmm_connector_dp_init(), modeled after the existing HDMI counterpart
drmm_connector_hdmi_init(). Cleanup is handled automatically via a
DRM-managed action.
The helper creates the following immutable connector properties to expose
DP link training capabilities and state to userspace:
- num_lanes: bitmask of supported lane counts (1, 2, 4)
- link_rate: Array of supported link rates.
- dsc_en: Display Stream Compression supported
- voltage_swingN: per-lane voltage swing level bitmask
- pre-emphasisN: per-lane pre-emphasis level bitmask
Link rates are passed by the driver in deca-kbps, following the DRM
convention, but exposed to userspace in kbps for clarity.
Two additional helpers are provided to update and reset those properties
at runtime:
- drm_connector_dp_set_link_train_properties()
- drm_connector_dp_reset_link_train_properties()
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/drm_dp_connector.c | 344 +++++++++++++++++++++++++++++++++++++
include/drm/drm_connector.h | 38 ++++
include/drm/drm_dp_connector.h | 109 ++++++++++++
4 files changed, 492 insertions(+)
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e97faabcd7830..8ff08c2fb863e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -42,6 +42,7 @@ drm-y := \
drm_color_mgmt.o \
drm_colorop.o \
drm_connector.o \
+ drm_dp_connector.o \
drm_crtc.o \
drm_displayid.o \
drm_drv.o \
diff --git a/drivers/gpu/drm/drm_dp_connector.c b/drivers/gpu/drm/drm_dp_connector.c
new file mode 100644
index 0000000000000..b25637a4378d5
--- /dev/null
+++ b/drivers/gpu/drm/drm_dp_connector.c
@@ -0,0 +1,344 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Google
+ * Author: Kory Maincent <kory.maincent@bootlin.com>
+ */
+#include <drm/drm_dp_connector.h>
+#include <drm/drm_print.h>
+#include <linux/list.h>
+
+/**
+ * drm_connector_dp_link_reset_properties() - Reset DisplayPort link configuration
+ * @connector: DRM connector
+ * @dp_link: Link training informations
+ *
+ * Returns: Zero on success, or an errno code otherwise.
+ */
+int
+drm_connector_dp_set_link_train_properties(struct drm_connector *connector,
+ const struct drm_connector_dp_link_train *dp_link_train)
+{
+ u32 lrate = 0;
+ int ret;
+
+ if (!connector)
+ return -ENODEV;
+
+ if (dp_link_train->nlanes && !is_power_of_2(dp_link_train->nlanes & DRM_NLANES_MASK)) {
+ drm_err(connector->dev, "Wrong lane number");
+ return -EINVAL;
+ }
+
+ if (dp_link_train->rate) {
+ struct drm_property_enum *prop_enum;
+ bool found = false;
+
+ list_for_each_entry(prop_enum, &connector->dp.link_rate_property->enum_list, head) {
+ u32 parsed_rate;
+
+ /* Convert dp_link_train->rate from deca-kbps to kbps */
+ if (!kstrtou32(prop_enum->name, 10, &parsed_rate) &&
+ dp_link_train->rate * 10 == parsed_rate) {
+ lrate = 1 << prop_enum->value;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ drm_err(connector->dev, "Wrong rate value");
+ return -EINVAL;
+ }
+ }
+
+ ret = drm_object_property_set_value(&connector->base, connector->dp.nlanes_property,
+ dp_link_train->nlanes);
+ if (ret)
+ return ret;
+
+ ret = drm_object_property_set_value(&connector->base, connector->dp.link_rate_property,
+ lrate);
+ if (ret)
+ return ret;
+
+ if (connector->dp.dsc_en_property) {
+ ret = drm_object_property_set_value(&connector->base, connector->dp.dsc_en_property,
+ dp_link_train->dsc_en);
+ if (ret)
+ return ret;
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if (connector->dp.v_swing_property[i]) {
+ ret = drm_object_property_set_value(&connector->base,
+ connector->dp.v_swing_property[i],
+ dp_link_train->v_swing[i]);
+ if (ret)
+ return ret;
+ }
+
+ if (connector->dp.pre_emph_property[i]) {
+ ret = drm_object_property_set_value(&connector->base,
+ connector->dp.pre_emph_property[i],
+ dp_link_train->pre_emph[i]);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(drm_connector_dp_set_link_train_properties);
+
+/**
+ * drm_connector_dp_link_reset_properties() - Reset DisplayPort link configuration
+ * @connector: DRM connector
+ */
+void drm_connector_dp_reset_link_train_properties(struct drm_connector *connector)
+{
+ struct drm_connector_dp_link_train dp_link_train = {0};
+
+ drm_connector_dp_set_link_train_properties(connector, &dp_link_train);
+}
+EXPORT_SYMBOL(drm_connector_dp_reset_link_train_properties);
+
+static int drm_connector_create_nlanes_prop(struct drm_connector *connector,
+ u8 sup_nlanes)
+{
+ static const struct drm_prop_enum_list props[] = {
+ {__builtin_ffs(DRM_DP_1LANE) - 1, "1" },
+ {__builtin_ffs(DRM_DP_2LANE) - 1, "2" },
+ {__builtin_ffs(DRM_DP_4LANE) - 1, "4" },
+ };
+ struct drm_property *prop;
+
+ if (drm_WARN_ON(connector->dev, sup_nlanes != (sup_nlanes & DRM_NLANES_MASK)))
+ return -EINVAL;
+
+ prop = drm_property_create_bitmask(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+ "num_lanes", props, ARRAY_SIZE(props),
+ sup_nlanes);
+ if (!prop)
+ return -ENOMEM;
+
+ drm_object_attach_property(&connector->base, prop, 0);
+
+ connector->dp.nlanes_property = prop;
+
+ return 0;
+}
+
+static int drm_connector_create_lrate_prop(struct drm_connector *connector,
+ u32 sup_nlrates,
+ const u32 *sup_lrates)
+{
+ struct drm_prop_enum_list *props;
+ u32 supp_nlrates_bitmask = 0;
+ struct drm_property *prop;
+ int ret = 0;
+
+ if (!sup_nlrates || !sup_lrates)
+ return 0;
+
+ props = kcalloc(sup_nlrates, sizeof(*props), GFP_KERNEL);
+ if (!props)
+ return -ENOMEM;
+
+ for (int i = 0; i < sup_nlrates; i++) {
+ props[i].type = i;
+ /* Convert deca-kbps to kbps */
+ props[i].name = kasprintf(GFP_KERNEL, "%d", sup_lrates[i] * 10);
+ if (!props[i].name) {
+ while (i--)
+ kfree(props[i].name);
+ kfree(props);
+ return -ENOMEM;
+ }
+ supp_nlrates_bitmask |= 1 << i;
+ }
+
+ prop = drm_property_create_bitmask(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+ "link_rate", props, sup_nlrates,
+ supp_nlrates_bitmask);
+ if (!prop) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ drm_object_attach_property(&connector->base, prop, 0);
+
+ connector->dp.link_rate_property = prop;
+
+out:
+ for (int i = 0; i < sup_nlrates; i++)
+ kfree(props[i].name);
+
+ kfree(props);
+ return ret;
+}
+
+static int drm_connector_create_dsc_prop(struct drm_connector *connector)
+{
+ struct drm_property *prop;
+
+ prop = drm_property_create_bool(connector->dev, DRM_MODE_PROP_IMMUTABLE, "dsc_en");
+ if (!prop)
+ return -ENOMEM;
+
+ drm_object_attach_property(&connector->base, prop, 0);
+
+ connector->dp.dsc_en_property = prop;
+
+ return 0;
+}
+
+static int drm_connector_create_vswing_prop(struct drm_connector *connector,
+ u8 sup_v_swings, int id)
+{
+ static const struct drm_prop_enum_list props[] = {
+ {__builtin_ffs(DRM_DP_VOLTAGE_SWING_LEVEL_0) - 1, "level_0" },
+ {__builtin_ffs(DRM_DP_VOLTAGE_SWING_LEVEL_1) - 1, "level_1" },
+ {__builtin_ffs(DRM_DP_VOLTAGE_SWING_LEVEL_2) - 1, "level_2" },
+ {__builtin_ffs(DRM_DP_VOLTAGE_SWING_LEVEL_3) - 1, "level_3" },
+ };
+ struct drm_property *prop;
+ char str[16];
+
+ if (!sup_v_swings)
+ return 0;
+
+ if (drm_WARN_ON(connector->dev, sup_v_swings != (sup_v_swings &
+ DRM_DP_VOLTAGE_SWING_LEVEL_MASK)))
+ return -EINVAL;
+
+ snprintf(str, sizeof(str), "voltage_swing%d", id);
+ prop = drm_property_create_bitmask(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+ str, props, ARRAY_SIZE(props),
+ sup_v_swings);
+ if (!prop)
+ return -ENOMEM;
+
+ drm_object_attach_property(&connector->base, prop, 0);
+
+ connector->dp.v_swing_property[id] = prop;
+
+ return 0;
+}
+
+static int drm_connector_create_pre_emph_prop(struct drm_connector *connector,
+ u8 sup_pre_emph, int id)
+{
+ static const struct drm_prop_enum_list props[] = {
+ {__builtin_ffs(DRM_DP_PRE_EMPH_LEVEL_0) - 1, "level_0" },
+ {__builtin_ffs(DRM_DP_PRE_EMPH_LEVEL_1) - 1, "level_1" },
+ {__builtin_ffs(DRM_DP_PRE_EMPH_LEVEL_2) - 1, "level_2" },
+ {__builtin_ffs(DRM_DP_PRE_EMPH_LEVEL_3) - 1, "level_3" },
+ };
+ struct drm_property *prop;
+ char str[16];
+
+ if (!sup_pre_emph)
+ return 0;
+
+ if (drm_WARN_ON(connector->dev, sup_pre_emph != (sup_pre_emph &
+ DRM_DP_PRE_EMPH_LEVEL_MASK)))
+ return -EINVAL;
+
+ snprintf(str, sizeof(str), "pre_emphasis%d", id);
+ prop = drm_property_create_bitmask(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+ str, props, ARRAY_SIZE(props),
+ sup_pre_emph);
+ if (!prop)
+ return -ENOMEM;
+
+ drm_object_attach_property(&connector->base, prop, 0);
+
+ connector->dp.pre_emph_property[id] = prop;
+
+ return 0;
+}
+
+static int
+drm_connector_dp_create_props(struct drm_connector *connector,
+ const struct drm_connector_dp_link_train_caps *dp_link_train_caps)
+{
+ u8 nlanes;
+ int ret;
+
+ ret = drm_connector_create_nlanes_prop(connector, dp_link_train_caps->nlanes);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_create_lrate_prop(connector, dp_link_train_caps->nrates,
+ dp_link_train_caps->rates);
+ if (ret)
+ return ret;
+
+ if (dp_link_train_caps->dsc) {
+ ret = drm_connector_create_dsc_prop(connector);
+ if (ret)
+ return ret;
+ }
+
+ nlanes = 1 << (fls(dp_link_train_caps->nlanes) - 1);
+ for (int i = 0; i < nlanes; i++) {
+ ret = drm_connector_create_vswing_prop(connector,
+ dp_link_train_caps->v_swings, i);
+ if (ret)
+ return ret;
+
+ ret = drm_connector_create_pre_emph_prop(connector,
+ dp_link_train_caps->pre_emphs, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * drmm_connector_dp_init - Init a preallocated DisplayPort connector
+ * @dev: DRM device
+ * @connector: A pointer to the DisplayPort connector to init
+ * @funcs: callbacks for this connector
+ * @dp_link_train_caps: DisplayPort link training capabilities. The pointer
+ * is not kept by the DRM core
+ * @connector_type: user visible type of the connector
+ * @ddc: optional pointer to the associated ddc adapter
+ *
+ * Initialises a preallocated DisplayPort connector. Connectors can be
+ * subclassed as part of driver connector objects.
+ *
+ * Cleanup is automatically handled with a call to
+ * drm_connector_cleanup() in a DRM-managed action.
+ *
+ * The connector structure should be allocated with drmm_kzalloc().
+ *
+ * The @drm_connector_funcs.destroy hook must be NULL.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_connector_dp_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_dp_link_train_caps *dp_link_train_caps,
+ int connector_type,
+ struct i2c_adapter *ddc)
+{
+ int ret;
+
+ if (!(connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+ connector_type == DRM_MODE_CONNECTOR_eDP))
+ return -EINVAL;
+
+ if (!dp_link_train_caps)
+ return -EINVAL;
+
+ ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
+ if (ret)
+ return ret;
+
+ return drm_connector_dp_create_props(connector, dp_link_train_caps);
+}
+EXPORT_SYMBOL(drmm_connector_dp_init);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f83f28cae2075..df3a71fed35b1 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1987,6 +1987,39 @@ struct drm_connector_cec {
void *data;
};
+/**
+ * struct drm_connector_dp - DRM Connector DisplayPort-related structure
+ */
+struct drm_connector_dp {
+ /**
+ * @nlanes_property: Connector property to report the number of lanes
+ */
+ struct drm_property *nlanes_property;
+
+ /**
+ * @link_rate_property: Connector property to report the link rate
+ */
+ struct drm_property *link_rate_property;
+
+ /**
+ * @dsc_en_property: Connector property to report the Display Stream
+ * Compression supporrt
+ */
+ struct drm_property *dsc_en_property;
+
+ /**
+ * @v_swing_property: Connector property to report the voltage
+ * swing per lane
+ */
+ struct drm_property *v_swing_property[4];
+
+ /**
+ * @pre_emph_property: Connector property to report the
+ * pre-emphasis per lane
+ */
+ struct drm_property *pre_emph_property[4];
+};
+
/**
* struct drm_connector - central DRM connector control structure
*
@@ -2410,6 +2443,11 @@ struct drm_connector {
* @cec: CEC-related data.
*/
struct drm_connector_cec cec;
+
+ /**
+ * @dp: DisplayPort-related variable and properties.
+ */
+ struct drm_connector_dp dp;
};
#define obj_to_connector(x) container_of(x, struct drm_connector, base)
diff --git a/include/drm/drm_dp_connector.h b/include/drm/drm_dp_connector.h
new file mode 100644
index 0000000000000..77d2f4bb6df68
--- /dev/null
+++ b/include/drm/drm_dp_connector.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef DRM_DP_CONNECTOR_H_
+#define DRM_DP_CONNECTOR_H_
+
+#include <drm/drm_connector.h>
+
+#define DRM_DP_1LANE BIT(0)
+#define DRM_DP_2LANE BIT(1)
+#define DRM_DP_4LANE BIT(2)
+#define DRM_NLANES_MASK (DRM_DP_1LANE | DRM_DP_2LANE | DRM_DP_4LANE)
+#define DRM_DP_VOLTAGE_SWING_LEVEL_0 BIT(0)
+#define DRM_DP_VOLTAGE_SWING_LEVEL_1 BIT(1)
+#define DRM_DP_VOLTAGE_SWING_LEVEL_2 BIT(2)
+#define DRM_DP_VOLTAGE_SWING_LEVEL_3 BIT(3)
+#define DRM_DP_VOLTAGE_SWING_LEVEL_MASK (DRM_DP_VOLTAGE_SWING_LEVEL_0 | \
+ DRM_DP_VOLTAGE_SWING_LEVEL_1 | \
+ DRM_DP_VOLTAGE_SWING_LEVEL_2 | \
+ DRM_DP_VOLTAGE_SWING_LEVEL_3)
+#define DRM_DP_PRE_EMPH_LEVEL_0 BIT(0)
+#define DRM_DP_PRE_EMPH_LEVEL_1 BIT(1)
+#define DRM_DP_PRE_EMPH_LEVEL_2 BIT(2)
+#define DRM_DP_PRE_EMPH_LEVEL_3 BIT(3)
+#define DRM_DP_PRE_EMPH_LEVEL_MASK (DRM_DP_PRE_EMPH_LEVEL_0 | \
+ DRM_DP_PRE_EMPH_LEVEL_1 | \
+ DRM_DP_PRE_EMPH_LEVEL_2 | \
+ DRM_DP_PRE_EMPH_LEVEL_3)
+
+/**
+ * struct drm_connector_dp_link_train_caps - DRM DisplayPort link training
+ * capabilities
+ */
+struct drm_connector_dp_link_train_caps {
+ /**
+ * @nlanes: Bitmask of lanes number supported
+ */
+ u8 nlanes;
+
+ /**
+ * @nrates: Number of link rates supported
+ */
+ u32 nrates;
+
+ /**
+ * @rates: Array listing the supported link rates in deca-kbps
+ */
+ const u32 *rates;
+
+ /**
+ * @dsc: Display Stream Compression supported
+ */
+ bool dsc;
+
+ /**
+ * @v_swings: Bitmask of voltage swing level supported
+ */
+ u8 v_swings;
+
+ /**
+ * @pre_emphs: Bitmask of pre-emphasis level supported
+ */
+ u8 pre_emphs;
+};
+
+/**
+ * struct drm_connector_dp_link_train - DRM DisplayPort link training
+ * information report
+ */
+struct drm_connector_dp_link_train {
+ /**
+ * @nlanes: The number of lanes used
+ */
+ u8 nlanes;
+
+ /**
+ * @rates: Link rate value selected in deca-kbps
+ */
+ u32 rate;
+
+ /**
+ * @dsc: Display Stream Compression enabled
+ */
+ bool dsc_en;
+
+ /**
+ * @v_swings: Array listing the bitmask voltage swing level per lanes
+ */
+ u8 v_swing[4];
+
+ /**
+ * @pre_emph: Array listing the bitmask pre-emphasis level per lanes
+ */
+ u8 pre_emph[4];
+};
+
+int drmm_connector_dp_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ const struct drm_connector_dp_link_train_caps *dp_link_train_caps,
+ int connector_type,
+ struct i2c_adapter *ddc);
+
+int
+drm_connector_dp_set_link_train_properties(struct drm_connector *con,
+ const struct drm_connector_dp_link_train *dp_link_train);
+
+void drm_connector_dp_reset_link_train_properties(struct drm_connector *connector);
+
+#endif // DRM_DP_CONNECTOR_H_
--
2.43.0
^ permalink raw reply related
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