Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v7 11/30] drm/display: bridge_connector: Wire up HDMI 2.0 scrambler callbacks
From: Maxime Ripard @ 2026-06-19 13:58 UTC (permalink / raw)
  To: Cristian Ciocaltea
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Sandy Huang,
	Heiko Stübner, Andy Yan, Daniel Stone, Dave Stevenson,
	Maíra Canal, Raspberry Pi Kernel Maintenance, kernel,
	dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip
In-Reply-To: <b14af457-0799-43cc-80b5-7ffff5901f24@collabora.com>

[-- Attachment #1: Type: text/plain, Size: 6479 bytes --]

On Fri, Jun 12, 2026 at 11:42:06PM +0300, Cristian Ciocaltea wrote:
> On 6/12/26 11:52 AM, Maxime Ripard wrote:
> > On Tue, Jun 02, 2026 at 01:44:11AM +0300, Cristian Ciocaltea wrote:
> >> Connect the bridge connector's .scrambler_{enable|disable} callbacks to
> >> the underlying bridge's .hdmi_scrambler_{enable|disable} funcs when
> >> DRM_BRIDGE_OP_HDMI_SCRAMBLER is advertised.
> >>
> >> This completes the bridge connector plumbing so that the SCDC
> >> scrambling helpers can control source-side scrambling through the
> >> bridge chain.
> >>
> >> Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
> >> ---
> >>  drivers/gpu/drm/display/drm_bridge_connector.c | 41 +++++++++++++++++++++++++-
> >>  1 file changed, 40 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
> >> index 9d21b1b57b0d..d048ab49eade 100644
> >> --- a/drivers/gpu/drm/display/drm_bridge_connector.c
> >> +++ b/drivers/gpu/drm/display/drm_bridge_connector.c
> >> @@ -555,6 +555,32 @@ static int drm_bridge_connector_write_spd_infoframe(struct drm_connector *connec
> >>  	return bridge->funcs->hdmi_write_spd_infoframe(bridge, buffer, len);
> >>  }
> >>  
> >> +static int drm_bridge_connector_scrambler_enable(struct drm_connector *connector)
> >> +{
> >> +	struct drm_bridge_connector *bridge_connector =
> >> +		to_drm_bridge_connector(connector);
> >> +	struct drm_bridge *bridge;
> >> +
> >> +	bridge = bridge_connector->bridge_hdmi;
> >> +	if (!bridge)
> >> +		return -EINVAL;
> >> +
> >> +	return bridge->funcs->hdmi_scrambler_enable(bridge);
> >> +}
> >> +
> >> +static int drm_bridge_connector_scrambler_disable(struct drm_connector *connector)
> >> +{
> >> +	struct drm_bridge_connector *bridge_connector =
> >> +		to_drm_bridge_connector(connector);
> >> +	struct drm_bridge *bridge;
> >> +
> >> +	bridge = bridge_connector->bridge_hdmi;
> >> +	if (!bridge)
> >> +		return -EINVAL;
> >> +
> >> +	return bridge->funcs->hdmi_scrambler_disable(bridge);
> >> +}
> >> +
> >>  static const struct drm_edid *
> >>  drm_bridge_connector_read_edid(struct drm_connector *connector)
> >>  {
> >> @@ -580,7 +606,7 @@ static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = {
> >>  		.clear_infoframe = drm_bridge_connector_clear_hdmi_infoframe,
> >>  		.write_infoframe = drm_bridge_connector_write_hdmi_infoframe,
> >>  	},
> >> -	/* audio, hdr_drm and spd are set dynamically during init */
> >> +	/* scrambler, audio, hdr_drm and spd are set dynamically during init */
> >>  };
> >>  
> >>  static const struct drm_connector_infoframe_funcs drm_bridge_connector_hdmi_audio_infoframe = {
> >> @@ -886,6 +912,11 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
> >>  			     !bridge->funcs->hdmi_clear_spd_infoframe))
> >>  				return ERR_PTR(-EINVAL);
> >>  
> >> +			if (bridge->ops & DRM_BRIDGE_OP_HDMI_SCRAMBLER &&
> >> +			    (!bridge->funcs->hdmi_scrambler_enable ||
> >> +			     !bridge->funcs->hdmi_scrambler_disable))
> >> +				return ERR_PTR(-EINVAL);
> >> +
> >>  			bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
> >>  
> >>  			if (bridge->supported_formats)
> >> @@ -990,6 +1021,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
> >>  			bridge_connector->hdmi_funcs.spd =
> >>  				drm_bridge_connector_hdmi_spd_infoframe;
> >>  
> >> +		if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_SCRAMBLER) {
> >> +			bridge_connector->hdmi_funcs.scrambler_enable =
> >> +				drm_bridge_connector_scrambler_enable;
> >> +			bridge_connector->hdmi_funcs.scrambler_disable =
> >> +				drm_bridge_connector_scrambler_disable;
> >> +			connector->hdmi.scrambler_supported = true;
> >> +		}
> >> +
> > 
> > I think we're taking this backwards. The scrambler support isn't
> > optional: either the controller supports HDMI < 2.0, and then it doesn't
> > exist, or it supports >= 2.0 and then it's mandatory.
> > 
> > You're considering it optional here, when it's never actually optional
> > (unlike YUV420 for example)
> > 
> > I still think we should list, somehow, the capabilities of the
> > controller to the helpers, like max tmds rate supported, formats, etc.
> > We've so far put everything as an argument to drmm_connector_hdmi_init
> > but it becomes a bit overloaded, and I wonder if introducing a callback
> > wouldn't solve this, kind of like what we have for planes and formats.
> 
> 
> What about introducing something like:
> 
> struct drm_connector_hdmi_caps {
>     ...
>     unsigned int supported_formats;
>     enum hdmi_version supported_hdmi_ver;
> };
> 
> struct drm_connector_hdmi_funcs {
>     ...
>     int (*get_caps)(struct drm_connector *connector,
>                     struct drm_connector_hdmi_caps *caps);
>     ...
> };
> 
> int drmm_connector_hdmi_init(struct drm_device *dev, ...)
> {
>     ...
>     if (hdmi_funcs->get_caps) {
>         struct drm_connector_hdmi_caps caps = { };
> 
>         ret = hdmi_funcs->get_caps(connector, &caps);
>         if (ret)
>             return ret;
> 
>         connector->hdmi.supported_formats  = caps.supported_formats;
>         ...
>         if (caps.supported_hdmi_ver > HDMI_2_0)
>             connector->hdmi.frl_supported = true;
>         else if (caps.supported_hdmi_ver == HDMI_2_0)
>             connector->hdmi.scrambler_supported = true;
>     }
>     ...
> }

That's what I initially had in mind, but it feels a bit over-the-top
when looking at it. I think I'd really like something that drivers
cannot forget about, screw up and/or mess with, so for example report
HDMI 2.1 but force disable FRL support, or report HDMI 1.4 but support
YUV420.

Adding more arguments to drm_connector_hdmi_init could work I guess, but
it won't scale to everything we need. Expecting the callers to fill
drm_connector_hdmi won't work either. So I somewhat think a get_caps
like we discussed is the less bad solution, but I'm definitely open to
suggestions.

> Not sure if max_tmds_char_rate should be listed as a capability, as we already
> have the .tmds_char_rate_valid() callback.

I guess it would make sense to move it there and consolidate
atomic_check / mode_valid checks, but I don't think it should be a
prerequisite for this patch either.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply

* Re: [PATCH v4 0/4] arm64: cross-CPU NMI via SDEI
From: Catalin Marinas @ 2026-06-19 14:00 UTC (permalink / raw)
  To: Kiryl Shutsemau
  Cc: Will Deacon, James Morse, Mark Rutland, Marc Zyngier,
	Doug Anderson, Petr Mladek, Thomas Gleixner, Andrew Morton,
	Baoquan He, Puranjay Mohan, Usama Arif, Breno Leitao,
	Julien Thierry, Lecopzer Chen, Sumit Garg, kernel-team, kexec,
	linux-arm-kernel, linux-kernel, Kiryl Shutsemau (Meta)
In-Reply-To: <cover.1781709543.git.kas@kernel.org>

Hi Kiryl,

On Wed, Jun 17, 2026 at 08:20:01PM +0100, Kiryl Shutsemau wrote:
>   - GICv3 pseudo-NMI (interrupt priority masking). Its cost is on the
>     interrupt mask/unmask hot path: local_irq_enable() becomes an
>     ICC_PMR_EL1 write plus a synchronising barrier, and exception
>     entry/exit save and restore the PMR, paid on every CPU whether or not
>     an NMI is ever delivered. In our measurements, enabling pseudo-NMI
>     costs up to ~5% on real workloads, and ~66% on a syscall-in-a-loop
>     microbenchmark. A fleet-wide ~5% regression is not acceptable, so
>     these systems run with pseudo-NMI disabled.

Does your firmware set ICC_CTLR_EL1.PMHE? I'd be curious to see the
numbers if the DSB was omitted on the enable path.

> This series adds a third delivery backend that costs nothing on the hot
> path: SDEI. Firmware delivers an SDEI event into a CPU regardless of its
> DAIF state, so interrupt masking stays the cheap PSTATE.DAIF operation and
> the firmware round-trip is paid only at the rare moment a CPU must be
> interrupted.

The direction of travel is to deprecated SDEI. I wouldn't add more stuff
on top of this interface.

(I haven't looked at the patches yet; Marc/Mark/James are more
knowledgeable than me in this area)

-- 
Catalin


^ permalink raw reply

* Re: [PATCH v2 2/5] docs: media: add documentation for media client usage stats
From: Nicolas Dufresne @ 2026-06-19 14:04 UTC (permalink / raw)
  To: Hans Verkuil, Detlev Casanova, Mauro Carvalho Chehab,
	Benjamin Gaignard, Philipp Zabel, Ezequiel Garcia, Heiko Stuebner
  Cc: linux-media, linux-kernel, linux-rockchip, kernel,
	linux-arm-kernel, Christopher Healy
In-Reply-To: <755cf7c1-6bcc-45d1-afea-192d393256af@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 9829 bytes --]

Hi,

Le vendredi 19 juin 2026 à 14:58 +0200, Hans Verkuil a écrit :
> Hi Detlev,
> 
> Interesting, I had never heard of fdinfo, so if nothing else, I learned something new!
> 
> On 17/06/2026 20:10, Detlev Casanova wrote:
> > From: Christopher Healy <healych@amazon.com>
> > 
> > Document the media fdinfo interface for per-file-descriptor usage
> > statistics exposed by stateless V4L2 codec drivers via
> > /proc/<pid>/fdinfo/<fd>.
> > 
> > This interface is designed for stateless (request API based) codec
> > devices where the kernel driver has per-job visibility into hardware
> > execution. Stateful codecs cannot support all of this because their
> > firmware manages job scheduling opaquely.
> > 
> > The specification defines media- prefixed keys for engine utilization
> > time, and operating frequency, following the same conventions as the DRM
> > fdinfo mechanism documented in drm-usage-stats.rst.
> > 
> > More fields can be added later.
> > 
> > Signed-off-by: Christopher Healy <healych@amazon.com>
> > Signed-off-by: Detlev Casanova <detlev.casanova@collabora.com>
> > ---
> >  .../userspace-api/media/drivers/index.rst          |  1 +
> >  .../media/drivers/media-usage-stats.rst            | 85 ++++++++++++++++++++++
> >  2 files changed, 86 insertions(+)
> > 
> > diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst
> > index 02967c9b18d6..61879738836c 100644
> > --- a/Documentation/userspace-api/media/drivers/index.rst
> > +++ b/Documentation/userspace-api/media/drivers/index.rst
> > @@ -34,6 +34,7 @@ For more details see the file COPYING in the source distribution of Linux.
> >  	imx-uapi
> >  	mali-c55
> >  	max2175
> > +	media-usage-stats
> >  	npcm-video
> >  	omap3isp-uapi
> >  	thp7312
> > diff --git a/Documentation/userspace-api/media/drivers/media-usage-stats.rst b/Documentation/userspace-api/media/drivers/media-usage-stats.rst
> > new file mode 100644
> > index 000000000000..d3dc07002f62
> > --- /dev/null
> > +++ b/Documentation/userspace-api/media/drivers/media-usage-stats.rst
> > @@ -0,0 +1,85 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +.. _media-usage-stats:
> > +
> > +==========================
> > +Media client usage stats
> 
> stats -> statistics
> 
> But are these really statistics? Isn't it just the current status?
> In many ways this feature looks to me similar to what VIDIOC_LOG_STATUS does,
> except in a nicer format. When VIDIOC_LOG_STATUS was first added, fdinfo
> didn't exist yet.
> 
> And I wouldn't call it 'Media client': it's specific to stateless V4L2
> codec drivers.

While it is currently implemented for sateless m2m codec drivers as example
(because we can't implement this everywhere at once really, its not practical),
there is no reason why we cannot track to a process fdinfo memory usage
associated with other video devices. In fact, I would pretty much want to see
capture devices showing up in v4l2top, as these are using a lot of memory too,
specially the new camera pipelines. And this is also perfectly suitable for
stateful codec, converters, everything.

So please, let's agree this is not "stateless V4L2 codec drivers" specific.

About VIDIOC_LOG_STATUS, this is effectively broken for m2m, only the owning
session process could call that. Its also pretty bad for tools to have to
monitor the dmesg and filter it up. fdinfo on the other end, does not require
opening the device and permissions are very clear, and bound to user process.

> 
> > +==========================
> > +
> > +Stateless V4L2 codec drivers can optionally expose per-file-descriptor usage
> > +statistics via ``/proc/<pid>/fdinfo/<fd>``. This is analogous to the DRM fdinfo
> > +mechanism documented in :ref:`drm-client-usage-stats`, but uses the ``media-``
> > +key prefix for V4L2 media devices.
> > +
> > +This interface is specific to stateless (request API based) codec devices,
> > +including both decoders and encoders. With stateless codecs, the kernel driver
> > +explicitly submits each frame to the hardware and receives a completion
> > +interrupt, providing a clean per-job boundary that can be attributed to the
> > +submitting file descriptor.
> > +
> > +Stateful codec devices cannot support this interface because their firmware
> > +manages job scheduling internally. The kernel driver submits bitstream data
> > +but has no visibility into per-frame hardware execution timing.
> > +
> > +Implementation
> > +==============
> > +
> > +The V4L2 core provides the plumbing: drivers implement the ``show_fdinfo``
> > +callback in ``struct v4l2_file_operations``, and the core wires it into the
> > +kernel ``struct file_operations`` so that ``/proc/<pid>/fdinfo/<fd>`` output
> > +includes the driver-provided keys.
> > +
> > +File format specification
> > +=========================
> > +
> > +- File shall contain one key value pair per one line of text.
> > +- Colon character (``:``) must be used to delimit keys and values.
> > +- All standardised keys shall be prefixed with ``media-``.
> > +- Driver-specific keys shall be prefixed with ``driver_name-``.
> > +
> > +Mandatory keys
> > +--------------
> > +
> > +- media-driver: <valstr>
> 
> I'd pick 'v4l2-driver'. Since 'media-driver' is too generic for this
> since that encompasses also DVB/CEC/RC drivers.
> 
> > +
> > +  String shall contain the name of the media driver.
> 
> 'V4L2 stateless codec driver'

With that said, we can refine, but not in that direction.

> 
> > +
> > +- media-type: <valstr>
> 
> Poor name.
> 
> > +
> > +  String shall identify the type of media engine exposed through this file
> > +  descriptor. Standard values are ``decoder`` and ``encoder``.
> 
> I think I would use 'stateless-decoder' and 'stateless-encoder'. It's more
> specific than de/encoder since that can be stateful as well.
> 
> So I am missing the big picture here: right now this patch adds support for
> this for stateless codecs, but what happens in the future if this is also
> added for regular video capture devices, ISPs, etc.?

We should work further on the type system, then we'd simply have to extend it.
Different type of driver will provide different key/value pair. We should well
document which key are mandatory for the type (or for all type, like giving a
name for general identification purpose).

Of course, the current proposal is very torward processing cores, hence the one
to one match with what GPUs present in fdinfo today in mainline, such are
frequency, core utilization time, etc.

Though, DMA devices such as capture may expose more of a bandwidth kind of
information. I really don't want to make up too may cases, so owners of these
don't feel like this is mandated though. As we develop software using our
drivers, we should be able to figure-out what kind of things are important to
monitor. For CODECs, we have two majors things. a) core utilization (which can
be with a timer like this one, but ideally using cycle counters, or firmware
provided data). b) memory usage.

> 
> The naming here matters, it has to have some sort of scheme so it can be
> extended to other types of drivers. So a 'media' prefix is too generic,
> and it also looks like it refers to the /dev/mediaX device.
> 
> > +
> > +Utilization keys
> > +----------------
> > +
> > +- media-engine-usage: <uint> ns
> 
> 'Media Engine': very vague.
> 
> > +
> > +  Time in nanoseconds that the hardware engine spent busy processing work
> 
> Cumulative since creating the file handle? Or since the last read?
> 
> > +  belonging to this file descriptor. The engine being measured is identified
> > +  by the ``media-type`` key.
> > +
> > +  Values are not required to be constantly monotonic if it makes the driver
> > +  implementation easier, but are required to catch up with the previously
> > +  reported larger value within a reasonable period.
> 
> Does this make sense for codecs?
> 
> I can tell that this is heavily influenced by the drm documentation, but that
> does not necessarily translate to V4L2.

I would guess this is bound to usage of cycle counter, which we definitely have
in some codec HW exposing. As the frequency fluctuate, I suppose there is ways
you can get your calculation a bit off, as this is being sampled. Basically, the
frequence change is not strictly tracked by our drivers, which is ok. But I also
don't seen why we wouldn't keep the counters monotonic, its really easy to do.

> 
> > +
> > +Frequency keys
> > +--------------
> > +
> > +- media-maxfreq: <uint> Hz
> > +
> > +  Maximum operating frequency of the main engine clock.
> > +
> > +- media-curfreq: <uint> Hz
> > +
> > +  Current operating frequency of the main engine clock.
> 
> 'Main engine clock'?

The word "engine" seems indeed alien in this subsystem. It is referring to a
system where you open one devices, so you have one fdinfo, but multiple cores.
Each cores may be running at it owns frequency. But I think this is a little too
vague to my taste.

Hope this feedback does not cross to many wires, but tagging this as specific to
stateless codec drivers did spark a little in my head. And we are really in need
for a way to monitor our drivers.

Nicolas

> 
> > +
> > +Example output
> > +==============
> > +
> > +::
> > +
> > +  media-driver:           hantro-vpu
> > +  media-type:             decoder
> > +  media-engine-usage:     123456789 ns
> > +  media-maxfreq:          600000000 Hz
> > +  media-curfreq:          600000000 Hz
> > 
> 
> Regards,
> 
> 	Hans

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH v6 00/20] dma-mapping: Use DMA_ATTR_CC_SHARED through direct, pool and swiotlb paths
From: Jason Gunthorpe @ 2026-06-19 14:06 UTC (permalink / raw)
  To: Aneesh Kumar K.V
  Cc: Alexey Kardashevskiy, Catalin Marinas, iommu, linux-arm-kernel,
	linux-kernel, linux-coco, Robin Murphy, Marek Szyprowski,
	Will Deacon, Marc Zyngier, Steven Price, Suzuki K Poulose,
	Jiri Pirko, Mostafa Saleh, Petr Tesarik, Dan Williams, Xu Yilun,
	linuxppc-dev, linux-s390, Madhavan Srinivasan, Michael Ellerman,
	Nicholas Piggin, Christophe Leroy (CS GROUP), Alexander Gordeev,
	Gerald Schaefer, Heiko Carstens, Vasily Gorbik,
	Christian Borntraeger, Sven Schnelle, x86
In-Reply-To: <yq5aldcaejos.fsf@kernel.org>

On Fri, Jun 19, 2026 at 02:36:19PM +0100, Aneesh Kumar K.V wrote:
> >> Agreed. If the device can do encrypted DMA and requires bouncing, it
> >> should bounce through encrypted pools. We don't support encrypted pools
> >> now and that means, we mark the option ("mem_encrypt=on iommu=pt
> >> swiotlb=force") not supported for now? 
> >
> > ?? if you don't have a CC system then the swiotlb is "encrypted"
> > meaning ordinary struct page system memory.
> >
> > The hypervisor should not be triggering any CC special stuff here, it
> > is not a CC guest.
> >
> > Agree we don't need to worry about swiotlb=force with a trusted device
> > in the GUEST for now, but it should be something to fix eventually.
> >
> 
> If i understand this correctly, the setup Alexey is referring to here is
> bare metal system with memory encryption enabled and dma address doesn't
> need C bit cleared because it is handled in iommu.

This is how I understand it too, if the iommu is turned on then it can
take the high PA with the C bit set and map it to an IOVA that matches
the device's dma limit.

> ( I consider this as memory encryption that is handled
> transparently, device can access any address because that encryption
> details are now managed by iommu).

Compared to the guest side there are some important host side differences:

 - On the host the iommu can fix it because this is only a matter of
   IOVA range not access control. On a guest even a IOMMU cannot
   permit access to private memory
 - On the host the state of the device is driven by the dma limit
   which is not set until after the driver probes. On guest the state is
   set by the tsm and device security level before the driver
   probes
 - Both flows end up using pgprot_decrypted and set_memory_decrypted()
   to create their special pools, but for completely different
   reasons.
 - The memory coming from the special swiotlb pool must NOT be used by
   a trusted device on a CC guest, while there is no problem for any
   device to use it on the host.

> Thinking about this more, I guess we should mark the swiotlb as
> cc_shared only with  CC_ATTR_GUEST_MEM_ENCRYPT instead of
> CC_ATTR_MEM_ENCRYPT as we have below.

The name cc_shared should be used for GUEST scenarios only.

I guess there is some merit in keeping swiotlb using "decrypted" to
mean it usinig pgprot_decrypted and set_memory_decyped() which AMD
gives meaning to on both host and guest.

IDK what AMD should do on the host by default. I guess it should setup
a swiotlb pool of low dma addrs "unencrypted", but not "cc_shared"?

But if we are operating on the host then this pool is not limited to
only T=0 devices, every device can "safely" use it. (ignoring this
destroys the security memory encryption on bare metal was supposed to
provide)

> Now we have the case of host memory encryption where the C-bit needs to
> be cleared in dma_addr_t. That requires special handling in the kernel, and
> I believe we need to mark swiotlb as unencrypted in this configuration.

I think we need to split the two things up, they have different
behaviors and need different flags and labels to make it all work
right.

> I am still not clear whether there is a config option or runtime check
> we can use to identify this case.

The dma api has to detect, after the driver sets the dma limit, that
none of system memory is usable when:
 - The direct path is being used
 - phys to dma for 0 is outside the dma limit

Then it should assume the arch has setup a swiotlb pool for it to use
to fix the high memory problem.

Similar hackery would be needed in the dma alloc path to know that
decrypted can be used to fix the high memory problem like for GUEST.

I guess some 'dev_cannot_reach_memory(dev)' sort of test in a
few key places? Setup with a static branch to be a nop on everything
but AMD, compiled out on every other arch.

Jason


^ permalink raw reply

* [PATCH RFC v2 0/4] Add support for DisplayPort link training information report
From: Kory Maincent @ 2026-06-19 14:08 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
	Tvrtko Ursulin, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	AngeloGioacchino Del Regno, Dmitry Baryshkov, Daniel Stone
  Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Manasi Navare,
	Drew Davenport, Louis Chauvet, Luca Ceresoli, dri-devel,
	linux-kernel, intel-gfx, intel-xe, linux-mediatek,
	linux-arm-kernel, Kory Maincent

DisplayPort link training negotiates the physical-layer parameters needed
for a reliable connection: lane count, link rate, 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 generic, managed and unmanaged DisplayPort
connector initialization helpers, for exposing DP link capabilities and
state as standard sysfs entries, modeled after the existing HDMI helper
drmm_connector_hdmi_init().

The aim of such development is to guide users to select the most suitable
DisplayPort connector for their needs. For example, if you have a USB-C
hub with lesser capabilities than your computer’s native DisplayPort
connector (such as HBR2 versus HBR3 support), the system could recommend
connecting high-resolution displays directly to the computer’s port
instead of through the hub to ensure optimal performance.

These new drmm_connector_dp_init() and drm_connector_dp_init_with_ddc()
helpers initialize a DP connector and expose link training capabilities
and state to userspace via sysfs attributes under dp_link.

Additional helpers are provided to manage link capabilities and parameters
at runtime.

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 changes updating the i915 driver to use DRM managed resources have been
removed due to cleanup path issues. The core problem is that some functions
do not consistently propagate errors through their call paths (whether this
is intentional or not) making it difficult to properly handle cleanup of
DRM objects (planes, encoders, connectors). A potential solution would be
to implement something similar to devres_group for each DRM object type,
but this represents a substantial undertaking that falls outside the scope
of this patch series.

The MST case in i915 driver is not supported yet.

Patch 1: Introduce the core drmm_connector_dp_init() framework
Patch 2: Wire the i915 DP connector to use the new helpers
Patch 3: Introduce DRM_BRIDGE_OP_DP and wire bridge connectors
Patch 4: Wire the MediaTek DP bridge to the new helpers [untested]

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
Changes in v2:
- Removed work converting i915 to DRM managed resource
- Remove voltage swing and pre-emphasis properties
- Expose link training state via sysfs dp_link/ group instead of
  connector properties
- Add comprehensive sysfs attributes for both source and sink capabilities
- Add new helpers for managing sink capabilities and for current link
  parameters
- Link to v1: https://lore.kernel.org/r/20260409-feat_link_cap-v1-0-7069e8199ce2@bootlin.com

---
Kory Maincent (4):
      drm: Introduce DisplayPort connector helpers with link training state
      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/display/drm_bridge_connector.c     |  24 ++++
 drivers/gpu/drm/display/drm_dp_helper.c            | 144 +++++++++++++++++++++
 drivers/gpu/drm/drm_connector.c                    | 122 +++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c                        | 100 ++++++++++++++
 drivers/gpu/drm/i915/display/intel_dp.c            |  26 +++-
 .../gpu/drm/i915/display/intel_dp_link_training.c  |  17 +++
 drivers/gpu/drm/mediatek/mtk_dp.c                  |  23 ++++
 include/drm/display/drm_dp_helper.h                |   7 +
 include/drm/drm_bridge.h                           |  13 ++
 include/drm/drm_connector.h                        | 105 +++++++++++++++
 10 files changed, 577 insertions(+), 4 deletions(-)
---
base-commit: 4d75f8bd845c10f126e0e66bcdd264e1f9772bde
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 v2 2/4] drm/i915/display/dp: Adopt dp_connector helpers to expose link training state
From: Kory Maincent @ 2026-06-19 14:08 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
	Tvrtko Ursulin, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	AngeloGioacchino Del Regno, Dmitry Baryshkov, Daniel Stone
  Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Manasi Navare,
	Drew Davenport, Louis Chauvet, Luca Ceresoli, dri-devel,
	linux-kernel, intel-gfx, intel-xe, linux-mediatek,
	linux-arm-kernel, Kory Maincent
In-Reply-To: <20260619-feat_link_cap-v2-0-a3dec4c02ad9@bootlin.com>

Switch the i915 DP connector initialization from
drm_connector_init_with_ddc() to drm_connector_dp_init_with_ddc(),
providing the source link capabilities (supported lane counts, link rates
and DSC support).

Add intel_dp_report_link_train() to collect the negotiated link
parameters (rate, lane count and DSC enable) and report them via
drm_dp_set_max_link_params() and drm_dp_set_cur_link_params() once
link training completes successfully.

Reset the link properties via drm_dp_reset_link_params()
when the connector is reported as disconnected or when the display device
is disabled, so the exposed state always reflects the current link status.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---

Changes in v2:
- Remove voltage swing and pre emphasis properties.
---
 drivers/gpu/drm/i915/display/intel_dp.c            | 26 ++++++++++++++++++----
 .../gpu/drm/i915/display/intel_dp_link_training.c  | 17 ++++++++++++++
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index f01a6eed38395..46c06c76952e0 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -6414,8 +6414,10 @@ intel_dp_detect(struct drm_connector *_connector,
 	drm_WARN_ON(display->drm,
 		    !drm_modeset_is_locked(&display->drm->mode_config.connection_mutex));
 
-	if (!intel_display_device_enabled(display))
+	if (!intel_display_device_enabled(display)) {
+		drm_dp_sink_reset_link_caps(_connector);
 		return connector_status_disconnected;
+	}
 
 	if (!intel_display_driver_check_access(display))
 		return connector->base.status;
@@ -6465,6 +6467,8 @@ intel_dp_detect(struct drm_connector *_connector,
 
 		intel_dp_tunnel_disconnect(intel_dp);
 
+		drm_dp_sink_reset_link_caps(_connector);
+
 		goto out_unset_edid;
 	}
 
@@ -7240,10 +7244,12 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
 			struct intel_connector *connector)
 {
 	struct intel_display *display = to_intel_display(dig_port);
+	struct drm_connector_dp_link_caps link_caps;
 	struct intel_dp *intel_dp = &dig_port->dp;
 	struct intel_encoder *encoder = &dig_port->base;
 	struct drm_device *dev = encoder->base.dev;
 	enum port port = encoder->port;
+	u32 *rates;
 	int type;
 
 	if (drm_WARN(dev, dig_port->max_lanes < 1,
@@ -7291,8 +7297,21 @@ 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);
+	intel_dp_set_source_rates(intel_dp);
+	link_caps.nlanes = 4;
+	link_caps.nlink_rates = intel_dp->num_source_rates;
+	rates = kmemdup_array(intel_dp->source_rates, intel_dp->num_source_rates,
+			      sizeof(*rates), GFP_KERNEL);
+	if (!rates)
+		goto fail;
+
+	link_caps.link_rates = rates;
+	link_caps.dsc = HAS_DSC(display);
+
+	drm_connector_dp_init_with_ddc(dev, &connector->base, &intel_dp_connector_funcs,
+				       &link_caps, type, &intel_dp->aux.ddc);
+	kfree(rates);
+
 	drm_connector_helper_add(&connector->base, &intel_dp_connector_helper_funcs);
 
 	if (!HAS_GMCH(display) && DISPLAY_VER(display) < 12)
@@ -7315,7 +7334,6 @@ intel_dp_init_connector(struct intel_digital_port *dig_port,
 		goto fail;
 	}
 
-	intel_dp_set_source_rates(intel_dp);
 	intel_dp_set_common_rates(intel_dp);
 	intel_dp_reset_link_params(intel_dp);
 
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
index a26094223f780..25e0e957fe36d 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
@@ -1231,6 +1231,18 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp,
 	return sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION ? 1 : 0;
 }
 
+static void intel_dp_report_link_train(struct intel_dp *intel_dp)
+{
+	struct intel_connector *connector = intel_dp->attached_connector;
+
+	drm_dp_set_max_link_params(&connector->base, intel_dp->link_rate,
+				   intel_dp->lane_count);
+
+	drm_dp_set_cur_link_params(&connector->base, intel_dp->link_rate,
+				   intel_dp->lane_count,
+				   connector->dp.dsc_decompression_enabled);
+}
+
 /**
  * intel_dp_stop_link_train - stop link training
  * @intel_dp: DP struct
@@ -1259,6 +1271,9 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp,
 	intel_dp_program_link_training_pattern(intel_dp, crtc_state, DP_PHY_DPRX,
 					       DP_TRAINING_PATTERN_DISABLE);
 
+	if (!intel_dp->is_mst)
+		intel_dp_report_link_train(intel_dp);
+
 	if (intel_dp_is_uhbr(crtc_state)) {
 		ret = poll_timeout_us(ret = intel_dp_128b132b_intra_hop(intel_dp, crtc_state),
 				      ret == 0,
@@ -1772,6 +1787,8 @@ void intel_dp_start_link_train(struct intel_atomic_state *state,
 	 */
 	int lttpr_count;
 
+	drm_dp_sink_set_link_caps(&intel_dp->attached_connector->base, &intel_dp->aux);
+
 	intel_hpd_block(encoder);
 
 	lttpr_count = intel_dp_init_lttpr_and_dprx_caps(intel_dp);

-- 
2.43.0



^ permalink raw reply related

* [PATCH RFC v2 3/4] drm/bridge: Wire drmm_connector_dp_init() via new DRM_BRIDGE_OP_DP flag
From: Kory Maincent @ 2026-06-19 14:08 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
	Tvrtko Ursulin, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	AngeloGioacchino Del Regno, Dmitry Baryshkov, Daniel Stone
  Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Manasi Navare,
	Drew Davenport, Louis Chauvet, Luca Ceresoli, dri-devel,
	linux-kernel, intel-gfx, intel-xe, linux-mediatek,
	linux-arm-kernel, Kory Maincent
In-Reply-To: <20260619-feat_link_cap-v2-0-a3dec4c02ad9@bootlin.com>

Introduce DRM_BRIDGE_OP_DP, a new bridge operation flag for bridges
that provide DisplayPort connector operations with link training support.
Bridges advertising this flag must fill the dp_link_caps field in
struct drm_bridge with their link capabilities.

In drm_bridge_connector_init(), when a bridge sets DRM_BRIDGE_OP_DP,
use drmm_connector_dp_init() instead of the generic drmm_connector_init()
so the connector exposes link training state properties to userspace.
This mirrors the existing pattern used for HDMI bridges.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
 drivers/gpu/drm/display/drm_bridge_connector.c | 24 ++++++++++++++++++++++++
 include/drm/drm_bridge.h                       | 13 +++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index cafa498c38482..6ea4d45e3146b 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -108,6 +108,13 @@ struct drm_bridge_connector {
 	 * HDMI Audio infrastructure, if any (see &DRM_BRIDGE_OP_HDMI_AUDIO).
 	 */
 	struct drm_bridge *bridge_hdmi_audio;
+	/**
+	 * @bridge_dp:
+	 *
+	 * The bridge in the chain that implements necessary support for the
+	 * DisplayPort connector infrastructure, if any (see &DRM_BRIDGE_OP_DP).
+	 */
+	struct drm_bridge *bridge_dp;
 	/**
 	 * @bridge_dp_audio:
 	 *
@@ -773,6 +780,7 @@ static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
 	drm_bridge_put(bridge_connector->bridge_hdmi_audio);
 	drm_bridge_put(bridge_connector->bridge_dp_audio);
 	drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+	drm_bridge_put(bridge_connector->bridge_dp);
 }
 
 /**
@@ -908,6 +916,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge);
 		}
 
+		if (bridge->ops & DRM_BRIDGE_OP_DP) {
+			if (bridge_connector->bridge_dp)
+				return ERR_PTR(-EBUSY);
+			if (!bridge->dp_link_caps)
+				return ERR_PTR(-EINVAL);
+
+			bridge_connector->bridge_dp = drm_bridge_get(bridge);
+		}
+
 		if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
 			if (bridge_connector->bridge_dp_audio)
 				return ERR_PTR(-EBUSY);
@@ -996,6 +1013,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 					       max_bpc);
 		if (ret)
 			return ERR_PTR(ret);
+	} else if (bridge_connector->bridge_dp) {
+		ret = drmm_connector_dp_init(drm, connector,
+					     &drm_bridge_connector_funcs,
+					     bridge_connector->bridge_dp->dp_link_caps,
+					     connector_type, ddc);
+		if (ret)
+			return ERR_PTR(ret);
 	} else {
 		ret = drmm_connector_init(drm, connector,
 					  &drm_bridge_connector_funcs,
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 4ba3a5deef9a6..02411e0b71c35 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1092,6 +1092,14 @@ enum drm_bridge_ops {
 	 * &drm_bridge_funcs->hdmi_clear_spd_infoframe callbacks.
 	 */
 	DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME = BIT(10),
+	/**
+	 * @DRM_BRIDGE_OP_DP: The bridge provides DisplayPort connector
+	 * operations, including link training support. Bridges that set
+	 * this flag must provide DisplayPort-related information and
+	 * fill the &drm_bridge->dp_link_train_caps link training
+	 * capabilities.
+	 */
+	DRM_BRIDGE_OP_DP = BIT(11),
 };
 
 /**
@@ -1267,6 +1275,11 @@ struct drm_bridge {
 	 */
 	void *hpd_data;
 
+	/**
+	 * @dp_link_caps: DisplayPort link capabilities
+	 */
+	const struct drm_connector_dp_link_caps *dp_link_caps;
+
 	/**
 	 * @next_bridge: Pointer to the following bridge, automatically put
 	 * when this bridge is freed (i.e. at destroy time). This is for

-- 
2.43.0



^ permalink raw reply related

* [PATCH RFC v2 1/4] drm: Introduce DisplayPort connector helpers with link training state
From: Kory Maincent @ 2026-06-19 14:08 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
	Tvrtko Ursulin, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	AngeloGioacchino Del Regno, Dmitry Baryshkov, Daniel Stone
  Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Manasi Navare,
	Drew Davenport, Louis Chauvet, Luca Ceresoli, dri-devel,
	linux-kernel, intel-gfx, intel-xe, linux-mediatek,
	linux-arm-kernel, Kory Maincent
In-Reply-To: <20260619-feat_link_cap-v2-0-a3dec4c02ad9@bootlin.com>

Add managed and unmanaged DisplayPort connector initialization helpers,
drmm_connector_dp_init() and drm_connector_dp_init_with_ddc(), modeled
after the existing HDMI counterpart drmm_connector_hdmi_init().

These helpers initialize DP-specific connector state and expose link
training capabilities and state to userspace via sysfs attributes under
dp_link:

  - source_link_rates_caps: Array of source-supported link rates
  - source_max_lane_count_caps: Source maximum lane count capability
  - source_dsc_caps: Source Display Stream Compression support
  - sink_max_link_rate_caps: Sink maximum link rate capability
  - sink_max_lane_count_caps: Sink maximum lane count capability
  - sink_dsc_caps: Sink DSC support
  - cur_link_rate: Current negotiated link rate
  - cur_lane_count: Current negotiated lane count
  - dsc_en: DSC enabled in current link training
  - max_link_rate: Maximum achievable link rate (limited by source/sink)
  - max_lane_count: Maximum achievable lane count (limited by source/sink)

Link rates are passed by the driver in deca-kbps, following the DRM
convention, but exposed to userspace in kbps for clarity.

Additional helpers are provided to manage link capabilities and parameters
at runtime:
  - drm_dp_sink_set_link_caps(): Set sink capabilities after DPCD read
  - drm_dp_sink_reset_link_caps(): Reset sink capabilities on disconnect
  - drm_dp_set_cur_link_params(): Update current link training parameters
  - drm_dp_set_max_link_params(): Update maximum achievable parameters

The aim of such development is to guide users to select the most suitable
DisplayPort connector for their needs. For example, if you have a USB-C
hub with lesser capabilities than your computer’s native DisplayPort
connector (such as HBR2 versus HBR3 support), the system could recommend
connecting high-resolution displays directly to the computer’s port
instead of through the hub to ensure optimal performance.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---

Changes in v2:
- Remove voltage swing and pre-emphasis properties
- Expose link training state via sysfs dp_link/ group instead of
  connector properties
- Rename variables from link_train to link as they relate directly to
  the link capabilities
- Add comprehensive sysfs attributes for both source and sink capabilities
- Add separate helpers for managing sink capabilities and for current and
  maximum link parameters
---
 drivers/gpu/drm/display/drm_dp_helper.c | 144 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_connector.c         | 122 +++++++++++++++++++++++++++
 drivers/gpu/drm/drm_sysfs.c             | 100 ++++++++++++++++++++++
 include/drm/display/drm_dp_helper.h     |   7 ++
 include/drm/drm_connector.h             | 105 +++++++++++++++++++++++
 5 files changed, 478 insertions(+)

diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index 9c31e14cc413b..bd0e1eb657412 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -4900,3 +4900,147 @@ int drm_dp_max_dprx_data_rate(int max_link_rate, int max_lanes)
 				  1000000 * 8);
 }
 EXPORT_SYMBOL(drm_dp_max_dprx_data_rate);
+
+static int drm_dp_dpcd_read_link_rate_caps(struct drm_dp_aux *aux)
+{
+	u8 data;
+	int ret;
+
+	ret = drm_dp_dpcd_read_byte(aux, DP_DP13_DPCD_REV + DP_MAIN_LINK_CHANNEL_CODING, &data);
+	if (ret < 0)
+		return ret;
+
+	if (data & DP_CAP_ANSI_128B132B) {
+		ret = drm_dp_dpcd_read_byte(aux, DP_128B132B_SUPPORTED_LINK_RATES, &data);
+		if (ret < 0)
+			return ret;
+
+		if (data & DP_UHBR20)
+			return 20000000;
+		if (data & DP_UHBR13_5)
+			return 13500000;
+		if (data & DP_UHBR10)
+			return 10000000;
+	}
+
+	ret = drm_dp_dpcd_read_byte(aux, DP_MAX_LINK_RATE, &data);
+	if (ret < 0)
+		return ret;
+
+	return data * 270000;
+}
+
+/**
+ * drm_dp_sink_set_link_caps - Set DisplayPort sink link capabilities
+ * @connector: DisplayPort connector
+ * @aux: The DP AUX channel to use
+ *
+ * This function sets the DisplayPort sink (monitor) link training capabilities
+ * for the given connector. These capabilities are typically read from the
+ * sink's DPCD registers during HPD processing.
+ */
+void drm_dp_sink_set_link_caps(struct drm_connector *connector,
+			       struct drm_dp_aux *aux)
+{
+	u32 lane_count, link_rate;
+	u8 data;
+	int ret;
+
+	if (!connector)
+		return;
+
+	WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex));
+
+	ret = drm_dp_dpcd_read_byte(aux, DP_MAX_LANE_COUNT, &data);
+	if (ret < 0)
+		return;
+
+	lane_count = data & DP_MAX_LANE_COUNT_MASK;
+
+	ret = drm_dp_dpcd_read_link_rate_caps(aux);
+	if (ret < 0)
+		return;
+
+	link_rate = ret;
+
+	ret = drm_dp_dpcd_read_byte(aux, DP_DSC_SUPPORT, &data);
+	if (ret < 0)
+		return;
+
+	connector->dp.sink_max_lane_count_caps = lane_count;
+	connector->dp.sink_max_link_rate_caps = link_rate;
+	connector->dp.sink_dsc_caps = ret & DP_DSC_DECOMPRESSION_IS_SUPPORTED;
+}
+EXPORT_SYMBOL_GPL(drm_dp_sink_set_link_caps);
+
+/**
+ * drm_dp_set_cur_link_params - Set current DisplayPort link parameters
+ * @connector: DisplayPort connector
+ * @link_rate: Current link rate in deca-kbps
+ * @lane_count: Current lane count
+ * @dsc_en: Display Stream Compression enabled
+ *
+ * This function sets the current active DisplayPort link parameters after
+ * link training has completed. These parameters represent the actual link
+ * configuration being used for display output.
+ */
+void drm_dp_set_cur_link_params(struct drm_connector *connector,
+				u32 link_rate, u32 lane_count, bool dsc_en)
+{
+	if (!connector)
+		return;
+
+	WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex));
+
+	/* Convert deca-kbps in kbps */
+	connector->dp.cur_link_rate = link_rate * 10;
+	connector->dp.cur_lane_count = lane_count;
+	connector->dp.dsc_en = dsc_en;
+}
+EXPORT_SYMBOL_GPL(drm_dp_set_cur_link_params);
+
+/**
+ * drm_dp_set_max_link_params - Set maximum DisplayPort link parameters
+ * @connector: DisplayPort connector
+ * @link_rate: Maximum link rate in kbps
+ * @lane_count: Maximum lane count
+ *
+ * This function sets the maximum achievable DisplayPort link parameters,
+ * which represent the intersection of source and sink capabilities. These
+ * values are the upper bounds for link training attempts.
+ */
+void drm_dp_set_max_link_params(struct drm_connector *connector, u32 link_rate,
+				u32 lane_count)
+{
+	if (!connector)
+		return;
+
+	WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex));
+
+	connector->dp.max_link_rate = link_rate;
+	connector->dp.max_lane_count = lane_count;
+}
+EXPORT_SYMBOL_GPL(drm_dp_set_max_link_params);
+
+/**
+ * drm_dp_sink_reset_link_caps - Reset DisplayPort sink capabilities
+ * @connector: DisplayPort connector
+ *
+ * This function resets all DisplayPort sink link capabilities and parameters
+ * to their default state. This should be called when a sink is disconnected
+ * to clear stale capability information.
+ */
+void drm_dp_sink_reset_link_caps(struct drm_connector *connector)
+{
+	if (!connector)
+		return;
+
+	WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex));
+
+	drm_dp_set_cur_link_params(connector, 0, 0, false);
+	drm_dp_set_max_link_params(connector, 0, 0);
+	connector->dp.sink_max_link_rate_caps = 0;
+	connector->dp.sink_max_lane_count_caps = 0;
+	connector->dp.sink_dsc_caps = false;
+}
+EXPORT_SYMBOL_GPL(drm_dp_sink_reset_link_caps);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index a5d13b92b665c..259af3240c057 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -489,6 +489,128 @@ int drm_connector_init_with_ddc(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_connector_init_with_ddc);
 
+static int drm_dp_source_set_link_caps(struct drm_connector *connector,
+				       const struct drm_connector_dp_link_caps *link_caps)
+{
+	u32 *_link_rates;
+
+	_link_rates = devm_kmemdup_array(connector->dev->dev,
+					 link_caps->link_rates,
+					 link_caps->nlink_rates,
+					 sizeof(*link_caps->link_rates),
+					 GFP_KERNEL);
+	if (!_link_rates)
+		return -ENOMEM;
+
+	for (int i = 0; i < link_caps->nlink_rates; i++)
+		/* Convert deca-kbps in kbps */
+		_link_rates[i] *= 10;
+
+	connector->dp.source_link_rates_caps = _link_rates;
+	connector->dp.source_num_link_rates_caps = link_caps->nlink_rates;
+	connector->dp.source_max_lane_count_caps = link_caps->nlanes;
+	connector->dp.source_dsc_caps = link_caps->dsc;
+
+	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_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_caps *dp_link_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_caps)
+		return -EINVAL;
+
+	ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc);
+	if (ret)
+		return ret;
+
+	return drm_dp_source_set_link_caps(connector, dp_link_caps);
+}
+EXPORT_SYMBOL(drmm_connector_dp_init);
+
+/**
+ * drm_connector_dp_init_with_ddc - Init a preallocated DisplayPort connector
+ * @dev: DRM device
+ * @connector: A pointer to the DisplayPort connector to init
+ * @funcs: callbacks for this connector
+ * @dp_link_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 connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ *
+ * At driver unload time the driver's &drm_connector_funcs.destroy hook
+ * should call drm_connector_cleanup() and free the connector structure.
+ * The connector structure should not be allocated with devm_kzalloc().
+ *
+ * Ensures that the ddc field of the connector is correctly set.
+ *
+ * Note: consider using drmm_connector_dp_init() instead of
+ * drm_connector_dp_init_with_ddc() to let the DRM managed resource
+ * infrastructure take care of cleanup and deallocation.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_dp_init_with_ddc(struct drm_device *dev,
+				   struct drm_connector *connector,
+				   const struct drm_connector_funcs *funcs,
+				   const struct drm_connector_dp_link_caps *dp_link_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_caps)
+		return -EINVAL;
+
+	ret = drm_connector_init_with_ddc(dev, connector, funcs, connector_type, ddc);
+	if (ret)
+		return ret;
+
+	return drm_dp_source_set_link_caps(connector, dp_link_caps);
+}
+EXPORT_SYMBOL(drm_connector_dp_init_with_ddc);
+
 static void drm_connector_cleanup_action(struct drm_device *dev,
 					 void *ptr)
 {
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index ef4e923a87284..653fecf23d717 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -340,6 +340,95 @@ static const struct attribute_group *connector_dev_groups[] = {
 	NULL
 };
 
+static ssize_t drm_link_rates_show(u32 num_link_rates, const u32 *link_rates, char *buf)
+{
+	int size = 0;
+
+	if (!num_link_rates)
+		return 0;
+
+	size += sysfs_emit_at(buf, size, "%d", link_rates[0]);
+	for (int i = 1; i < num_link_rates; i++)
+		size += sysfs_emit_at(buf, size, " %d", link_rates[i]);
+
+	size += sysfs_emit_at(buf, size, "\n");
+	return size;
+}
+
+static ssize_t source_link_rates_caps_show(struct device *device,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct drm_connector *connector = to_drm_connector(device);
+	ssize_t size;
+
+	drm_modeset_lock(&connector->dev->mode_config.connection_mutex, NULL);
+	size = drm_link_rates_show(connector->dp.source_num_link_rates_caps,
+				   connector->dp.source_link_rates_caps, buf);
+	drm_modeset_unlock(&connector->dev->mode_config.connection_mutex);
+	return size;
+}
+
+#define DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(_name) \
+static ssize_t _name##_show(struct device *device, \
+			    struct device_attribute *attr, \
+			    char *buf) \
+{ \
+	struct drm_connector *connector = to_drm_connector(device); \
+	int ret; \
+	drm_modeset_lock(&connector->dev->mode_config.connection_mutex, NULL); \
+	if (!connector->dp._name) { \
+		drm_modeset_unlock(&connector->dev->mode_config.connection_mutex); \
+		return 0; \
+	} \
+	ret = sysfs_emit(buf, "%d\n", connector->dp._name); \
+	drm_modeset_unlock(&connector->dev->mode_config.connection_mutex); \
+	return ret; \
+}
+
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(source_max_lane_count_caps);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(source_dsc_caps);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(sink_dsc_caps);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(sink_max_link_rate_caps);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(sink_max_lane_count_caps);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(cur_link_rate);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(cur_lane_count);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(dsc_en);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(max_link_rate);
+DRM_CONNECTOR_DP_ATTR_SHOW_SIMPLE(max_lane_count);
+
+static DEVICE_ATTR_RO(source_link_rates_caps);
+static DEVICE_ATTR_RO(source_max_lane_count_caps);
+static DEVICE_ATTR_RO(source_dsc_caps);
+static DEVICE_ATTR_RO(sink_max_link_rate_caps);
+static DEVICE_ATTR_RO(sink_max_lane_count_caps);
+static DEVICE_ATTR_RO(sink_dsc_caps);
+static DEVICE_ATTR_RO(cur_link_rate);
+static DEVICE_ATTR_RO(cur_lane_count);
+static DEVICE_ATTR_RO(dsc_en);
+static DEVICE_ATTR_RO(max_link_rate);
+static DEVICE_ATTR_RO(max_lane_count);
+
+static struct attribute *connector_dp_link_attrs[] = {
+	&dev_attr_source_link_rates_caps.attr,
+	&dev_attr_source_max_lane_count_caps.attr,
+	&dev_attr_source_dsc_caps.attr,
+	&dev_attr_sink_max_link_rate_caps.attr,
+	&dev_attr_sink_max_lane_count_caps.attr,
+	&dev_attr_sink_dsc_caps.attr,
+	&dev_attr_cur_link_rate.attr,
+	&dev_attr_cur_lane_count.attr,
+	&dev_attr_dsc_en.attr,
+	&dev_attr_max_link_rate.attr,
+	&dev_attr_max_lane_count.attr,
+	NULL
+};
+
+static const struct attribute_group connector_dp_link_group = {
+	.name = "dp_link",
+	.attrs = connector_dp_link_attrs,
+};
+
 int drm_sysfs_connector_add(struct drm_connector *connector)
 {
 	struct drm_device *dev = connector->dev;
@@ -376,6 +465,15 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
 
 	connector->kdev = kdev;
 
+	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+	    connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+		r = sysfs_create_group(&connector->kdev->kobj, &connector_dp_link_group);
+		if (r) {
+			drm_err(dev, "failed to create DP connector sysfs: %d\n", r);
+			goto err_dp_sysfs;
+		}
+	}
+
 	if (dev_fwnode(kdev)) {
 		r = component_add(kdev, &typec_connector_ops);
 		if (r)
@@ -384,6 +482,8 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
 
 	return 0;
 
+err_dp_sysfs:
+	device_del(kdev);
 err_free:
 	put_device(kdev);
 	return r;
diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h
index 8c2d77a032f06..e7620ecb2380a 100644
--- a/include/drm/display/drm_dp_helper.h
+++ b/include/drm/display/drm_dp_helper.h
@@ -1031,4 +1031,11 @@ ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc, struct dp_sdp *sdp
 int drm_dp_link_symbol_cycles(int lane_count, int pixels, int dsc_slice_count,
 			      int bpp_x16, int symbol_size, bool is_mst);
 
+void drm_dp_sink_set_link_caps(struct drm_connector *connector, struct drm_dp_aux *aux);
+void drm_dp_sink_reset_link_caps(struct drm_connector *connector);
+void drm_dp_set_cur_link_params(struct drm_connector *connector, u32 link_rate,
+				u32 lane_count, bool dsc_en);
+void drm_dp_set_max_link_params(struct drm_connector *connector, u32 link_rate,
+				u32 lane_count);
+
 #endif /* _DRM_DP_HELPER_H_ */
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 529755c2e8620..a08b8e9766abe 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2003,6 +2003,93 @@ struct drm_connector_cec {
 	void *data;
 };
 
+/**
+ * struct drm_connector_dp_link_caps - DRM DisplayPort link capabilities
+ */
+struct drm_connector_dp_link_caps {
+	/**
+	 * @nlanes: Maximum number of lanes number supported
+	 */
+	u8 nlanes;
+
+	/**
+	 * @nlink_rates: Number of link rates supported
+	 */
+	u32 nlink_rates;
+
+	/**
+	 * @link_rates: Array listing the supported link rates in deca-kbps
+	 */
+	const u32 *link_rates;
+
+	/**
+	 * @dsc: Display Stream Compression supported
+	 */
+	bool dsc;
+};
+
+/**
+ * struct drm_connector_dp - DRM Connector DisplayPort-related structure
+ */
+struct drm_connector_dp {
+	/**
+	 * @source_link_rates_caps: Array of supported link rates by the
+	 * source in kbps
+	 */
+	const u32 *source_link_rates_caps;
+	/**
+	 * @source_num_link_rates_caps: Number of link rates in
+	 * @source_link_rates_caps array
+	 */
+	u32 source_num_link_rates_caps;
+	/**
+	 * @source_max_lane_count_caps: Maximum number of lanes supported by
+	 * the source
+	 */
+	u32 source_max_lane_count_caps;
+	/**
+	 * @source_dsc_caps: Display Stream Compression capability of the
+	 * source
+	 */
+	bool source_dsc_caps;
+	/**
+	 * @sink_max_link_rate_caps: Maximum link rate supported by the sink
+	 * in kbps
+	 */
+	u32 sink_max_link_rate_caps;
+	/**
+	 * @sink_max_lane_count_caps: Maximum number of lanes supported by the
+	 * sink
+	 */
+	u32 sink_max_lane_count_caps;
+	/**
+	 * @sink_dsc_caps: Display Stream Compression capability of the sink
+	 */
+	bool sink_dsc_caps;
+	/**
+	 * @cur_link_rate: Current negotiated link rate in kbps
+	 */
+	u32 cur_link_rate;
+	/**
+	 * @cur_lane_count: Current negotiated number of lanes
+	 */
+	u32 cur_lane_count;
+	/**
+	 * @dsc_en: Display Stream Compression enabled status
+	 */
+	bool dsc_en;
+	/**
+	 * @max_link_rate: Maximum achievable link rate considering both
+	 * source and sink capabilities in deca-kbps
+	 */
+	u32 max_link_rate;
+	/**
+	 * @max_lane_count: Maximum achievable lane count considering both
+	 * source and sink capabilities
+	 */
+	u32 max_lane_count;
+};
+
 /**
  * struct drm_connector - central DRM connector control structure
  *
@@ -2426,6 +2513,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)
@@ -2458,6 +2550,19 @@ int drmm_connector_hdmi_init(struct drm_device *dev,
 			     struct i2c_adapter *ddc,
 			     unsigned long supported_formats,
 			     unsigned int max_bpc);
+int drm_connector_dp_init_with_ddc(struct drm_device *dev,
+				   struct drm_connector *connector,
+				   const struct drm_connector_funcs *funcs,
+				   const struct drm_connector_dp_link_caps *dp_link_caps,
+				   int connector_type,
+				   struct i2c_adapter *ddc);
+
+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_caps *dp_link_caps,
+			   int connector_type,
+			   struct i2c_adapter *ddc);
 void drm_connector_attach_edid_property(struct drm_connector *connector);
 int drm_connector_register(struct drm_connector *connector);
 int drm_connector_dynamic_register(struct drm_connector *connector);

-- 
2.43.0



^ permalink raw reply related

* [PATCH RFC v2 4/4] drm/mediatek: Use dp_connector helpers to report link training state
From: Kory Maincent @ 2026-06-19 14:08 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Jani Nikula, Rodrigo Vivi, Joonas Lahtinen,
	Tvrtko Ursulin, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
	Chun-Kuang Hu, Philipp Zabel, Matthias Brugger,
	AngeloGioacchino Del Regno, Dmitry Baryshkov, Daniel Stone
  Cc: Thomas Petazzoni, Mark Yacoub, Sean Paul, Manasi Navare,
	Drew Davenport, Louis Chauvet, Luca Ceresoli, dri-devel,
	linux-kernel, intel-gfx, intel-xe, linux-mediatek,
	linux-arm-kernel, Kory Maincent
In-Reply-To: <20260619-feat_link_cap-v2-0-a3dec4c02ad9@bootlin.com>

Set DRM_BRIDGE_OP_DP and populate dp_link_train_caps with the supported
link rates, lane counts, voltage swing and pre-emphasis levels so the
bridge connector uses drmm_connector_dp_init() and exposes the link
training state properties to userspace.

Store per-lane voltage swing and pre-emphasis values in
mtk_dp_train_info and report the negotiated link parameters via
drm_connector_dp_set_link_train_properties() on training completion.
Clear them via drm_connector_dp_reset_link_train_properties() when a
disconnect is detected in the HPD event thread.

Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---

This patch has still not been tested. I am in the process of finding the
hardware for that.

Changes in v2:
- Remove voltage swing and pre emphasis properties.
---
 drivers/gpu/drm/mediatek/mtk_dp.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index eefbc7e0f9c8d..c1e7086104116 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -1789,6 +1789,8 @@ static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
 		}
 	}
 
+	drm_dp_sink_set_link_caps(mtk_dp->conn, &mtk_dp->aux);
+
 	return 0;
 }
 
@@ -1812,6 +1814,17 @@ static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
 	mtk_dp_reset_swing_pre_emphasis(mtk_dp);
 }
 
+static void mtk_dp_report_link_train(struct mtk_dp *mtk_dp)
+{
+	u32 rate, nlanes;
+
+	rate = drm_dp_bw_code_to_link_rate(mtk_dp->train_info.link_rate);
+	nlanes = mtk_dp->train_info.lane_count;
+
+	drm_dp_set_cur_link_params(mtk_dp->conn, rate, nlanes, false);
+	drm_dp_set_max_link_params(mtk_dp->conn, rate, nlanes);
+}
+
 static int mtk_dp_training(struct mtk_dp *mtk_dp)
 {
 	int ret;
@@ -1892,6 +1905,7 @@ static int mtk_dp_training(struct mtk_dp *mtk_dp)
 	mtk_dp_training_set_scramble(mtk_dp, true);
 	mtk_dp_set_enhanced_frame_mode(mtk_dp);
 
+	mtk_dp_report_link_train(mtk_dp);
 	return 0;
 }
 
@@ -2004,6 +2018,7 @@ static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
 			mtk_dp->need_debounce = false;
 			mod_timer(&mtk_dp->debounce_timer,
 				  jiffies + msecs_to_jiffies(100) - 1);
+			drm_dp_sink_reset_link_caps(mtk_dp->conn);
 		} else {
 			mtk_dp_aux_panel_poweron(mtk_dp, true);
 
@@ -2742,6 +2757,12 @@ static int mtk_dp_edp_link_panel(struct drm_dp_aux *mtk_aux)
 
 static int mtk_dp_probe(struct platform_device *pdev)
 {
+	static const u32 dp_rates[] = {162000, 270000, 540000, 810000};
+	static const struct drm_connector_dp_link_caps dp_link_caps = {
+		.nlanes = 4,
+		.nlink_rates = ARRAY_SIZE(dp_rates),
+		.link_rates = dp_rates,
+	};
 	struct mtk_dp *mtk_dp;
 	struct device *dev = &pdev->dev;
 	int ret;
@@ -2809,6 +2830,8 @@ static int mtk_dp_probe(struct platform_device *pdev)
 
 	mtk_dp->bridge.of_node = dev->of_node;
 	mtk_dp->bridge.type = mtk_dp->data->bridge_type;
+	mtk_dp->bridge.dp_link_caps = &dp_link_caps;
+	mtk_dp->bridge.ops = DRM_BRIDGE_OP_DP;
 
 	if (mtk_dp->bridge.type == DRM_MODE_CONNECTOR_eDP) {
 		/*

-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH] arm64: uapi: Use __u128 instead of __uint128_t in UAPI headers
From: Marc Zyngier @ 2026-06-19 14:09 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, Arnd Bergmann, Nick Desaulniers, Steffen Eiden,
	Andreas Grapentin, Catalin Marinas, Dave Martin, Mark Rutland
In-Reply-To: <20260619130835.5678-1-will@kernel.org>

On Fri, 19 Jun 2026 14:08:34 +0100,
Will Deacon <will@kernel.org> wrote:
> 
> The arm64 UAPI exposes '__uint128_t' types in the members of
> 'struct user_fpsimd_state', 'struct user_pac_address_keys' and in the
> signal frame via 'struct fpsimd_context'. Since the alignment of such
> a type appears to be non-portable (16 bytes on arm64, 8 bytes on s390),
> prefer the '__u128' typedef from uapi/linux/types.h, which makes the
> alignment explicit and allows the definitions to be reused by other
> host architectures.
> 
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
> Cc: Steffen Eiden <seiden@linux.ibm.com>
> Cc: Andreas Grapentin <gra@linux.ibm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Dave Martin <dave.martin@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Marc Zyngier <maz@kernel.org>
> Signed-off-by: Will Deacon <will@kernel.org>

FWIW:

Reviewed-by: Marc Zyngier <maz@kernel.org>

	M.

-- 
Without deviation from the norm, progress is not possible.


^ permalink raw reply

* Re: [PATCH v5 1/1] arm64: dts: add tqma9596la-mba95xxca
From: Frank Li @ 2026-06-19 14:10 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Geert Uytterhoeven, Magnus Damm, Markus Niebel, imx,
	linux-arm-kernel, devicetree, linux-kernel, linux,
	linux-renesas-soc
In-Reply-To: <20260619112434.1121610-1-alexander.stein@ew.tq-group.com>

On Fri, Jun 19, 2026 at 01:24:30PM +0200, Alexander Stein wrote:
> From: Markus Niebel <Markus.Niebel@ew.tq-group.com>
>
> This adds support for TQMa95xxLA modules, designed to be soldered
> on a carrier board. MBa95xxCA is a carrier reference board / starter kit
> design.
>
> There is a common device tree for all variants with e.g. reduced
> CPU core / feature count.
>
> Enable the external accessible PCIe controllers as host,
> add clocking and reset GPIO. While at it, add hogs for GPIO
> lines from the M.2 slots until M.2 connector driver is available.
>
> Signed-off-by: Markus Niebel <Markus.Niebel@ew.tq-group.com>
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> ---
> Changes in v5:
> * Limit LPSPI4 max frequency
> * Replace PCIe M2 Key-E GPIO hogs with dedicated connector node
> * Fix PCIe clock configuration
>
> Changes in v4:
> * Fix LPSPI4 pad muxing and control
>
> Changes in v3:
> * Moved reserved-memory to board-lebel
> * Remove VPU reserved memory (unused for now)
> * Fix typo in connector comment
>
> Changes in v2:
> * removed useless regulator
> * added USB PD source configuration
> * Removed unused uart-has-rtscts properties (unused by LPUART)
> * Fixed RTS/CTS pullups in pinctrl
> * Added thermalzone on module
>
>  arch/arm64/boot/dts/freescale/Makefile        |   1 +
>  .../freescale/imx95-tqma9596la-mba95xxca.dts  | 963 ++++++++++++++++++
>  .../boot/dts/freescale/imx95-tqma9596la.dtsi  | 278 +++++
>  3 files changed, 1242 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/freescale/imx95-tqma9596la-mba95xxca.dts
>  create mode 100644 arch/arm64/boot/dts/freescale/imx95-tqma9596la.dtsi
>
> diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
> index 8ddaab127ab9c..43e1dc51b11d7 100644
> --- a/arch/arm64/boot/dts/freescale/Makefile
> +++ b/arch/arm64/boot/dts/freescale/Makefile
> @@ -649,6 +649,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx95-19x19-frdm-pro.dtb
>  dtb-$(CONFIG_ARCH_MXC) += imx95-aquila-clover.dtb
>  dtb-$(CONFIG_ARCH_MXC) += imx95-aquila-dev.dtb
>  dtb-$(CONFIG_ARCH_MXC) += imx95-toradex-smarc-dev.dtb
> +dtb-$(CONFIG_ARCH_MXC) += imx95-tqma9596la-mba95xxca.dtb
>  dtb-$(CONFIG_ARCH_MXC) += imx95-tqma9596sa-mb-smarc-2.dtb
>  dtb-$(CONFIG_ARCH_MXC) += imx95-var-dart-sonata.dtb
>  dtb-$(CONFIG_ARCH_MXC) += imx95-verdin-nonwifi-dahlia.dtb
...
> +
> +	ptn5110: usb-typec@50 {
> +		compatible = "nxp,ptn5110", "tcpci";
> +		reg = <0x50>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_typec>;
> +		interrupt-parent = <&gpio2>;
> +		interrupts = <28 IRQ_TYPE_LEVEL_LOW>;
> +
> +		typec_con: connector {
> +			compatible = "usb-c-connector";
> +			label = "X9";
> +			power-role = "source";
> +			data-role = "dual";
> +			source-pdos = <PDO_FIXED(5000, 500, PDO_FIXED_USB_COMM)>;
> +			self-powered;
> +
> +			port {
> +				typec_con_hs: endpoint {
> +					remote-endpoint = <&typec_hs>;
> +				};
> +			};
> +		};
> +	};
> +
> +	sensor_mb: temperature-sensor@1e {

please order by hex address value

> +		compatible = "nxp,se97b", "jedec,jc-42.4-temp";
> +		reg = <0x1e>;
> +	};
> +
...
> +		m2-keye-dev_bt-wake-hog {
> +			gpio-hog;
> +			gpios = <4 GPIO_ACTIVE_LOW>;
> +			input;
> +			line-name = "M2_KEYE_DEV_BT_WAKE#";
> +		};

Now Key E connector already be upstreamed.

Documentation/devicetree/bindings/connector/pcie-m2-e-connector.yaml,
which already define these gpios.

Frank



^ permalink raw reply

* Re: [PATCH 1/1] PCI: imx6: Use dev_err_probe
From: Frank Li @ 2026-06-19 14:11 UTC (permalink / raw)
  To: Alexander Stein
  Cc: Richard Zhu, Lucas Stach, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Manivannan Sadhasivam, Rob Herring,
	Bjorn Helgaas, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, linux-pci, linux-arm-kernel, imx, linux-kernel
In-Reply-To: <20260619112535.1127850-1-alexander.stein@ew.tq-group.com>

On Fri, Jun 19, 2026 at 01:25:33PM +0200, Alexander Stein wrote:
> pci_pwrctrl_power_on_devices() might return -EPROBE_DEFER resulting in
> an error message. Use dev_err_probe to silence this non-error message.
>
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/pci/controller/dwc/pci-imx6.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
> index 98e1db751132a..b7a502e9a1c59 100644
> --- a/drivers/pci/controller/dwc/pci-imx6.c
> +++ b/drivers/pci/controller/dwc/pci-imx6.c
> @@ -1381,7 +1381,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
>
>  	ret = pci_pwrctrl_power_on_devices(dev);
>  	if (ret) {
> -		dev_err(dev, "failed to power on pwrctrl devices\n");
> +		dev_err_probe(dev, ret, "failed to power on pwrctrl devices\n");
>  		goto err_pwrctrl_destroy;
>  	}
>
> --
> 2.54.0
>
>


^ permalink raw reply

* Re: [PATCH v4 2/2] media: i2c: ov5640: Add reset controller support with GPIO fallback
From: Frank Li @ 2026-06-19 14:18 UTC (permalink / raw)
  To: robby.cai
  Cc: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, festevam,
	sebastian.krzyszkowiak, slongerbeam, sakari.ailus, mchehab,
	p.zabel, kieran.bingham, kernel, devicetree, imx,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260619100532.3779934-3-robby.cai@oss.nxp.com>

On Fri, Jun 19, 2026 at 06:05:32PM +0800, robby.cai@oss.nxp.com wrote:
> [You don't often get email from robby.cai@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> From: Robby Cai <robby.cai@nxp.com>
>
> Add support for the reset controller framework by acquiring the reset
> line using devm_reset_control_get_optional_shared_deasserted(). This
> allows the driver to handle reset lines provided by a reset controller,
> including shared ones, while avoiding unbalanced deassert counts.
>
> Retain support for legacy reset-gpios as a fallback when no reset
> controller is defined. In that case, request the GPIO and keep it in the
> deasserted state as the initial configuration.
>
> This enables the driver to support both reset-controller-backed reset
> lines and older GPIO-based descriptions while preserving the existing
> power-up sequencing behavior.
>
> Signed-off-by: Robby Cai <robby.cai@nxp.com>
> ---
>  drivers/media/i2c/ov5640.c | 80 +++++++++++++++++++++++++++++++++-----
>  1 file changed, 70 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
> index 85ecc23b3587..5e6db8aacb11 100644
> --- a/drivers/media/i2c/ov5640.c
> +++ b/drivers/media/i2c/ov5640.c
> @@ -17,6 +17,7 @@
>  #include <linux/module.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
>  #include <linux/slab.h>
>  #include <linux/types.h>
>  #include <media/v4l2-async.h>
> @@ -442,6 +443,7 @@ struct ov5640_dev {
>         u32 xclk_freq;
>
>         struct regulator_bulk_data supplies[OV5640_NUM_SUPPLIES];
> +       struct reset_control *reset;
>         struct gpio_desc *reset_gpio;
>         struct gpio_desc *pwdn_gpio;
>         bool   upside_down;
> @@ -2431,6 +2433,48 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor)
>         return ov5640_set_framefmt(sensor, &sensor->fmt);
>  }
>
> +static int ov5640_get_reset(struct device *dev, struct ov5640_dev *sensor)
> +{
> +       /* use deasserted version to avoid unbalanced deassert counts */
> +       sensor->reset =
> +           devm_reset_control_get_optional_shared_deasserted(dev, NULL);
> +       if (IS_ERR(sensor->reset))
> +               return dev_err_probe(dev, PTR_ERR(sensor->reset),
> +                                    "Failed to get reset\n");
> +       else if (sensor->reset)
> +               return 0;
> +
> +       /*
> +        * fallback to legacy reset-gpios
> +        * GPIOD_OUT_HIGH ensures deasserted state for ACTIVE_LOW reset
> +        */
> +       sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> +                                                    GPIOD_OUT_HIGH);
> +       if (IS_ERR(sensor->reset_gpio))
> +               return dev_err_probe(dev, PTR_ERR(sensor->reset_gpio),
> +                                    "Failed to get reset gpio");

I think needn't fallback here, NO ABI change, just change to use reset-gpio
driver.

> +
> +       return 0;
> +}
> +
> +static int ov5640_reset_assert(struct ov5640_dev *sensor)
> +{
> +       if (sensor->reset)
> +               return reset_control_assert(sensor->reset);

needn't check sensor->reset, reset_control_assert() is no ops if NULL.

> +
> +       gpiod_set_value_cansleep(sensor->reset_gpio, 1);

Needn't fallback, directly replace.

Frank

> +       return 0;
> +}
> +
> +static int ov5640_reset_deassert(struct ov5640_dev *sensor)
> +{
> +       if (sensor->reset)
> +               return reset_control_deassert(sensor->reset);
> +
> +       gpiod_set_value_cansleep(sensor->reset_gpio, 0);
> +       return 0;
> +}
> +
>  static void ov5640_power(struct ov5640_dev *sensor, bool enable)
>  {
>         gpiod_set_value_cansleep(sensor->pwdn_gpio, enable ? 0 : 1);
> @@ -2448,12 +2492,19 @@ static void ov5640_power(struct ov5640_dev *sensor, bool enable)
>   *
>   * In such cases, this gpio should be mapped to pwdn_gpio in the driver, and we
>   * should still toggle the pwdn_gpio below with the appropriate delays, while
> - * the calls to reset_gpio will be ignored.
> + * reset handling (via reset controller or GPIO) will be ignored.
>   */
> -static void ov5640_powerup_sequence(struct ov5640_dev *sensor)
> +static int ov5640_powerup_sequence(struct ov5640_dev *sensor)
>  {
> +       int ret;
> +
>         if (sensor->pwdn_gpio) {
> -               gpiod_set_value_cansleep(sensor->reset_gpio, 1);
> +               ret = ov5640_reset_assert(sensor);
> +               if (ret) {
> +                       dev_err(&sensor->i2c_client->dev,
> +                               "Failed to assert reset: %d\n", ret);
> +                       return ret;
> +               }
>
>                 /* camera power cycle */
>                 ov5640_power(sensor, false);
> @@ -2461,7 +2512,13 @@ static void ov5640_powerup_sequence(struct ov5640_dev *sensor)
>                 ov5640_power(sensor, true);
>                 usleep_range(1000, 2000);       /* t3 */
>
> -               gpiod_set_value_cansleep(sensor->reset_gpio, 0);
> +               ret = ov5640_reset_deassert(sensor);
> +               if (ret) {
> +                       dev_err(&sensor->i2c_client->dev,
> +                               "Failed to deassert reset: %d\n", ret);
> +                       ov5640_power(sensor, false);
> +                       return ret;
> +               }
>         } else {
>                 /* software reset */
>                 ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
> @@ -2475,6 +2532,8 @@ static void ov5640_powerup_sequence(struct ov5640_dev *sensor)
>          */
>         ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
>                          OV5640_REG_SYS_CTRL0_SW_PWDN);
> +
> +       return 0;
>  }
>
>  static int ov5640_set_power_on(struct ov5640_dev *sensor)
> @@ -2497,7 +2556,9 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor)
>                 goto xclk_off;
>         }
>
> -       ov5640_powerup_sequence(sensor);
> +       ret = ov5640_powerup_sequence(sensor);
> +       if (ret)
> +               goto regulator_off;
>
>         ret = ov5640_init_slave_id(sensor);
>         if (ret)
> @@ -2507,6 +2568,7 @@ static int ov5640_set_power_on(struct ov5640_dev *sensor)
>
>  power_off:
>         ov5640_power(sensor, false);
> +regulator_off:
>         regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
>  xclk_off:
>         clk_disable_unprepare(sensor->xclk);
> @@ -3914,11 +3976,9 @@ static int ov5640_probe(struct i2c_client *client)
>         if (IS_ERR(sensor->pwdn_gpio))
>                 return PTR_ERR(sensor->pwdn_gpio);
>
> -       /* request optional reset pin */
> -       sensor->reset_gpio = devm_gpiod_get_optional(dev, "reset",
> -                                                    GPIOD_OUT_HIGH);
> -       if (IS_ERR(sensor->reset_gpio))
> -               return PTR_ERR(sensor->reset_gpio);
> +       ret = ov5640_get_reset(dev, sensor);
> +       if (ret)
> +               return ret;
>
>         v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
>         sensor->sd.internal_ops = &ov5640_internal_ops;
> --
> 2.50.1
>
>


^ permalink raw reply

* Re: [PATCH] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: Tomasz Figa @ 2026-06-19 14:20 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Sean Wang, Sergey Senozhatsky, Marcel Holtmann,
	Luiz Augusto von Dentz, Mark-yw Chen, Sean Wang, linux-bluetooth,
	linux-kernel, linux-arm-kernel, linux-mediatek, stable
In-Reply-To: <87tsqyirsn.wl-tiwai@suse.de>

[Resending without HTML... Damn Gmail]

On Fri, Jun 19, 2026 at 10:27 PM Takashi Iwai <tiwai@suse.de> wrote:
>
> On Wed, 10 Jun 2026 08:52:31 +0200,
> Sean Wang wrote:
> >
> > Hi,
> >
> > On Tue, Jun 9, 2026 at 7:19 AM Sergey Senozhatsky
> > <senozhatsky@chromium.org> wrote:
> > >
> > > Every once in a while we see a hung btmtksdio_flush() task:
> > >
> > >  INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
> > >  __cancel_work_timer+0x3f4/0x460
> > >  cancel_work_sync+0x1c/0x2c
> > >  btmtksdio_flush+0x2c/0x40
> > >  hci_dev_open_sync+0x10c4/0x2190
> > >  [..]
> > >
> > > It all boils down to incorrect time_is_before_jiffies() usage in
> > > btmtksdio_txrx_work().  The btmtksdio_txrx_work() loop is expected
> > > to be terminated if running for longer than 5*HZ.  However the
> > > timeout check is twisted:  time_is_before_jiffies(old_jiffies + 5*HZ)
> > > evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a
> > > timeout has occurred.  Using OR with time_is_before_jiffies(txrx_timeout)
> > > means that:
> > > - before the 5-second timeout: the condition is `int_status || false`,
> > >   so it loops as long as there are pending interrupts.
> > > - after the 5-second timeout: the condition becomes `int_status || true`,
> > >   which is always true.
> > >
> > > When the loop becomes infinite btmtksdio_txrx_work() loop never
> > > terminates and never releases the SDIO host.
> > >
> > > Fix loop termination condition to actually enforce a 5*HZ timeout.
> > >
> > > Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work")
> > > Cc: stable@vger.kernel.org
> > > Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > > ---
> > >  drivers/bluetooth/btmtksdio.c | 2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
> > > index 5b0fab7b89b5..c6f80c419e90 100644
> > > --- a/drivers/bluetooth/btmtksdio.c
> > > +++ b/drivers/bluetooth/btmtksdio.c
> > > @@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct *work)
> > >                         if (btmtksdio_rx_packet(bdev, rx_size) < 0)
> > >                                 bdev->hdev->stat.err_rx++;
> > >                 }
> > > -       } while (int_status || time_is_before_jiffies(txrx_timeout));
> > > +       } while (int_status && time_is_after_jiffies(txrx_timeout));
> >
> > yes, loop continues only while there is interrupt work and the timeout
> > deadline is still in the future
>
> I stumbled on this while backporting to distro kernels, and I wonder
> whether this change is correct.
>
> IIUC, this essentially makes the loop exiting right after the first
> cycle; the patch changed from time_is_before_jiffies() to *_after_*(),
> not only the logical OR to AND, and *_after_*() returns false, so the
> whole condition becomes false, too.

The intention is for the loop to keep running as long as there is
still an interrupt left to handle (int_status != 0) and the timeout
has not elapsed (jiffies < txrx_timeout).

Note that time_is_after_jiffies(x) returns true if x > jiffies (or jiffies < x):

/**
 * time_is_after_jiffies - return true if a is after jiffies
 * @a: time (unsigned long) to compare to jiffies
 *
 * Return: %true is time a is after jiffies, otherwise %false.
 */
#define time_is_after_jiffies(a) time_before(jiffies, a)

Or am I missing something?

Best,
Tomasz


^ permalink raw reply

* Re: [PATCH v3 1/1] reset: imx7: Correct polarity of MIPI CSI resets on i.MX8MQ
From: Frank Li @ 2026-06-19 14:20 UTC (permalink / raw)
  To: robby.cai
  Cc: p.zabel, Frank.Li, s.hauer, festevam, krzk+dt, andrew.smirnov,
	kernel, imx, linux-arm-kernel, linux-kernel, aisheng.dong,
	guoniu.zhou
In-Reply-To: <20260619073115.3778313-1-robby.cai@oss.nxp.com>

On Fri, Jun 19, 2026 at 03:31:15PM +0800, robby.cai@oss.nxp.com wrote:
> [You don't often get email from robby.cai@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> From: Robby Cai <robby.cai@nxp.com>
>
> On i.MX8MQ, the MIPI CSI reset lines are active-low and not self-clearing.
> Writing '0' asserts reset and it remains asserted until explicitly
> deasserted by software.
>
> This driver previously treated the MIPI CSI reset signals as active-high,
> which led to incorrect reset assert/deassert sequencing. This issue was
> exposed by commit 6d79bb8fd2aa ("media: imx8mq-mipi-csi2: Explicitly
> release reset").
>
> Fix this by reflecting the correct reset polarity and ensuring proper
> reset handling.
>
> Fixes: c979dbf59987 ("reset: imx7: Add support for i.MX8MQ IP block variant")
> Cc: <stable@vger.kernel.org> # 6d79bb8fd2aa: media: imx8mq-mipi-csi2: Explicitly release reset
> Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
> Signed-off-by: Robby Cai <robby.cai@nxp.com>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>
> ---
>
> Changes in v3:
>  - Add Cc tag as suggested by Philipp Zabel
>  - Add R-b tag from Philipp Zabel
>
> Link to v2: https://lore.kernel.org/imx/20260417080851.489303-1-robby.cai@nxp.com/
>
> Changes in v2:
>  - Drop the naming change in response to feedback from Krzysztof Kozlowski
>  - Refine the patch subject and commit message
>
> Link to v1: https://lore.kernel.org/imx/20260331101331.1405588-1-robby.cai@nxp.com/
>
> ---
>  drivers/reset/reset-imx7.c | 6 ++++++
>  1 file changed, 6 insertions(+)
>
> diff --git a/drivers/reset/reset-imx7.c b/drivers/reset/reset-imx7.c
> index dd01fe11c5cb..a3cb8244d76a 100644
> --- a/drivers/reset/reset-imx7.c
> +++ b/drivers/reset/reset-imx7.c
> @@ -236,6 +236,12 @@ static int imx8mq_reset_set(struct reset_controller_dev *rcdev,
>
>         case IMX8MQ_RESET_PCIE_CTRL_APPS_EN:
>         case IMX8MQ_RESET_PCIE2_CTRL_APPS_EN:
> +       case IMX8MQ_RESET_MIPI_CSI1_CORE_RESET:
> +       case IMX8MQ_RESET_MIPI_CSI1_PHY_REF_RESET:
> +       case IMX8MQ_RESET_MIPI_CSI1_ESC_RESET:
> +       case IMX8MQ_RESET_MIPI_CSI2_CORE_RESET:
> +       case IMX8MQ_RESET_MIPI_CSI2_PHY_REF_RESET:
> +       case IMX8MQ_RESET_MIPI_CSI2_ESC_RESET:
>         case IMX8MQ_RESET_MIPI_DSI_PCLK_RESET_N:
>         case IMX8MQ_RESET_MIPI_DSI_ESC_RESET_N:
>         case IMX8MQ_RESET_MIPI_DSI_DPI_RESET_N:
> --
> 2.50.1
>
>


^ permalink raw reply

* Re: [PATCH v2 4/5] arm64: defconfig: Drop unused Ethernet vendors
From: Geert Uytterhoeven @ 2026-06-19 14:21 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Krzysztof Kozlowski, linux-kernel, linux-arm-kernel,
	Catalin Marinas, Will Deacon, Alexandre Belloni, Linus Walleij,
	Drew Fustini, soc
In-Reply-To: <b04972c7-2c5e-40fe-a8a6-7ade300cd4b2@app.fastmail.com>

Hi Arnd,

On Fri, 19 Jun 2026 at 15:54, Arnd Bergmann <arnd@arndb.de> wrote:
> On Fri, Jun 19, 2026, at 15:11, Krzysztof Kozlowski wrote:
> > On 19/06/2026 12:12, Geert Uytterhoeven wrote:
> >>> +# CONFIG_NET_VENDOR_ACTIONS is not set
> >>> +# CONFIG_NET_VENDOR_ADAPTEC is not set
> >>> +# CONFIG_NET_VENDOR_AGERE is not set
> >>> +# CONFIG_NET_VENDOR_ALACRITECH is not set
> >>> +# CONFIG_NET_VENDOR_ALLWINNER is not set
> >>
> >> [...]
> >>
> >> I am not sure this new policy is a good idea: none of this affects
> >
> > I would not call it new policy. Just an idea which got accepted. :)
>
> I think when we originally added the CONFIG_NET_VENDOR_xxx symbols,
> the idea would be that we would one day change them to
> default-disabled, but that never happened, and generally causes
> more problems than it solves.

Yeah. While "make oldconfig" would handle that fine, all defconfigs would
need an update.

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


^ permalink raw reply

* Re: [PATCH v4 0/4] arm64: cross-CPU NMI via SDEI
From: Marc Zyngier @ 2026-06-19 14:26 UTC (permalink / raw)
  To: Catalin Marinas
  Cc: Kiryl Shutsemau, Will Deacon, James Morse, Mark Rutland,
	Doug Anderson, Petr Mladek, Thomas Gleixner, Andrew Morton,
	Baoquan He, Puranjay Mohan, Usama Arif, Breno Leitao,
	Julien Thierry, Lecopzer Chen, Sumit Garg, kernel-team, kexec,
	linux-arm-kernel, linux-kernel, Kiryl Shutsemau (Meta)
In-Reply-To: <ajVLeseWOZn0XXY2@arm.com>

On Fri, 19 Jun 2026 15:00:26 +0100,
Catalin Marinas <catalin.marinas@arm.com> wrote:
> 
> Hi Kiryl,
> 
> On Wed, Jun 17, 2026 at 08:20:01PM +0100, Kiryl Shutsemau wrote:
> >   - GICv3 pseudo-NMI (interrupt priority masking). Its cost is on the
> >     interrupt mask/unmask hot path: local_irq_enable() becomes an
> >     ICC_PMR_EL1 write plus a synchronising barrier, and exception
> >     entry/exit save and restore the PMR, paid on every CPU whether or not
> >     an NMI is ever delivered. In our measurements, enabling pseudo-NMI
> >     costs up to ~5% on real workloads, and ~66% on a syscall-in-a-loop
> >     microbenchmark. A fleet-wide ~5% regression is not acceptable, so
> >     these systems run with pseudo-NMI disabled.
> 
> Does your firmware set ICC_CTLR_EL1.PMHE? I'd be curious to see the
> numbers if the DSB was omitted on the enable path.

I certainly don't observe this sort of overhead on the HW I have
access to, and would like to understand where this is coming from with
actual profiling data.

	M.

-- 
Without deviation from the norm, progress is not possible.


^ permalink raw reply

* Re: [PATCH 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
From: Frank Li @ 2026-06-19 14:26 UTC (permalink / raw)
  To: Yuanshen Cao
  Cc: Vinod Koul, Frank Li, Chen-Yu Tsai, Jernej Skrabec,
	Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Maxime Ripard, dmaengine, linux-arm-kernel, linux-sunxi,
	linux-kernel, devicetree
In-Reply-To: <20260619-sun60i-a733-dma-v1-1-da4b649fc72a@gmail.com>

On Fri, Jun 19, 2026 at 04:53:30AM +0000, Yuanshen Cao wrote:
>
> This patch is the first step in a refactoring effort to support the

avoid use "this patch/commit", Just

Refactor ... to support Allwinner A733 DMA controller.

> Allwinner A733 DMA controller. Currently, the `sun6i-dma` driver has
> several functions related to interrupt handling (reading/writing
> interrupt enable and status registers) and register dumping that are
> hardcoded.
>
> To support the A733, which has different register layouts and interrupt
> handling logic, these functions are being moved into the
> `sun6i_dma_config` structure as function pointers. This allows the
> driver to use a polymorphic approach where the specific implementation
> is determined by the hardware configuration assigned during device
> probing.
>
> Changes:
> - Added function pointers to `struct sun6i_dma_config` for:
>     - `dump_com_regs`
>     - `read_irq_en`
>     - `write_irq_en`
>     - `read_irq_stat`
>     - `write_irq_stat`
> - Implemented generic `sun6i_read/write_irq_*` functions for existing
>   hardware.
> - Updated existing `sun6i_dma_config` instances (A31, A23, H3, A64,
>   A100, H6, V3S) to use these new function pointers.
>
> Signed-off-by: Yuanshen Cao <alex.caoys@gmail.com>
> ---
>  drivers/dma/sun6i-dma.c | 74 +++++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 69 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index a9a254dbf8cb..d92e702320d9 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -138,6 +138,11 @@ struct sun6i_dma_config {
>         void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst);
>         void (*set_drq)(u32 *p_cfg, s8 src_drq, s8 dst_drq);
>         void (*set_mode)(u32 *p_cfg, s8 src_mode, s8 dst_mode);
> +       void (*dump_com_regs)(struct sun6i_dma_dev *sdev);
> +       u32 (*read_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num);
> +       void (*write_irq_en)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val);
> +       u32 (*read_irq_stat)(struct sun6i_dma_dev *sdev, u32 chan_num);
> +       void (*write_irq_stat)(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status);
>         u32 src_burst_lengths;
>         u32 dst_burst_lengths;
>         u32 src_addr_widths;
> @@ -347,6 +352,25 @@ static void sun6i_set_mode_h6(u32 *p_cfg, s8 src_mode, s8 dst_mode)
>                   DMA_CHAN_CFG_DST_MODE_H6(dst_mode);
>  }
>
> +static u32 sun6i_read_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num)
> +{
> +       return readl(sdev->base + DMA_IRQ_EN(chan_num));
> +}
> +
> +static void sun6i_write_irq_en(struct sun6i_dma_dev *sdev, u32 chan_num, u32 irq_val)
> +{
> +       writel(irq_val, sdev->base + DMA_IRQ_EN(chan_num));
> +}
> +static u32 sun6i_read_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num)
> +{
> +       return readl(sdev->base + DMA_IRQ_STAT(chan_num));
> +}
> +
> +static void sun6i_write_irq_stat(struct sun6i_dma_dev *sdev, u32 chan_num, u32 status)
> +{
> +       writel(status, sdev->base + DMA_IRQ_STAT(chan_num));
> +}
> +
>  static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan)
>  {
>         struct sun6i_desc *txd = pchan->desc;
> @@ -460,16 +484,16 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
>
>         vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
>
> -       irq_val = readl(sdev->base + DMA_IRQ_EN(irq_reg));
> +       irq_val = sdev->cfg->read_irq_en(sdev, irq_reg);
>         irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
>                         (irq_offset * DMA_IRQ_CHAN_WIDTH));
>         irq_val |= vchan->irq_type << (irq_offset * DMA_IRQ_CHAN_WIDTH);
> -       writel(irq_val, sdev->base + DMA_IRQ_EN(irq_reg));
> +       sdev->cfg->write_irq_en(sdev, irq_reg, irq_val);
>
>         writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR);
>         writel(DMA_CHAN_ENABLE_START, pchan->base + DMA_CHAN_ENABLE);
>
> -       sun6i_dma_dump_com_regs(sdev);
> +       sdev->cfg->dump_com_regs(sdev);
>         sun6i_dma_dump_chan_regs(sdev, pchan);
>
>         return 0;
> @@ -549,14 +573,14 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
>         u32 status;
>
>         for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
> -               status = readl(sdev->base + DMA_IRQ_STAT(i));
> +               status = sdev->cfg->read_irq_stat(sdev, i);
>                 if (!status)
>                         continue;
>
>                 dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
>                         str_high_low(i), status);
>
> -               writel(status, sdev->base + DMA_IRQ_STAT(i));
> +               sdev->cfg->write_irq_stat(sdev, i, status);
>
>                 for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
>                         pchan = sdev->pchans + j;
> @@ -1124,6 +1148,11 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_a31,
>         .set_drq          = sun6i_set_drq_a31,
>         .set_mode         = sun6i_set_mode_a31,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,

Can you define macro like to avoid duplicate these init code. Or set it at
probe if it is NULL.

Frank

>         .src_burst_lengths = BIT(1) | BIT(8),
>         .dst_burst_lengths = BIT(1) | BIT(8),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1147,6 +1176,11 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_a31,
>         .set_drq          = sun6i_set_drq_a31,
>         .set_mode         = sun6i_set_mode_a31,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(8),
>         .dst_burst_lengths = BIT(1) | BIT(8),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1165,6 +1199,11 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_a31,
>         .set_drq          = sun6i_set_drq_a31,
>         .set_mode         = sun6i_set_mode_a31,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(8),
>         .dst_burst_lengths = BIT(1) | BIT(8),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1190,6 +1229,11 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_h3,
>         .set_drq          = sun6i_set_drq_a31,
>         .set_mode         = sun6i_set_mode_a31,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1211,6 +1255,11 @@ static struct sun6i_dma_config sun50i_a64_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_h3,
>         .set_drq          = sun6i_set_drq_a31,
>         .set_mode         = sun6i_set_mode_a31,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1232,6 +1281,11 @@ static struct sun6i_dma_config sun50i_a100_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_h3,
>         .set_drq          = sun6i_set_drq_h6,
>         .set_mode         = sun6i_set_mode_h6,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1255,6 +1309,11 @@ static struct sun6i_dma_config sun50i_h6_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_h3,
>         .set_drq          = sun6i_set_drq_h6,
>         .set_mode         = sun6i_set_mode_h6,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> @@ -1281,6 +1340,11 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = {
>         .set_burst_length = sun6i_set_burst_length_a31,
>         .set_drq          = sun6i_set_drq_a31,
>         .set_mode         = sun6i_set_mode_a31,
> +       .dump_com_regs    = sun6i_dma_dump_com_regs,
> +       .read_irq_en      = sun6i_read_irq_en,
> +       .write_irq_en     = sun6i_write_irq_en,
> +       .read_irq_stat    = sun6i_read_irq_stat,
> +       .write_irq_stat   = sun6i_write_irq_stat,
>         .src_burst_lengths = BIT(1) | BIT(8),
>         .dst_burst_lengths = BIT(1) | BIT(8),
>         .src_addr_widths   = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
>
> --
> 2.54.0
>


^ permalink raw reply

* Re: [PATCH] docs: arm64: Document that text_offset is always 0
From: Will Deacon @ 2026-06-19 14:33 UTC (permalink / raw)
  To: Rasmus Villemoes
  Cc: linux-arm-kernel, Ard Biesheuvel, Jonathan Corbet, linux-doc,
	linux-kernel
In-Reply-To: <20260604140839.1930847-1-linux@rasmusvillemoes.dk>

On Thu, Jun 04, 2026 at 04:08:39PM +0200, Rasmus Villemoes wrote:
> When trying to figure out where to place and call an arm64 Image in
> memory, reading booting.rst should provide the answer. However, it
> requires quite some digging to figure out that text_offset is set via
> ".quad 0" in head.S and is thus actually always 0 since v5.10.
> 
> Update the documentation and make that explicit. Reword the 2MB
> requirement accordingly, and remove the paragraphs that only apply to
> the ancient versions where text_offset could be non-zero, as they only
> confuse a current reader.

Doesn't this needlessly prevent us from having a non-zero offset in future,
if we wanted that for some reason?

Will


^ permalink raw reply

* Re: [PATCH] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: Takashi Iwai @ 2026-06-19 14:35 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Takashi Iwai, Sean Wang, Sergey Senozhatsky, Marcel Holtmann,
	Luiz Augusto von Dentz, Mark-yw Chen, Sean Wang, linux-bluetooth,
	linux-kernel, linux-arm-kernel, linux-mediatek, stable
In-Reply-To: <CAAFQd5DyDQo9vBH80YYQBW7Bgf64F1m9q44-jhf1cc75XYpftA@mail.gmail.com>

On Fri, 19 Jun 2026 16:17:31 +0200,
Tomasz Figa wrote:
> 
> 
> On Fri, Jun 19, 2026 at 10:27 PM Takashi Iwai <tiwai@suse.de> wrote:
> >
> > On Wed, 10 Jun 2026 08:52:31 +0200,
> > Sean Wang wrote:
> > >
> > > Hi,
> > >
> > > On Tue, Jun 9, 2026 at 7:19 AM Sergey Senozhatsky
> > > <senozhatsky@chromium.org> wrote:
> > > >
> > > > Every once in a while we see a hung btmtksdio_flush() task:
> > > >
> > > >  INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
> > > >  __cancel_work_timer+0x3f4/0x460
> > > >  cancel_work_sync+0x1c/0x2c
> > > >  btmtksdio_flush+0x2c/0x40
> > > >  hci_dev_open_sync+0x10c4/0x2190
> > > >  [..]
> > > >
> > > > It all boils down to incorrect time_is_before_jiffies() usage in
> > > > btmtksdio_txrx_work().  The btmtksdio_txrx_work() loop is expected
> > > > to be terminated if running for longer than 5*HZ.  However the
> > > > timeout check is twisted:  time_is_before_jiffies(old_jiffies + 5*HZ)
> > > > evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a
> > > > timeout has occurred.  Using OR with time_is_before_jiffies
> (txrx_timeout)
> > > > means that:
> > > > - before the 5-second timeout: the condition is `int_status || false`,
> > > >   so it loops as long as there are pending interrupts.
> > > > - after the 5-second timeout: the condition becomes `int_status || true
> `,
> > > >   which is always true.
> > > >
> > > > When the loop becomes infinite btmtksdio_txrx_work() loop never
> > > > terminates and never releases the SDIO host.
> > > >
> > > > Fix loop termination condition to actually enforce a 5*HZ timeout.
> > > >
> > > > Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to
> work")
> > > > Cc: stable@vger.kernel.org
> > > > Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > > > ---
> > > >  drivers/bluetooth/btmtksdio.c | 2 +-
> > > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/
> btmtksdio.c
> > > > index 5b0fab7b89b5..c6f80c419e90 100644
> > > > --- a/drivers/bluetooth/btmtksdio.c
> > > > +++ b/drivers/bluetooth/btmtksdio.c
> > > > @@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct
> *work)
> > > >                         if (btmtksdio_rx_packet(bdev, rx_size) < 0)
> > > >                                 bdev->hdev->stat.err_rx++;
> > > >                 }
> > > > -       } while (int_status || time_is_before_jiffies(txrx_timeout));
> > > > +       } while (int_status && time_is_after_jiffies(txrx_timeout));
> > >
> > > yes, loop continues only while there is interrupt work and the timeout
> > > deadline is still in the future
> >
> > I stumbled on this while backporting to distro kernels, and I wonder
> > whether this change is correct.
> >
> > IIUC, this essentially makes the loop exiting right after the first
> > cycle; the patch changed from time_is_before_jiffies() to *_after_*(),
> > not only the logical OR to AND, and *_after_*() returns false, so the
> > whole condition becomes false, too.
> 
> The intention is for the loop to keep running as long as there is still an
> interrupt left to handle (int_status != 0) and the timeout has not elapsed
> (jiffies < txrx_timeout).
> 
> Note that time_is_after_jiffies(x) returns true if x > jiffies (or jiffies <
> x):
> 
>     /**
>      * time_is_after_jiffies - return true if a is after jiffies
>      * @a: time (unsigned long) to compare to jiffies
>      *
>      * Return: %true is time a is after jiffies, otherwise %false.
>      */
>     #define time_is_after_jiffies(a) time_before(jiffies, a)
> 
> Or am I missing something?

Doh, scratch my comment.  It's enough confusing about time_after() vs
time_is_after_jiffies().  Too hot here to review something today :-<

Sorry for the noise!


Takashi


^ permalink raw reply

* Re: [PATCH] iommu/arm-smmu-v3: Declare eats_s1chk and eats_trans as host-endian u64
From: Will Deacon @ 2026-06-19 14:38 UTC (permalink / raw)
  To: Nicolin Chen
  Cc: Robin Murphy, joro, Jason Gunthorpe, Shuai Xue, linux-arm-kernel,
	iommu, linux-kernel
In-Reply-To: <20260615194533.3290010-1-nicolinc@nvidia.com>

On Mon, Jun 15, 2026 at 12:45:33PM -0700, Nicolin Chen wrote:
> arm_smmu_get_ste_update_safe() declares the eats_s1chk and eats_trans
> locals as __le64, but initializes them from FIELD_PREP(), which returns a
> host-endian value, and passes them through cpu_to_le64() at the use sites.
> 
> Sparse reports the following warnings:
> 
> >> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:1122:38: sparse: sparse: cast from restricted __le64
>    drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c:1124:33: sparse: sparse: cast from restricted __le64
> 
> Declare both locals as u64 so the type matches FIELD_PREP() and the
> existing cpu_to_le64() at the use sites performs the host-to-little-endian
> conversion. No functional change.
> 
> Fixes: 7cad80048595 ("iommu/arm-smmu-v3: Mark EATS_TRANS safe when computing the update sequence")
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/all/202606151017.QU0evpH9-lkp@intel.com/
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
> ---
>  drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> index 8ce3e801eda3b..4c0f7b17b1f37 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> @@ -1240,9 +1240,9 @@ VISIBLE_IF_KUNIT
>  void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
>  				  __le64 *safe_bits)
>  {
> -	const __le64 eats_s1chk =
> +	const u64 eats_s1chk =
>  		FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_S1CHK);
> -	const __le64 eats_trans =
> +	const u64 eats_trans =
>  		FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_TRANS);

Aha, this is the fix for that sparse report from the kbuild robot. Thanks!

Will


^ permalink raw reply

* Re: [RFC] arm64: early_ioremap fails to map ACPI MADT on 64K pages
From: Will Deacon @ 2026-06-19 14:43 UTC (permalink / raw)
  To: Yu Peng
  Cc: Catalin Marinas, linux-arm-kernel, Rafael J. Wysocki, Len Brown,
	linux-acpi, Andrew Morton, linux-mm, linux-kernel, lpieralisi,
	guohanjun, sudeep.holla
In-Reply-To: <20260617060110.2846851-1-pengyu@kylinos.cn>

+arm64 ACPI maintainers

On Wed, Jun 17, 2026 at 02:01:10PM +0800, Yu Peng wrote:
> I hit an early boot failure on an arm64 system built with 64K pages while
> parsing the ACPI MADT.
> 
> The failing system reports:
> 
>   PAGE_SIZE: 64K
>   MADT physical address: 0x5a7ae018
>   MADT length: 0x32094

The MADT isn't even 4k aligned, so why does the page size matter in this
case?

> The failure happens when acpi_table_parse_madt() calls into early_memremap()
> via __acpi_map_table(). The MADT itself is smaller than 256K, but its
> placement causes the early mapping to require 5 64K pages:
> 
>   offset within 64K page = 0x5a7ae018 & 0xffff = 0xe018
>   mapped range           = PAGE_ALIGN(0xe018 + 0x32094)
>                          = PAGE_ALIGN(0x400ac)
>                          = 0x50000
>   nrpages                = 0x50000 / 0x10000 = 5
> 
> On arm64, NR_FIX_BTMAPS is currently derived from a 256K per-slot budget:
> 
>   #define NR_FIX_BTMAPS        (SZ_256K / PAGE_SIZE)
> 
> So for 64K pages, NR_FIX_BTMAPS is 4. The mapping therefore fails the
> early_ioremap() check:
> 
>   if (WARN_ON(nrpages > NR_FIX_BTMAPS))
>           return NULL;
> 
> After that, MADT parsing fails and the boot continues with symptoms such as:
> 
>   ACPI: APIC not present
>   missing boot CPU MPIDR, not enabling secondaries
>   Kernel panic - not syncing: No interrupt controller found.
> 
> A firmware change can avoid this by placing MADT so that:
> 
>   (madt_phys & 0xffff) + madt_length <= SZ_256K
> 
> However, I do not think ACPI requires such placement, so this looks like a
> kernel-side robustness issue as well, especially on large arm64 systems where
> MADT can grow with CPU topology.
> 
> One possible kernel-side change is to increase the boot-time mapping budget for
> CONFIG_ARM64_64K_PAGES, for example using a 512K per-slot budget only in that
> configuration. I do not think this should be applied unconditionally to all
> page sizes, since the arm64 early fixmap code expects the boot-ioremap range
> to stay within one PMD.
> 
> Has anyone seen similar failures on arm64 64K systems?
> 
> Would maintainers prefer treating this as a firmware layout issue, or would
> increasing the early_ioremap budget for 64K pages be acceptable?

It think it boils down to what ACPI says about the alignment of the MADT.

Lorenzo?

Will


^ permalink raw reply

* Re: [PATCH] arm64: uapi: Use __u128 instead of __uint128_t in UAPI headers
From: Mark Rutland @ 2026-06-19 14:51 UTC (permalink / raw)
  To: Will Deacon
  Cc: linux-arm-kernel, Arnd Bergmann, Nick Desaulniers, Steffen Eiden,
	Andreas Grapentin, Catalin Marinas, Dave Martin, Marc Zyngier
In-Reply-To: <20260619130835.5678-1-will@kernel.org>

On Fri, Jun 19, 2026 at 02:08:34PM +0100, Will Deacon wrote:
> The arm64 UAPI exposes '__uint128_t' types in the members of
> 'struct user_fpsimd_state', 'struct user_pac_address_keys' and in the
> signal frame via 'struct fpsimd_context'. Since the alignment of such
> a type appears to be non-portable (16 bytes on arm64, 8 bytes on s390),
> prefer the '__u128' typedef from uapi/linux/types.h, which makes the
> alignment explicit and allows the definitions to be reused by other
> host architectures.
> 
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
> Cc: Steffen Eiden <seiden@linux.ibm.com>
> Cc: Andreas Grapentin <gra@linux.ibm.com>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Dave Martin <dave.martin@arm.com>
> Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: Marc Zyngier <maz@kernel.org>
> Signed-off-by: Will Deacon <will@kernel.org>
> ---
> 
> This aims to help a little with the s390/arm64 KVM series at:
> 
> https://lore.kernel.org/r/20260529155050.2902245-1-seiden@linux.ibm.com
> 
> by allowing the relevant parts of the arm64 UAPI to be used directly by
> s390 rather than copied and modified.
> 
> I think it's a straightforward change, but the only thing that makes me
> pause for thought is whether there are toolchains out there which accept
> __uint128_t but not __int128. Then again, if that crops up as an issue
> we can probably just tweak the typedef we have in uapi/linux/types.h.

That's all fine to me in concept, so:

Acked-by: Mark Rutland <mark.rutland@arm.com>

A few places in arch/arm64/kernel/fpsimd.c and
arch/arm64/kernel/ptrace.c use __uint128_t to match the uapi headers.
For consistency (and to avoid people accidentally using __uint128_t in
future), I reckon it would be good to fix those up to match, but that
can be a follow-up.

Mark.

>  arch/arm64/include/uapi/asm/ptrace.h     | 12 ++++++------
>  arch/arm64/include/uapi/asm/sigcontext.h |  6 +++---
>  2 files changed, 9 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
> index 6fed93fb2536..15649a253a57 100644
> --- a/arch/arm64/include/uapi/asm/ptrace.h
> +++ b/arch/arm64/include/uapi/asm/ptrace.h
> @@ -93,7 +93,7 @@ struct user_pt_regs {
>  };
>  
>  struct user_fpsimd_state {
> -	__uint128_t	vregs[32];
> +	__u128		vregs[32];
>  	__u32		fpsr;
>  	__u32		fpcr;
>  	__u32		__reserved[2];
> @@ -258,14 +258,14 @@ struct user_pac_mask {
>  /* pointer authentication keys (NT_ARM_PACA_KEYS, NT_ARM_PACG_KEYS) */
>  
>  struct user_pac_address_keys {
> -	__uint128_t	apiakey;
> -	__uint128_t	apibkey;
> -	__uint128_t	apdakey;
> -	__uint128_t	apdbkey;
> +	__u128	apiakey;
> +	__u128	apibkey;
> +	__u128	apdakey;
> +	__u128	apdbkey;
>  };
>  
>  struct user_pac_generic_keys {
> -	__uint128_t	apgakey;
> +	__u128	apgakey;
>  };
>  
>  /* ZA state (NT_ARM_ZA) */
> diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
> index e29bf3e2d0cc..d250ca7a1d46 100644
> --- a/arch/arm64/include/uapi/asm/sigcontext.h
> +++ b/arch/arm64/include/uapi/asm/sigcontext.h
> @@ -78,7 +78,7 @@ struct fpsimd_context {
>  	struct _aarch64_ctx head;
>  	__u32 fpsr;
>  	__u32 fpcr;
> -	__uint128_t vregs[32];
> +	__u128 vregs[32];
>  };
>  
>  /*
> @@ -266,8 +266,8 @@ struct gcs_context {
>   *	-	----				-----------
>   *	REGS					the entire SVE context
>   *
> - *	ZREGS	__uint128_t[SVE_NUM_ZREGS][vq]	all Z-registers
> - *	ZREG	__uint128_t[vq]			individual Z-register Zn
> + *	ZREGS	__u128[SVE_NUM_ZREGS][vq]	all Z-registers
> + *	ZREG	__u128[vq]			individual Z-register Zn
>   *
>   *	PREGS	uint16_t[SVE_NUM_PREGS][vq]	all P-registers
>   *	PREG	uint16_t[vq]			individual P-register Pn
> -- 
> 2.55.0.rc0.738.g0c8ab3ebcc-goog
> 


^ permalink raw reply

* Re: [PATCH] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: Tomasz Figa @ 2026-06-19 14:53 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Sean Wang, Sergey Senozhatsky, Marcel Holtmann,
	Luiz Augusto von Dentz, Mark-yw Chen, Sean Wang, linux-bluetooth,
	linux-kernel, linux-arm-kernel, linux-mediatek, stable
In-Reply-To: <87jyruiomq.wl-tiwai@suse.de>

On Fri, Jun 19, 2026 at 11:36 PM Takashi Iwai <tiwai@suse.de> wrote:
>
> On Fri, 19 Jun 2026 16:17:31 +0200,
> Tomasz Figa wrote:
> >
> >
> > On Fri, Jun 19, 2026 at 10:27 PM Takashi Iwai <tiwai@suse.de> wrote:
> > >
> > > On Wed, 10 Jun 2026 08:52:31 +0200,
> > > Sean Wang wrote:
> > > >
> > > > Hi,
> > > >
> > > > On Tue, Jun 9, 2026 at 7:19 AM Sergey Senozhatsky
> > > > <senozhatsky@chromium.org> wrote:
> > > > >
> > > > > Every once in a while we see a hung btmtksdio_flush() task:
> > > > >
> > > > >  INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
> > > > >  __cancel_work_timer+0x3f4/0x460
> > > > >  cancel_work_sync+0x1c/0x2c
> > > > >  btmtksdio_flush+0x2c/0x40
> > > > >  hci_dev_open_sync+0x10c4/0x2190
> > > > >  [..]
> > > > >
> > > > > It all boils down to incorrect time_is_before_jiffies() usage in
> > > > > btmtksdio_txrx_work().  The btmtksdio_txrx_work() loop is expected
> > > > > to be terminated if running for longer than 5*HZ.  However the
> > > > > timeout check is twisted:  time_is_before_jiffies(old_jiffies + 5*HZ)
> > > > > evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a
> > > > > timeout has occurred.  Using OR with time_is_before_jiffies
> > (txrx_timeout)
> > > > > means that:
> > > > > - before the 5-second timeout: the condition is `int_status || false`,
> > > > >   so it loops as long as there are pending interrupts.
> > > > > - after the 5-second timeout: the condition becomes `int_status || true
> > `,
> > > > >   which is always true.
> > > > >
> > > > > When the loop becomes infinite btmtksdio_txrx_work() loop never
> > > > > terminates and never releases the SDIO host.
> > > > >
> > > > > Fix loop termination condition to actually enforce a 5*HZ timeout.
> > > > >
> > > > > Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to
> > work")
> > > > > Cc: stable@vger.kernel.org
> > > > > Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
> > > > > ---
> > > > >  drivers/bluetooth/btmtksdio.c | 2 +-
> > > > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/
> > btmtksdio.c
> > > > > index 5b0fab7b89b5..c6f80c419e90 100644
> > > > > --- a/drivers/bluetooth/btmtksdio.c
> > > > > +++ b/drivers/bluetooth/btmtksdio.c
> > > > > @@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct
> > *work)
> > > > >                         if (btmtksdio_rx_packet(bdev, rx_size) < 0)
> > > > >                                 bdev->hdev->stat.err_rx++;
> > > > >                 }
> > > > > -       } while (int_status || time_is_before_jiffies(txrx_timeout));
> > > > > +       } while (int_status && time_is_after_jiffies(txrx_timeout));
> > > >
> > > > yes, loop continues only while there is interrupt work and the timeout
> > > > deadline is still in the future
> > >
> > > I stumbled on this while backporting to distro kernels, and I wonder
> > > whether this change is correct.
> > >
> > > IIUC, this essentially makes the loop exiting right after the first
> > > cycle; the patch changed from time_is_before_jiffies() to *_after_*(),
> > > not only the logical OR to AND, and *_after_*() returns false, so the
> > > whole condition becomes false, too.
> >
> > The intention is for the loop to keep running as long as there is still an
> > interrupt left to handle (int_status != 0) and the timeout has not elapsed
> > (jiffies < txrx_timeout).
> >
> > Note that time_is_after_jiffies(x) returns true if x > jiffies (or jiffies <
> > x):
> >
> >     /**
> >      * time_is_after_jiffies - return true if a is after jiffies
> >      * @a: time (unsigned long) to compare to jiffies
> >      *
> >      * Return: %true is time a is after jiffies, otherwise %false.
> >      */
> >     #define time_is_after_jiffies(a) time_before(jiffies, a)
> >
> > Or am I missing something?
>
> Doh, scratch my comment.  It's enough confusing about time_after() vs
> time_is_after_jiffies().  Too hot here to review something today :-<
>
> Sorry for the noise!

Haha, no worries, it got me too! (In our internal discussion with
Sergey) I had to look up the definition and think about it for quite a
while to ensure it was really what we needed. ;)

Best,
Tomasz


^ permalink raw reply

* Re: [PATCH v3] arm64: errata: Workaround NVIDIA Olympus device store/load ordering erratum
From: Will Deacon @ 2026-06-19 14:58 UTC (permalink / raw)
  To: Shanker Donthineni
  Cc: Jason Gunthorpe, Catalin Marinas, Vladimir Murzin,
	linux-arm-kernel@lists.infradead.org, Mark Rutland,
	linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org,
	Vikram Sethi, Jason Sequeira
In-Reply-To: <25495831-f32d-4332-a7c2-fb1463b96174@nvidia.com>

On Tue, Jun 16, 2026 at 08:22:39AM -0500, Shanker Donthineni wrote:
> On 6/12/2026 7:48 AM, Jason Gunthorpe wrote:
> > On Thu, Jun 11, 2026 at 08:13:48PM -0500, Shanker Donthineni wrote:
> > 
> > > For the scalar MMIO helpers, the workaround promotes the raw writes to
> > > store-release on affected CPUs as v1/v2 shown below. For the memcpy-toIO
> > > helpers, could you please clarify the specific reason for adding a dmb despite
> > > the documented no-ordering contract? Is the concern that some drivers may
> > > be relying on ordering across memcpy_toio_*() today even though the API
> > > does not guarantee it, and that we should cover those cases defensively?
> > I think given how arm implements them today the iocopy's are actually
> > the _relaxed variations.. I wonder if this matters to any user?
> 
> Following Jason's observation that on arm64 the memcpy_toio()
> /__iowrite{32,64}_copy() helpers are effectively the relaxed
> (write-combining) variants, I'd like to settle one open point before posting
> v4: should the workaround also promote dgh() > dmb on affected CPUs (now
> Olympus core), or leave dgh() as a plain hint?
> 
>        If you'd still prefer the dmb defensively, to cover drivers that may
> rely on ordering across memcpy_toio() today despite the relaxed contract,
> I'm happy to fold it into v4.

The point is, you're going to have different behaviour to every other
arm64 system out there. You may be able to find vague comments in the
code that imply that you don't need to provide ordering, but at the end
of the day it's a pretty cavalier attitude imo and if a driver ever shows
up that relies on it then you're in trouble.

> Please let me know how you'd like me to proceed.

It's up to you. It's your broken CPU, not mine. You also haven't actually
provided any performance data for others to assess the trade-off.

If it was up to _me_, I'd upgrade dgh() on these CPUs to that I don't
need to worry about this again.

Will


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox