summaryrefslogtreecommitdiffstats
path: root/drivers/ssb/scan.c
diff options
context:
space:
mode:
authorAdrian Betschart <adrian.betschart@cinemaone.ch>2026-09-11 17:13:14 +0200
committerAlex Deucher <alexander.deucher@amd.com>2026-10-01 17:18:05 -0400
commit106b4a2a78b2a2f14885604891957b84bba502db (patch)
tree5d40eef2428fb1a4791ab46a4b19091a5910eac2 /drivers/ssb/scan.c
downloadlinux-stable-106b4a2a78b2a2f14885604891957b84bba502db.tar.gz
linux-stable-106b4a2a78b2a2f14885604891957b84bba502db.zip
drm/amd/display: Try RGB before YCbCr 4:4:4 in stream validationgrafted
amdgpu_dm_create_validate_stream_for_sink() walks encoding_order[] and uses the first encoding that validates. YCbCr 4:4:4 is listed before RGB, so an HDMI sink that advertises 4:4:4 gets YCbCr 4:4:4 whenever the "color format" property is left at AUTO, even though RGB fits the same link. That contradicts the documented AUTO behaviour for HDMI in enum drm_connector_color_format (RGB, falling back to YCbCr 4:2:0 only when the bandwidth is not available or the mode is 4:2:0-only), which the amdgpu implementation of the property also describes. It also leaves the "Broadcast RGB" property without effect on such sinks, since the quantization range it selects only applies to RGB output. Try RGB first. The mask still holds every encoding the sink supports, so a mode that cannot carry RGB falls back exactly as before. For reference, v7.2 picked RGB here unless YCbCr 4:4:4 was forced through debugfs, while earlier kernels picked YCbCr 4:4:4 for any HDMI sink that advertised it. Fixes: 0b0ff65d3ca1 ("drm/amd/display: Refactor stream validation") Suggested-by: Adolfo Rodrigues <adolfotregosa@gmail.com> Assisted-by: Claude Code:claude-fable-5-1 Signed-off-by: Adrian Betschart <adrian.betschart@cinemaone.ch> Reviewed-by: Fangzhi Zuo <jerry.zuo@amd.com> Tested-by: Adolfo Rodrigues <adolfotregosa@gmail.com> Tested-by: Daniel Wheeler <daniel.wheeler@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> (cherry picked from commit 7de432bc753133764dc40d37f9388da56de251e7)
Diffstat (limited to 'drivers/ssb/scan.c')
-rw-r--r--drivers/ssb/scan.c446
1 files changed, 446 insertions, 0 deletions
diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c
new file mode 100644
index 000000000..4161e5d1f
--- /dev/null
+++ b/drivers/ssb/scan.c
@@ -0,0 +1,446 @@
+/*
+ * Sonics Silicon Backplane
+ * Bus scanning
+ *
+ * Copyright (C) 2005-2007 Michael Buesch <m@bues.ch>
+ * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
+ * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
+ * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
+ * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+ * Copyright (C) 2006 Broadcom Corporation.
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "ssb_private.h"
+
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+
+const char *ssb_core_name(u16 coreid)
+{
+ switch (coreid) {
+ case SSB_DEV_CHIPCOMMON:
+ return "ChipCommon";
+ case SSB_DEV_ILINE20:
+ return "ILine 20";
+ case SSB_DEV_SDRAM:
+ return "SDRAM";
+ case SSB_DEV_PCI:
+ return "PCI";
+ case SSB_DEV_MIPS:
+ return "MIPS";
+ case SSB_DEV_ETHERNET:
+ return "Fast Ethernet";
+ case SSB_DEV_V90:
+ return "V90";
+ case SSB_DEV_USB11_HOSTDEV:
+ return "USB 1.1 Hostdev";
+ case SSB_DEV_ADSL:
+ return "ADSL";
+ case SSB_DEV_ILINE100:
+ return "ILine 100";
+ case SSB_DEV_IPSEC:
+ return "IPSEC";
+ case SSB_DEV_PCMCIA:
+ return "PCMCIA";
+ case SSB_DEV_INTERNAL_MEM:
+ return "Internal Memory";
+ case SSB_DEV_MEMC_SDRAM:
+ return "MEMC SDRAM";
+ case SSB_DEV_EXTIF:
+ return "EXTIF";
+ case SSB_DEV_80211:
+ return "IEEE 802.11";
+ case SSB_DEV_MIPS_3302:
+ return "MIPS 3302";
+ case SSB_DEV_USB11_HOST:
+ return "USB 1.1 Host";
+ case SSB_DEV_USB11_DEV:
+ return "USB 1.1 Device";
+ case SSB_DEV_USB20_HOST:
+ return "USB 2.0 Host";
+ case SSB_DEV_USB20_DEV:
+ return "USB 2.0 Device";
+ case SSB_DEV_SDIO_HOST:
+ return "SDIO Host";
+ case SSB_DEV_ROBOSWITCH:
+ return "Roboswitch";
+ case SSB_DEV_PARA_ATA:
+ return "PATA";
+ case SSB_DEV_SATA_XORDMA:
+ return "SATA XOR-DMA";
+ case SSB_DEV_ETHERNET_GBIT:
+ return "GBit Ethernet";
+ case SSB_DEV_PCIE:
+ return "PCI-E";
+ case SSB_DEV_MIMO_PHY:
+ return "MIMO PHY";
+ case SSB_DEV_SRAM_CTRLR:
+ return "SRAM Controller";
+ case SSB_DEV_MINI_MACPHY:
+ return "Mini MACPHY";
+ case SSB_DEV_ARM_1176:
+ return "ARM 1176";
+ case SSB_DEV_ARM_7TDMI:
+ return "ARM 7TDMI";
+ case SSB_DEV_ARM_CM3:
+ return "ARM Cortex M3";
+ }
+ return "UNKNOWN";
+}
+
+static u16 pcidev_to_chipid(struct pci_dev *pci_dev)
+{
+ u16 chipid_fallback = 0;
+
+ switch (pci_dev->device) {
+ case 0x4301:
+ chipid_fallback = 0x4301;
+ break;
+ case 0x4305 ... 0x4307:
+ chipid_fallback = 0x4307;
+ break;
+ case 0x4403:
+ chipid_fallback = 0x4402;
+ break;
+ case 0x4610 ... 0x4615:
+ chipid_fallback = 0x4610;
+ break;
+ case 0x4710 ... 0x4715:
+ chipid_fallback = 0x4710;
+ break;
+ case 0x4320 ... 0x4325:
+ chipid_fallback = 0x4309;
+ break;
+ case PCI_DEVICE_ID_BCM4401:
+ case PCI_DEVICE_ID_BCM4401B0:
+ case PCI_DEVICE_ID_BCM4401B1:
+ chipid_fallback = 0x4401;
+ break;
+ default:
+ dev_err(&pci_dev->dev, "PCI-ID not in fallback list\n");
+ }
+
+ return chipid_fallback;
+}
+
+static u8 chipid_to_nrcores(u16 chipid)
+{
+ switch (chipid) {
+ case 0x5365:
+ return 7;
+ case 0x4306:
+ return 6;
+ case 0x4310:
+ return 8;
+ case 0x4307:
+ case 0x4301:
+ return 5;
+ case 0x4401:
+ case 0x4402:
+ return 3;
+ case 0x4710:
+ case 0x4610:
+ case 0x4704:
+ return 9;
+ default:
+ pr_err("CHIPID not in nrcores fallback list\n");
+ }
+
+ return 1;
+}
+
+static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx,
+ u16 offset)
+{
+ u32 lo, hi;
+
+ switch (bus->bustype) {
+ case SSB_BUSTYPE_SSB:
+ offset += current_coreidx * SSB_CORE_SIZE;
+ break;
+ case SSB_BUSTYPE_PCI:
+ break;
+ case SSB_BUSTYPE_PCMCIA:
+ if (offset >= 0x800) {
+ ssb_pcmcia_switch_segment(bus, 1);
+ offset -= 0x800;
+ } else
+ ssb_pcmcia_switch_segment(bus, 0);
+ lo = readw(bus->mmio + offset);
+ hi = readw(bus->mmio + offset + 2);
+ return lo | (hi << 16);
+ case SSB_BUSTYPE_SDIO:
+ offset += current_coreidx * SSB_CORE_SIZE;
+ return ssb_sdio_scan_read32(bus, offset);
+ }
+ return readl(bus->mmio + offset);
+}
+
+static int scan_switchcore(struct ssb_bus *bus, u8 coreidx)
+{
+ switch (bus->bustype) {
+ case SSB_BUSTYPE_SSB:
+ break;
+ case SSB_BUSTYPE_PCI:
+ return ssb_pci_switch_coreidx(bus, coreidx);
+ case SSB_BUSTYPE_PCMCIA:
+ return ssb_pcmcia_switch_coreidx(bus, coreidx);
+ case SSB_BUSTYPE_SDIO:
+ return ssb_sdio_scan_switch_coreidx(bus, coreidx);
+ }
+ return 0;
+}
+
+void ssb_iounmap(struct ssb_bus *bus)
+{
+ switch (bus->bustype) {
+ case SSB_BUSTYPE_SSB:
+ case SSB_BUSTYPE_PCMCIA:
+ iounmap(bus->mmio);
+ break;
+ case SSB_BUSTYPE_PCI:
+#ifdef CONFIG_SSB_PCIHOST
+ pci_iounmap(bus->host_pci, bus->mmio);
+#else
+ WARN_ON(1); /* Can't reach this code. */
+#endif
+ break;
+ case SSB_BUSTYPE_SDIO:
+ break;
+ }
+ bus->mmio = NULL;
+ bus->mapped_device = NULL;
+}
+
+static void __iomem *ssb_ioremap(struct ssb_bus *bus,
+ unsigned long baseaddr)
+{
+ void __iomem *mmio = NULL;
+
+ switch (bus->bustype) {
+ case SSB_BUSTYPE_SSB:
+ /* Only map the first core for now. */
+ fallthrough;
+ case SSB_BUSTYPE_PCMCIA:
+ mmio = ioremap(baseaddr, SSB_CORE_SIZE);
+ break;
+ case SSB_BUSTYPE_PCI:
+#ifdef CONFIG_SSB_PCIHOST
+ mmio = pci_iomap(bus->host_pci, 0, ~0UL);
+#else
+ WARN_ON(1); /* Can't reach this code. */
+#endif
+ break;
+ case SSB_BUSTYPE_SDIO:
+ /* Nothing to ioremap in the SDIO case, just fake it */
+ mmio = (void __iomem *)baseaddr;
+ break;
+ }
+
+ return mmio;
+}
+
+static int we_support_multiple_80211_cores(struct ssb_bus *bus)
+{
+ /* More than one 802.11 core is only supported by special chips.
+ * There are chips with two 802.11 cores, but with dangling
+ * pins on the second core. Be careful and reject them here.
+ */
+
+#ifdef CONFIG_SSB_PCIHOST
+ if (bus->bustype == SSB_BUSTYPE_PCI) {
+ if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM &&
+ ((bus->host_pci->device == 0x4313) ||
+ (bus->host_pci->device == 0x431A) ||
+ (bus->host_pci->device == 0x4321) ||
+ (bus->host_pci->device == 0x4324)))
+ return 1;
+ }
+#endif /* CONFIG_SSB_PCIHOST */
+ return 0;
+}
+
+int ssb_bus_scan(struct ssb_bus *bus,
+ unsigned long baseaddr)
+{
+ int err = -ENOMEM;
+ void __iomem *mmio;
+ u32 idhi, cc, rev, tmp;
+ int dev_i, i;
+ struct ssb_device *dev;
+ int nr_80211_cores = 0;
+
+ mmio = ssb_ioremap(bus, baseaddr);
+ if (!mmio)
+ goto out;
+ bus->mmio = mmio;
+
+ err = scan_switchcore(bus, 0); /* Switch to first core */
+ if (err)
+ goto err_unmap;
+
+ idhi = scan_read32(bus, 0, SSB_IDHIGH);
+ cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT;
+ rev = (idhi & SSB_IDHIGH_RCLO);
+ rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT;
+
+ bus->nr_devices = 0;
+ if (cc == SSB_DEV_CHIPCOMMON) {
+ tmp = scan_read32(bus, 0, SSB_CHIPCO_CHIPID);
+
+ bus->chip_id = (tmp & SSB_CHIPCO_IDMASK);
+ bus->chip_rev = (tmp & SSB_CHIPCO_REVMASK) >>
+ SSB_CHIPCO_REVSHIFT;
+ bus->chip_package = (tmp & SSB_CHIPCO_PACKMASK) >>
+ SSB_CHIPCO_PACKSHIFT;
+ if (rev >= 4) {
+ bus->nr_devices = (tmp & SSB_CHIPCO_NRCORESMASK) >>
+ SSB_CHIPCO_NRCORESSHIFT;
+ }
+ tmp = scan_read32(bus, 0, SSB_CHIPCO_CAP);
+ bus->chipco.capabilities = tmp;
+ } else {
+ if (bus->bustype == SSB_BUSTYPE_PCI) {
+ bus->chip_id = pcidev_to_chipid(bus->host_pci);
+ bus->chip_rev = bus->host_pci->revision;
+ bus->chip_package = 0;
+ } else {
+ bus->chip_id = 0x4710;
+ bus->chip_rev = 0;
+ bus->chip_package = 0;
+ }
+ }
+ pr_info("Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
+ bus->chip_id, bus->chip_rev, bus->chip_package);
+ if (!bus->nr_devices)
+ bus->nr_devices = chipid_to_nrcores(bus->chip_id);
+ if (bus->nr_devices > ARRAY_SIZE(bus->devices)) {
+ pr_err("More than %d ssb cores found (%d)\n",
+ SSB_MAX_NR_CORES, bus->nr_devices);
+ err = -EINVAL;
+ goto err_unmap;
+ }
+ if (bus->bustype == SSB_BUSTYPE_SSB) {
+ /* Now that we know the number of cores,
+ * remap the whole IO space for all cores.
+ */
+ err = -ENOMEM;
+ iounmap(mmio);
+ mmio = ioremap(baseaddr, SSB_CORE_SIZE * bus->nr_devices);
+ if (!mmio)
+ goto out;
+ bus->mmio = mmio;
+ }
+
+ /* Fetch basic information about each core/device */
+ for (i = 0, dev_i = 0; i < bus->nr_devices; i++) {
+ err = scan_switchcore(bus, i);
+ if (err)
+ goto err_unmap;
+ dev = &(bus->devices[dev_i]);
+
+ idhi = scan_read32(bus, i, SSB_IDHIGH);
+ dev->id.coreid = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT;
+ dev->id.revision = (idhi & SSB_IDHIGH_RCLO);
+ dev->id.revision |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT;
+ dev->id.vendor = (idhi & SSB_IDHIGH_VC) >> SSB_IDHIGH_VC_SHIFT;
+ dev->core_index = i;
+ dev->bus = bus;
+ dev->ops = bus->ops;
+
+ pr_debug("Core %d found: %s (cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n",
+ i, ssb_core_name(dev->id.coreid),
+ dev->id.coreid, dev->id.revision, dev->id.vendor);
+
+ switch (dev->id.coreid) {
+ case SSB_DEV_80211:
+ nr_80211_cores++;
+ if (nr_80211_cores > 1) {
+ if (!we_support_multiple_80211_cores(bus)) {
+ pr_debug("Ignoring additional 802.11 core\n");
+ continue;
+ }
+ }
+ break;
+ case SSB_DEV_EXTIF:
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+ if (bus->extif.dev) {
+ pr_warn("WARNING: Multiple EXTIFs found\n");
+ break;
+ }
+ bus->extif.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_EXTIF */
+ break;
+ case SSB_DEV_CHIPCOMMON:
+ if (bus->chipco.dev) {
+ pr_warn("WARNING: Multiple ChipCommon found\n");
+ break;
+ }
+ bus->chipco.dev = dev;
+ break;
+ case SSB_DEV_MIPS:
+ case SSB_DEV_MIPS_3302:
+#ifdef CONFIG_SSB_DRIVER_MIPS
+ if (bus->mipscore.dev) {
+ pr_warn("WARNING: Multiple MIPS cores found\n");
+ break;
+ }
+ bus->mipscore.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_MIPS */
+ break;
+ case SSB_DEV_PCI:
+ case SSB_DEV_PCIE:
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+ if (bus->bustype == SSB_BUSTYPE_PCI) {
+ /* Ignore PCI cores on PCI-E cards.
+ * Ignore PCI-E cores on PCI cards.
+ */
+ if (dev->id.coreid == SSB_DEV_PCI) {
+ if (pci_is_pcie(bus->host_pci))
+ continue;
+ } else {
+ if (!pci_is_pcie(bus->host_pci))
+ continue;
+ }
+ }
+ if (bus->pcicore.dev) {
+ pr_warn("WARNING: Multiple PCI(E) cores found\n");
+ break;
+ }
+ bus->pcicore.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+ break;
+ case SSB_DEV_ETHERNET:
+ if (bus->bustype == SSB_BUSTYPE_PCI) {
+ if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM &&
+ (bus->host_pci->device & 0xFF00) == 0x4300) {
+ /* This is a dangling ethernet core on a
+ * wireless device. Ignore it.
+ */
+ continue;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ dev_i++;
+ }
+ bus->nr_devices = dev_i;
+
+ err = 0;
+out:
+ return err;
+err_unmap:
+ ssb_iounmap(bus);
+ goto out;
+}