Linux Documentation
 help / color / mirror / Atom feed
* Re: [PATCH RFC v2 9/9] docs: iio: add documentation for ad9910 driver
From: Jonathan Cameron @ 2026-03-22 17:34 UTC (permalink / raw)
  To: Rodrigo Alencar via B4 Relay
  Cc: rodrigo.alencar, linux-iio, devicetree, linux-kernel, linux-doc,
	Lars-Peter Clausen, Michael Hennerich, David Lechner,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Philipp Zabel, Jonathan Corbet, Shuah Khan
In-Reply-To: <20260318-ad9910-iio-driver-v2-9-e79f93becf11@analog.com>

On Wed, 18 Mar 2026 17:56:09 +0000
Rodrigo Alencar via B4 Relay <devnull+rodrigo.alencar.analog.com@kernel.org> wrote:

> From: Rodrigo Alencar <rodrigo.alencar@analog.com>
> 
> Add documentation for the AD9910 DDS IIO driver, which describes channels,
> DDS modes, attributes and ABI usage examples.
> 
> Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>

A few things inline. I've not cropped as there is a lot here and I'd like
it all to remain visible in the reply.


Overall this is a very interesting device so whilst I think we are making
progress it might still take a while to come to an overall conclusion
on the ABI!

> ---
>  Documentation/iio/ad9910.rst | 654 +++++++++++++++++++++++++++++++++++++++++++
>  Documentation/iio/index.rst  |   1 +
>  MAINTAINERS                  |   1 +
>  3 files changed, 656 insertions(+)
> 
> diff --git a/Documentation/iio/ad9910.rst b/Documentation/iio/ad9910.rst
> new file mode 100644
> index 000000000000..116f6af4bc2e
> --- /dev/null
> +++ b/Documentation/iio/ad9910.rst
> @@ -0,0 +1,654 @@
> +.. SPDX-License-Identifier: GPL-2.0-only
> +
> +=============
> +AD9910 driver
> +=============
> +
> +DDS (Direct Digital Synthesizer) driver for the Analog Devices Inc. AD9910.
> +The module name is ``ad9910``.
> +
> +* `AD9910 <https://www.analog.com/en/products/ad9910.html>`_
> +
> +The AD9910 is a 1 GSPS DDS with a 14-bit DAC, driven over SPI. The driver
> +exposes the device through the IIO ``altvoltage`` channel type and supports
> +five DDS operating modes: single tone, parallel port modulation, digital ramp
> +generation (DRG), RAM playback and output shift keying (OSK). The device has
> +8 hardware profiles, each capable of storing independent single tone and RAM
> +playback parameters.
> +
> +
> +Channel hierarchy
> +=================
> +
> +The driver exposes the following IIO output channels, each identified by a
> +unique channel number and a human-readable label:
> +
> +* ``out_altvoltage100``: ``phy``: Physical output: system clock and profile control
> +
> +  * ``out_altvoltage110``: ``single_tone``: Single tone mode: per-profile
> +    frequency, phase, amplitude
> +
> +  * ``out_altvoltage120``: ``parallel_port``: Parallel port modulation: enable
> +    and offset/scale parameters
> +
> +  * ``out_altvoltage130``: ``digital_ramp_generator``: DRG control: enable,
> +    destination, operating mode
> +
> +    * ``out_altvoltage131``: ``digital_ramp_up``: DRG ramp-up parameters:
> +      limits, step sizes, ramp rate
> +    * ``out_altvoltage132``: ``digital_ramp_down``: DRG ramp-down parameters:
> +      limits, step sizes, ramp rate
> +
> +  * ``out_altvoltage140``: ``ram_control``: RAM playback: enable, destination,
> +    operating mode, address range
> +
> +  * ``out_altvoltage150``: ``output_shift_keying``: OSK: enable, amplitude
> +    scale, ramp rate, auto/manual control
> +
> +The ``phy`` channel is the root of the hierarchy. Changing its
> +``sampling_frequency`` reconfigures the system clock (SYSCLK) which affects all
> +other channels. The ``profile`` attribute on this channel selects the active
> +hardware profile (0-7) used by the single tone and RAM channels.
I asked out this profile thing in one of the other patches.  Key to me is
that how we write non active profiles?  The most similar thing we've seen
in the past has been setting other frequencies for FSK or phases for PSK or
more mundane DC DAC output that are symbol based. (often an external signal)

For those we have added an additional index so we can see which symbol we
are changing parameters for.  Here it might need to be done in the channel
numbering. I'm not sure.

> +
> +All mode-specific channels (parallel port, DRG, RAM, OSK) have an ``enable``
> +attribute. The DRG and RAM channels additionally have ``destination`` and
> +``operating_mode`` attributes that configure which DDS core parameter is
> +modulated and how.

I wonder if we flatten things out and have separate channels for each type
of modulation. Might lead to a more standard looking interfaces. We don't really
have a standard path to control one type of thing feeding another, whereas
we do have simple 'enable' interfaces.

> +
> +DDS modes
> +=========
> +
> +The AD9910 supports multiple modes of operation that can be configured
> +independently or in combination. Such modes and their corresponding IIO channels
> +are described in this section. The following tables are extracted from the
> +AD9910 datasheet and summarizes the control parameters for each mode and their
> +priority when multiple sources are enabled simultaneously:
> +
> +.. flat-table:: DDS Frequency Control
> +   :header-rows: 1
> +
> +   * - Priority
> +     - Data Source
> +     - Conditions
> +
> +   * - Highest Priority
> +     - RAM
> +     - RAM enabled and data destination is frequency
> +
> +   * -
> +     - DRG
> +     - DRG enabled and data destination is frequency
> +
> +   * -
> +     - Parallel data and FTW (frequency_offset)
> +     - Parallel data port enabled and data destination is frequency
> +
> +   * -
> +     - FTW (frequency)
> +     - RAM enabled and data destination is not frequency
> +
> +   * -
> +     - FTW (frequency) in single tone channel for the active profile
> +     - DRG enabled and data destination is not frequency
> +
> +   * -
> +     - FTW (frequency) in single tone channel for the active profile
> +     - Parallel data port enabled and data destination is not frequency
> +
> +   * - Lowest Priority
> +     - FTW (frequency) in single tone channel for the active profile
> +     - None
> +
> +.. flat-table:: DDS Phase Control
> +   :header-rows: 1
> +
> +   * - Priority
> +     - Data Source
> +     - Conditions
> +
> +   * - Highest Priority
> +     - RAM
> +     - RAM enabled and data destination is phase or polar
> +
> +   * -
> +     - DRG
> +     - DRG enabled and data destination is phase
> +
> +   * -
> +     - Parallel data port
> +     - Parallel data port enabled and data destination is phase
> +
> +   * -
> +     - Parallel data port and POW register LSBs (phase_offset)
> +     - Parallel data port enabled and data destination is polar
> +
> +   * -
> +     - POW (phase)
> +     - RAM enabled and destination is not phase nor polar
> +
> +   * -
> +     - POW (phase) in single tone channel for the active profile
> +     - DRG enabled and data destination is not phase
> +
> +   * -
> +     - POW (phase) in single tone channel for the active profile
> +     - Parallel data port enabled and data destination is not phase nor polar
> +
> +   * - Lowest Priority
> +     - POW (phase) in single tone channel for the active profile
> +     - None
> +
> +.. flat-table:: DDS Amplitude Control
> +   :header-rows: 1
> +
> +   * - Priority
> +     - Data Source
> +     - Conditions
> +
> +   * - Highest Priority
> +     - OSK generator
> +     - OSK enabled (auto mode)
> +
> +   * -
> +     - ASF register
> +     - OSK enabled (manual mode)
> +
> +   * -
> +     - RAM
> +     - RAM enabled and data destination is amplitude or polar
> +
> +   * -
> +     - DRG
> +     - DRG enabled and data destination is amplitude
> +
> +   * -
> +     - Parallel data port
> +     - Parallel data port enabled and data destination is amplitude
> +
> +   * -
> +     - Parallel data port and ASF register LSBs (scale_offset)
> +     - Parallel data port enabled and data destination is polar
> +
> +   * - Lowest Priority
> +     - ASF (scale) in single tone channel for the active profile
> +     - (Amplitude scale is already enabled by default)
> +
> +Single tone mode
> +----------------
> +
> +Single tone is the baseline operating mode. The ``single_tone`` channel
> +provides per-profile frequency, phase and amplitude control:
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``out_altvoltage110_frequency``
> +     - Hz
> +     - Output frequency. Range [0, SYSCLK/2). Stored in the active profile's
> +       frequency tuning word (FTW).
> +
> +   * - ``out_altvoltage110_phase``
> +     - rad
> +     - Phase offset. Range [0, 2*pi). Stored in the active profile's phase
> +       offset word (POW).
> +
> +   * - ``out_altvoltage110_scale``
> +     - fractional
> +     - Amplitude scale factor. Range [0, 1]. Stored in the active profile's
> +       amplitude scale factor (ASF).
> +
> +When RAM mode is enabled, single tone parameters are stored in a shadow
> +register and are not written to hardware until RAM mode is disabled.
> +
> +Usage examples
> +^^^^^^^^^^^^^^
> +
> +Set the active profile to 2 and configure a 100 MHz tone:
> +
> +.. code-block:: bash
> +
> +	echo 2 > /sys/bus/iio/devices/iio:device0/out_altvoltage100_profile
> +	echo 100000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage110_frequency
> +	echo 0.5 > /sys/bus/iio/devices/iio:device0/out_altvoltage110_scale
> +	echo 0 > /sys/bus/iio/devices/iio:device0/out_altvoltage110_phase
> +
> +Read back the current single tone frequency:
> +
> +.. code-block:: bash
> +
> +	cat /sys/bus/iio/devices/iio:device0/out_altvoltage110_frequency
> +
> +Parallel port mode
> +------------------
> +
> +When enabled, the parallel port allows real-time modulation of DDS parameters
> +through a 16-bit external data bus.
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``out_altvoltage120_en``
> +     - boolean
> +     - Enable/disable the parallel data port.
> +
> +   * - ``out_altvoltage120_frequency_scale``
> +     - power-of-2
> +     - FM gain multiplier applied to 16-bit parallel input. Range [1, 32768],
> +       must be a power of 2.
> +
> +   * - ``out_altvoltage120_frequency_offset``
> +     - Hz
> +     - Base FTW to which scaled parallel data is added. Range [0, SYSCLK/2).
> +
> +   * - ``out_altvoltage120_phase_offset``
> +     - rad
> +     - Base phase for polar modulation. Lower 8 bits of POW register.
> +       Range [0, 2*pi/256).
> +
> +   * - ``out_altvoltage120_scale_offset``
> +     - fractional
> +     - Base amplitude for polar modulation. Lower 6 bits of ASF register.
> +       Range [0, 1/256).
> +
> +Usage examples
> +^^^^^^^^^^^^^^
> +
> +Enable parallel port with a frequency scale of 16 and a 50 MHz offset:
> +
> +.. code-block:: bash
> +
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage120_en
> +	echo 16 > /sys/bus/iio/devices/iio:device0/out_altvoltage120_frequency_scale
> +	echo 50000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage120_frequency_offset
> +
> +Digital ramp generator (DRG)
> +----------------------------
> +
> +The DRG produces linear frequency, phase or amplitude sweeps using dedicated
> +hardware. It is controlled through three channels: a parent control channel
> +(``digital_ramp_generator``) and two child ramp channels
> +(``digital_ramp_up``, ``digital_ramp_down``).
> +
> +Control channel attributes
> +^^^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``out_altvoltage130_en``
> +     - boolean
> +     - Enable/disable the DRG.
> +
> +   * - ``out_altvoltage130_destination``
> +     - enum
> +     - Which DDS parameter is swept: ``frequency``, ``phase`` or
> +       ``amplitude``.
> +
> +   * - ``out_altvoltage130_destination_available``
> +     - string
> +     - Lists available destination values.
> +
> +   * - ``out_altvoltage130_operating_mode``
> +     - enum
> +     - Ramp behavior (see table below).
> +
> +   * - ``out_altvoltage130_operating_mode_available``
> +     - string
> +     - Lists available operating mode values.
> +
> +DRG operating modes:
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Mode
> +     - Description
> +
> +   * - ``bidirectional``
> +     - Ramp up then down, dwelling at limits.
> +
> +   * - ``ramp_down``
> +     - No-dwell low; resets to upper limit at lower limit.
> +
> +   * - ``ramp_up``
> +     - No-dwell high; resets to lower limit at upper limit.
> +
> +   * - ``bidirectional_continuous``
> +     - Continuous sweep without dwelling at either limit.
> +
> +Ramp channel attributes
> +^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +The ``digital_ramp_up`` (channel 131) and ``digital_ramp_down`` (channel 132)
> +channels share the same attribute set but configure ascending and descending
> +ramp parameters independently:
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``frequency``
> +     - Hz
> +     - Ramp limit when destination is ``frequency``. Range [0, SYSCLK/2).
> +
> +   * - ``phase``
> +     - rad
> +     - Ramp limit when destination is ``phase``. Range [0, 2*pi).
> +
> +   * - ``scale``
> +     - fractional
> +     - Ramp limit when destination is ``amplitude``. Range [0, 1).
> +
> +   * - ``sampling_frequency``
> +     - Hz
> +     - Ramp clock rate: SYSCLK / (4 * divider).
> +
> +   * - ``frequency_step``
> +     - Hz
> +     - Per-tick frequency increment/decrement when destination is
> +       ``frequency``.
> +
> +   * - ``phase_step``
> +     - rad
> +     - Per-tick phase increment/decrement when destination is ``phase``.
> +
> +   * - ``scale_step``
> +     - fractional
> +     - Per-tick amplitude increment/decrement when destination is
> +       ``amplitude``. Range [0, 1).
> +
> +Usage examples
> +^^^^^^^^^^^^^^
> +
> +Configure a frequency sweep from 10 MHz to 100 MHz at a 1 MHz step:
> +
> +.. code-block:: bash
> +
> +	# Set DRG destination to frequency
> +	echo frequency > /sys/bus/iio/devices/iio:device0/out_altvoltage130_destination
> +
> +	# Set operating mode
> +	echo bidirectional_continuous > /sys/bus/iio/devices/iio:device0/out_altvoltage130_operating_mode
> +
> +	# Set ramp limits
> +	echo 60000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_frequency
> +	echo 40000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage132_frequency
> +
> +	# Set ramp step size to 1 MHz
> +	echo 1000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_frequency_step
> +	echo 1000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage132_frequency_step
> +
> +	# Set ramp clock rate
> +	echo 50000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage131_sampling_frequency
> +
> +	# Enable the DRG
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage130_en
> +
> +Read the current DRG operating mode:
> +
> +.. code-block:: bash
> +
> +	cat /sys/bus/iio/devices/iio:device0/out_altvoltage130_operating_mode
> +
> +RAM mode
> +--------
> +
> +The AD9910 contains a 1024 x 32-bit RAM that can be loaded with waveform data
> +and played back to modulate frequency, phase, amplitude, or polar (phase +
> +amplitude) parameters.
> +
> +RAM control channel attributes
> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``out_altvoltage140_en``
> +     - boolean
> +     - Enable/disable RAM playback. Toggling swaps profile registers between
> +       single tone and RAM configurations across all 8 profiles.
> +
> +   * - ``out_altvoltage140_destination``
> +     - enum
> +     - RAM data target: ``frequency``, ``phase``, ``amplitude`` or ``polar``.
> +       Cannot be changed while RAM mode is enabled.
> +
> +   * - ``out_altvoltage140_destination_available``
> +     - string
> +     - Lists available destination values.
> +
> +   * - ``out_altvoltage140_operating_mode``
> +     - enum
> +     - Playback behavior (see table below).
> +
> +   * - ``out_altvoltage140_operating_mode_available``
> +     - string
> +     - Lists available operating mode values.
> +
> +   * - ``out_altvoltage140_frequency``
> +     - Hz
> +     - Frequency tuning word used as the single tone frequency when
> +       RAM destination is not ``frequency``. Range [0, SYSCLK/2).
> +
> +   * - ``out_altvoltage140_phase``
> +     - rad
> +     - Phase offset word used as the single tone phase when RAM destination
> +       is not ``phase``. Range [0, 2*pi).
> +
> +   * - ``out_altvoltage140_sampling_frequency``
> +     - Hz
> +     - RAM playback step rate controlling how fast the address counter
> +       advances: SYSCLK / (4 * step_rate). Stored per-profile.
> +
> +   * - ``out_altvoltage140_address_start``
Do we need this flexibility to set the start?  We needed a length, but
if we want different effective start can just load a different image.
> +     - integer
> +     - Start address for the active profile. Range [0, 1023]. Cannot be
> +       changed while RAM mode is enabled. If set above current end address,
> +       end address is automatically adjusted.
> +
> +   * - ``out_altvoltage140_address_end``
> +     - integer
> +     - End address for the active profile. Range [address_start, 1023].
> +       Cannot be changed while RAM mode is enabled.
> +
> +RAM operating modes:
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Mode
> +     - Description
> +
> +   * - ``direct_switch``
> +     - Start address defines a fixed word used by the selected profile.
> +
> +   * - ``ramp_up``
> +     - One-shot ramp through the current profile's address range.
> +
> +   * - ``bidirectional``
> +     - Ramp up then down through profile 0's address range.
> +
> +   * - ``bidirectional_continuous``
> +     - Continuous ramp up/down through current profile's address range.
> +
> +   * - ``ramp_up_continuous``
> +     - Continuous ramp up through current profile's address range.
> +
> +   * - ``sequenced``
> +     - Sequential playback from profile 0 to the active profile.
> +       Requires active profile > 0.
> +
> +   * - ``sequenced_continuous``
> +     - Continuous sequential playback. Requires active profile > 0.
> +
> +Loading RAM data
> +^^^^^^^^^^^^^^^^
> +
> +RAM data is loaded through the firmware upload framework. The driver registers
> +a firmware upload device named ``iio_deviceX:ram``. Data must be a multiple of
> +4 bytes (32-bit words) and at most 4096 bytes (1024 words).
> +
> +Usage examples
> +^^^^^^^^^^^^^^
> +
> +Configure RAM mode with frequency destination and load a waveform:
> +
> +.. code-block:: bash
> +
> +	# Set RAM address range for profile 0
> +	echo 0 > /sys/bus/iio/devices/iio:device0/out_altvoltage140_address_start
> +	echo 999 > /sys/bus/iio/devices/iio:device0/out_altvoltage140_address_end
> +
> +	# Set destination and operating mode
> +	echo frequency > /sys/bus/iio/devices/iio:device0/out_altvoltage140_destination
> +	echo ramp_up_continuous > /sys/bus/iio/devices/iio:device0/out_altvoltage140_operating_mode
> +
> +	# Set playback rate
> +	echo 250000 > /sys/bus/iio/devices/iio:device0/out_altvoltage140_sampling_frequency
> +
> +	# Load RAM data via firmware upload
> +	echo 1 > /sys/class/firmware/iio\:device0\:ram/loading
> +	cat waveform.bin > /sys/class/firmware/iio\:device0\:ram/data
> +	echo 0 > /sys/class/firmware/iio\:device0\:ram/loading
> +
> +	# Enable RAM mode
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage140_en
> +
> +Read the current RAM operating mode:
> +
> +.. code-block:: bash
> +
> +	cat /sys/bus/iio/devices/iio:device0/out_altvoltage140_operating_mode
> +
> +Output shift keying (OSK)
> +-------------------------
> +
> +OSK controls the output amplitude envelope, allowing the output to be ramped
> +on/off rather than switched abruptly.
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``out_altvoltage150_en``
> +     - boolean
> +     - Enable/disable OSK.
> +
> +   * - ``out_altvoltage150_scale``
> +     - fractional
> +     - Target amplitude for the OSK ramp. 14-bit ASF field. Range [0, 1).
> +
> +   * - ``out_altvoltage150_sampling_frequency``
> +     - Hz
> +     - OSK ramp rate: SYSCLK / (4 * divider).
> +
> +   * - ``out_altvoltage150_pinctrl_en``
> +     - boolean
> +     - Enable manual external pin control. When enabled, the OSK pin directly
> +       gates the output on/off instead of using the automatic ramp.
> +
> +   * - ``out_altvoltage150_scale_step``
> +     - fractional
> +     - Automatic OSK amplitude step. Writing non-zero enables automatic OSK
> +       and sets the per-tick increment. Writing ``0`` disables it. Rounded to
> +       nearest hardware step: 0.000061, 0.000122, 0.000244 or 0.000488.
> +
> +Usage examples
> +^^^^^^^^^^^^^^
> +
> +Enable OSK with automatic ramping:
> +
> +.. code-block:: bash
> +
> +	# Set ramp rate
> +	echo 1000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_sampling_frequency
> +
> +	# Enable automatic OSK with step size
> +	echo 0.000244 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale_step
> +
> +	# Enable OSK
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_en
> +
> +Enable manual pin-controlled OSK:
> +
> +.. code-block:: bash
> +
> +	# Set target amplitude to full scale
> +	echo 1.0 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_scale
> +
> +	# Enable manual pin control
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_pinctrl_en
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage150_en
> +
> +
> +Physical channel
> +================
> +
> +The ``phy`` channel provides device-level control:
> +
> +.. flat-table::
> +   :header-rows: 1
> +
> +   * - Attribute
> +     - Unit
> +     - Description
> +
> +   * - ``out_altvoltage100_sampling_frequency``
> +     - Hz
> +     - System clock (SYSCLK) frequency. With PLL enabled, configures the PLL
> +       multiplier (range 420-1000 MHz). Without PLL, ref clock can only be
> +       divided by 2.
> +
> +   * - ``out_altvoltage100_profile``
> +     - integer
> +     - Active profile index [0, 7]. Selected via GPIO pins. Each profile
> +       stores an independent set of single tone and RAM playback parameters.
> +
> +   * - ``out_altvoltage100_powerdown``
> +     - boolean
> +     - Software power-down. Writing 1 powers down the digital core, DAC,
> +       reference clock input and auxiliary DAC simultaneously.
> +
> +Usage examples
> +--------------
> +
> +Set the system clock to 1 GHz and select profile 3:
> +
> +.. code-block:: bash
> +
> +	echo 1000000000 > /sys/bus/iio/devices/iio:device0/out_altvoltage100_sampling_frequency
> +	echo 3 > /sys/bus/iio/devices/iio:device0/out_altvoltage100_profile
> +
> +Read current system clock frequency:
> +
> +.. code-block:: bash
> +
> +	cat /sys/bus/iio/devices/iio:device0/out_altvoltage100_sampling_frequency
> +
> +Power down the device:
> +
> +.. code-block:: bash
> +
> +	echo 1 > /sys/bus/iio/devices/iio:device0/out_altvoltage100_powerdown
> diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
> index ba3e609c6a13..55cb1ce84ba8 100644
> --- a/Documentation/iio/index.rst
> +++ b/Documentation/iio/index.rst
> @@ -29,6 +29,7 @@ Industrial I/O Kernel Drivers
>     ad7606
>     ad7625
>     ad7944
> +   ad9910
>     ade9000
>     adis16475
>     adis16480
> diff --git a/MAINTAINERS b/MAINTAINERS
> index edd87ee7da5f..14e4272357ce 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1637,6 +1637,7 @@ S:	Supported
>  W:	https://ez.analog.com/linux-software-drivers
>  F:	Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910
>  F:	Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml
> +F:	Documentation/iio/ad9910.rst
>  F:	drivers/iio/frequency/ad9910.c
>  
>  ANALOG DEVICES INC MAX22007 DRIVER
> 


^ permalink raw reply

* Re: [PATCH v4 7/9] bus: mhi: Capture DDR training data using command mode
From: kernel test robot @ 2026-03-22 17:39 UTC (permalink / raw)
  To: Kishore Batta, Jonathan Corbet, Shuah Khan, Jeff Hugo,
	Carl Vanderlip, Oded Gabbay, Manivannan Sadhasivam, andersson
  Cc: oe-kbuild-all, linux-doc, linux-kernel, linux-arm-msm, dri-devel,
	mhi, Kishore Batta
In-Reply-To: <20260319-sahara_protocol_new_v2-v4-7-47ad79308762@oss.qualcomm.com>

Hi Kishore,

kernel test robot noticed the following build warnings:

[auto build test WARNING on a0ae2a256046c0c5d3778d1a194ff2e171f16e5f]

url:    https://github.com/intel-lab-lkp/linux/commits/Kishore-Batta/Add-documentation-for-Sahara-protocol/20260320-144614
base:   a0ae2a256046c0c5d3778d1a194ff2e171f16e5f
patch link:    https://lore.kernel.org/r/20260319-sahara_protocol_new_v2-v4-7-47ad79308762%40oss.qualcomm.com
patch subject: [PATCH v4 7/9] bus: mhi: Capture DDR training data using command mode
config: i386-randconfig-141-20260322 (https://download.01.org/0day-ci/archive/20260323/202603230107.6EzMoVPn-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
smatch: v0.5.0-9004-gb810ac53

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603230107.6EzMoVPn-lkp@intel.com/

New smatch warnings:
drivers/bus/mhi/sahara/sahara.c:639 sahara_command_execute_resp() warn: unsigned '(context->rx->command_execute_resp.response_length)' is never less than zero.

Old smatch warnings:
drivers/bus/mhi/sahara/sahara.c:353 sahara_select_variant() warn: this array is probably non-NULL. 'id->chan'

vim +639 drivers/bus/mhi/sahara/sahara.c

   628	
   629	static void sahara_command_execute_resp(struct sahara_context *context)
   630	{
   631		struct device *dev = &context->mhi_dev->mhi_cntrl->mhi_dev->dev;
   632		struct sahara_ctrl_trng_data *ct;
   633		u32 client_cmd, resp_len;
   634		int ret;
   635		u64 remaining;
   636		u32 i;
   637	
   638		if (le32_to_cpu(context->rx->length) != SAHARA_COMMAND_EXEC_RESP_LENGTH ||
 > 639		    le32_to_cpu(context->rx->command_execute_resp.response_length) < 0) {
   640			dev_err(&context->mhi_dev->dev,
   641				"Malformed command execute resp packet - length %d\n",
   642				le32_to_cpu(context->rx->length));
   643			return;
   644		}
   645	
   646		client_cmd = le32_to_cpu(context->rx->command_execute_resp.client_command);
   647		resp_len = le32_to_cpu(context->rx->command_execute_resp.response_length);
   648	
   649		sahara_command_execute_data(context, client_cmd);
   650	
   651		if (client_cmd == SAHARA_EXEC_CMD_GET_COMMAND_ID_LIST) {
   652			sahara_command_execute(context, SAHARA_EXEC_CMD_GET_TRAINING_DATA);
   653			return;
   654		}
   655	
   656		if (client_cmd != SAHARA_EXEC_CMD_GET_TRAINING_DATA)
   657			return;
   658	
   659		ct = sahara_ctrl_trng_get(dev);
   660		if (!ct) {
   661			context->is_cmd_mode = false;
   662			sahara_switch_mode_to_img_tx(context);
   663			return;
   664		}
   665	
   666		mutex_lock(&ct->lock);
   667		kfree(ct->data);
   668		ct->data = kzalloc(resp_len, GFP_KERNEL);
   669		ct->size = resp_len;
   670		ct->copied = 0;
   671		ct->receiving = true;
   672		mutex_unlock(&ct->lock);
   673	
   674		if (!ct->data) {
   675			context->is_cmd_mode = false;
   676			sahara_switch_mode_to_img_tx(context);
   677			return;
   678		}
   679	
   680		context->trng_size = resp_len;
   681		context->trng_rcvd = 0;
   682		context->receiving_trng_data = true;
   683	
   684		remaining = resp_len;
   685		for (i = 0; i < SAHARA_NUM_CMD_BUF && remaining; i++) {
   686			size_t pkt = min_t(size_t, remaining, SAHARA_PACKET_MAX_SIZE);
   687	
   688			ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE,
   689					    context->cmd_buff[i], pkt,
   690					    (remaining <= pkt) ? MHI_EOT : MHI_CHAIN);
   691			if (ret)
   692				break;
   693	
   694			remaining -= pkt;
   695		}
   696	
   697		context->trng_nbuf = i;
   698	}
   699	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* [PATCH v2] docs: add advanced search for kernel documentation
From: Rito Rhymes @ 2026-03-22 18:17 UTC (permalink / raw)
  To: Jonathan Corbet, Mauro Carvalho Chehab, linux-doc
  Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260321181511.11706-1-rito@ritovision.com>

Replace the stock Sphinx search page with one that reuses the
existing searchindex.js while adding structured result grouping,
filtering, and exact identifier matching.

Results are grouped into Symbols, Sections, Index entries, and
Pages, each in a collapsible section with a count. An Advanced
panel exposes filters for documentation area, object type, result
kind, and exact match mode. All state is URL-encoded so searches
remain shareable.

Page summary snippets are lazy-loaded via IntersectionObserver to
avoid fetching every matching page up front.

The sidebar keeps the existing quick-search box and adds an
"Advanced search" link below it.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
Assisted-by: Claude:Opus-4.6
---
v2: add Assisted-by attribution

 Documentation/sphinx-static/custom.css        | 163 ++++
 Documentation/sphinx-static/kernel-search.js  | 746 ++++++++++++++++++
 Documentation/sphinx/templates/search.html    | 106 +++
 Documentation/sphinx/templates/searchbox.html |  18 +
 4 files changed, 1033 insertions(+)
 create mode 100644 Documentation/sphinx-static/kernel-search.js
 create mode 100644 Documentation/sphinx/templates/search.html
 create mode 100644 Documentation/sphinx/templates/searchbox.html

diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..dd7cc221e 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -169,3 +169,166 @@ a.manpage {
 	font-weight: bold;
 	font-family: "Courier New", Courier, monospace;
 }
+
+/* Keep the quick search box as-is and add a secondary advanced search link. */
+div.sphinxsidebar p.search-advanced-link {
+    margin: 0.5em 0 0 0;
+    font-size: 0.95em;
+}
+
+/*
+ * The enhanced search page keeps the stock GET workflow but adds
+ * filter controls and grouped results.
+ */
+form.kernel-search-form {
+    margin-bottom: 2em;
+}
+
+div.kernel-search-query-row {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 1em;
+    align-items: end;
+}
+
+div.kernel-search-query-field {
+    flex: 1 1 26em;
+}
+
+div.kernel-search-query-field label,
+div.kernel-search-field label,
+fieldset.kernel-search-kind-filters legend {
+    display: block;
+    font-weight: bold;
+    margin-bottom: 0.35em;
+}
+
+div.kernel-search-query-field input[type="text"],
+div.kernel-search-field select {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+div.kernel-search-query-actions {
+    display: flex;
+    align-items: center;
+    gap: 0.75em;
+}
+
+span.kernel-search-progress {
+    min-height: 1.2em;
+    color: #666;
+}
+
+details.kernel-search-advanced {
+    margin-top: 1em;
+    padding: 0.75em 1em 1em 1em;
+    border: 1px solid #cccccc;
+    background: #f7f7f7;
+}
+
+details.kernel-search-advanced summary {
+    cursor: pointer;
+    font-weight: bold;
+}
+
+div.kernel-search-advanced-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(16em, 1fr));
+    gap: 1em 1.25em;
+    margin-top: 1em;
+}
+
+fieldset.kernel-search-kind-filters {
+    margin: 0;
+    padding: 0;
+    border: none;
+}
+
+label.kernel-search-checkbox {
+    display: flex;
+    align-items: flex-start;
+    gap: 0.5em;
+    margin-bottom: 0.35em;
+}
+
+div.kernel-search-results {
+    margin-top: 1.5em;
+}
+
+p.kernel-search-status {
+    margin-bottom: 1.5em;
+}
+
+details.kernel-search-group {
+    margin-top: 2em;
+}
+
+summary.kernel-search-group-summary {
+    cursor: pointer;
+    font-size: 150%;
+    margin: 0 0 0.6em 0;
+}
+
+summary.kernel-search-group-summary h2.kernel-search-group-title {
+    display: inline;
+    margin: 0;
+    font-size: inherit;
+    font-weight: normal;
+}
+
+span.kernel-search-group-count {
+    color: #666666;
+    margin-left: 0.35em;
+}
+
+ol.kernel-search-list {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+li.kernel-search-result {
+    padding: 0.9em 0;
+    border-top: 1px solid #dddddd;
+}
+
+li.kernel-search-result:first-child {
+    border-top: none;
+}
+
+div.kernel-search-result-heading {
+    font-weight: bold;
+}
+
+div.kernel-search-path,
+div.kernel-search-meta,
+p.kernel-search-summary {
+    margin-top: 0.3em;
+    color: #555555;
+}
+
+div.kernel-search-path,
+div.kernel-search-meta {
+    font-size: 0.95em;
+}
+
+p.kernel-search-summary {
+    margin-bottom: 0;
+}
+
+@media screen and (max-width: 65em) {
+    div.kernel-search-query-actions {
+        width: 100%;
+        justify-content: flex-start;
+    }
+}
+
+@media screen and (min-width: 65em) {
+    div.kernel-search-result-heading,
+    div.kernel-search-path,
+    div.kernel-search-meta,
+    p.kernel-search-summary {
+        margin-left: 2rem;
+    }
+}
diff --git a/Documentation/sphinx-static/kernel-search.js b/Documentation/sphinx-static/kernel-search.js
new file mode 100644
index 000000000..bcf79f820
--- /dev/null
+++ b/Documentation/sphinx-static/kernel-search.js
@@ -0,0 +1,746 @@
+"use strict";
+
+(() => {
+  const RESULT_KIND_ORDER = ["object", "title", "index", "text"];
+  const RESULT_KIND_LABELS = {
+    object: "Symbols",
+    title: "Sections",
+    index: "Index entries",
+    text: "Pages",
+  };
+  const TOP_LEVEL_AREA = "__top_level__";
+  const OBJECT_PRIORITY = {
+    0: 15,
+    1: 5,
+    2: -5,
+  };
+  const SUMMARY_ROOT_MARGIN = "300px 0px";
+  const documentTextCache = new Map();
+  const summaryTargets = new WeakMap();
+  let summaryObserver = null;
+
+  window.Search = window.Search || {};
+  window.Search._callbacks = window.Search._callbacks || [];
+  window.Search._index = window.Search._index || null;
+  window.Search.setIndex = (index) => {
+    window.Search._index = index;
+    const callbacks = window.Search._callbacks.slice();
+    window.Search._callbacks.length = 0;
+    callbacks.forEach((callback) => callback(index));
+  };
+  window.Search.whenReady = (callback) => {
+    if (window.Search._index) callback(window.Search._index);
+    else window.Search._callbacks.push(callback);
+  };
+
+  const splitQuery = (query) =>
+    query
+      .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
+      .filter((term) => term);
+
+  const getStemmer = () =>
+    typeof Stemmer === "function" ? new Stemmer() : { stemWord: (word) => word };
+
+  const hasOwn = (object, key) =>
+    Object.prototype.hasOwnProperty.call(object, key);
+
+  const getContentRoot = () =>
+    document.documentElement.dataset.content_root
+    || (typeof DOCUMENTATION_OPTIONS !== "undefined" ? DOCUMENTATION_OPTIONS.URL_ROOT || "" : "");
+
+  const compareResults = (left, right) => {
+    if (left.score === right.score) {
+      const leftTitle = left.title.toLowerCase();
+      const rightTitle = right.title.toLowerCase();
+      if (leftTitle === rightTitle) return 0;
+      return leftTitle < rightTitle ? -1 : 1;
+    }
+    return right.score - left.score;
+  };
+
+  const getAreaValue = (docName) =>
+    docName.includes("/") ? docName.split("/", 1)[0] : TOP_LEVEL_AREA;
+
+  const getAreaLabel = (area) =>
+    area === TOP_LEVEL_AREA ? "Top level" : area;
+
+  const matchArea = (docName, area) => {
+    if (!area) return true;
+    if (area === TOP_LEVEL_AREA) return !docName.includes("/");
+    return docName === area || docName.startsWith(area + "/");
+  };
+
+  const buildDocUrls = (docName) => {
+    const contentRoot = getContentRoot();
+    const builder = DOCUMENTATION_OPTIONS.BUILDER;
+    const fileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
+    const linkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
+
+    if (builder === "dirhtml") {
+      let dirname = docName + "/";
+      if (dirname.match(/\/index\/$/)) dirname = dirname.substring(0, dirname.length - 6);
+      else if (dirname === "index/") dirname = "";
+
+      return {
+        requestUrl: contentRoot + dirname,
+        linkUrl: contentRoot + dirname,
+      };
+    }
+
+    return {
+      requestUrl: contentRoot + docName + fileSuffix,
+      linkUrl: docName + linkSuffix,
+    };
+  };
+
+  const htmlToText = (htmlString, anchor) => {
+    const htmlElement = new DOMParser().parseFromString(htmlString, "text/html");
+    for (const selector of [".headerlink", "script", "style"]) {
+      htmlElement.querySelectorAll(selector).forEach((element) => element.remove());
+    }
+
+    if (anchor) {
+      const anchorId = anchor[0] === "#" ? anchor.substring(1) : anchor;
+      const anchorContent = htmlElement.getElementById(anchorId);
+      if (anchorContent) return anchorContent.textContent;
+    }
+
+    const docContent = htmlElement.querySelector('[role="main"]');
+    return docContent ? docContent.textContent : "";
+  };
+
+  const makeSummary = (htmlText, keywords, anchor) => {
+    const text = htmlToText(htmlText, anchor);
+    if (!text) return null;
+
+    const lowered = text.toLowerCase();
+    const positions = keywords
+      .map((keyword) => lowered.indexOf(keyword.toLowerCase()))
+      .filter((position) => position > -1);
+    const actualStart = positions.length ? positions[0] : 0;
+    const start = Math.max(actualStart - 120, 0);
+    const prefix = start === 0 ? "" : "...";
+    const suffix = start + 240 < text.length ? "..." : "";
+
+    const summary = document.createElement("p");
+    summary.className = "kernel-search-summary";
+    summary.textContent = prefix + text.substring(start, start + 240).trim() + suffix;
+    return summary;
+  };
+
+  const loadDocumentText = (requestUrl) => {
+    if (!documentTextCache.has(requestUrl)) {
+      documentTextCache.set(
+        requestUrl,
+        fetch(requestUrl)
+          .then((response) => (response.ok ? response.text() : ""))
+          .catch(() => ""),
+      );
+    }
+
+    return documentTextCache.get(requestUrl);
+  };
+
+  const pushBest = (resultMap, result) => {
+    const key = [result.kind, result.docName, result.anchor || "", result.title].join("|");
+    const existing = resultMap.get(key);
+    if (!existing || existing.score < result.score) resultMap.set(key, result);
+  };
+
+  const buildQueryState = (query, exact) => {
+    const rawTerms = splitQuery(query.trim());
+    const rawTermsLower = rawTerms.map((term) => term.toLowerCase());
+    const objectTerms = new Set(rawTermsLower);
+    const highlightTerms = exact ? rawTermsLower : [];
+    const searchTerms = new Set();
+    const excludedTerms = new Set();
+
+    if (!exact) {
+      const stemmer = getStemmer();
+      rawTerms.forEach((term) => {
+        const lowered = term.toLowerCase();
+        if ((typeof stopwords !== "undefined" && stopwords.has(lowered)) || /^\d+$/.test(term)) {
+          return;
+        }
+
+        const word = stemmer.stemWord(lowered);
+        if (!word) return;
+
+        if (word[0] === "-") excludedTerms.add(word.substring(1));
+        else {
+          searchTerms.add(word);
+          highlightTerms.push(lowered);
+        }
+      });
+    } else {
+      rawTermsLower.forEach((term) => searchTerms.add(term));
+    }
+
+    if (typeof SPHINX_HIGHLIGHT_ENABLED !== "undefined" && SPHINX_HIGHLIGHT_ENABLED) {
+      localStorage.setItem("sphinx_highlight_terms", [...new Set(highlightTerms)].join(" "));
+    }
+
+    return {
+      exact,
+      query,
+      queryLower: query.toLowerCase().trim(),
+      rawTerms: rawTermsLower,
+      objectTerms,
+      searchTerms,
+      excludedTerms,
+      highlightTerms: [...new Set(highlightTerms)],
+    };
+  };
+
+  const candidateMatches = (candidateLower, state) => {
+    if (!state.queryLower) return false;
+    if (state.exact) return candidateLower === state.queryLower;
+
+    if (
+      candidateLower.includes(state.queryLower)
+      && state.queryLower.length >= Math.ceil(candidateLower.length / 2)
+    ) {
+      return true;
+    }
+
+    return state.rawTerms.length > 0
+      && state.rawTerms.every((term) => candidateLower.includes(term));
+  };
+
+  const scoreLabelMatch = (candidateLower, state, baseScore, partialScore) => {
+    if (state.exact) return baseScore + 20;
+    if (candidateLower === state.queryLower) return baseScore + 10;
+    if (candidateLower.includes(state.queryLower)) {
+      return Math.max(partialScore, Math.round((baseScore * state.queryLower.length) / candidateLower.length));
+    }
+
+    return partialScore * Math.max(1, state.rawTerms.filter((term) => candidateLower.includes(term)).length);
+  };
+
+  const collectObjectResults = (index, state, filters) => {
+    const resultMap = new Map();
+    const objects = index.objects || {};
+    const objNames = index.objnames || {};
+    const objTypes = index.objtypes || {};
+
+    Object.keys(objects).forEach((prefix) => {
+      objects[prefix].forEach((match) => {
+        const fileIndex = match[0];
+        const typeIndex = match[1];
+        const priority = match[2];
+        const anchorValue = match[3];
+        const name = match[4];
+        const docName = index.docnames[fileIndex];
+        const fileName = index.filenames[fileIndex];
+        const pageTitle = index.titles[fileIndex];
+        const objectLabel = objNames[typeIndex] ? objNames[typeIndex][2] : "Object";
+        const objectType = objTypes[typeIndex];
+
+        if (!matchArea(docName, filters.area)) return;
+        if (filters.objtype && filters.objtype !== objectType) return;
+
+        const fullName = prefix ? prefix + "." + name : name;
+        const fullNameLower = fullName.toLowerCase();
+        const lastNameLower = fullNameLower.split(".").slice(-1)[0];
+        const nameLower = name.toLowerCase();
+
+        let score = 0;
+        if (state.exact) {
+          if (
+            fullNameLower !== state.queryLower
+            && lastNameLower !== state.queryLower
+            && nameLower !== state.queryLower
+          ) {
+            return;
+          }
+          score = 120;
+        } else {
+          const haystack = `${fullName} ${objectLabel} ${pageTitle}`.toLowerCase();
+          if (state.objectTerms.size === 0) return;
+          if ([...state.objectTerms].some((term) => !haystack.includes(term))) return;
+          const matchedNameTerms = state.rawTerms.filter(
+            (term) =>
+              fullNameLower.includes(term)
+              || lastNameLower.includes(term)
+              || nameLower.includes(term),
+          ).length;
+
+          if (
+            fullNameLower === state.queryLower
+            || lastNameLower === state.queryLower
+            || nameLower === state.queryLower
+          ) {
+            score += 11;
+          } else if (
+            lastNameLower.includes(state.queryLower)
+            || nameLower.includes(state.queryLower)
+          ) {
+            score += 6;
+          } else if (fullNameLower.includes(state.queryLower)) {
+            score += 4;
+          } else if (matchedNameTerms > 0) {
+            score += matchedNameTerms;
+          } else {
+            return;
+          }
+        }
+
+        score += OBJECT_PRIORITY[priority] || 0;
+
+        let anchor = anchorValue;
+        if (anchor === "") anchor = fullName;
+        else if (anchor === "-" && objNames[typeIndex]) anchor = objNames[typeIndex][1] + "-" + fullName;
+
+        pushBest(resultMap, {
+          kind: "object",
+          docName,
+          fileName,
+          title: fullName,
+          anchor: anchor ? "#" + anchor : "",
+          description: `${objectLabel}, in ${pageTitle}`,
+          score,
+        });
+      });
+    });
+
+    return [...resultMap.values()].sort(compareResults);
+  };
+
+  const collectSectionResults = (index, state, filters) => {
+    const resultMap = new Map();
+    const allTitles = index.alltitles || {};
+
+    Object.entries(allTitles).forEach(([sectionTitle, entries]) => {
+      const lowered = sectionTitle.toLowerCase().trim();
+      if (!candidateMatches(lowered, state)) return;
+
+      entries.forEach(([fileIndex, anchorId]) => {
+        const docName = index.docnames[fileIndex];
+        const fileName = index.filenames[fileIndex];
+        const pageTitle = index.titles[fileIndex];
+        if (!matchArea(docName, filters.area)) return;
+
+        if (anchorId === null && sectionTitle === pageTitle) return;
+
+        pushBest(resultMap, {
+          kind: "title",
+          docName,
+          fileName,
+          title: pageTitle !== sectionTitle ? `${pageTitle} > ${sectionTitle}` : sectionTitle,
+          anchor: anchorId ? "#" + anchorId : "",
+          description: pageTitle,
+          score: scoreLabelMatch(lowered, state, 15, 7),
+        });
+      });
+    });
+
+    return [...resultMap.values()].sort(compareResults);
+  };
+
+  const collectIndexResults = (index, state, filters) => {
+    const resultMap = new Map();
+    const entries = index.indexentries || {};
+
+    Object.entries(entries).forEach(([entry, matches]) => {
+      const lowered = entry.toLowerCase().trim();
+      if (!candidateMatches(lowered, state)) return;
+
+      matches.forEach(([fileIndex, anchorId, isMain]) => {
+        const docName = index.docnames[fileIndex];
+        const fileName = index.filenames[fileIndex];
+        const pageTitle = index.titles[fileIndex];
+        if (!matchArea(docName, filters.area)) return;
+
+        let score = scoreLabelMatch(lowered, state, 20, 8);
+        if (!isMain) score -= 5;
+
+        pushBest(resultMap, {
+          kind: "index",
+          docName,
+          fileName,
+          title: entry,
+          anchor: anchorId ? "#" + anchorId : "",
+          description: pageTitle,
+          score,
+        });
+      });
+    });
+
+    return [...resultMap.values()].sort(compareResults);
+  };
+
+  const collectTextResults = (index, state, filters) => {
+    const resultMap = new Map();
+    const terms = index.terms || {};
+    const titleTerms = index.titleterms || {};
+    const searchTerms = [...state.searchTerms];
+
+    if (searchTerms.length === 0) return [];
+
+    const scoreMap = new Map();
+    const fileMap = new Map();
+
+    searchTerms.forEach((word) => {
+      const files = [];
+      const candidates = [
+        {
+          files: hasOwn(terms, word) ? terms[word] : undefined,
+          score: 5,
+        },
+        {
+          files: hasOwn(titleTerms, word) ? titleTerms[word] : undefined,
+          score: 15,
+        },
+      ];
+
+      if (!state.exact && word.length > 2) {
+        if (!hasOwn(terms, word)) {
+          Object.keys(terms).forEach((term) => {
+            if (term.includes(word)) candidates.push({ files: terms[term], score: 2 });
+          });
+        }
+        if (!hasOwn(titleTerms, word)) {
+          Object.keys(titleTerms).forEach((term) => {
+            if (term.includes(word)) candidates.push({ files: titleTerms[term], score: 7 });
+          });
+        }
+      }
+
+      if (candidates.every((candidate) => candidate.files === undefined)) return;
+
+      candidates.forEach((candidate) => {
+        if (candidate.files === undefined) return;
+
+        let recordFiles = candidate.files;
+        if (recordFiles.length === undefined) recordFiles = [recordFiles];
+        files.push(...recordFiles);
+
+        recordFiles.forEach((fileIndex) => {
+          if (!scoreMap.has(fileIndex)) scoreMap.set(fileIndex, new Map());
+          const currentScore = scoreMap.get(fileIndex).get(word) || 0;
+          scoreMap.get(fileIndex).set(word, Math.max(currentScore, candidate.score));
+        });
+      });
+
+      files.forEach((fileIndex) => {
+        if (!fileMap.has(fileIndex)) fileMap.set(fileIndex, [word]);
+        else if (!fileMap.get(fileIndex).includes(word)) fileMap.get(fileIndex).push(word);
+      });
+    });
+
+    const filteredTermCount = state.exact
+      ? searchTerms.length
+      : searchTerms.filter((term) => term.length > 2).length;
+
+    for (const [fileIndex, matchedWords] of fileMap.entries()) {
+      const docName = index.docnames[fileIndex];
+      const fileName = index.filenames[fileIndex];
+      if (!matchArea(docName, filters.area)) continue;
+
+      if (matchedWords.length !== searchTerms.length && matchedWords.length !== filteredTermCount) {
+        continue;
+      }
+
+      if (
+        [...state.excludedTerms].some(
+          (term) =>
+            terms[term] === fileIndex
+            || titleTerms[term] === fileIndex
+            || (terms[term] || []).includes(fileIndex)
+            || (titleTerms[term] || []).includes(fileIndex),
+        )
+      ) {
+        continue;
+      }
+
+      let score = Math.max(...matchedWords.map((word) => scoreMap.get(fileIndex).get(word)));
+      if (state.exact && index.titles[fileIndex].toLowerCase() === state.queryLower) score += 10;
+
+      pushBest(resultMap, {
+        kind: "text",
+        docName,
+        fileName,
+        title: index.titles[fileIndex],
+        anchor: "",
+        description: null,
+        score,
+      });
+    }
+
+    return [...resultMap.values()].sort(compareResults);
+  };
+
+  const buildFilters = (state) => ({
+    area: state.area,
+    objtype: state.objtype,
+  });
+
+  const ensureSummaryObserver = () => {
+    if (summaryObserver || typeof IntersectionObserver !== "function") return summaryObserver;
+
+    summaryObserver = new IntersectionObserver((entries) => {
+      entries.forEach((entry) => {
+        if (!entry.isIntersecting) return;
+
+        const target = entry.target;
+        summaryObserver.unobserve(target);
+        const payload = summaryTargets.get(target);
+        if (!payload || payload.loaded) return;
+
+        payload.loaded = true;
+        loadDocumentText(payload.requestUrl).then((htmlText) => {
+          if (!htmlText) return;
+
+          const summary = makeSummary(htmlText, payload.keywords, payload.anchor);
+          if (!summary) return;
+
+          payload.item.appendChild(summary);
+        });
+      });
+    }, { rootMargin: SUMMARY_ROOT_MARGIN });
+
+    return summaryObserver;
+  };
+
+  const queueSummaryLoad = (result, item, keywords) => {
+    const urls = buildDocUrls(result.docName);
+    const payload = {
+      anchor: result.anchor,
+      item,
+      keywords,
+      loaded: false,
+      requestUrl: urls.requestUrl,
+    };
+
+    const observer = ensureSummaryObserver();
+    if (!observer) {
+      payload.loaded = true;
+      loadDocumentText(payload.requestUrl).then((htmlText) => {
+        if (!htmlText) return;
+
+        const summary = makeSummary(htmlText, keywords, result.anchor);
+        if (summary) item.appendChild(summary);
+      });
+      return;
+    }
+
+    summaryTargets.set(item, payload);
+    observer.observe(item);
+  };
+
+  const createResultItem = (result, keywords) => {
+    const urls = buildDocUrls(result.docName);
+    const item = document.createElement("li");
+    item.className = `kernel-search-result kind-${result.kind}`;
+
+    const heading = item.appendChild(document.createElement("div"));
+    heading.className = "kernel-search-result-heading";
+
+    const link = heading.appendChild(document.createElement("a"));
+    link.href = urls.linkUrl + result.anchor;
+    link.dataset.score = String(result.score);
+    link.textContent = result.title;
+
+    const path = item.appendChild(document.createElement("div"));
+    path.className = "kernel-search-path";
+    path.textContent = result.fileName;
+
+    if (result.description) {
+      const meta = item.appendChild(document.createElement("div"));
+      meta.className = "kernel-search-meta";
+      meta.textContent = result.description;
+    }
+
+    if (result.kind === "text") {
+      queueSummaryLoad(result, item, keywords);
+    }
+    return item;
+  };
+
+  const renderResults = (state, resultsByKind) => {
+    const container = document.getElementById("kernel-search-results");
+    const totalResults = RESULT_KIND_ORDER.reduce(
+      (count, kind) => count + resultsByKind[kind].length,
+      0,
+    );
+    if (summaryObserver) {
+      summaryObserver.disconnect();
+      summaryObserver = null;
+    }
+    container.replaceChildren();
+
+    const summary = document.createElement("p");
+    summary.className = "kernel-search-status";
+    if (!state.queryLower) {
+      summary.textContent = "Enter a search query to browse kernel documentation.";
+      container.appendChild(summary);
+      return;
+    }
+
+    if (!totalResults) {
+      summary.textContent =
+        "No matching results were found for the current query and filters.";
+      container.appendChild(summary);
+      return;
+    }
+
+    summary.textContent =
+      `Found ${totalResults} result${totalResults === 1 ? "" : "s"} for "${state.query}".`;
+    container.appendChild(summary);
+
+    RESULT_KIND_ORDER.forEach((kind) => {
+      const results = resultsByKind[kind];
+      if (!results.length) return;
+
+      const group = container.appendChild(document.createElement("details"));
+      group.className = `kernel-search-group kind-${kind}`;
+      group.open = true;
+
+      const summary = group.appendChild(document.createElement("summary"));
+      summary.className = "kernel-search-group-summary";
+
+      const heading = summary.appendChild(document.createElement("h2"));
+      heading.className = "kernel-search-group-title";
+      heading.textContent = RESULT_KIND_LABELS[kind];
+
+      const count = summary.appendChild(document.createElement("span"));
+      count.className = "kernel-search-group-count";
+      count.textContent = `(${results.length})`;
+
+      const list = group.appendChild(document.createElement("ol"));
+      list.className = "kernel-search-list";
+
+      results.forEach((result) => {
+        list.appendChild(createResultItem(result, state.highlightTerms));
+      });
+    });
+  };
+
+  const populateAreaOptions = (select, state) => {
+    const areas = new Set();
+    window.Search._index.docnames.forEach((docName) => areas.add(getAreaValue(docName)));
+
+    const options = [new Option("All documentation areas", "", false, !state.area)];
+    [...areas]
+      .sort((left, right) => {
+        if (left === TOP_LEVEL_AREA) return -1;
+        if (right === TOP_LEVEL_AREA) return 1;
+        return left.localeCompare(right);
+      })
+      .forEach((area) => {
+        options.push(new Option(getAreaLabel(area), area, false, area === state.area));
+      });
+
+    select.replaceChildren(...options);
+  };
+
+  const populateObjectTypeOptions = (select, state) => {
+    const objTypes = window.Search._index.objtypes || {};
+    const objNames = window.Search._index.objnames || {};
+    const entries = Object.keys(objTypes)
+      .map((key) => ({
+        value: objTypes[key],
+        label: objNames[key] ? objNames[key][2] : objTypes[key],
+      }))
+      .sort((left, right) => left.label.localeCompare(right.label));
+
+    const seen = new Set();
+    const options = [new Option("All object types", "", false, !state.objtype)];
+    entries.forEach((entry) => {
+      if (seen.has(entry.value)) return;
+      seen.add(entry.value);
+      options.push(new Option(entry.label, entry.value, false, entry.value === state.objtype));
+    });
+
+    select.replaceChildren(...options);
+  };
+
+  const parseState = () => {
+    const params = new URLSearchParams(window.location.search);
+    const kinds = params.getAll("kind").filter((kind) => RESULT_KIND_ORDER.includes(kind));
+
+    return {
+      query: params.get("q") || "",
+      queryLower: (params.get("q") || "").toLowerCase().trim(),
+      exact: params.get("exact") === "1",
+      area: params.get("area") || "",
+      objtype: params.get("objtype") || "",
+      advanced: params.get("advanced") === "1",
+      kinds: kinds.length ? new Set(kinds) : new Set(RESULT_KIND_ORDER),
+    };
+  };
+
+  const shouldOpenAdvanced = (state) =>
+    state.advanced
+    || state.exact
+    || state.area !== ""
+    || state.objtype !== ""
+    || RESULT_KIND_ORDER.some((kind) => !state.kinds.has(kind));
+
+  const bindFormState = (state) => {
+    document.getElementById("kernel-search-query").value = state.query;
+    document.getElementById("kernel-search-exact").checked = state.exact;
+    RESULT_KIND_ORDER.forEach((kind) => {
+      const checkbox = document.getElementById(`kernel-search-kind-${kind}`);
+      if (checkbox) checkbox.checked = state.kinds.has(kind);
+    });
+
+    const advanced = document.getElementById("kernel-search-advanced");
+    const advancedFlag = document.getElementById("kernel-search-advanced-flag");
+    const open = shouldOpenAdvanced(state);
+    advanced.open = open;
+    advancedFlag.disabled = !open;
+    advanced.addEventListener("toggle", () => {
+      advancedFlag.disabled = !advanced.open;
+    });
+  };
+
+  const runSearch = () => {
+    const baseState = parseState();
+    bindFormState(baseState);
+    populateAreaOptions(document.getElementById("kernel-search-area"), baseState);
+    populateObjectTypeOptions(document.getElementById("kernel-search-objtype"), baseState);
+
+    const queryState = buildQueryState(baseState.query, baseState.exact);
+    const filters = buildFilters(baseState);
+    const resultsByKind = {
+      object: [],
+      title: [],
+      index: [],
+      text: [],
+    };
+
+    if (!baseState.queryLower) {
+      renderResults(baseState, resultsByKind);
+      return;
+    }
+
+    if (baseState.kinds.has("object")) {
+      resultsByKind.object = collectObjectResults(window.Search._index, queryState, filters);
+    }
+    if (baseState.kinds.has("title")) {
+      resultsByKind.title = collectSectionResults(window.Search._index, queryState, filters);
+    }
+    if (baseState.kinds.has("index")) {
+      resultsByKind.index = collectIndexResults(window.Search._index, queryState, filters);
+    }
+    if (baseState.kinds.has("text")) {
+      resultsByKind.text = collectTextResults(window.Search._index, queryState, filters);
+    }
+
+    renderResults(baseState, resultsByKind);
+  };
+
+  document.addEventListener("DOMContentLoaded", () => {
+    const container = document.getElementById("kernel-search-results");
+    if (!container) return;
+
+    const progress = document.getElementById("search-progress");
+    if (progress) progress.textContent = "Preparing search...";
+
+    window.Search.whenReady(() => {
+      if (progress) progress.textContent = "";
+      runSearch();
+    });
+  });
+})();
diff --git a/Documentation/sphinx/templates/search.html b/Documentation/sphinx/templates/search.html
new file mode 100644
index 000000000..966740c12
--- /dev/null
+++ b/Documentation/sphinx/templates/search.html
@@ -0,0 +1,106 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{# Enhanced search page for kernel documentation. #}
+{%- extends "layout.html" %}
+{% set title = _('Search') %}
+{%- block scripts %}
+    {{ super() }}
+    <script src="{{ pathto('_static/language_data.js', 1) }}"></script>
+    <script src="{{ pathto('_static/kernel-search.js', 1) }}"></script>
+{%- endblock %}
+{% block extrahead %}
+    <script src="{{ pathto('searchindex.js', 1) }}" defer="defer"></script>
+    <meta name="robots" content="noindex" />
+    {{ super() }}
+{% endblock %}
+{% block body %}
+  <h1 id="search-documentation">{{ _('Search') }}</h1>
+  <noscript>
+  <div class="admonition warning">
+  <p>
+    {% trans %}Please activate JavaScript to enable the search
+    functionality.{% endtrans %}
+  </p>
+  </div>
+  </noscript>
+  <p class="kernel-search-help">
+    {% trans %}Searching for multiple words only shows matches that contain
+    all words.{% endtrans %}
+  </p>
+  <form id="kernel-search-form" class="kernel-search-form" action="" method="get">
+    <div class="kernel-search-query-row">
+      <div class="kernel-search-query-field">
+        <label for="kernel-search-query">{{ _('Search query') }}</label>
+        <input
+          id="kernel-search-query"
+          type="text"
+          name="q"
+          value=""
+          autocomplete="off"
+          autocorrect="off"
+          autocapitalize="off"
+          spellcheck="false"
+        />
+      </div>
+      <div class="kernel-search-query-actions">
+        <input type="submit" value="{{ _('Search') }}" />
+        <span id="search-progress" class="kernel-search-progress"></span>
+      </div>
+    </div>
+
+    <details id="kernel-search-advanced" class="kernel-search-advanced">
+      <summary>{{ _('Advanced search') }}</summary>
+      <input
+        id="kernel-search-advanced-flag"
+        type="hidden"
+        name="advanced"
+        value="1"
+        disabled="disabled"
+      />
+      <div class="kernel-search-advanced-grid">
+        <div class="kernel-search-field">
+          <label class="kernel-search-checkbox" for="kernel-search-exact">
+            <input id="kernel-search-exact" type="checkbox" name="exact" value="1" />
+            <span>{{ _('Exact identifier match') }}</span>
+          </label>
+        </div>
+
+        <fieldset class="kernel-search-kind-filters">
+          <legend>{{ _('Result kinds') }}</legend>
+          <label class="kernel-search-checkbox" for="kernel-search-kind-object">
+            <input id="kernel-search-kind-object" type="checkbox" name="kind" value="object" />
+            <span>{{ _('Symbols') }}</span>
+          </label>
+          <label class="kernel-search-checkbox" for="kernel-search-kind-title">
+            <input id="kernel-search-kind-title" type="checkbox" name="kind" value="title" />
+            <span>{{ _('Sections') }}</span>
+          </label>
+          <label class="kernel-search-checkbox" for="kernel-search-kind-index">
+            <input id="kernel-search-kind-index" type="checkbox" name="kind" value="index" />
+            <span>{{ _('Index entries') }}</span>
+          </label>
+          <label class="kernel-search-checkbox" for="kernel-search-kind-text">
+            <input id="kernel-search-kind-text" type="checkbox" name="kind" value="text" />
+            <span>{{ _('Pages') }}</span>
+          </label>
+        </fieldset>
+
+        <div class="kernel-search-field">
+          <label for="kernel-search-area">{{ _('Documentation area') }}</label>
+          <select id="kernel-search-area" name="area">
+            <option value="">{{ _('All documentation areas') }}</option>
+          </select>
+        </div>
+
+        <div class="kernel-search-field">
+          <label for="kernel-search-objtype">{{ _('Object type') }}</label>
+          <select id="kernel-search-objtype" name="objtype">
+            <option value="">{{ _('All object types') }}</option>
+          </select>
+        </div>
+      </div>
+    </details>
+  </form>
+
+  <div id="kernel-search-results" class="kernel-search-results"></div>
+{% endblock %}
diff --git a/Documentation/sphinx/templates/searchbox.html b/Documentation/sphinx/templates/searchbox.html
new file mode 100644
index 000000000..6caa0498a
--- /dev/null
+++ b/Documentation/sphinx/templates/searchbox.html
@@ -0,0 +1,18 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{# Sphinx sidebar template: quick search box plus advanced search link. #}
+{%- if pagename != "search" and builder != "singlehtml" %}
+<search id="searchbox" style="display: none" role="search">
+  <h3 id="searchlabel">{{ _('Quick search') }}</h3>
+    <div class="searchformwrapper">
+    <form class="search" action="{{ pathto('search') }}" method="get">
+      <input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
+      <input type="submit" value="{{ _('Go') }}" />
+    </form>
+    </div>
+    <p class="search-advanced-link">
+      <a href="{{ pathto('search') }}?advanced=1">{{ _('Advanced search') }}</a>
+    </p>
+</search>
+<script>document.getElementById('searchbox').style.display = "block"</script>
+{%- endif %}
-- 
2.51.0

^ permalink raw reply related

* [PATCH v2] docs: rework footer with semantic markup and responsive layout
From: Rito Rhymes @ 2026-03-22 18:22 UTC (permalink / raw)
  To: Jonathan Corbet, Mauro Carvalho Chehab, linux-doc
  Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260321131857.14166-1-rito@ritovision.com>

The current Alabaster footer uses a generic `div` container, is
hidden entirely at the theme's small-screen breakpoint, and does
not provide a responsive layout for narrower viewports.

Rework the Alabaster footer to use semantic markup while
preserving its existing content, including the copyright notice,
theme attribution, and Page source link. Structure it with a
responsive flexbox so those items remain visible and orderly on
smaller screens.

Scope the change to Alabaster so other supported themes remain
unaffected.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution

 Documentation/conf.py                      |  6 ++++
 Documentation/sphinx-static/custom.css     | 25 ++++++++++++++++
 Documentation/sphinx/templates/layout.html | 33 ++++++++++++++++++++++
 3 files changed, 64 insertions(+)
 create mode 100644 Documentation/sphinx/templates/layout.html

diff --git a/Documentation/conf.py b/Documentation/conf.py
index 679861503..7ad1db83e 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -432,6 +432,12 @@ if html_theme == "alabaster":
 
 sys.stderr.write("Using %s theme\n" % html_theme)
 
+# Expose the active theme so local templates can avoid overriding
+# theme-specific markup for non-default HTML builds.
+html_context = {
+    "kernel_docs_theme": html_theme,
+}
+
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..dfe550adf 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -23,6 +23,31 @@ div.document {
     margin: 20px 10px 0 10px;
     width: auto;
 }
+/*
+ * Use a semantic footer that stays visible on small screens and wraps
+ * its content as needed.
+ */
+footer.footer {
+    max-width: 65em;
+    margin: 20px auto 30px auto;
+    padding: 0 15px 0 10px;
+    box-sizing: border-box;
+    font-size: 14px;
+    color: #888;
+}
+
+footer.footer a {
+    color: inherit;
+}
+
+footer.footer div.footer-inner {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-wrap: wrap;
+    gap: 0.35em 0.6em;
+    text-align: center;
+}
 
 /* Size the logo appropriately */
 img.logo {
diff --git a/Documentation/sphinx/templates/layout.html b/Documentation/sphinx/templates/layout.html
new file mode 100644
index 000000000..52727cabc
--- /dev/null
+++ b/Documentation/sphinx/templates/layout.html
@@ -0,0 +1,33 @@
+{# SPDX-License-Identifier: GPL-2.0 #}
+
+{%- extends "!layout.html" %}
+
+{%- block footer %}
+  {% if kernel_docs_theme == "alabaster" %}
+    <footer class="footer" role="contentinfo">
+      <div class="footer-inner">
+      {% if show_copyright %}
+        <span class="footer-item">&#169;{{ copyright }}.</span>
+      {% endif %}
+      {% if show_sphinx %}
+      {% if show_copyright %}
+        <span class="footer-sep" aria-hidden="true">|</span>
+      {% endif %}
+        <span class="footer-item">
+          Powered by <a href="https://www.sphinx-doc.org/">Sphinx {{ sphinx_version }}</a>
+          &amp; <a href="https://alabaster.readthedocs.io">Alabaster {{ alabaster_version }}</a>
+        </span>
+      {% endif %}
+      {%- if show_source and has_source and sourcename %}
+      {% if show_copyright or show_sphinx %}
+        <span class="footer-sep" aria-hidden="true">|</span>
+      {% endif %}
+        <a class="footer-item" href="{{ pathto('_sources/' + sourcename, resource=True)|e }}"
+           rel="nofollow">{{ _('Page source') }}</a>
+      {%- endif %}
+      </div>
+    </footer>
+  {% else %}
+    {{ super() }}
+  {% endif %}
+{%- endblock %}
-- 
2.51.0

^ permalink raw reply related

* [PATCH v2 0/1] docs: examples of pages affected by inline literal overflow
From: Rito Rhymes @ 2026-03-22 18:32 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260321141118.23828-1-rito@ritovision.com>

Examples of affected pages on docs.kernel.org this patch improves:
  userspace-api/futex2.html
  userspace-api/seccomp_filter.html
  hid/intel-ish-hid.html
  hid/hid-bpf.html
  userspace-api/media/v4l/ext-ctrls-codec.html
  core-api/dma-api-howto.html
  security/IMA-templates.html
  virt/kvm/arm/hypercalls.html
  hwmon/hp-wmi-sensors.html
  bpf/map_devmap.html

Rito Rhymes (1):
  docs: allow inline literals in paragraphs to wrap to prevent overflow

 Documentation/sphinx-static/custom.css | 9 +++++++++
 1 file changed, 9 insertions(+)

-- 
2.51.0

^ permalink raw reply

* [PATCH v2 1/1] docs: allow inline literals in paragraphs to wrap to prevent overflow
From: Rito Rhymes @ 2026-03-22 18:32 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260322183213.52235-1-rito@ritovision.com>

Some documentation pages contain long inline literals in paragraph
text that can force page-wide horizontal scroll overflow and break
layout on smaller screens.

Override the default `span.pre` white-space behavior for inline
literals and use `overflow-wrap: anywhere` so they can wrap when
needed. For code used as part of a paragraph, wrapping is appropriate
because it is stylistically part of the surrounding text. Code blocks,
by contrast, are meant to preserve formatting fidelity and are better
served by contained horizontal scrolling.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution

 Documentation/sphinx-static/custom.css | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..dd69df2a7 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -149,6 +149,15 @@ div.language-selection ul li:hover {
     background: #dddddd;
 }
 
+/*
+ * Let long inline literals in paragraph text wrap as needed to prevent
+ * overflow.
+ */
+code.docutils.literal span.pre {
+    white-space: normal;
+    overflow-wrap: anywhere;
+}
+
 /* Make xrefs more universally visible */
 a.reference, a.reference:hover {
     border-bottom: none;
-- 
2.51.0

^ permalink raw reply related

* [PATCH v2 0/1] docs: examples of pages affected by heading overflow
From: Rito Rhymes @ 2026-03-22 19:09 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260321144855.30429-1-rito@ritovision.com>

Examples of affected pages on docs.kernel.org this patch improves:
  userspace-api/gpio/gpio-handle-set-line-values-ioctl.html
  userspace-api/gpio/gpio-v2-get-lineinfo-watch-ioctl.html
  userspace-api/gpio/gpio-handle-get-line-values-ioctl.html
  userspace-api/gpio/gpio-get-lineinfo-unwatch-ioctl.html
  userspace-api/gpio/gpio-v2-lineinfo-changed-read.html
  userspace-api/gpio/gpio-get-lineinfo-watch-ioctl.html
  userspace-api/gpio/gpio-v2-line-set-config-ioctl.html
  userspace-api/gpio/gpio-v2-line-get-values-ioctl.html
  userspace-api/sysfs-platform_profile.html

Rito Rhymes (1):
  docs: allow long unbroken headings to wrap and prevent overflow

 Documentation/sphinx-static/custom.css | 7 +++++++
 1 file changed, 7 insertions(+)

-- 
2.51.0

^ permalink raw reply

* [PATCH v2 1/1] docs: allow long unbroken headings to wrap and prevent overflow
From: Rito Rhymes @ 2026-03-22 19:09 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260322190908.60412-1-rito@ritovision.com>

Some documentation pages contain long headings with unbroken strings
that can exceed the content width and cause page-wide horizontal scroll
overflow.

Allow headings to wrap when needed so they stay within the content
column and do not break page layout.

Browsers do not treat underscores as natural wrap points, so some
code-style headings may still wrap awkwardly. That trade-off is
preferable to allowing horizontal scroll overflow, since headings
should remain immediately visible rather than partly hidden behind
horizontal scrolling.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution and clarify wrapping trade-offs

 Documentation/sphinx-static/custom.css | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..14711c7be 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -8,6 +8,13 @@ div.body h1 { font-size: 180%; }
 div.body h2 { font-size: 150%; }
 div.body h3 { font-size: 130%; }
 div.body h4 { font-size: 110%; }
+/*
+ * Let long headings wrap before they overflow page layout.
+ */
+div.body h1, div.body h2, div.body h3, div.body h4,
+div.body h5, div.body h6 {
+    overflow-wrap: anywhere;
+}
 
 /* toctree captions are styled like h2 */
 div.toctree-wrapper p.caption[role=heading] { font-size: 150%; }
-- 
2.51.0

^ permalink raw reply related

* [PATCH v2 0/2] docs: contain overflow from long links
From: Rito Rhymes @ 2026-03-22 19:20 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260321180841.10166-1-rito@ritovision.com>

Patch 1 allows long link text to wrap per character so unbroken links
in the main document body do not force page-wide horizontal scroll
overflow.

Patch 2 remains necessary for rendered reference links inside docutils
tables, where the affected table case still needs an explicit selector
to keep the table within the content column.

Some of these pages may also overflow for other reasons, but the
examples listed under [2/2] specifically include tables containing
long reference links that may still need a table-specific CSS rule
to keep the table within the content column.


Examples of affected pages on docs.kernel.org this patch improves:

[1/2]
  firmware-guide/acpi/non-d0-probe.html
  firmware-guide/acpi/lpit.html
  arch/arm/vlocks.html
  arch/arm/keystone/overview.html
  arch/arm/keystone/knav-qmss.html
  arch/loongarch/introduction.html
  arch/nios2/nios2.html
  arch/x86/earlyprintk.html
  arch/x86/orc-unwinder.html#etymology
  arch/x86/tdx.html
  arch/x86/sva.html
  driver-api/xilinx/eemi.html

[2/2]
  arch/openrisc/openrisc_port.html
  power/apm-acpi.html
  networking/devlink/stmmac.html
  filesystems/ext2.html
  networking/l2tp.html
  process/embargoed-hardware-issues.html
  arch/x86/boot.html
  networking/devlink/devlink-info.html
  admin-guide/sysctl/fs.html
  userspace-api/ioctl/ioctl-number.html

Rito Rhymes (2):
  docs: allow long links to wrap per character to prevent page overflow
  docs: allow long table reference links to wrap and prevent overflow

 Documentation/sphinx-static/custom.css | 10 ++++++++++
 1 file changed, 10 insertions(+)

-- 
2.51.0

^ permalink raw reply

* [PATCH v2 1/2] docs: allow long links to wrap per character to prevent page overflow
From: Rito Rhymes @ 2026-03-22 19:20 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260322192021.63859-1-rito@ritovision.com>

Some documentation pages contain long link text without natural
break points, which can force page-wide horizontal scroll overflow
on small screens.

Use overflow-wrap: anywhere for anchor text in the docs stylesheet so
links can wrap per character as a fallback when normal word boundaries
are unavailable.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution

 Documentation/sphinx-static/custom.css | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..4ec617d40 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -149,6 +149,11 @@ div.language-selection ul li:hover {
     background: #dddddd;
 }
 
+/* Let long link text wrap instead of forcing overflow. */
+a {
+    overflow-wrap: anywhere;
+}
+
 /* Make xrefs more universally visible */
 a.reference, a.reference:hover {
     border-bottom: none;
-- 
2.51.0

^ permalink raw reply related

* [PATCH v2 2/2] docs: allow long table reference links to wrap and prevent overflow
From: Rito Rhymes @ 2026-03-22 19:20 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260322192021.63859-1-rito@ritovision.com>

Some documentation pages contain docutils tables with reference links
that use long unbroken strings. Those strings can expand the table
width beyond the content column and cause page-wide horizontal
overflow.

Allow reference links in docutils tables in the main document body to
wrap when needed so the table stays within the content column and does
not break page layout.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution

 Documentation/sphinx-static/custom.css | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index 4ec617d40..b41c54c71 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -154,6 +154,11 @@ a {
     overflow-wrap: anywhere;
 }
 
+/* Let rendered reference links in tables wrap when needed. */
+div.body table.docutils a.reference {
+    overflow-wrap: anywhere;
+}
+
 /* Make xrefs more universally visible */
 a.reference, a.reference:hover {
     border-bottom: none;
-- 
2.51.0

^ permalink raw reply related

* [PATCH v2 0/1] docs: examples of pages affected by table overflow
From: Rito Rhymes @ 2026-03-22 19:24 UTC (permalink / raw)
  To: Jonathan Corbet, Mauro Carvalho Chehab, linux-doc
  Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260321133811.17854-1-rito@ritovision.com>

Examples of affected pages this patch improves:
  kernel-hacking/locking.html
  arch/arc/features.html
  arch/arm/memory.html
  arch/arm/omap/dss.html
  arch/mips/features.html
  arch/nios2/features.html
  arch/openrisc/features.html
  arch/parisc/features.html
  arch/powerpc/features.html
  arch/riscv/features.html
  arch/s390/features.html
  arch/sparc/features.html
  arch/x86/boot.html
  arch/x86/zero-page.html
  arch/x86/pat.html
  arch/x86/amd-hfi.html
  arch/x86/tsx_async_abort.html
  arch/x86/features.html
  driver-api/parport-lowlevel.html

Rito Rhymes (1):
  docs: wrap generated tables to contain small-screen overflow

 Documentation/conf.py                  |  1 +
 Documentation/sphinx-static/custom.css | 16 ++++++++++++++
 Documentation/sphinx/table_wrapper.py  | 30 ++++++++++++++++++++++++++
 3 files changed, 47 insertions(+)
 create mode 100644 Documentation/sphinx/table_wrapper.py

-- 
2.51.0

^ permalink raw reply

* [PATCH v2 1/1] docs: wrap generated tables to contain small-screen overflow
From: Rito Rhymes @ 2026-03-22 19:24 UTC (permalink / raw)
  To: Jonathan Corbet, Mauro Carvalho Chehab, linux-doc
  Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260322192426.65357-1-rito@ritovision.com>

Some documentation tables exceed the fixed-width main content column.
On desktop this is usually acceptable because they can overflow the
800px body without harming readability, but on smaller screens the
same tables create page-wide horizontal scroll overflow that breaks the
layout.

Wrap generated HTML tables in a dedicated container. Above
Alabaster's existing 65em breakpoint, the wrapper uses
`display: contents` to preserve current desktop rendering. At and
below that width, it becomes a horizontal scroll container so table
overflow is contained locally instead of breaking page layout.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution

 Documentation/conf.py                  |  1 +
 Documentation/sphinx-static/custom.css | 16 ++++++++++++++
 Documentation/sphinx/table_wrapper.py  | 30 ++++++++++++++++++++++++++
 3 files changed, 47 insertions(+)
 create mode 100644 Documentation/sphinx/table_wrapper.py

diff --git a/Documentation/conf.py b/Documentation/conf.py
index 679861503..51756d779 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -159,6 +159,7 @@ extensions = [
     "sphinx.ext.autodoc",
     "sphinx.ext.autosectionlabel",
     "sphinx.ext.ifconfig",
+    "table_wrapper",
     "translations",
 ]
 # Since Sphinx version 3, the C function parser is more pedantic with regards
diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..d7c8c4f18 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -23,6 +23,13 @@ div.document {
     margin: 20px 10px 0 10px;
     width: auto;
 }
+/*
+ * Wrap generated tables in a container that preserves desktop overflow
+ * while allowing contained scrolling on smaller screens.
+ */
+div.body div.table-overflow {
+    display: contents;
+}
 
 /* Size the logo appropriately */
 img.logo {
@@ -96,6 +103,15 @@ input.kernel-toc-toggle { display: none; }
     div.kerneltoc a { color: black; }
 }
 
+@media screen and (max-width: 65em) {
+    div.body div.table-overflow {
+        display: block;
+        max-width: 100%;
+        overflow-x: auto;
+        overflow-y: hidden;
+    }
+}
+
 /* Language selection menu */
 
 div.admonition {
diff --git a/Documentation/sphinx/table_wrapper.py b/Documentation/sphinx/table_wrapper.py
new file mode 100644
index 000000000..dfe8c139b
--- /dev/null
+++ b/Documentation/sphinx/table_wrapper.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+"""Wrap generated HTML tables in a responsive overflow container."""
+
+from sphinx.writers.html5 import HTML5Translator
+
+__version__ = "1.0"
+
+
+class TableWrapperHTMLTranslator(HTML5Translator):
+    """Add a wrapper around tables so CSS can control overflow behavior."""
+
+    def visit_table(self, node):
+        self.body.append('<div class="table-overflow">\n')
+        super().visit_table(node)
+
+    def depart_table(self, node):
+        super().depart_table(node)
+        self.body.append("</div>\n")
+
+
+def setup(app):
+    for builder in ("html", "dirhtml", "singlehtml"):
+        app.set_translator(builder, TableWrapperHTMLTranslator, override=True)
+
+    return dict(
+        version=__version__,
+        parallel_read_safe=True,
+        parallel_write_safe=True,
+    )
-- 
2.51.0

^ permalink raw reply related

* Re: [PATCH 0/1] docs: examples of pages affected by heading overflow
From: Randy Dunlap @ 2026-03-22 19:29 UTC (permalink / raw)
  To: Rito Rhymes, Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel
In-Reply-To: <DH9AX5UKDIXH.Q8NWGNIUI82X@ritovision.com>

Hi,

Thanks for the explanations.


On 3/22/26 5:14 AM, Rito Rhymes wrote:
> Thanks for testing things out and for asking about the logic behind the
> implementation choices.
> 
> Is there consistency in the solutions chosen?
> 
> Those 4 patches are not a single series for a reason. They are targeted
> fixes for distinct contexts and use cases where one solution may be
> preferable to the other, i.e. wrapping vs. contained horizontal scroll
> overflow. They are logically consistent in that the reasons for the
> chosen solution do not directly contradict each other across these
> contexts, but they should not all be treated as requiring the same
> generic behavior, such as wrapping everywhere or contained horizontal
> scrolling everywhere. I also did not submit them as a single patch
> series because I did not want each patch to have to justify itself
> against every other patch as a single thematic unit, because the cases
> are very different even if they share an underlying symptom (page-wide
> horizontal scroll overflow).

OK.

>> docs: allow long unbroken headings to wrap and prevent overflow
> 
> Why prefer wrapping?
> 
> You found an unfortunate edge case, but the core regression this fix
> addresses is headings pushing past the right page margin and creating
> page-wide horizontal scroll overflow that breaks the page layout
> instead of wrapping.

Yes, I understand that. And in general the patch is a good idea,
even though I observed that edge case.

> The edge case being:
> 
> GPIO_V2_LINE_GET_VALUES_IOCTL
> 
> Modern browsers do not treat underscores as natural word-break points.
> That whole string is a long unbroken token without a natural breakpoint,
> so without the added wrapping behavior it will create page-wide
> horizontal overflow on small enough screen sizes, as you can test on the
> live site.
> 
> So semantically, it looks like a regression to see
> 
> GPIO_V2_LINE_GET_VALUES_IOCT
> L
> 
> instead of:
> 
> GPIO_V2_LINE_GET_VALUES_
> IOCTL
> 
> But that is still the expected browser behavior. It only looks
> semantically incorrect because the string has internal structure that
> the browser does not understand for wrapping purposes. It is equivalent
> to taking a long unbroken string like:
> 
> GPIOV2LINEGETVALUESIOCTL
> 
> and getting:
> 
> GPIOV2LINEGETVALUESIOCT
> L
> 
> There may be semantically parseable words in there, but regular browser
> wrapping does not know that.
> 
> So this solution does come with a trade-off.

Yes, I wasn't trying to suggest using underscore as a break character.

> Long unbroken strings that have semantic shape but no natural
> breakpoint, including code-style names, will wrap unnaturally.
> 
> But for the majority of headings, wrapping produces clean shapes where
> the semantic structure is preserved because there is spacing or a `-`
> somewhere in the heading.

OK.

> For headings, having everything immediately visible without requiring
> horizontal scrolling is the preferred trade-off. It is tolerable for
> headings to wrap differently across screen sizes, whether that means 1
> line, 2 lines, or 3 lines. In most cases, however many lines the
> heading wraps to, it remains readable and semantically preserved. I do
> not think it would be a reasonable trade-off to introduce contained
> horizontal scrolling for headings just to avoid the edge cases
> involving long unbroken code-style strings.
> 
> Thus, wrapping is the appropriate choice here.

OK, sounds reasonable.

> With that said, we could go a step further and add JavaScript to help
> treat underscores as wrap points, but I do not think that is ideal. It
> would be client-side post-processing, and I do not see similar
> non-Sphinx-native JavaScript being used here for this kind of text
> shaping. I am open to that if people think it is worth it, but it would
> be a new precedent here and not a standard web approach.

nah.

>> docs: contain horizontal overflow in C API descriptions
> 
> Why prefer contained horizontal scroll overflow?
> 
> For this patch, contained horizontal scrolling is preferred because
> code fidelity is the priority. The code is isolated from the rest of the
> page in a dedicated block and presented in a form that is intended to
> remain representative of the code itself. Wrapping distorts spacing and
> line structure, which affects fidelity, creates misleading renderings,
> and reduces readability. Therefore, wrapping is not a good trade-off
> here.

I see.

>> docs: allow inline literals in paragraphs to wrap to prevent overflow
> 
> Why prefer wrapping?
> 
> If preserving code fidelity were the priority here, the code would not
> be embedded inside explanatory prose. The purpose in this context is to
> read a paragraph that includes code as part of the explanation, and it
> would be distracting and hurt readability if the user needed to scroll
> horizontally mid-paragraph just to continue reading. In that context, we
> treat it like any other `<span>` inside of a `<p>`.
> 
>> docs: wrap generated tables to contain small-screen overflow
> 
> Why prefer contained horizontal scroll overflow?
> 
> Wrapping is generally not practical here.
> 
> Many of these tables, often under a `*/features.html*`, exceed at least
> double the viewport width of a mobile screen size. Wrapping the cell
> content enough to fit within a small viewport would often create
> unreadable vertical stacks of wrapped text. Having the rows or columns
> reorganize themselves in some way to fit the viewport would
> fundamentally change the table structure and how it is intended to be
> navigated, which defeats the simplicity of keeping a single table
> structure for many purposes.

Sounds messy.

> The simplest solution is to respect the width the generated table
> creates and allow horizontal scrolling within the table container,
> without allowing the entire page width to expand.

I see that Jon asked about provenance of these patches.
I meant to do that as well but got wrapped up in Advanced Search
testing.

-- 
~Randy


^ permalink raw reply

* [PATCH v2 0/1] docs: examples of pages affected by C API signature overflow
From: Rito Rhymes @ 2026-03-22 19:37 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260321142559.26005-2-rito@ritovision.com>

Examples of affected pages on docs.kernel.org this patch improves:
  core-api/genalloc.html
  userspace-api/iommufd.html
  userspace-api/liveupdate.html
  core-api/liveupdate.html
  arch/sh/index.html
  arch/x86/sgx.html
  devicetree/kernel-api.html
  userspace-api/fwctl/fwctl-cxl.html
  driver-api/regulator.html
  driver-api/reset.html
  driver-api/s390-drivers.html
  driver-api/scsi.html
  driver-api/spi.html
  driver-api/target.html
  driver-api/wbrf.html
  driver-api/wmi.html

Rito Rhymes (1):
  docs: contain horizontal overflow in C API descriptions

 Documentation/sphinx-static/custom.css | 7 +++++++
 1 file changed, 7 insertions(+)

-- 
2.51.0

^ permalink raw reply

* [PATCH v2 1/1] docs: contain horizontal overflow in C API descriptions
From: Rito Rhymes @ 2026-03-22 19:37 UTC (permalink / raw)
  To: Jonathan Corbet, linux-doc; +Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260322193740.68784-1-rito@ritovision.com>

Some documentation pages contain long C API signatures that can exceed
the content width and cause page-wide horizontal scroll overflow.

Apply contained horizontal scrolling to C API description blocks and
keep their signature rows on one line. This preserves signature
formatting while preventing them from breaking page layout.

Contained horizontal scrolling is preferred over wrapping here because
code fidelity is the priority. These blocks are intended to remain
representative of the code itself. Wrapping distorts spacing and line
structure, which affects fidelity, creates misleading renderings, and
reduces readability.

Signed-off-by: Rito Rhymes <rito@ritovision.com>
Assisted-by: Codex:GPT-5.4
---
v2: add Assisted-by attribution and clarify scrolling trade-offs

 Documentation/sphinx-static/custom.css | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css
index db24f4344..18bf8813b 100644
--- a/Documentation/sphinx-static/custom.css
+++ b/Documentation/sphinx-static/custom.css
@@ -40,6 +40,13 @@ li { text-indent: 0em; }
 dl.function, dl.struct, dl.enum { margin-top: 2em; background-color: #ecf0f3; }
 /* indent lines 2+ of multi-line function prototypes */
 dl.function dt { margin-left: 10em; text-indent: -10em; }
+/*
+ * Preserve C API signatures on one line and apply contained horizontal
+ * scrolling to prevent them from exceeding their container width and
+ * breaking page layout.
+ */
+dl.c { overflow-x: auto; overflow-y: hidden; }
+dl.c > dt.sig.sig-object { white-space: nowrap; }
 dt.sig-object { font-size: larger; }
 div.kernelindent { margin-left: 2em; margin-right: 4em; }
 
-- 
2.51.0

^ permalink raw reply related

* Re: [PATCH 0/1] docs: examples of pages affected by heading overflow
From: Rito Rhymes @ 2026-03-22 19:57 UTC (permalink / raw)
  To: Randy Dunlap, Rito Rhymes, Jonathan Corbet, linux-doc
  Cc: Shuah Khan, linux-kernel
In-Reply-To: <ac4d4ec8-90d5-48df-96d4-38de535a6a9a@infradead.org>

I am glad the reasoning and trade-offs make sense.

I also saw Jon's note on coding-assistant attribution/disclosure
and have already rerolled the relevant patches accordingly.

Rito

^ permalink raw reply

* Re: [PATCH] docs: add advanced search for kernel documentation
From: Randy Dunlap @ 2026-03-22 19:59 UTC (permalink / raw)
  To: Rito Rhymes, Jonathan Corbet, Mauro Carvalho Chehab, linux-doc
  Cc: Shuah Khan, linux-kernel
In-Reply-To: <DH8WUB5VFIDH.B2WQGIM3163@ritovision.com>



On 3/21/26 6:12 PM, Rito Rhymes wrote:
> I was using the in-tree build (`make htmldocs`) before. I just ran
> fresh rebuilds with both in-tree and out-of-tree (`make O=DOCS
> htmldocs`) builds, and I got identical search results of 280 hits for
> "futex" when serving both.
> 
> I ran the builds and served the output on Linux/x86_64, and tested the
> pages in Chrome, Edge, and Firefox on Windows.
> 
> So at this point I have not been able to reproduce the "no output"
> behavior with either build mode, I think we can rule out build mode
> quirks.
> 
> More debugging:
> 
> As a comparison, WITHOUT using my patch, upstream only build, does the
> normal Quick Search work? What results do you get for Futex?

Yes:
Search Results
Search finished, found 225 pages matching the search query.


> WITH my patch, do Quick Search return any results for Futex?

No. Just a search dialog page with no matches listed.


> How are you opening or serving the generated docs? For example, via a
> local web server (`http://...`) or directly from disk (`file://...`)?
> I am serving the generated output over HTTP locally via
> `python -m http.server`.

file://...

> What Sphinx version are you using for the build?
> I built with Sphinx 9.1.0.

8.2.3-4.2 (latest from openSUSE Tumbleweed, rolling updates)

> If possible, could you also check in the browser network tab whether
> `_static/kernel-search.js`, `_static/language_data.js`, and
> `searchindex.js` are all loading successfully?

They appear to be (this is new to me).
They are listed and I don't see any errors associated with them.

-- 
~Randy


^ permalink raw reply

* Re: [PATCH] docs: add advanced search for kernel documentation
From: Jonathan Corbet @ 2026-03-22 20:25 UTC (permalink / raw)
  To: Rito Rhymes, Rito Rhymes, Mauro Carvalho Chehab, linux-doc
  Cc: Shuah Khan, linux-kernel
In-Reply-To: <DH9H5TPM3W0J.2XCBEY9U8NQF6@ritovision.com>

"Rito Rhymes" <rito@ritovision.com> writes:

> Hi Jon
>
>> Documentation/process/coding-assistants.rst
>
> That was my oversight. I failed to include the appropriate
> coding-assistant attribution/disclosure, and I will reroll my patches
> accordingly.

That's a good step in the right direction.

>> I'm curious about where you are going with this in general
>
> I am not contributing as a kernel developer. My background is in
> front-end engineering, product/UX, and developer-facing documentation
> and platform surfaces, and that is where I believe I can add value here.

The reason I ask is that submissions to the kernel - even those for the
documentation - have to be evaluated with an eye toward ongoing
maintenance.  A couple of lines of CSS tweak are easily accepted.  1,000
lines of uncommented Javascript, CSS, and Jinja -- none of which fall in
the core strengths of most kernel developers -- have to be looked at
more carefully.

Who is going to maintain this code going forward?  How well do you truly
understand this code, which you did not write yourself?  Will you be
there to help resolve problems that show up in six months or a year?

> Linux is important infrastructure, and I have already been making
> related contributions in its ecosystem. In trying to improve
> lore.kernel.org, I contributed patches merged upstream to Public Inbox
> for small-screen layout behavior and for enabling admin-injected meta
> tags in the document head.

You did get a few patches past Eric, that says something :)

> More broadly, I have worked on improving developer-facing surfaces,
> including documentation and related tooling, in other OSS projects.
> That is the kind of value I am aiming to add here: not direct kernel
> development, but improving usability, discoverability, and developer
> experience around important technical infrastructure.

Worthy goals, certainly.

For the moment I'll ask you to slow down a bit; there are real humans on
the receiving side of these patches who have to deal with them.  I'll
get there shortly, but you're not the only thing in the queue.  The
simple changes seem generally OK from a first quick glance.

I am far from convinced about this particular patch, though.  Before I
accept code that will run in the browser of everybody who reads the
rendered kernel docs, I need to understand that code well, and the
current posting is not entirely amenable to that.

Thanks,

jon

^ permalink raw reply

* Re: [PATCH v2 0/1] docs: examples of pages affected by C API signature overflow
From: Jonathan Corbet @ 2026-03-22 20:35 UTC (permalink / raw)
  To: Rito Rhymes, linux-doc; +Cc: Shuah Khan, linux-kernel, rdunlap, Rito Rhymes
In-Reply-To: <20260322193740.68784-1-rito@ritovision.com>

Rito Rhymes <rito@ritovision.com> writes:

> Examples of affected pages on docs.kernel.org this patch improves:
>   core-api/genalloc.html
>   userspace-api/iommufd.html
>   userspace-api/liveupdate.html
>   core-api/liveupdate.html
>   arch/sh/index.html
>   arch/x86/sgx.html
>   devicetree/kernel-api.html
>   userspace-api/fwctl/fwctl-cxl.html
>   driver-api/regulator.html
>   driver-api/reset.html
>   driver-api/s390-drivers.html
>   driver-api/scsi.html
>   driver-api/spi.html
>   driver-api/target.html
>   driver-api/wbrf.html
>   driver-api/wmi.html
>
> Rito Rhymes (1):
>   docs: contain horizontal overflow in C API descriptions
>
>  Documentation/sphinx-static/custom.css | 7 +++++++
>  1 file changed, 7 insertions(+)

This is a general comment applying to most of the patches you have sent:
is not a typical or particularly helpful cover letter.  The cover letter
tells reviewers what the series as a whole does; this one does not.
Perhaps more to the point, a cover letter is rarely warranted for a
single patch; just put any relevant information into the changelog of
the patch itself.

In this case, some examples of the problem being solved are certainly
warranted, though perhaps not so many as given here.  They should be in
the patch changelog so that somebody wondering, years from now, why the
patch was applied can gain that understanding.

The names of HTML files are perhaps not ideal; since you're talking
about docs.kernel.org in particular, you could give URLs that people
could view directly.  So, for example:

  https://docs.kernel.org/6.19/core-api/genalloc.html

Note the explicit version so that the problem will be findable in the
distant future, even after the fix is applied.

Thanks,

jon

^ permalink raw reply

* Re: [PATCH] docs: set canonical base URL for HTML output
From: Jonathan Corbet @ 2026-03-22 20:40 UTC (permalink / raw)
  To: Rito Rhymes, linux-doc; +Cc: Shuah Khan, linux-kernel, Rito Rhymes
In-Reply-To: <20260321124907.8524-1-rito@ritovision.com>

Rito Rhymes <rito@ritovision.com> writes:

> Set the published docs URL as html_baseurl so generated HTML pages
> advertise the correct canonical URL metadata.
>
> This helps search engines and other consumers treat docs.kernel.org as
> the canonical location for published documentation pages.
>
> Signed-off-by: Rito Rhymes <rito@ritovision.com>
> ---
>  Documentation/conf.py | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/Documentation/conf.py b/Documentation/conf.py
> index 679861503..f517dfeaf 100644
> --- a/Documentation/conf.py
> +++ b/Documentation/conf.py
> @@ -437,6 +437,9 @@ sys.stderr.write("Using %s theme\n" % html_theme)
>  # so a file named "default.css" will overwrite the builtin "default.css".
>  html_static_path = ["sphinx-static"]
>  
> +# Canonical base URL for generated HTML pages.
> +html_baseurl = "https://docs.kernel.org/"
> +

What problem does this actually solve; what isn't working as well as it
should due to the lack of this configuration?

...and how does it help all of the people who do their own docs builds?

Thanks,

jon

^ permalink raw reply

* Re: [PATCH v2 0/2] docs/pt_BR: translations for SoC-related maintainer handbooks
From: Jonathan Corbet @ 2026-03-22 20:44 UTC (permalink / raw)
  To: Daniel Pereira; +Cc: linux-doc
In-Reply-To: <20260319115416.495020-1-danielmaraboo@gmail.com>

Daniel Pereira <danielmaraboo@gmail.com> writes:

> This series adds the Portuguese translations for the SoC-related maintainer
> handbooks.
>
> Changes in v2:
> - Fixed a typo in maintainer-handbooks.rst: "mantainer" -> "maintainer".
> - Small alignment adjustments to follow the kernel-doc style.
>
> Daniel Pereira (2):
>   docs/pt_BR: translation of maintainer-soc.rst
>   docs/pt_BR: translation of maintainer-soc-clean-dts.rst
> ...

Series applied, thanks.

jon

^ permalink raw reply

* Re: [PATCH v2] docs: pt_BR: translate process/1.Intro.rst
From: Jonathan Corbet @ 2026-03-22 20:47 UTC (permalink / raw)
  To: Daniel Pereira; +Cc: Daniel Castro, linux-doc
In-Reply-To: <CAMAsx6eaXHDquZqCjp7P0e7hwK7UHi0mr=QTusCxPixzBPGFdQ@mail.gmail.com>

Daniel Pereira <danielmaraboo@gmail.com> writes:

> Hello Jon,
>
> Initially, no, there are no further blocking issues for application.
>
> The purpose of my comments was solely related to the initial
> organization of the collaboration and ensuring adherence to community
> submission practices.
>
> As for the content itself, the Portuguese documentation is
> grammatically perfect.

The normal way of saying that is Reviewed-by or Acked-by :)

I've applied the patch, thanks.

jon

^ permalink raw reply

* Re: [PATCH v2 0/5] docs: reporting-issues: tweak a few areas and rewrite the ending
From: Jonathan Corbet @ 2026-03-22 20:48 UTC (permalink / raw)
  To: Thorsten Leemhuis; +Cc: linux-doc, regressions, linux-kernel
In-Reply-To: <87pl529y01.fsf@trenco.lwn.net>

Jonathan Corbet <corbet@lwn.net> writes:

> Thorsten Leemhuis <linux@leemhuis.info> writes:
>
>> This patch-set tweaks a few parts in the fringes of our text on bug
>> reporting while replacing two sections at the end with a new text as
>> proper appendix that hopefully better fits the purpose and covers more
>> aspects.
>>
>> This is the first versions of this patch-set, but it starts as v2, as it
>> contains three patches and a few bits that were part of an earlier and
>> bigger patch-set. This incorporates review feedback given there. The
>> last patch in this series is mostly new, but includes a few bits from
>> patch 4 and 5 of the earlier series.
>
> I don't see anything to complain about here ... if nobody screams, I'll
> apply them in a few days or so.

...and I have now done that, thanks.

jon

^ permalink raw reply

* Re: [PATCH v2] coccinelle: update Coccinelle URL
From: Jonathan Corbet @ 2026-03-22 20:53 UTC (permalink / raw)
  To: Julia Lawall
  Cc: Shuah Khan, workflows, cocci, Nicolas Palix, Hu Haowen, Alex Shi,
	Yanteng Si, Dongliang Mu, linux-doc, linux-kernel, Federico Vaga,
	Carlos Bilbao, Avadhut Naik, LIU Haoyang
In-Reply-To: <20260310121431.362091-1-Julia.Lawall@inria.fr>

Julia Lawall <Julia.Lawall@inria.fr> writes:

> The LIP6 URL no longer functions.
>
> Signed-off-by: Julia Lawall <Julia.Lawall@inria.fr>
>
> ---
>
> v2: corrected the character encoding, no other changes
>
>  Documentation/dev-tools/coccinelle.rst                |    2 +-
>  Documentation/translations/it_IT/process/4.Coding.rst |    3 ++-
>  Documentation/translations/sp_SP/process/4.Coding.rst |    3 ++-
>  Documentation/translations/zh_CN/process/4.Coding.rst |    2 +-
>  Documentation/translations/zh_TW/process/4.Coding.rst |    2 +-
>  5 files changed, 7 insertions(+), 5 deletions(-)

Applied, thanks.

jon

^ 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