All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Onur Özkan" <work@onurozkan.dev>
To: Deborah Brouwer <deborah.brouwer@collabora.com>
Cc: "Daniel Almeida" <daniel.almeida@collabora.com>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Benno Lossin" <lossin@kernel.org>, "Gary Guo" <gary@garyguo.net>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun@kernel.org>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	"FUJITA Tomonori" <fujita.tomonori@gmail.com>,
	"Frederic Weisbecker" <frederic@kernel.org>,
	"Thomas Gleixner" <tglx@kernel.org>,
	"Anna-Maria Behnsen" <anna-maria@linutronix.de>,
	"John Stultz" <jstultz@google.com>,
	"Stephen Boyd" <sboyd@kernel.org>,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	rust-for-linux@vger.kernel.org, boris.brezillon@collabora.com,
	beata.michalska@arm.com, lyude@redhat.com, acourbot@nvidia.com,
	alvin.sun@linux.dev
Subject: Re: [PATCH v4 12/20] drm/tyr: add parser for firmware binary
Date: Mon, 27 Apr 2026 11:09:25 +0300	[thread overview]
Message-ID: <20260427080926.73950-1-work@onurozkan.dev> (raw)
In-Reply-To: <20260424-b4-fw-boot-v4-v4-12-a5d91050789d@collabora.com>

On Fri, 24 Apr 2026 16:39:06 -0700
Deborah Brouwer <deborah.brouwer@collabora.com> wrote:

> From: Daniel Almeida <daniel.almeida@collabora.com>
> 
> Add a parser for the Mali CSF GPU firmware binary format. The firmware
> consists of a header followed by entries describing how to load firmware
> sections into the MCU's memory.
> 
> The parser extracts section metadata including virtual address ranges,
> data byte offsets within the binary, and section flags controlling
> permissions and cache modes. It validates the basic firmware structure
> and alignment and ignores protected-mode sections for now.
> 
> Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
> Co-developed-by: Beata Michalska <beata.michalska@arm.com>
> Signed-off-by: Beata Michalska <beata.michalska@arm.com>
> Co-developed-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Co-developed-by: Deborah Brouwer <deborah.brouwer@collabora.com>
> Signed-off-by: Deborah Brouwer <deborah.brouwer@collabora.com>
> ---
>  drivers/gpu/drm/tyr/fw/parser.rs | 519 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 519 insertions(+)
> 
> diff --git a/drivers/gpu/drm/tyr/fw/parser.rs b/drivers/gpu/drm/tyr/fw/parser.rs
> new file mode 100644
> index 000000000000..638707430701
> --- /dev/null
> +++ b/drivers/gpu/drm/tyr/fw/parser.rs
> @@ -0,0 +1,519 @@
> +// SPDX-License-Identifier: GPL-2.0 or MIT
> +
> +//! Firmware binary parser for Mali CSF (Command Stream Frontend) GPU.
> +//!
> +//! This module implements a parser for the Mali GPU firmware binary format. The firmware
> +//! file contains a header followed by a sequence of entries, each describing how to load
> +//! firmware sections into the MCU (Microcontroller Unit) memory. The parser extracts section metadata including:
> +//! - Virtual address ranges where sections should be mapped
> +//! - Data ranges (byte offsets) within the firmware binary
> +//! - Section flags (permissions, cache modes)
> +
> +use core::{
> +    mem::size_of,
> +    ops::Range, //
> +};
> +
> +use kernel::{
> +    bits::bit_u32,
> +    prelude::*,
> +    str::CString, //
> +};
> +
> +use crate::{
> +    fw::{
> +        SectionFlag,
> +        SectionFlags,
> +        CSF_MCU_SHARED_REGION_START, //
> +    },
> +    vm::{
> +        VmFlag,
> +        VmMapFlags, //
> +    }, //
> +};
> +
> +/// A parsed firmware section ready for loading into MCU memory.
> +///
> +/// Represents a single firmware section extracted from the firmware binary, containing
> +/// all information needed to map the section's data into the MCU's virtual address space.
> +pub(super) struct ParsedSection {
> +    /// Byte offset range within the firmware binary where this section's data resides.
> +    pub(super) data_range: Range<u32>,
> +    /// MCU virtual address range where this section should be mapped.
> +    pub(super) va: Range<u32>,
> +    /// Memory protection and caching flags for the mapping.
> +    pub(super) vm_map_flags: VmMapFlags,
> +}
> +
> +/// A bare-bones `std::io::Cursor<[u8]>` clone to keep track of the current position in the firmware binary.
> +///
> +/// Provides methods to sequentially read primitive types and byte arrays from the firmware
> +/// binary while maintaining the current read position.
> +struct Cursor<'a> {
> +    data: &'a [u8],
> +    pos: usize,
> +}
> +
> +impl<'a> Cursor<'a> {
> +    fn new(data: &'a [u8]) -> Self {
> +        Self { data, pos: 0 }
> +    }
> +
> +    fn len(&self) -> usize {
> +        self.data.len()
> +    }
> +
> +    fn pos(&self) -> usize {
> +        self.pos
> +    }
> +
> +    /// Returns a view into the cursor's data.
> +    ///
> +    /// This spawns a new cursor, leaving the current cursor unchanged.
> +    fn view(&self, range: Range<usize>) -> Result<Cursor<'_>> {
> +        if range.start < self.pos || range.end > self.data.len() {
> +            pr_err!(
> +                "Invalid cursor range {:?} for data of length {}",
> +                range,
> +                self.data.len()
> +            );
> +
> +            Err(EINVAL)
> +        } else {
> +            Ok(Self {
> +                data: &self.data[range],
> +                pos: 0,
> +            })
> +        }
> +    }
> +
> +    /// Reads a slice of bytes from the current position and advances the cursor.
> +    ///
> +    /// Returns an error if the read would exceed the data bounds.
> +    fn read(&mut self, nbytes: usize) -> Result<&[u8]> {
> +        let start = self.pos;
> +        let end = start + nbytes;
> +
> +        if end > self.data.len() {
> +            pr_err!(
> +                "Invalid firmware file: read of size {} at position {} is out of bounds",
> +                nbytes,
> +                start,
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        self.pos += nbytes;
> +        Ok(&self.data[start..end])
> +    }
> +
> +    /// Reads a little-endian `u8` from the current position and advances the cursor.
> +    fn read_u8(&mut self) -> Result<u8> {
> +        let bytes = self.read(size_of::<u8>())?;
> +        Ok(bytes[0])
> +    }
> +
> +    /// Reads a little-endian `u16` from the current position and advances the cursor.
> +    fn read_u16(&mut self) -> Result<u16> {
> +        let bytes = self.read(size_of::<u16>())?;
> +        Ok(u16::from_le_bytes(bytes.try_into().unwrap()))
> +    }
> +
> +    /// Reads a little-endian `u32` from the current position and advances the cursor.
> +    fn read_u32(&mut self) -> Result<u32> {
> +        let bytes = self.read(size_of::<u32>())?;
> +        Ok(u32::from_le_bytes(bytes.try_into().unwrap()))
> +    }
> +
> +    /// Advances the cursor position by the specified number of bytes.
> +    ///
> +    /// Returns an error if the advance would exceed the data bounds.
> +    fn advance(&mut self, nbytes: usize) -> Result {
> +        if self.pos + nbytes > self.data.len() {
> +            pr_err!(
> +                "Invalid firmware file: advance of size {} at position {} is out of bounds",
> +                nbytes,
> +                self.pos,
> +            );
> +            return Err(EINVAL);
> +        }
> +        self.pos += nbytes;
> +        Ok(())
> +    }
> +}
> +
> +/// Parser for Mali CSF GPU firmware binaries.
> +///
> +/// Parses the firmware binary format, extracting section metadata including virtual
> +/// address ranges, data offsets, and memory protection flags needed to load firmware
> +/// into the MCU's memory.
> +pub(super) struct FwParser<'a> {
> +    cursor: Cursor<'a>,
> +}
> +
> +impl<'a> FwParser<'a> {
> +    /// Creates a new firmware parser for the given firmware binary data.
> +    pub(super) fn new(data: &'a [u8]) -> Self {
> +        Self {
> +            cursor: Cursor::new(data),
> +        }
> +    }
> +
> +    /// Parses the firmware binary and returns a collection of parsed sections.
> +    ///
> +    /// This method validates the firmware header and iterates through all entries
> +    /// in the binary, extracting section information needed for loading.
> +    pub(super) fn parse(&mut self) -> Result<KVec<ParsedSection>> {
> +        let fw_header = self.parse_fw_header()?;
> +
> +        let mut parsed_sections = KVec::new();
> +        while (self.cursor.pos() as u32) < fw_header.size {
> +            let entry_section = self.parse_entry()?;
> +
> +            if let Some(inner) = entry_section.inner {
> +                parsed_sections.push(inner, GFP_KERNEL)?;
> +            }
> +        }
> +
> +        Ok(parsed_sections)
> +    }
> +
> +    fn parse_fw_header(&mut self) -> Result<FirmwareHeader> {
> +        let fw_header: FirmwareHeader = match FirmwareHeader::new(&mut self.cursor) {
> +            Ok(fw_header) => fw_header,
> +            Err(e) => {
> +                pr_err!("Invalid firmware file: {}", e.to_errno());
> +                return Err(e);
> +            }
> +        };
> +
> +        if fw_header.size > self.cursor.len() as u32 {
> +            pr_err!("Firmware image is truncated");
> +            return Err(EINVAL);
> +        }
> +        Ok(fw_header)
> +    }
> +
> +    fn parse_entry(&mut self) -> Result<EntrySection> {
> +        let entry_section = EntrySection {
> +            entry_hdr: EntryHeader(self.cursor.read_u32()?),
> +            inner: None,
> +        };
> +
> +        if self.cursor.pos() % size_of::<u32>() != 0
> +            || entry_section.entry_hdr.size() as usize % size_of::<u32>() != 0
> +        {
> +            pr_err!(
> +                "Firmware entry isn't 32 bit aligned, offset={:#x} size={:#x}\n",
> +                self.cursor.pos() - size_of::<u32>(),
> +                entry_section.entry_hdr.size()
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        let section_hdr_size = entry_section.entry_hdr.size() as usize - size_of::<EntryHeader>();
> +
> +        let entry_section = {
> +            let mut entry_cursor = self
> +                .cursor
> +                .view(self.cursor.pos()..self.cursor.pos() + section_hdr_size)?;
> +
> +            match entry_section.entry_hdr.entry_type() {
> +                Ok(EntryType::Iface) => Ok(EntrySection {
> +                    entry_hdr: entry_section.entry_hdr,
> +                    inner: Self::parse_section_entry(&mut entry_cursor)?,
> +                }),
> +                Ok(
> +                    EntryType::Config
> +                    | EntryType::FutfTest
> +                    | EntryType::TraceBuffer
> +                    | EntryType::TimelineMetadata
> +                    | EntryType::BuildInfoMetadata,
> +                ) => Ok(entry_section),
> +
> +                entry_type => {
> +                    if entry_type.is_err() || !entry_section.entry_hdr.optional() {
> +                        if !entry_section.entry_hdr.optional() {
> +                            pr_err!(
> +                                "Failed to handle firmware entry type: {}\n",
> +                                entry_type
> +                                    .map_or(entry_section.entry_hdr.entry_type_raw(), |e| e as u8)
> +                            );
> +                            Err(EINVAL)
> +                        } else {
> +                            Ok(entry_section)
> +                        }
> +                    } else {
> +                        Ok(entry_section)
> +                    }
> +                }
> +            }
> +        };
> +
> +        if entry_section.is_ok() {
> +            self.cursor.advance(section_hdr_size)?;
> +        }
> +
> +        entry_section
> +    }
> +
> +    fn parse_section_entry(entry_cursor: &mut Cursor<'_>) -> Result<Option<ParsedSection>> {
> +        let section_hdr: SectionHeader = SectionHeader::new(entry_cursor)?;
> +
> +        if section_hdr.data.end < section_hdr.data.start {
> +            pr_err!(
> +                "Firmware corrupted, data.end < data.start (0x{:x} < 0x{:x})\n",
> +                section_hdr.data.end,
> +                section_hdr.data.start
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        if section_hdr.va.end < section_hdr.va.start {
> +            pr_err!(
> +                "Firmware corrupted, section_hdr.va.end < section_hdr.va.start (0x{:x} < 0x{:x})\n",
> +                section_hdr.va.end,
> +                section_hdr.va.start
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        if section_hdr.section_flags.contains(SectionFlag::Prot) {
> +            pr_info!("Firmware protected mode entry not supported, ignoring");
> +            return Ok(None);
> +        }
> +
> +        if section_hdr.va.start == CSF_MCU_SHARED_REGION_START
> +            && !section_hdr.section_flags.contains(SectionFlag::Shared)
> +        {
> +            pr_err!(
> +                "Interface at 0x{:x} must be shared",
> +                CSF_MCU_SHARED_REGION_START
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        let name_len = entry_cursor.len() - entry_cursor.pos();
> +        let name_bytes = entry_cursor.read(name_len)?;
> +
> +        let mut name = KVec::with_capacity(name_bytes.len() + 1, GFP_KERNEL)?;
> +        name.extend_from_slice(name_bytes, GFP_KERNEL)?;
> +        name.push(0, GFP_KERNEL)?;
> +
> +        let _name = CStr::from_bytes_with_nul(&name)
> +            .ok()
> +            .and_then(|name| CString::try_from(name).ok());
> +
> +        let cache_mode = section_hdr.section_flags.cache_mode();
> +        let mut vm_map_flags = VmMapFlags::empty();
> +
> +        if !section_hdr.section_flags.contains(SectionFlag::Write) {
> +            vm_map_flags |= VmFlag::Readonly;
> +        }
> +        if !section_hdr.section_flags.contains(SectionFlag::Exec) {
> +            vm_map_flags |= VmFlag::Noexec;
> +        }
> +        if cache_mode != SectionFlag::CacheModeCached.into() {
> +            vm_map_flags |= VmFlag::Uncached;
> +        }
> +
> +        Ok(Some(ParsedSection {
> +            data_range: section_hdr.data.clone(),
> +            va: section_hdr.va,
> +            vm_map_flags,
> +        }))
> +    }
> +}
> +
> +/// Firmware binary header containing version and size information.
> +///
> +/// The header is located at the beginning of the firmware binary and contains
> +/// a magic value for validation, version information, and the total size of
> +/// all structured headers that follow.
> +#[expect(dead_code)]
> +struct FirmwareHeader {
> +    /// Magic value to check binary validity.
> +    magic: u32,
> +
> +    /// Minor firmware version.
> +    minor: u8,
> +
> +    /// Major firmware version.
> +    major: u8,
> +
> +    /// Padding. Must be set to zero.
> +    _padding1: u16,
> +
> +    /// Firmware version hash.
> +    version_hash: u32,
> +
> +    /// Padding. Must be set to zero.
> +    _padding2: u32,
> +
> +    /// Total size of all the structured data headers at beginning of firmware binary.
> +    size: u32,
> +}
> +
> +impl FirmwareHeader {
> +    const FW_BINARY_MAGIC: u32 = 0xc3f13a6e;
> +    const FW_BINARY_MAJOR_MAX: u8 = 0;
> +
> +    /// Reads and validates a firmware header from the cursor.
> +    ///
> +    /// Verifies the magic value, version compatibility, and padding fields.
> +    fn new(cursor: &mut Cursor<'_>) -> Result<Self> {
> +        let magic = cursor.read_u32()?;
> +        if magic != Self::FW_BINARY_MAGIC {
> +            pr_err!("Invalid firmware magic");
> +            return Err(EINVAL);
> +        }
> +
> +        let minor = cursor.read_u8()?;
> +        let major = cursor.read_u8()?;
> +
> +        if major > Self::FW_BINARY_MAJOR_MAX {
> +            pr_err!(
> +                "Unsupported firmware binary header version {}.{} (expected {}.x)\n",
> +                major,
> +                minor,
> +                Self::FW_BINARY_MAJOR_MAX
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        let padding1 = cursor.read_u16()?;
> +        let version_hash = cursor.read_u32()?;
> +        let padding2 = cursor.read_u32()?;
> +        let size = cursor.read_u32()?;
> +
> +        if padding1 != 0 || padding2 != 0 {
> +            pr_err!("Invalid firmware file: header padding is not zero");
> +            return Err(EINVAL);
> +        }
> +
> +        let fw_header = Self {
> +            magic,
> +            minor,
> +            major,
> +            _padding1: padding1,
> +            version_hash,
> +            _padding2: padding2,

nit: I would write it like this:

	_padding1: 0,
	version_hash,
	_padding2: 0,

to be more explicit.

> +            size,
> +        };
> +
> +        Ok(fw_header)
> +    }
> +}
> +
> +/// Firmware section header for loading binary sections into MCU memory.
> +#[derive(Debug)]
> +struct SectionHeader {
> +    section_flags: SectionFlags,
> +    /// MCU virtual range to map this binary section to.
> +    va: Range<u32>,
> +    /// References the data in the FW binary.
> +    data: Range<u32>,
> +}
> +
> +impl SectionHeader {
> +    /// Reads and validates a section header from the cursor.
> +    ///
> +    /// Parses section flags, virtual address range, and data range from the firmware binary.
> +    fn new(cursor: &mut Cursor<'_>) -> Result<Self> {
> +        let section_flags = cursor.read_u32()?;
> +        let section_flags = SectionFlags::try_from(section_flags)?;
> +
> +        let va_start = cursor.read_u32()?;
> +        let va_end = cursor.read_u32()?;
> +
> +        let va = va_start..va_end;
> +
> +        if va.is_empty() {
> +            pr_err!(
> +                "Invalid firmware file: empty VA range at pos {}\n",
> +                cursor.pos(),
> +            );
> +            return Err(EINVAL);
> +        }
> +
> +        let data_start = cursor.read_u32()?;
> +        let data_end = cursor.read_u32()?;
> +        let data = data_start..data_end;
> +
> +        Ok(Self {
> +            section_flags,
> +            va,
> +            data,
> +        })
> +    }
> +}
> +
> +/// A firmware entry containing a header and optional parsed section data.
> +///
> +/// Represents a single entry in the firmware binary, which may contain loadable
> +/// section data or metadata that doesn't require loading.
> +struct EntrySection {
> +    entry_hdr: EntryHeader,
> +    inner: Option<ParsedSection>,
> +}
> +
> +/// Header for a firmware entry, packed into a single u32.
> +///
> +/// The entry header encodes the entry type, size, and optional flag in a
> +/// 32-bit value with the following layout:
> +/// - Bits 0-7: Entry type
> +/// - Bits 8-15: Size in bytes
> +/// - Bit 31: Optional flag
> +struct EntryHeader(u32);
> +
> +impl EntryHeader {
> +    fn entry_type_raw(&self) -> u8 {
> +        (self.0 & 0xff) as u8
> +    }
> +
> +    fn entry_type(&self) -> Result<EntryType> {
> +        let v = self.entry_type_raw();
> +        EntryType::try_from(v)
> +    }
> +
> +    fn optional(&self) -> bool {
> +        self.0 & bit_u32(31) != 0
> +    }
> +
> +    fn size(&self) -> u32 {
> +        self.0 >> 8 & 0xff
> +    }
> +}
> +
> +#[derive(Clone, Copy, Debug)]
> +#[repr(u8)]
> +enum EntryType {
> +    /// Host <-> FW interface.
> +    Iface = 0,
> +    /// FW config.
> +    Config = 1,
> +    /// Unit tests.
> +    FutfTest = 2,
> +    /// Trace buffer interface.
> +    TraceBuffer = 3,
> +    /// Timeline metadata interface.
> +    TimelineMetadata = 4,
> +    /// Metadata about how the FW binary was built.
> +    BuildInfoMetadata = 6,
> +}
> +
> +impl TryFrom<u8> for EntryType {
> +    type Error = Error;
> +
> +    fn try_from(value: u8) -> Result<Self, Self::Error> {
> +        match value {
> +            0 => Ok(EntryType::Iface),
> +            1 => Ok(EntryType::Config),
> +            2 => Ok(EntryType::FutfTest),
> +            3 => Ok(EntryType::TraceBuffer),
> +            4 => Ok(EntryType::TimelineMetadata),
> +            6 => Ok(EntryType::BuildInfoMetadata),
> +            _ => Err(EINVAL),
> +        }
> +    }
> +}
> 
> -- 
> 2.53.0
> 

  reply	other threads:[~2026-04-27  8:09 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-24 23:38 [PATCH v4 00/20] drm/tyr: firmware loading and MCU boot support Deborah Brouwer
2026-04-24 23:38 ` [PATCH v4 01/20] drm/tyr: remove unused device from platform data Deborah Brouwer
2026-04-24 23:38 ` [PATCH v4 02/20] drm/tyr: select required dependencies in Kconfig Deborah Brouwer
2026-04-27  7:23   ` Boris Brezillon
2026-04-27 23:36     ` Deborah Brouwer
2026-04-28  7:13       ` Boris Brezillon
2026-04-24 23:38 ` [PATCH v4 03/20] drm/tyr: move clock cleanup into Clocks Drop impl Deborah Brouwer
2026-04-24 23:38 ` [PATCH v4 04/20] drm/tyr: rename TyrObject to BoData Deborah Brouwer
2026-04-24 23:38 ` [PATCH v4 05/20] drm/tyr: use shmem GEM object type in TyrDrmDriver Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 06/20] drm/tyr: set DMA mask using GPU physical address Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 07/20] drm/tyr: add shmem backing for GEM objects Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 08/20] drm/tyr: Add generic slot manager Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 09/20] drm/tyr: add MMU module Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 10/20] drm/tyr: add GPU virtual memory module Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 11/20] drm/tyr: add a kernel buffer object Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 12/20] drm/tyr: add parser for firmware binary Deborah Brouwer
2026-04-27  8:09   ` Onur Özkan [this message]
2026-04-27  8:20     ` Boris Brezillon
2026-04-24 23:39 ` [PATCH v4 13/20] drm/tyr: add firmware loading and MCU boot support Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 14/20] drm/tyr: add Wait type for GPU events Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 15/20] drm/tyr: add Job IRQ handling Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 16/20] drm/tyr: wait for global interface readiness Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 17/20] drm/tyr: validate presence of CSF shared section Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 18/20] drm/tyr: add CSF firmware interface support Deborah Brouwer
2026-04-27  9:08   ` Onur Özkan
2026-04-24 23:39 ` [PATCH v4 19/20] rust: time: add arch_timer_get_rate wrapper Deborah Brouwer
2026-04-27  7:42   ` Andreas Hindborg
2026-04-27 23:34     ` Deborah Brouwer
2026-04-27  7:53   ` Alice Ryhl
2026-04-27 23:34     ` Deborah Brouwer
2026-04-27  8:59   ` Onur Özkan
2026-04-27 23:35     ` Deborah Brouwer
2026-04-24 23:39 ` [PATCH v4 20/20] drm/tyr: program CSF global interface Deborah Brouwer
2026-04-27  8:07 ` [PATCH v4 00/20] drm/tyr: firmware loading and MCU boot support Boris Brezillon
2026-04-28 10:56   ` Alice Ryhl
2026-04-28 16:41     ` Deborah Brouwer
2026-04-28 17:02       ` Alice Ryhl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260427080926.73950-1-work@onurozkan.dev \
    --to=work@onurozkan.dev \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=alvin.sun@linux.dev \
    --cc=anna-maria@linutronix.de \
    --cc=beata.michalska@arm.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun@kernel.org \
    --cc=boris.brezillon@collabora.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=deborah.brouwer@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=frederic@kernel.org \
    --cc=fujita.tomonori@gmail.com \
    --cc=gary@garyguo.net \
    --cc=jstultz@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=sboyd@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tglx@kernel.org \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.