mirror of
https://github.com/ps5-linux/ps5-linux-patches.git
synced 2026-07-15 21:42:27 +00:00
4077 lines
114 KiB
Diff
4077 lines
114 KiB
Diff
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
|
|
index fe106da41ae5..6fd78cbfe1d6 100644
|
|
--- a/arch/x86/Kconfig
|
|
+++ b/arch/x86/Kconfig
|
|
@@ -605,6 +605,14 @@ config X86_NUMACHIP
|
|
enable more than ~168 cores.
|
|
If you don't have one of these, you should say N here.
|
|
|
|
+config X86_PS5
|
|
+ bool "Sony PlayStation 5"
|
|
+ depends on X86_64
|
|
+ depends on X86_EXTENDED_PLATFORM
|
|
+ depends on PCI
|
|
+ help
|
|
+ Select to include support for the Sony PlayStation 5 game console.
|
|
+
|
|
config X86_VSMP
|
|
bool "ScaleMP vSMP"
|
|
select HYPERVISOR_GUEST
|
|
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
|
|
index 92bb6b2f778e..0a02f339b98a 100644
|
|
--- a/arch/x86/include/asm/msr-index.h
|
|
+++ b/arch/x86/include/asm/msr-index.h
|
|
@@ -26,6 +26,7 @@
|
|
#define _EFER_LMSLE 13 /* Long Mode Segment Limit Enable */
|
|
#define _EFER_FFXSR 14 /* Enable Fast FXSAVE/FXRSTOR */
|
|
#define _EFER_TCE 15 /* Enable Translation Cache Extensions */
|
|
+#define _EFER_NDA 16 /* No data access enable */
|
|
#define _EFER_AUTOIBRS 21 /* Enable Automatic IBRS */
|
|
|
|
#define EFER_SCE (1<<_EFER_SCE)
|
|
@@ -36,6 +37,7 @@
|
|
#define EFER_LMSLE (1<<_EFER_LMSLE)
|
|
#define EFER_FFXSR (1<<_EFER_FFXSR)
|
|
#define EFER_TCE (1<<_EFER_TCE)
|
|
+#define EFER_NDA (1<<_EFER_NDA)
|
|
#define EFER_AUTOIBRS (1<<_EFER_AUTOIBRS)
|
|
|
|
/*
|
|
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
|
|
index 2ec250ba467e..f4654b2e63d0 100644
|
|
--- a/arch/x86/include/asm/pgtable_types.h
|
|
+++ b/arch/x86/include/asm/pgtable_types.h
|
|
@@ -21,8 +21,9 @@
|
|
#define _PAGE_BIT_SOFTW2 10 /* " */
|
|
#define _PAGE_BIT_SOFTW3 11 /* " */
|
|
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
|
|
-#define _PAGE_BIT_SOFTW4 57 /* available for programmer */
|
|
-#define _PAGE_BIT_SOFTW5 58 /* available for programmer */
|
|
+#define _PAGE_BIT_SOFTW4 56 /* available for programmer */
|
|
+#define _PAGE_BIT_SOFTW5 57 /* available for programmer */
|
|
+#define _PAGE_BIT_NDA 58 /* No data access */
|
|
#define _PAGE_BIT_PKEY_BIT0 59 /* Protection Keys, bit 1/4 */
|
|
#define _PAGE_BIT_PKEY_BIT1 60 /* Protection Keys, bit 2/4 */
|
|
#define _PAGE_BIT_PKEY_BIT2 61 /* Protection Keys, bit 3/4 */
|
|
@@ -65,6 +66,7 @@
|
|
#define _PAGE_SPECIAL (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL)
|
|
#define _PAGE_CPA_TEST (_AT(pteval_t, 1) << _PAGE_BIT_CPA_TEST)
|
|
#define _PAGE_KERNEL_4K (_AT(pteval_t, 1) << _PAGE_BIT_KERNEL_4K)
|
|
+#define _PAGE_NDA (_AT(pteval_t, 1) << _PAGE_BIT_NDA)
|
|
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
|
|
#define _PAGE_PKEY_BIT0 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT0)
|
|
#define _PAGE_PKEY_BIT1 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT1)
|
|
@@ -192,6 +194,7 @@ enum page_cache_mode {
|
|
#define ___D _PAGE_DIRTY
|
|
#define ___G _PAGE_GLOBAL
|
|
#define __NX _PAGE_NX
|
|
+#define _NDA _PAGE_NDA
|
|
|
|
#define _ENC _PAGE_ENC
|
|
#define __WP _PAGE_CACHE_WP
|
|
@@ -210,6 +213,7 @@ enum page_cache_mode {
|
|
#define PAGE_COPY __pg(__PP| 0|_USR|___A|__NX| 0| 0| 0)
|
|
#define PAGE_READONLY __pg(__PP| 0|_USR|___A|__NX| 0| 0| 0)
|
|
#define PAGE_READONLY_EXEC __pg(__PP| 0|_USR|___A| 0| 0| 0| 0)
|
|
+#define PAGE_EXECONLY __pg(__PP| 0|_USR|___A|_NDA| 0| 0| 0)
|
|
|
|
/*
|
|
* Page tables needs to have Write=1 in order for any lower PTEs to be
|
|
diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h
|
|
index 914eb32581c7..1199b0762d60 100644
|
|
--- a/arch/x86/include/asm/setup.h
|
|
+++ b/arch/x86/include/asm/setup.h
|
|
@@ -69,6 +69,12 @@ extern void x86_ce4100_early_setup(void);
|
|
static inline void x86_ce4100_early_setup(void) { }
|
|
#endif
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+extern void x86_ps5_early_setup(void);
|
|
+#else
|
|
+static inline void x86_ps5_early_setup(void) { }
|
|
+#endif
|
|
+
|
|
#include <linux/kexec_handover.h>
|
|
|
|
#ifndef _SETUP
|
|
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
|
|
index dafbf581c515..791687363cbf 100644
|
|
--- a/arch/x86/include/uapi/asm/bootparam.h
|
|
+++ b/arch/x86/include/uapi/asm/bootparam.h
|
|
@@ -207,6 +207,7 @@ enum x86_hardware_subarch {
|
|
X86_SUBARCH_XEN,
|
|
X86_SUBARCH_INTEL_MID,
|
|
X86_SUBARCH_CE4100,
|
|
+ X86_SUBARCH_PS5,
|
|
X86_NR_SUBARCHS,
|
|
};
|
|
|
|
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
|
|
index fd28b53dbac5..569fc764d267 100644
|
|
--- a/arch/x86/kernel/head64.c
|
|
+++ b/arch/x86/kernel/head64.c
|
|
@@ -303,6 +303,11 @@ void __init __noreturn x86_64_start_reservations(char *real_mode_data)
|
|
case X86_SUBARCH_INTEL_MID:
|
|
x86_intel_mid_early_setup();
|
|
break;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ case X86_SUBARCH_PS5:
|
|
+ x86_ps5_early_setup();
|
|
+ break;
|
|
+#endif
|
|
default:
|
|
break;
|
|
}
|
|
diff --git a/arch/x86/kernel/i8253.c b/arch/x86/kernel/i8253.c
|
|
index cb9852ad6098..83bb0cf9e40b 100644
|
|
--- a/arch/x86/kernel/i8253.c
|
|
+++ b/arch/x86/kernel/i8253.c
|
|
@@ -31,6 +31,11 @@ struct clock_event_device *global_clock_event;
|
|
*/
|
|
static bool __init use_pit(void)
|
|
{
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* PIT is not available. */
|
|
+ return false;
|
|
+#endif
|
|
+
|
|
if (!IS_ENABLED(CONFIG_X86_TSC) || !boot_cpu_has(X86_FEATURE_TSC))
|
|
return true;
|
|
|
|
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
|
|
index ccc7b2e34a11..7d941c4d30f6 100644
|
|
--- a/arch/x86/kvm/x86.c
|
|
+++ b/arch/x86/kvm/x86.c
|
|
@@ -112,7 +112,11 @@ EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_host);
|
|
*/
|
|
#ifdef CONFIG_X86_64
|
|
static
|
|
+#ifdef CONFIG_X86_PS5
|
|
+u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA | EFER_NDA));
|
|
+#else
|
|
u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA));
|
|
+#endif
|
|
#else
|
|
static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE);
|
|
#endif
|
|
diff --git a/arch/x86/mm/pgprot.c b/arch/x86/mm/pgprot.c
|
|
index dc1afd5c839d..745db92fb2c1 100644
|
|
--- a/arch/x86/mm/pgprot.c
|
|
+++ b/arch/x86/mm/pgprot.c
|
|
@@ -10,7 +10,11 @@ static pgprot_t protection_map[16] __ro_after_init = {
|
|
[VM_READ] = PAGE_READONLY,
|
|
[VM_WRITE] = PAGE_COPY,
|
|
[VM_WRITE | VM_READ] = PAGE_COPY,
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ [VM_EXEC] = PAGE_EXECONLY,
|
|
+#else
|
|
[VM_EXEC] = PAGE_READONLY_EXEC,
|
|
+#endif
|
|
[VM_EXEC | VM_READ] = PAGE_READONLY_EXEC,
|
|
[VM_EXEC | VM_WRITE] = PAGE_COPY_EXEC,
|
|
[VM_EXEC | VM_WRITE | VM_READ] = PAGE_COPY_EXEC,
|
|
@@ -18,7 +22,11 @@ static pgprot_t protection_map[16] __ro_after_init = {
|
|
[VM_SHARED | VM_READ] = PAGE_READONLY,
|
|
[VM_SHARED | VM_WRITE] = PAGE_SHARED,
|
|
[VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED,
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ [VM_SHARED | VM_EXEC] = PAGE_EXECONLY,
|
|
+#else
|
|
[VM_SHARED | VM_EXEC] = PAGE_READONLY_EXEC,
|
|
+#endif
|
|
[VM_SHARED | VM_EXEC | VM_READ] = PAGE_READONLY_EXEC,
|
|
[VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_SHARED_EXEC,
|
|
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_SHARED_EXEC
|
|
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
|
|
index 63105b29fbf5..f38e2601531c 100644
|
|
--- a/arch/x86/pci/common.c
|
|
+++ b/arch/x86/pci/common.c
|
|
@@ -57,14 +57,100 @@ int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+static DEFINE_RAW_SPINLOCK(pci_lock);
|
|
+
|
|
+static int sfc_read(unsigned int domain, unsigned int bus, unsigned int rid,
|
|
+ int reg, int len, u32 *val)
|
|
+{
|
|
+ int busno;
|
|
+ int slot;
|
|
+ int func;
|
|
+
|
|
+ busno = (rid >> 8) & 0xff;
|
|
+ slot = (rid & 0xff) >> 3;
|
|
+ func = rid & 0x07;
|
|
+
|
|
+ if (busno == 0 || busno == 32) {
|
|
+ raw_pci_write(domain, bus, 0, 0xd90, 4, 4);
|
|
+ raw_pci_write(domain, bus, 0, 0xd80, 4, (32 << 24) | (slot << 19) | (func << 16) | (reg & 0xf80));
|
|
+ return raw_pci_read(domain, bus, 0, (reg & 0x7f) | 0xf80, len, val);
|
|
+ } else if (busno < 32) {
|
|
+ raw_pci_write(domain, bus, 0, 0xd10, 4, busno == 1 ? 4 : 5);
|
|
+ raw_pci_write(domain, bus, 0, 0xd00, 4, (busno << 24) | (slot << 19) | (func << 16) | (reg & 0xf80));
|
|
+ return raw_pci_read(domain, bus, 0, (reg & 0x7f) | 0xf00, len, val);
|
|
+ } else {
|
|
+ panic("wrong bus number");
|
|
+ }
|
|
+}
|
|
+
|
|
+static int sfc_write(unsigned int domain, unsigned int bus, unsigned int rid,
|
|
+ int reg, int len, u32 val)
|
|
+{
|
|
+ int busno;
|
|
+ int slot;
|
|
+ int func;
|
|
+
|
|
+ busno = (rid >> 8) & 0xff;
|
|
+ slot = (rid & 0xff) >> 3;
|
|
+ func = rid & 0x07;
|
|
+
|
|
+ if (busno == 0 || busno == 32) {
|
|
+ raw_pci_write(domain, bus, 0, 0xd90, 4, 4);
|
|
+ raw_pci_write(domain, bus, 0, 0xd80, 4, (32 << 24) | (slot << 19) | (func << 16) | (reg & 0xf80));
|
|
+ return raw_pci_write(domain, bus, 0, (reg & 0x7f) | 0xf80, len, val);
|
|
+ } else if (busno < 32) {
|
|
+ raw_pci_write(domain, bus, 0, 0xd10, 4, busno == 1 ? 4 : 5);
|
|
+ raw_pci_write(domain, bus, 0, 0xd00, 4, (busno << 24) | (slot << 19) | (func << 16) | (reg & 0xf80));
|
|
+ return raw_pci_write(domain, bus, 0, (reg & 0x7f) | 0xf00, len, val);
|
|
+ } else {
|
|
+ panic("wrong bus number");
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
|
|
{
|
|
+ int ret;
|
|
+ unsigned long flags;
|
|
+
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (pci_ari_enabled(bus)) {
|
|
+ u32 rid = 0;
|
|
+ raw_spin_lock_irqsave(&pci_lock, flags);
|
|
+ raw_pci_read(pci_domain_nr(bus), bus->number, devfn, 0x150, 2, &rid);
|
|
+ if (rid != 0 && rid != 0xffff) {
|
|
+ ret = sfc_read(pci_domain_nr(bus), bus->number, rid, where, size, value);
|
|
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
|
|
+ return ret;
|
|
+ }
|
|
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
|
|
+ }
|
|
+#endif
|
|
+
|
|
return raw_pci_read(pci_domain_nr(bus), bus->number,
|
|
devfn, where, size, value);
|
|
}
|
|
|
|
static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
|
|
{
|
|
+ int ret;
|
|
+ unsigned long flags;
|
|
+
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (pci_ari_enabled(bus)) {
|
|
+ u32 rid = 0;
|
|
+ raw_spin_lock_irqsave(&pci_lock, flags);
|
|
+ raw_pci_read(pci_domain_nr(bus), bus->number, devfn, 0x150, 2, &rid);
|
|
+ if (rid != 0 && rid != 0xffff) {
|
|
+ ret = sfc_write(pci_domain_nr(bus), bus->number, rid, where, size, value);
|
|
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
|
|
+ return ret;
|
|
+ }
|
|
+ raw_spin_unlock_irqrestore(&pci_lock, flags);
|
|
+ }
|
|
+#endif
|
|
+
|
|
return raw_pci_write(pci_domain_nr(bus), bus->number,
|
|
devfn, where, size, value);
|
|
}
|
|
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c
|
|
index 0de436316a1d..dde625251c99 100644
|
|
--- a/arch/x86/pci/irq.c
|
|
+++ b/arch/x86/pci/irq.c
|
|
@@ -1720,6 +1720,11 @@ static int pirq_enable_irq(struct pci_dev *dev)
|
|
{
|
|
u8 pin = 0;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Legacy IRQ is not available. */
|
|
+ return 0;
|
|
+#endif
|
|
+
|
|
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
|
|
if (pin && !pcibios_lookup_irq(dev, 1)) {
|
|
char *msg = "";
|
|
@@ -1801,6 +1806,11 @@ bool mp_should_keep_irq(struct device *dev)
|
|
|
|
static void pirq_disable_irq(struct pci_dev *dev)
|
|
{
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Legacy IRQ is not available. */
|
|
+ return;
|
|
+#endif
|
|
+
|
|
if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
|
|
dev->irq_managed && dev->irq) {
|
|
mp_unmap_irq(dev->irq);
|
|
diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile
|
|
index 3ed03a2552d0..40ffa1a2156a 100644
|
|
--- a/arch/x86/platform/Makefile
|
|
+++ b/arch/x86/platform/Makefile
|
|
@@ -9,6 +9,7 @@ obj-y += intel/
|
|
obj-y += intel-mid/
|
|
obj-y += intel-quark/
|
|
obj-y += olpc/
|
|
+obj-y += ps5/
|
|
obj-y += scx200/
|
|
obj-y += ts5500/
|
|
obj-y += uv/
|
|
diff --git a/arch/x86/platform/ps5/Makefile b/arch/x86/platform/ps5/Makefile
|
|
new file mode 100644
|
|
index 000000000000..5263181876f8
|
|
--- /dev/null
|
|
+++ b/arch/x86/platform/ps5/Makefile
|
|
@@ -0,0 +1 @@
|
|
+obj-$(CONFIG_X86_PS5) += ps5.o
|
|
diff --git a/arch/x86/platform/ps5/ps5.c b/arch/x86/platform/ps5/ps5.c
|
|
new file mode 100644
|
|
index 000000000000..731fed163a83
|
|
--- /dev/null
|
|
+++ b/arch/x86/platform/ps5/ps5.c
|
|
@@ -0,0 +1,51 @@
|
|
+#include <linux/module.h>
|
|
+#include <linux/ps5.h>
|
|
+
|
|
+#include <asm/apic.h>
|
|
+#include <asm/i8259.h>
|
|
+#include <asm/reboot.h>
|
|
+#include <asm/setup.h>
|
|
+
|
|
+#define PS5_DEFAULT_TSC_FREQ 1596300000
|
|
+
|
|
+static __noreturn void ps5_power_off(void) {
|
|
+ icc_power_shutdown();
|
|
+}
|
|
+
|
|
+static __noreturn void ps5_emergency_restart(void) {
|
|
+ icc_power_reboot();
|
|
+}
|
|
+
|
|
+static __noreturn void ps5_restart(char *cmd) {
|
|
+ ps5_emergency_restart();
|
|
+}
|
|
+
|
|
+static unsigned long __init ps5_calibrate_tsc(void)
|
|
+{
|
|
+ return PS5_DEFAULT_TSC_FREQ / 1000;
|
|
+}
|
|
+
|
|
+static void ps5_get_wallclock(struct timespec64 *now)
|
|
+{
|
|
+ now->tv_sec = now->tv_nsec = 0;
|
|
+}
|
|
+
|
|
+static int ps5_set_wallclock(const struct timespec64 *now)
|
|
+{
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+void __init x86_ps5_early_setup(void)
|
|
+{
|
|
+ x86_platform.calibrate_tsc = ps5_calibrate_tsc;
|
|
+ x86_platform.get_wallclock = ps5_get_wallclock;
|
|
+ x86_platform.set_wallclock = ps5_set_wallclock;
|
|
+ x86_platform.legacy.rtc = 0;
|
|
+
|
|
+ machine_ops.restart = ps5_restart;
|
|
+ machine_ops.power_off = ps5_power_off;
|
|
+ machine_ops.emergency_restart = ps5_emergency_restart;
|
|
+ pm_power_off = ps5_power_off;
|
|
+
|
|
+ legacy_pic = &null_legacy_pic;
|
|
+}
|
|
diff --git a/block/partitions/efi.c b/block/partitions/efi.c
|
|
index 75474fb3848e..44a0744533a0 100644
|
|
--- a/block/partitions/efi.c
|
|
+++ b/block/partitions/efi.c
|
|
@@ -90,6 +90,9 @@
|
|
#include "check.h"
|
|
#include "efi.h"
|
|
|
|
+#define PS5_M2_PART44_LBA_OFFSET 34224
|
|
+#define PS5_M2_MBR_LBA 65536
|
|
+
|
|
/* This allows a kernel command line option 'gpt' to override
|
|
* the test for invalid PMBR. Not __initdata because reloading
|
|
* the partition tables happens after init too.
|
|
@@ -133,6 +136,12 @@ efi_crc32(const void *buf, unsigned long len)
|
|
*/
|
|
static u64 last_lba(struct gendisk *disk)
|
|
{
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (strncmp(disk->disk_name, "nvme", 4) == 0) {
|
|
+ return div_u64(bdev_nr_bytes(disk->part0),
|
|
+ queue_logical_block_size(disk->queue)) - 1ULL - PS5_M2_PART44_LBA_OFFSET;
|
|
+ } else
|
|
+#endif
|
|
return div_u64(bdev_nr_bytes(disk->part0),
|
|
queue_logical_block_size(disk->queue)) - 1ULL;
|
|
}
|
|
@@ -143,8 +152,14 @@ static inline int pmbr_part_valid(gpt_mbr_record *part)
|
|
goto invalid;
|
|
|
|
/* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA &&
|
|
+ le32_to_cpu(part->starting_lba) != PS5_M2_MBR_LBA + GPT_PRIMARY_PARTITION_TABLE_LBA)
|
|
+ goto invalid;
|
|
+#else
|
|
if (le32_to_cpu(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA)
|
|
goto invalid;
|
|
+#endif
|
|
|
|
return GPT_MBR_PROTECTIVE;
|
|
invalid:
|
|
@@ -599,6 +614,11 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt,
|
|
if (!legacymbr)
|
|
goto fail;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (strncmp(state->disk->disk_name, "nvme", 4) == 0) {
|
|
+ read_lba(state, PS5_M2_MBR_LBA, (u8 *)legacymbr, sizeof(*legacymbr));
|
|
+ } else
|
|
+#endif
|
|
read_lba(state, 0, (u8 *)legacymbr, sizeof(*legacymbr));
|
|
good_pmbr = is_pmbr_valid(legacymbr, total_sectors);
|
|
kfree(legacymbr);
|
|
@@ -611,6 +631,12 @@ static int find_valid_gpt(struct parsed_partitions *state, gpt_header **gpt,
|
|
"protective" : "hybrid");
|
|
}
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (strncmp(state->disk->disk_name, "nvme", 4) == 0) {
|
|
+ good_pgpt = is_gpt_valid(state, PS5_M2_MBR_LBA + GPT_PRIMARY_PARTITION_TABLE_LBA,
|
|
+ &pgpt, &pptes);
|
|
+ } else
|
|
+#endif
|
|
good_pgpt = is_gpt_valid(state, GPT_PRIMARY_PARTITION_TABLE_LBA,
|
|
&pgpt, &pptes);
|
|
if (good_pgpt)
|
|
diff --git a/drivers/Makefile b/drivers/Makefile
|
|
index 53fbd2e0acdd..d36c1909e0e6 100644
|
|
--- a/drivers/Makefile
|
|
+++ b/drivers/Makefile
|
|
@@ -199,3 +199,5 @@ obj-y += resctrl/
|
|
|
|
obj-$(CONFIG_DIBS) += dibs/
|
|
obj-$(CONFIG_S390) += s390/
|
|
+
|
|
+obj-$(CONFIG_X86_PS5) += ps5/
|
|
diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c
|
|
index 477a37e2ef80..e1af70946c89 100644
|
|
--- a/drivers/firmware/dmi-id.c
|
|
+++ b/drivers/firmware/dmi-id.c
|
|
@@ -228,8 +228,16 @@ static int __init dmi_id_init(void)
|
|
{
|
|
int ret;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ dmi_set_system_info(DMI_SYS_VENDOR, "Sony Interactive Entertainment");
|
|
+ dmi_set_system_info(DMI_PRODUCT_NAME, "PlayStation 5");
|
|
+ /* Filled by spcie.c */
|
|
+ dmi_set_system_info(DMI_PRODUCT_VERSION, "Unknown");
|
|
+ dmi_set_system_info(DMI_PRODUCT_SERIAL, "Unknown");
|
|
+#else
|
|
if (!dmi_available)
|
|
return -ENODEV;
|
|
+#endif
|
|
|
|
dmi_id_init_attr_table();
|
|
|
|
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
|
|
index 70d39adf50dc..5d283649817e 100644
|
|
--- a/drivers/firmware/dmi_scan.c
|
|
+++ b/drivers/firmware/dmi_scan.c
|
|
@@ -953,6 +953,14 @@ const char *dmi_get_system_info(int field)
|
|
}
|
|
EXPORT_SYMBOL(dmi_get_system_info);
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+void dmi_set_system_info(int field, const char *str)
|
|
+{
|
|
+ dmi_ident[field] = str;
|
|
+}
|
|
+EXPORT_SYMBOL(dmi_set_system_info);
|
|
+#endif
|
|
+
|
|
/**
|
|
* dmi_name_in_serial - Check if string is in the DMI product serial information
|
|
* @str: string to check for
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
|
|
index 447e734c362b..c52c81e084a7 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
|
|
@@ -271,6 +271,10 @@ extern int amdgpu_user_queue;
|
|
|
|
extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+extern int amdgpu_force_1080p;
|
|
+#endif
|
|
+
|
|
#define AMDGPU_VM_MAX_NUM_CTX 4096
|
|
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
|
|
#define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
|
|
index 35d04e69aec0..314ceaaaab9f 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
|
|
@@ -221,8 +221,14 @@ static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev)
|
|
|
|
static bool amdgpu_read_platform_bios(struct amdgpu_device *adev)
|
|
{
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Hardcode vbios rom address. */
|
|
+ phys_addr_t rom = 0xc0000;
|
|
+ size_t romlen = 0x10000;
|
|
+#else
|
|
phys_addr_t rom = adev->pdev->rom;
|
|
size_t romlen = adev->pdev->romlen;
|
|
+#endif
|
|
void __iomem *bios;
|
|
|
|
adev->bios = NULL;
|
|
@@ -436,6 +442,7 @@ static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
|
|
|
|
static bool amdgpu_get_bios_apu(struct amdgpu_device *adev)
|
|
{
|
|
+#ifndef CONFIG_X86_PS5
|
|
if (amdgpu_acpi_vfct_bios(adev)) {
|
|
dev_info(adev->dev, "Fetched VBIOS from VFCT\n");
|
|
goto success;
|
|
@@ -450,6 +457,7 @@ static bool amdgpu_get_bios_apu(struct amdgpu_device *adev)
|
|
dev_info(adev->dev, "Fetched VBIOS from ROM BAR\n");
|
|
goto success;
|
|
}
|
|
+#endif
|
|
|
|
if (amdgpu_read_platform_bios(adev)) {
|
|
dev_info(adev->dev, "Fetched VBIOS from platform\n");
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
|
|
index 6d8531f9b882..3f857b0da756 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
|
|
@@ -2512,6 +2512,26 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev)
|
|
case CHIP_CYAN_SKILLFISH:
|
|
if (adev->discovery.bin)
|
|
return 0;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Hardcode gfxinfo. */
|
|
+ adev->gfx.config.max_shader_engines = 2;
|
|
+ adev->gfx.config.max_cu_per_sh = 10;
|
|
+ adev->gfx.config.max_sh_per_se = 2;
|
|
+ adev->gfx.config.max_backends_per_se = 8;
|
|
+ adev->gfx.config.max_texture_channel_caches = 16;
|
|
+ adev->gfx.config.max_gprs = 1024;
|
|
+ adev->gfx.config.max_gs_threads = 32;
|
|
+ adev->gfx.config.gs_vgt_table_depth = 32;
|
|
+ adev->gfx.config.gs_prim_buffer_depth = 1792;
|
|
+ adev->gfx.config.double_offchip_lds_buf = 1;
|
|
+ adev->gfx.cu_info.wave_front_size = 32;
|
|
+ adev->gfx.cu_info.max_waves_per_simd = 20;
|
|
+ adev->gfx.cu_info.max_scratch_slots_per_cu = 32;
|
|
+ adev->gfx.cu_info.lds_size = 64;
|
|
+ adev->gfx.config.num_sc_per_sh = 1;
|
|
+ adev->gfx.config.num_packer_per_sc = 2;
|
|
+ return 0;
|
|
+#endif
|
|
chip_name = "cyan_skillfish";
|
|
break;
|
|
}
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
|
|
index af3d2fd61cf3..c31152f5a158 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
|
|
@@ -2914,6 +2914,9 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev)
|
|
adev->ip_versions[SMUIO_HWIP][0] = IP_VERSION(11, 0, 8);
|
|
adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 1, 3);
|
|
adev->ip_versions[UVD_HWIP][0] = IP_VERSION(2, 0, 3);
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(2, 0, 3);
|
|
+#endif
|
|
}
|
|
break;
|
|
default:
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
|
|
index c91638e65174..c25b073ca658 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
|
|
@@ -41,6 +41,10 @@
|
|
#include <linux/suspend.h>
|
|
#include <linux/vga_switcheroo.h>
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+#include <linux/ps5.h>
|
|
+#endif
|
|
+
|
|
#include "amdgpu.h"
|
|
#include "amdgpu_amdkfd.h"
|
|
#include "amdgpu_dma_buf.h"
|
|
@@ -1134,6 +1138,12 @@ module_param_named(user_queue, amdgpu_user_queue, int, 0444);
|
|
MODULE_PARM_DESC(hdmi_hpd_debounce_delay_ms, "HDMI HPD disconnect debounce delay in milliseconds (0 to disable (by default), 1500 is common)");
|
|
module_param_named(hdmi_hpd_debounce_delay_ms, amdgpu_hdmi_hpd_debounce_delay_ms, uint, 0644);
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+int amdgpu_force_1080p = 0;
|
|
+MODULE_PARM_DESC(force_1080p, "Force 1080p (0 = disabled (default), 1 = enabled)");
|
|
+module_param_named(force_1080p, amdgpu_force_1080p, int, 0444);
|
|
+#endif
|
|
+
|
|
/* These devices are not supported by amdgpu.
|
|
* They are supported by the mach64, r128, radeon drivers
|
|
*/
|
|
@@ -2179,6 +2189,7 @@ static const struct pci_device_id pciidlist[] = {
|
|
{0x1002, 0x7410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN},
|
|
|
|
/* CYAN_SKILLFISH */
|
|
+ {0x1002, 0x13DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
|
|
{0x1002, 0x13DB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
|
|
{0x1002, 0x13F9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
|
|
{0x1002, 0x13FA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
|
|
@@ -2393,6 +2404,12 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
|
|
int ret, retry = 0, i;
|
|
bool supports_atomic = false;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (!isHdmiEnabled()) {
|
|
+ return -EPROBE_DEFER;
|
|
+ }
|
|
+#endif
|
|
+
|
|
if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA ||
|
|
(pdev->class >> 8) == PCI_CLASS_DISPLAY_OTHER) {
|
|
if (drm_firmware_drivers_only() && amdgpu_modeset == -1)
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
|
|
index 77578ecc6782..b037a6f57a6d 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
|
|
@@ -2344,6 +2344,11 @@ void amdgpu_gfx_profile_ring_begin_use(struct amdgpu_ring *ring)
|
|
enum PP_SMC_POWER_PROFILE profile;
|
|
int r;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* DPM is not supported. */
|
|
+ return;
|
|
+#endif
|
|
+
|
|
if (amdgpu_dpm_is_overdrive_enabled(adev))
|
|
return;
|
|
|
|
@@ -2379,6 +2384,11 @@ void amdgpu_gfx_profile_ring_end_use(struct amdgpu_ring *ring)
|
|
{
|
|
struct amdgpu_device *adev = ring->adev;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* DPM is not supported. */
|
|
+ return;
|
|
+#endif
|
|
+
|
|
if (amdgpu_dpm_is_overdrive_enabled(adev))
|
|
return;
|
|
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c
|
|
index 321310ba2c08..764da52902ac 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c
|
|
@@ -210,6 +210,9 @@ int amdgpu_sdma_init_microcode(struct amdgpu_device *adev,
|
|
const struct sdma_firmware_header_v3_0 *sdma_hv3;
|
|
uint16_t version_major;
|
|
char ucode_prefix[30];
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ struct common_firmware_header *hdr;
|
|
+#endif
|
|
|
|
amdgpu_ucode_ip_version_decode(adev, SDMA0_HWIP, ucode_prefix, sizeof(ucode_prefix));
|
|
if (instance == 0)
|
|
@@ -222,6 +225,11 @@ int amdgpu_sdma_init_microcode(struct amdgpu_device *adev,
|
|
"amdgpu/%s%d.bin", ucode_prefix, instance);
|
|
if (err)
|
|
goto out;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ hdr = (struct common_firmware_header *)adev->sdma.instance[instance].fw->data;
|
|
+ hdr->ucode_array_offset_bytes = cpu_to_le32(le32_to_cpu(hdr->ucode_array_offset_bytes) + 0x100);
|
|
+ hdr->ucode_size_bytes = cpu_to_le32(le32_to_cpu(hdr->ucode_size_bytes) - 0x200);
|
|
+#endif
|
|
|
|
header = (const struct common_firmware_header *)
|
|
adev->sdma.instance[instance].fw->data;
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
|
|
index 1893ceeeb26c..64605a0018c1 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
|
|
@@ -4202,6 +4202,14 @@ static void gfx_v10_0_check_gfxoff_flag(struct amdgpu_device *adev)
|
|
}
|
|
}
|
|
|
|
+static void gfx_v10_0_patch_ucode(const u8 *data)
|
|
+{
|
|
+ struct common_firmware_header *hdr;
|
|
+ hdr = (struct common_firmware_header *)data;
|
|
+ hdr->ucode_array_offset_bytes = cpu_to_le32(le32_to_cpu(hdr->ucode_array_offset_bytes) + 0x100);
|
|
+ hdr->ucode_size_bytes = cpu_to_le32(le32_to_cpu(hdr->ucode_size_bytes) - 0x200);
|
|
+}
|
|
+
|
|
static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
{
|
|
char fw_name[53];
|
|
@@ -4224,6 +4232,9 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
"amdgpu/%s_pfp%s.bin", ucode_prefix, wks);
|
|
if (err)
|
|
goto out;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ gfx_v10_0_patch_ucode(adev->gfx.pfp_fw->data);
|
|
+#endif
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_PFP);
|
|
|
|
err = amdgpu_ucode_request(adev, &adev->gfx.me_fw,
|
|
@@ -4231,6 +4242,9 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
"amdgpu/%s_me%s.bin", ucode_prefix, wks);
|
|
if (err)
|
|
goto out;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ gfx_v10_0_patch_ucode(adev->gfx.me_fw->data);
|
|
+#endif
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_ME);
|
|
|
|
err = amdgpu_ucode_request(adev, &adev->gfx.ce_fw,
|
|
@@ -4238,6 +4252,9 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
"amdgpu/%s_ce%s.bin", ucode_prefix, wks);
|
|
if (err)
|
|
goto out;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ gfx_v10_0_patch_ucode(adev->gfx.ce_fw->data);
|
|
+#endif
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_CE);
|
|
|
|
if (!amdgpu_sriov_vf(adev)) {
|
|
@@ -4245,6 +4262,9 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
err = request_firmware(&adev->gfx.rlc_fw, fw_name, adev->dev);
|
|
if (err)
|
|
goto out;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ gfx_v10_0_patch_ucode(adev->gfx.rlc_fw->data);
|
|
+#endif
|
|
|
|
/* don't validate this firmware. There are apparently firmwares
|
|
* in the wild with incorrect size in the header
|
|
@@ -4262,6 +4282,9 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
"amdgpu/%s_mec%s.bin", ucode_prefix, wks);
|
|
if (err)
|
|
goto out;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ gfx_v10_0_patch_ucode(adev->gfx.mec_fw->data);
|
|
+#endif
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_MEC1);
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_MEC1_JT);
|
|
|
|
@@ -4269,6 +4292,9 @@ static int gfx_v10_0_init_microcode(struct amdgpu_device *adev)
|
|
AMDGPU_UCODE_REQUIRED,
|
|
"amdgpu/%s_mec2%s.bin", ucode_prefix, wks);
|
|
if (!err) {
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ gfx_v10_0_patch_ucode(adev->gfx.mec2_fw->data);
|
|
+#endif
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_MEC2);
|
|
amdgpu_gfx_cp_init_microcode(adev, AMDGPU_UCODE_ID_CP_MEC2_JT);
|
|
} else {
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c
|
|
index f17c3839aea1..a561038f3f79 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/nv.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/nv.c
|
|
@@ -868,6 +868,10 @@ static int nv_common_early_init(struct amdgpu_ip_block *ip_block)
|
|
adev->cg_flags = 0;
|
|
adev->pg_flags = 0;
|
|
adev->external_rev_id = adev->rev_id + 0x82;
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Hardcode bc250's id. Remove when mesa's patch is widely available. */
|
|
+ adev->external_rev_id = 0x84;
|
|
+#endif
|
|
break;
|
|
case IP_VERSION(10, 3, 6):
|
|
adev->cg_flags = AMD_CG_SUPPORT_GFX_MGCG |
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
|
|
index e3a035c9fece..6eb67dd7b52c 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
|
|
@@ -1362,6 +1362,12 @@ static int sdma_v5_0_early_init(struct amdgpu_ip_block *ip_block)
|
|
struct amdgpu_device *adev = ip_block->adev;
|
|
int r;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Reset SDMA to prevent ring test failures after reload. */
|
|
+ sdma_v5_0_soft_reset_engine(adev, 0);
|
|
+ sdma_v5_0_soft_reset_engine(adev, 1);
|
|
+#endif
|
|
+
|
|
r = sdma_v5_0_init_microcode(adev);
|
|
if (r)
|
|
return r;
|
|
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
|
index 2328c1aa0ead..cf3820f2ca17 100644
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
|
@@ -80,6 +80,10 @@
|
|
#include <linux/component.h>
|
|
#include <linux/sort.h>
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+#include <linux/ps5.h>
|
|
+#endif
|
|
+
|
|
#include <drm/drm_privacy_screen_consumer.h>
|
|
#include <drm/display/drm_dp_mst_helper.h>
|
|
#include <drm/display/drm_hdmi_helper.h>
|
|
@@ -3003,7 +3007,10 @@ static int dm_hw_init(struct amdgpu_ip_block *ip_block)
|
|
r = amdgpu_dm_init(adev);
|
|
if (r)
|
|
return r;
|
|
+#ifndef CONFIG_X86_PS5
|
|
+ /* HPD is not supported. */
|
|
amdgpu_dm_hpd_init(adev);
|
|
+#endif
|
|
|
|
r = dm_oem_i2c_hw_init(adev);
|
|
if (r)
|
|
@@ -3024,7 +3031,10 @@ static int dm_hw_fini(struct amdgpu_ip_block *ip_block)
|
|
{
|
|
struct amdgpu_device *adev = ip_block->adev;
|
|
|
|
+#ifndef CONFIG_X86_PS5
|
|
+ /* HPD is not supported. */
|
|
amdgpu_dm_hpd_fini(adev);
|
|
+#endif
|
|
|
|
amdgpu_dm_irq_fini(adev);
|
|
amdgpu_dm_fini(adev);
|
|
@@ -6805,6 +6815,12 @@ static void fill_stream_properties_from_drm_display_mode(
|
|
|
|
stream->output_color_space = get_output_color_space(timing_out, connector_state);
|
|
stream->content_type = get_output_content_type(connector_state);
|
|
+
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Hardcode RGB888. */
|
|
+ // timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
|
|
+ // timing_out->display_color_depth = COLOR_DEPTH_888;
|
|
+#endif
|
|
}
|
|
|
|
static void fill_audio_info(struct audio_info *audio_info,
|
|
@@ -8194,6 +8210,11 @@ enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connec
|
|
*/
|
|
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (!isHdmiModeValid(mode, amdgpu_force_1080p))
|
|
+ return MODE_ERROR;
|
|
+#endif
|
|
+
|
|
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
|
|
(mode->flags & DRM_MODE_FLAG_DBLSCAN))
|
|
return result;
|
|
@@ -10454,6 +10475,9 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
|
|
new_crtc_state, i) {
|
|
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
|
|
+#endif
|
|
dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
|
|
|
|
if (old_crtc_state->active &&
|
|
@@ -10461,6 +10485,11 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
|
|
drm_atomic_crtc_needs_modeset(new_crtc_state))) {
|
|
manage_dm_interrupts(adev, acrtc, NULL);
|
|
dc_stream_release(dm_old_crtc_state->stream);
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (dm_new_crtc_state->stream && dc_is_dp_signal(dm_new_crtc_state->stream->signal)) {
|
|
+ sceHdmiInitVideoConfig();
|
|
+ }
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
@@ -11031,6 +11060,22 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
|
|
dc_stream_retain(dm_new_crtc_state->stream);
|
|
acrtc->dm_irq_params.stream = dm_new_crtc_state->stream;
|
|
manage_dm_interrupts(adev, acrtc, dm_new_crtc_state);
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (dm_new_crtc_state->stream && dc_is_dp_signal(dm_new_crtc_state->stream->signal)) {
|
|
+ struct dc_stream_state *stream = dm_new_crtc_state->stream;
|
|
+ int channels = 0;
|
|
+
|
|
+ for (i = 0; i < stream->audio_info.mode_count; i++) {
|
|
+ if (stream->audio_info.modes[i].channel_count > channels)
|
|
+ channels = stream->audio_info.modes[i].channel_count;
|
|
+ }
|
|
+
|
|
+ sceHdmiSetVideoConfig(&new_crtc_state->mode);
|
|
+ sceHdmiDeviceSetVideoMute(0);
|
|
+ sceHdmiSetAudioConfig(channels);
|
|
+ sceHdmiSetAudioMute(0);
|
|
+ }
|
|
+#endif
|
|
}
|
|
/* Handle vrr on->off / off->on transitions */
|
|
amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, dm_new_crtc_state);
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
|
|
index 03d125f794b0..aa25e593a000 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
|
|
@@ -204,6 +204,7 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id)
|
|
asic_id.chip_id == DEVICE_ID_NV_13FA ||
|
|
asic_id.chip_id == DEVICE_ID_NV_13FB ||
|
|
asic_id.chip_id == DEVICE_ID_NV_13FC ||
|
|
+ asic_id.chip_id == DEVICE_ID_NV_13DA ||
|
|
asic_id.chip_id == DEVICE_ID_NV_13DB) {
|
|
dc_version = DCN_VERSION_2_01;
|
|
break;
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_link_encoder.c
|
|
index 9459e8f28338..063e4a4eefdd 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_link_encoder.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/dcn201/dcn201_link_encoder.c
|
|
@@ -98,8 +98,11 @@ static const struct link_encoder_funcs dcn201_link_enc_funcs = {
|
|
.disable_hpd = dcn10_link_encoder_disable_hpd,
|
|
.is_dig_enabled = dcn10_is_dig_enabled,
|
|
.destroy = dcn10_link_encoder_destroy,
|
|
+#ifndef CONFIG_X86_PS5
|
|
+ /* Ignore this to prevent blackscreen. */
|
|
.fec_set_enable = enc2_fec_set_enable,
|
|
.fec_set_ready = enc2_fec_set_ready,
|
|
+#endif
|
|
.get_dig_frontend = dcn10_get_dig_frontend,
|
|
.fec_is_active = enc2_fec_is_active,
|
|
.is_in_alt_mode = dcn201_link_encoder_is_in_alt_mode,
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c
|
|
index 13e14aad3daa..edc4b9ccfea4 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/dio/dcn10/dcn10_link_encoder.c
|
|
@@ -986,6 +986,11 @@ void dcn10_link_encoder_enable_dp_output(
|
|
struct bp_transmitter_control cntl = { 0 };
|
|
enum bp_result result;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Ignore this to prevent link training failure. */
|
|
+ return;
|
|
+#endif
|
|
+
|
|
/* Enable the PHY */
|
|
|
|
/* number_of_lanes is used for pixel clock adjust,
|
|
@@ -1065,6 +1070,11 @@ void dcn10_link_encoder_disable_output(
|
|
struct bp_transmitter_control cntl = { 0 };
|
|
enum bp_result result;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Ignore this to prevent link training failure. */
|
|
+ return;
|
|
+#endif
|
|
+
|
|
if (enc->funcs->is_dig_enabled && !enc->funcs->is_dig_enabled(enc)) {
|
|
/* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */
|
|
/*in DP_Alt_No_Connect case, we turn off the dig already,
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
|
|
index 699a756bbc40..4a9d4ba6abf4 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
|
|
@@ -1753,6 +1753,11 @@ static void power_down_clock_sources(struct dc *dc)
|
|
{
|
|
int i;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Ignore this to prevent blackscreen. */
|
|
+ return;
|
|
+#endif
|
|
+
|
|
if (dc->res_pool->dp_clock_source->funcs->cs_power_down(
|
|
dc->res_pool->dp_clock_source) == false)
|
|
dm_error("Failed to power down pll! (dp clk src)\n");
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
|
|
index cdc7587cf0b6..7a7b17fe5a89 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
|
|
@@ -1015,6 +1015,12 @@ bool link_decide_link_settings(struct dc_stream_state *stream,
|
|
decide_dp_link_settings(link, link_setting, req_bw);
|
|
}
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* Hardcode HBR3x4 link setting. */
|
|
+ link_setting->link_rate = LINK_RATE_HIGH3;
|
|
+ link_setting->lane_count = LANE_COUNT_FOUR;
|
|
+#endif
|
|
+
|
|
return link_setting->lane_count != LANE_COUNT_UNKNOWN &&
|
|
link_setting->link_rate != LINK_RATE_UNKNOWN;
|
|
}
|
|
@@ -1455,6 +1461,8 @@ void dpcd_set_source_specific_data(struct dc_link *link)
|
|
(uint8_t *)(&amd_device_id),
|
|
sizeof(amd_device_id));
|
|
|
|
+#ifndef CONFIG_X86_PS5
|
|
+ /* Ignore unsupported dpcd address. */
|
|
if (link->ctx->dce_version >= DCN_VERSION_2_0 &&
|
|
link->dc->caps.min_horizontal_blanking_period != 0) {
|
|
|
|
@@ -1464,6 +1472,7 @@ void dpcd_set_source_specific_data(struct dc_link *link)
|
|
DP_SOURCE_MINIMUM_HBLANK_SUPPORTED, (uint8_t *)(&hblank_size),
|
|
sizeof(hblank_size));
|
|
}
|
|
+#endif
|
|
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
|
|
WPP_BIT_FLAG_DC_DETECTION_DP_CAPS,
|
|
"result=%u link_index=%u enum dce_version=%d DPCD=0x%04X min_hblank=%u branch_dev_id=0x%x branch_dev_name='%c%c%c%c%c%c'",
|
|
diff --git a/drivers/gpu/drm/amd/display/include/dal_asic_id.h b/drivers/gpu/drm/amd/display/include/dal_asic_id.h
|
|
index 8aea50aa9533..8b647bd95691 100644
|
|
--- a/drivers/gpu/drm/amd/display/include/dal_asic_id.h
|
|
+++ b/drivers/gpu/drm/amd/display/include/dal_asic_id.h
|
|
@@ -217,6 +217,7 @@ enum {
|
|
#define DEVICE_ID_NV_13FA 0x13FA
|
|
#define DEVICE_ID_NV_13FB 0x13FB
|
|
#define DEVICE_ID_NV_13FC 0x13FC
|
|
+#define DEVICE_ID_NV_13DA 0x13DA
|
|
#define DEVICE_ID_NV_13DB 0x13DB
|
|
#define FAMILY_VGH 144
|
|
#define DEVICE_ID_VGH_163F 0x163F
|
|
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
|
|
index ff432ac6b569..2efeaec03ea1 100644
|
|
--- a/drivers/gpu/drm/drm_edid.c
|
|
+++ b/drivers/gpu/drm/drm_edid.c
|
|
@@ -2219,6 +2219,10 @@ static void connector_bad_edid(struct drm_connector *connector,
|
|
edid_block_dump(KERN_DEBUG, edid + i, i);
|
|
}
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+extern struct drm_edid *real_edid;
|
|
+#endif
|
|
+
|
|
/* Get override or firmware EDID */
|
|
static const struct drm_edid *drm_edid_override_get(struct drm_connector *connector)
|
|
{
|
|
@@ -2234,6 +2238,11 @@ static const struct drm_edid *drm_edid_override_get(struct drm_connector *connec
|
|
if (!override)
|
|
override = drm_edid_load_firmware(connector);
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ if (IS_ERR(override) && real_edid)
|
|
+ override = drm_edid_dup(real_edid);
|
|
+#endif
|
|
+
|
|
return IS_ERR(override) ? NULL : override;
|
|
}
|
|
|
|
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
|
|
index a5d8f45b7881..3c7b76af13e4 100644
|
|
--- a/drivers/hwmon/k10temp.c
|
|
+++ b/drivers/hwmon/k10temp.c
|
|
@@ -476,6 +476,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
break;
|
|
case 0x31: /* Zen2 Threadripper */
|
|
case 0x47: /* Cyan Skillfish */
|
|
+ case 0x48: /* PS5 */
|
|
case 0x60: /* Renoir */
|
|
case 0x68: /* Lucienne */
|
|
case 0x71: /* Zen2 */
|
|
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
|
|
index f3fd7f39efb4..7dd1b138ae78 100644
|
|
--- a/drivers/iommu/amd/init.c
|
|
+++ b/drivers/iommu/amd/init.c
|
|
@@ -3093,6 +3093,8 @@ static void __init free_iommu_resources(void)
|
|
free_pci_segments();
|
|
}
|
|
|
|
+#ifndef CONFIG_X86_PS5
|
|
+
|
|
/* SB IOAPIC is always on this device in AMD systems */
|
|
#define IOAPIC_SB_DEVID ((0x00 << 8) | PCI_DEVFN(0x14, 0))
|
|
|
|
@@ -3145,6 +3147,8 @@ static bool __init check_ioapic_information(void)
|
|
return ret;
|
|
}
|
|
|
|
+#endif
|
|
+
|
|
static void __init free_dma_resources(void)
|
|
{
|
|
amd_iommu_pdom_id_destroy();
|
|
@@ -3269,8 +3273,11 @@ static int __init early_amd_iommu_init(void)
|
|
if (!is_kdump_kernel() || amd_iommu_disabled)
|
|
disable_iommus();
|
|
|
|
+#ifndef CONFIG_X86_PS5
|
|
+ /* IOAPIC is not available. */
|
|
if (amd_iommu_irq_remap)
|
|
amd_iommu_irq_remap = check_ioapic_information();
|
|
+#endif
|
|
|
|
if (amd_iommu_irq_remap) {
|
|
struct amd_iommu_pci_seg *pci_seg;
|
|
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
|
|
index bccc7a4bdd79..ed434b9650e9 100644
|
|
--- a/drivers/pci/probe.c
|
|
+++ b/drivers/pci/probe.c
|
|
@@ -2802,6 +2802,14 @@ static int next_ari_fn(struct pci_bus *bus, struct pci_dev *dev, int fn)
|
|
u16 cap = 0;
|
|
unsigned int next_fn;
|
|
|
|
+#ifdef CONFIG_X86_PS5
|
|
+ /* ARI capability is broken, so we just assume contiguous functions until 45. */
|
|
+ if (fn < 45)
|
|
+ return fn + 1;
|
|
+ else
|
|
+ return -ENODEV;
|
|
+#endif
|
|
+
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
diff --git a/drivers/ps5/Makefile b/drivers/ps5/Makefile
|
|
new file mode 100644
|
|
index 000000000000..7acb030a4976
|
|
--- /dev/null
|
|
+++ b/drivers/ps5/Makefile
|
|
@@ -0,0 +1 @@
|
|
+obj-y += spcie.o tpcie.o hdmi.o mp1.o
|
|
diff --git a/drivers/ps5/hdmi.c b/drivers/ps5/hdmi.c
|
|
new file mode 100644
|
|
index 000000000000..a24459de573a
|
|
--- /dev/null
|
|
+++ b/drivers/ps5/hdmi.c
|
|
@@ -0,0 +1,1237 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/ps5.h>
|
|
+#include <drm/drm_edid.h>
|
|
+
|
|
+#define HDMI_IC_TYPE_FLAVA3 2
|
|
+#define HDMI_IC_TYPE_VERDE 3
|
|
+
|
|
+struct icc_i2c_msg {
|
|
+ u8 code;
|
|
+ u16 length;
|
|
+ u8 count;
|
|
+ u8 data[];
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_hdr {
|
|
+ u8 major;
|
|
+ u8 length;
|
|
+ u8 minor;
|
|
+ u8 count;
|
|
+ u8 data[];
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_2_1 {
|
|
+ u8 length;
|
|
+ u8 reg_high;
|
|
+ u8 reg_low;
|
|
+ u8 data[];
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_write {
|
|
+ u8 length;
|
|
+ u8 reg_high;
|
|
+ u8 reg_low;
|
|
+ u8 data[];
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_mask {
|
|
+ u8 length;
|
|
+ u8 reg_high;
|
|
+ u8 reg_low;
|
|
+ u8 value;
|
|
+ u8 mask[];
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_delay {
|
|
+ u8 length;
|
|
+ u8 time_low;
|
|
+ u8 time_high;
|
|
+ u8 unk_03;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_waitset {
|
|
+ u8 length;
|
|
+ u8 reg_high;
|
|
+ u8 reg_low;
|
|
+ u8 value;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_waitclear {
|
|
+ u8 length;
|
|
+ u8 reg_high;
|
|
+ u8 reg_low;
|
|
+ u8 value;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_3_5 {
|
|
+ u8 length;
|
|
+ u8 reg_high;
|
|
+ u8 reg_low;
|
|
+ u8 value;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_5_3 {
|
|
+ u8 value;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_5_4 {
|
|
+ u8 value;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_cmd_5_5 {
|
|
+ u8 value;
|
|
+} __packed;
|
|
+
|
|
+struct i2c_block {
|
|
+ u8 length;
|
|
+ u16 reg;
|
|
+ u8 data[32];
|
|
+};
|
|
+
|
|
+struct i2c_context {
|
|
+ struct i2c_block blocks[128];
|
|
+ int block_index;
|
|
+ u8 msg_buf[ICC_MSG_MAX_SIZE - sizeof(struct icc_msg)];
|
|
+ u8 *msg_cur;
|
|
+ struct icc_i2c_msg *msg_hdr;
|
|
+};
|
|
+
|
|
+static struct i2c_context i2c_ctx;
|
|
+
|
|
+static bool hdmi_is_enabled;
|
|
+static u8 hdmi_ic_type;
|
|
+static u8 tmds_polarity;
|
|
+static u8 tmds_ch_swap;
|
|
+static u8 dp_polarity;
|
|
+static u8 dp_ch_swap;
|
|
+
|
|
+static void i2c_init(u8 code)
|
|
+{
|
|
+ i2c_ctx.msg_cur = i2c_ctx.msg_buf;
|
|
+ i2c_ctx.msg_hdr = (struct icc_i2c_msg *)i2c_ctx.msg_cur;
|
|
+
|
|
+ i2c_ctx.msg_hdr->code = code;
|
|
+ i2c_ctx.msg_hdr->length = 0;
|
|
+ i2c_ctx.msg_hdr->count = 0;
|
|
+ i2c_ctx.block_index = -1;
|
|
+
|
|
+ i2c_ctx.msg_cur += sizeof(*i2c_ctx.msg_hdr);
|
|
+}
|
|
+
|
|
+static int i2c_exec(void)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+ int ret;
|
|
+
|
|
+ i2c_ctx.msg_hdr->length = i2c_ctx.msg_cur - i2c_ctx.msg_buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_HDMI;
|
|
+ msg->msg_type = 0;
|
|
+ msg->length = sizeof(*msg) + i2c_ctx.msg_hdr->length;
|
|
+ memcpy(msg->data, i2c_ctx.msg_hdr, i2c_ctx.msg_hdr->length);
|
|
+
|
|
+ ret = icc_query(buf, buf);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void i2c_write_block(struct i2c_block *block, size_t count)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ int i;
|
|
+
|
|
+ hdr->major = 2;
|
|
+ hdr->minor = 2;
|
|
+ hdr->count = count;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ for (i = 0; i < count; i++) {
|
|
+ struct i2c_cmd_write *cmd = (struct i2c_cmd_write *)i2c_ctx.msg_cur;
|
|
+
|
|
+ cmd->length = block[i].length;
|
|
+ cmd->reg_low = block[i].reg & 0xff;
|
|
+ cmd->reg_high = block[i].reg >> 8;
|
|
+ memcpy(cmd->data, block[i].data, block[i].length);
|
|
+
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+ }
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_begin_block(void)
|
|
+{
|
|
+ i2c_ctx.block_index = 0;
|
|
+}
|
|
+
|
|
+static void i2c_end_block(void)
|
|
+{
|
|
+ if (i2c_ctx.block_index > 0)
|
|
+ i2c_write_block(i2c_ctx.blocks, i2c_ctx.block_index);
|
|
+ i2c_ctx.block_index = -1;
|
|
+}
|
|
+
|
|
+static void i2c_write_data(u16 reg, u8 value[], size_t count)
|
|
+{
|
|
+ struct i2c_block *block;
|
|
+ bool new_block = i2c_ctx.block_index == -1;
|
|
+
|
|
+ if (new_block)
|
|
+ i2c_begin_block();
|
|
+
|
|
+ block = &i2c_ctx.blocks[i2c_ctx.block_index++];
|
|
+ block->reg = reg;
|
|
+ block->length = count;
|
|
+ memcpy(block->data, value, count);
|
|
+
|
|
+ if (new_block)
|
|
+ i2c_end_block();
|
|
+}
|
|
+
|
|
+static void i2c_write(u16 reg, u8 value)
|
|
+{
|
|
+ i2c_write_data(reg, &value, 1);
|
|
+}
|
|
+
|
|
+static void i2c_cmd_2_1(u16 reg, u8 unk)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 2;
|
|
+ hdr->minor = 1;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_2_1 *cmd = (struct i2c_cmd_2_1 *)i2c_ctx.msg_cur;
|
|
+ cmd->length = 1;
|
|
+ cmd->reg_low = reg & 0xff;
|
|
+ cmd->reg_high = reg >> 8;
|
|
+ cmd->data[0] = unk;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_delay(u16 time)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 3;
|
|
+ hdr->minor = 1;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_delay *cmd = (struct i2c_cmd_delay *)i2c_ctx.msg_cur;
|
|
+ cmd->length = 0;
|
|
+ cmd->time_low = time & 0xff;
|
|
+ cmd->time_high = time >> 8;
|
|
+ cmd->unk_03 = 0;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_mask(u16 reg, u8 value, u8 mask)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 2;
|
|
+ hdr->minor = 3;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_mask *cmd = (struct i2c_cmd_mask *)i2c_ctx.msg_cur;
|
|
+ cmd->length = 1;
|
|
+ cmd->reg_low = reg & 0xff;
|
|
+ cmd->reg_high = reg >> 8;
|
|
+ cmd->value = value;
|
|
+ cmd->mask[0] = mask;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_waitset(u16 reg, u8 value)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 3;
|
|
+ hdr->minor = 2;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_waitset *cmd = (struct i2c_cmd_waitset *)i2c_ctx.msg_cur;
|
|
+ cmd->length = 0;
|
|
+ cmd->reg_low = reg & 0xff;
|
|
+ cmd->reg_high = reg >> 8;
|
|
+ cmd->value = value;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_waitclear(u16 reg, u8 value)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 3;
|
|
+ hdr->minor = 3;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_waitclear *cmd = (struct i2c_cmd_waitclear *)i2c_ctx.msg_cur;
|
|
+ cmd->length = 0;
|
|
+ cmd->reg_low = reg & 0xff;
|
|
+ cmd->reg_high = reg >> 8;
|
|
+ cmd->value = value;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_cmd_3_5(u16 reg, u8 value)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 3;
|
|
+ hdr->minor = 5;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_3_5 *cmd = (struct i2c_cmd_3_5 *)i2c_ctx.msg_cur;
|
|
+ cmd->length = 0;
|
|
+ cmd->reg_low = reg & 0xff;
|
|
+ cmd->reg_high = reg >> 8;
|
|
+ cmd->value = value;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd) + cmd->length;
|
|
+ hdr->length += sizeof(*cmd) + cmd->length;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_cmd_4_16(void *data, size_t size)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 4;
|
|
+ hdr->minor = 16;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ memcpy(i2c_ctx.msg_cur, data, size);
|
|
+ i2c_ctx.msg_cur += size;
|
|
+ hdr->length += size;
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_cmd_5_3(u8 unk)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 5;
|
|
+ hdr->minor = 3;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_5_3 *cmd = (struct i2c_cmd_5_3 *)i2c_ctx.msg_cur;
|
|
+ cmd->value = unk;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd);
|
|
+ hdr->length += sizeof(*cmd);
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_cmd_5_4(u8 event)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 5;
|
|
+ hdr->minor = 4;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_5_4 *cmd = (struct i2c_cmd_5_4 *)i2c_ctx.msg_cur;
|
|
+ cmd->value = event;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd);
|
|
+ hdr->length += sizeof(*cmd);
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static void i2c_cmd_5_5(u8 event)
|
|
+{
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 5;
|
|
+ hdr->minor = 5;
|
|
+ hdr->count = 1;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ struct i2c_cmd_5_5 *cmd = (struct i2c_cmd_5_5 *)i2c_ctx.msg_cur;
|
|
+ cmd->value = event;
|
|
+ i2c_ctx.msg_cur += sizeof(*cmd);
|
|
+ hdr->length += sizeof(*cmd);
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+}
|
|
+
|
|
+static int stopHdcpHw(void)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_GENERAL;
|
|
+ msg->msg_type = 0x1f;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+
|
|
+static void sceSetBackToUnpluggedSequence(void)
|
|
+{
|
|
+ static u8 data[] = {0x11, 0x51, 0x09, 0x00, 0x02, 0x09, 0x03, 0x01, 0x01, 0x70, 0x5f, 0x80, 0x80, 0x03, 0x08, 0x01, 0x01, 0x00, 0x32, 0x00, 0x00, 0x02, 0x09, 0x03, 0x01, 0x01, 0x7a, 0x88, 0xff, 0xff, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x09, 0x03, 0x01, 0x01, 0x74, 0x0a, 0xff, 0xff, 0x03, 0x08, 0x03, 0x01, 0x00, 0x74, 0x0a, 0xff, 0x02, 0x09, 0x03, 0x01, 0x01, 0x74, 0x19, 0x05, 0x07, 0x02, 0x09, 0x03, 0x01, 0x01, 0x7a, 0x8b, 0x00, 0x07, 0x03, 0x08, 0x01, 0x01, 0x00, 0x32, 0x00, 0x00};
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_4_16(data, sizeof(data));
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceSetBackToWaitResolutionSequence(void)
|
|
+{
|
|
+ static u8 data[] = {0x12, 0x47, 0x08, 0x00, 0x02, 0x09, 0x03, 0x01, 0x01, 0x70, 0x5f, 0x80, 0x80, 0x02, 0x08, 0x02, 0x01, 0x01, 0x7a, 0x88, 0xff, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x08, 0x02, 0x01, 0x01, 0x74, 0x0a, 0xff, 0x03, 0x08, 0x03, 0x01, 0x00, 0x74, 0x0a, 0xff, 0x02, 0x09, 0x03, 0x01, 0x01, 0x74, 0x19, 0x05, 0x07, 0x02, 0x09, 0x03, 0x01, 0x01, 0x7a, 0x8b, 0x00, 0x07, 0x03, 0x08, 0x01, 0x01, 0x00, 0x64, 0x00, 0x00};
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_4_16(data, sizeof(data));
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void i2c_cmd_4_2(void)
|
|
+{
|
|
+ static u8 data[] = {0x31, 0x7a, 0xb8, 0x08, 0x32, 0x7a, 0xb0, 0x05, 0x33, 0x7a, 0xa0, 0x05, 0x34, 0x7a, 0xe1, 0x01, 0x35, 0x7a, 0xe2, 0x01, 0x36, 0x7a, 0xe0, 0x01, 0x37, 0x7c, 0x00, 0x05};
|
|
+
|
|
+ i2c_init(0);
|
|
+
|
|
+ struct i2c_cmd_hdr *hdr = (struct i2c_cmd_hdr *)i2c_ctx.msg_cur;
|
|
+ hdr->major = 4;
|
|
+ hdr->minor = 2;
|
|
+ hdr->count = 7;
|
|
+ hdr->length = sizeof(*hdr);
|
|
+ i2c_ctx.msg_cur += sizeof(*hdr);
|
|
+
|
|
+ memcpy(i2c_ctx.msg_cur, data, sizeof(data));
|
|
+ i2c_ctx.msg_cur += sizeof(data);
|
|
+ hdr->length += sizeof(data);
|
|
+
|
|
+ i2c_ctx.msg_hdr->count++;
|
|
+
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceSetHdcpSequence1st(void)
|
|
+{
|
|
+ static u8 data[] = {0x0b, 0x91, 0x0f, 0x00, 0x02, 0x0c, 0x02, 0x02, 0x01, 0x7a, 0x8b, 0x05, 0x01, 0x7a, 0x88, 0xff, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x11, 0x02, 0x03, 0x02, 0x10, 0xe5, 0xff, 0xff, 0x01, 0x7a, 0x85, 0x00, 0x01, 0x7a, 0x83, 0x84, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x08, 0x01, 0x01, 0x01, 0x7a, 0x83, 0x83, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x03, 0x08, 0x01, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x02, 0x08, 0x01, 0x01, 0x01, 0x7a, 0x83, 0x84, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x10, 0x02, 0x03, 0x01, 0x10, 0xe6, 0xff, 0x01, 0x7a, 0x85, 0x00, 0x01, 0x7a, 0x83, 0xe3, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01};
|
|
+
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_4_16(data, sizeof(data));
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceSetHdcpSequence2nd(void)
|
|
+{
|
|
+ static u8 data[] = {0x0c, 0x39, 0x06, 0x00, 0x02, 0x0d, 0x02, 0x02, 0x02, 0x10, 0xe5, 0xff, 0xff, 0x01, 0x7a, 0x83, 0xdc, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x08, 0x01, 0x01, 0x01, 0x7a, 0x83, 0xc4, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01};
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_4_16(data, sizeof(data));
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceSetHdcpSequence3rd(void)
|
|
+{
|
|
+ static u8 data[] = {0x0d, 0x21, 0x01, 0x00, 0x02, 0x1d, 0x02, 0x06, 0x01, 0x10, 0xe7, 0xff, 0x01, 0x7a, 0x8b, 0x03, 0x02, 0x10, 0xe5, 0xff, 0xff, 0x01, 0x7a, 0x9d, 0x4f, 0x01, 0x7a, 0x83, 0x8e, 0x01, 0x7e, 0x03, 0x10};
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_4_16(data, sizeof(data));
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceSetEdidSequence(int sequence)
|
|
+{
|
|
+ static u8 seq_0[] = {0x04, 0x3f, 0x06, 0x00, 0x02, 0x0c, 0x02, 0x02, 0x01, 0x7a, 0x88, 0xff, 0x01, 0x7a, 0x83, 0x88, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x0f, 0x02, 0x02, 0x01, 0x7a, 0x9c, 0x0e, 0x04, 0x7a, 0x80, 0x00, 0x00, 0x7f, 0x82, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01};
|
|
+
|
|
+ static u8 seq_1[] = {0x05, 0x3f, 0x06, 0x00, 0x02, 0x0c, 0x02, 0x02, 0x01, 0x7a, 0x88, 0xff, 0x01, 0x7a, 0x83, 0x88, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x0f, 0x02, 0x02, 0x01, 0x7a, 0x9c, 0x0e, 0x04, 0x7a, 0x80, 0x00, 0x80, 0x7f, 0x82, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01};
|
|
+
|
|
+ static u8 seq_2[] = {0x06, 0x3f, 0x06, 0x00, 0x02, 0x0c, 0x02, 0x02, 0x01, 0x7a, 0x88, 0xff, 0x01, 0x7a, 0x83, 0x88, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x0f, 0x02, 0x02, 0x01, 0x7a, 0x9c, 0x0e, 0x04, 0x7a, 0x80, 0x01, 0x00, 0x7f, 0x82, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01};
|
|
+
|
|
+ static u8 seq_3[] = {0x07, 0x3f, 0x06, 0x00, 0x02, 0x0c, 0x02, 0x02, 0x01, 0x7a, 0x88, 0xff, 0x01, 0x7a, 0x83, 0x88, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01, 0x02, 0x0f, 0x02, 0x02, 0x01, 0x7a, 0x9c, 0x0e, 0x04, 0x7a, 0x80, 0x01, 0x80, 0x7f, 0x82, 0x03, 0x08, 0x02, 0x01, 0x00, 0x7a, 0x84, 0xa0, 0x03, 0x08, 0x03, 0x01, 0x00, 0x7a, 0x84, 0x01};
|
|
+
|
|
+ i2c_init(0);
|
|
+ if (sequence == 0) {
|
|
+ i2c_cmd_4_16(seq_0, sizeof(seq_0));
|
|
+ } else if (sequence == 1) {
|
|
+ i2c_cmd_4_16(seq_1, sizeof(seq_1));
|
|
+ } else if (sequence == 2) {
|
|
+ i2c_cmd_4_16(seq_2, sizeof(seq_2));
|
|
+ } else if (sequence == 3) {
|
|
+ i2c_cmd_4_16(seq_3, sizeof(seq_3));
|
|
+ }
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceSetWaitPllSequence(void)
|
|
+{
|
|
+ static u8 data[] = {0x0a, 0x15, 0x02, 0x00, 0x02, 0x09, 0x03, 0x01, 0x01, 0x70, 0x21, 0xff, 0xff, 0x03, 0x08, 0x01, 0x01, 0x00, 0x64, 0x00, 0x00};
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_4_16(data, sizeof(data));
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void unk_akv(void)
|
|
+{
|
|
+ i2c_init(4);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7006, 0x12);
|
|
+ i2c_write(0x7a88, 0xff);
|
|
+ i2c_end_block();
|
|
+ i2c_waitclear(0x7a84, 0x01);
|
|
+ i2c_cmd_2_1(0x7a83, 0x88);
|
|
+ i2c_waitset(0x7a84, 0xa0);
|
|
+ i2c_waitclear(0x7a84, 0x01);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7a8b, 0x05);
|
|
+ i2c_write(0x7a89, 0x01);
|
|
+ i2c_end_block();
|
|
+ i2c_delay(20);
|
|
+ i2c_cmd_2_1(0x7a83, 0x0e);
|
|
+ i2c_waitset(0x7a84, 0xa0);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceControlHdmiEvent(u8 enable)
|
|
+{
|
|
+ i2c_init(4);
|
|
+ i2c_cmd_5_4(enable);
|
|
+ i2c_exec();
|
|
+ hdmi_is_enabled = !!enable;
|
|
+}
|
|
+
|
|
+void hdmiSystemResume(void)
|
|
+{
|
|
+ // sceSetBackToUnpluggedSequence();
|
|
+ // sceSetBackToWaitResolutionSequence();
|
|
+ if (hdmi_ic_type == HDMI_IC_TYPE_FLAVA3) {
|
|
+ // i2c_cmd_4_2();
|
|
+ // sceSetHdcpSequence1st();
|
|
+ // sceSetHdcpSequence2nd();
|
|
+ // sceSetHdcpSequence3rd();
|
|
+ // sceSetEdidSequence(0);
|
|
+ // sceSetEdidSequence(1);
|
|
+ // sceSetEdidSequence(2);
|
|
+ // sceSetEdidSequence(3);
|
|
+ // sceSetWaitPllSequence();
|
|
+ // unk_akv();
|
|
+ sceControlHdmiEvent(1);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(hdmiSystemResume);
|
|
+
|
|
+static void sceDisableEncode(void)
|
|
+{
|
|
+ i2c_init(4);
|
|
+ i2c_mask(0x705f, 0x80, 0x80);
|
|
+ i2c_cmd_5_3(0x00);
|
|
+ i2c_mask(0x7021, 0x00, 0xf0);
|
|
+ i2c_cmd_2_1(0x7a88, 0xff);
|
|
+ i2c_waitclear(0x7a84, 0x01);
|
|
+ i2c_cmd_2_1(0x740a, 0xff);
|
|
+ i2c_waitclear(0x740a, 0xff);
|
|
+ i2c_mask(0x7419, 0x05, 0x07);
|
|
+ i2c_mask(0x7a8b, 0x00, 0x07);
|
|
+ i2c_delay(100);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void initIsrForFlava3(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x705f, 0x12);
|
|
+ i2c_write(0x6004, 0x80);
|
|
+ i2c_write(0x6020, 0x00);
|
|
+ i2c_write(0x7007, 0xff);
|
|
+ i2c_write(0x100c, 0x01);
|
|
+ i2c_write(0x6008, 0xc0);
|
|
+ i2c_write(0x6207, 0x00);
|
|
+ i2c_write(0x621b, 0x00);
|
|
+ i2c_write_data(0x6080, (u8[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 6);
|
|
+ i2c_write_data(0x6090, (u8[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 8);
|
|
+ i2c_write_data(0x10e7, (u8[]){0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 8);
|
|
+ i2c_write_data(0x10e9, (u8[]){0xff, 0xff}, 2);
|
|
+ i2c_write_data(0x10f0, (u8[]){0xff, 0x07, 0x00, 0x0f, 0x00, 0x56, 0x00, 0x00, 0xd0, 0x00}, 10);
|
|
+ i2c_write(0x7018, (tmds_polarity == 1) << 1);
|
|
+ i2c_end_block();
|
|
+ if (tmds_ch_swap == 1) {
|
|
+ i2c_mask(0x701a, 0xb1, 0xff);
|
|
+ }
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void initDpForFlava3(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x6a03, 0x04);
|
|
+ i2c_write(0x60a2, 0xf1);
|
|
+ i2c_write(0x60bf, 0x03);
|
|
+ i2c_write(0x60c0, 0xef);
|
|
+ i2c_write(0x60c3, 0x4d);
|
|
+ i2c_write(0x60c7, 0x85);
|
|
+ i2c_write(0x60bf, 0x04);
|
|
+ i2c_write(0x60c7, 0x84);
|
|
+ if (dp_polarity != 0xff) {
|
|
+ i2c_write(0x600e, dp_polarity << 4);
|
|
+ }
|
|
+ if (dp_ch_swap != 0xff) {
|
|
+ i2c_write(0x600b, dp_ch_swap);
|
|
+ }
|
|
+ i2c_write_data(0x6220, (u8[]){0x40, 0x00, 0x80, 0x00, 0x00, 0x01, 0x02}, 7);
|
|
+ i2c_write_data(0x6028, (u8[]){0x01, 0x02}, 2);
|
|
+ i2c_write_data(0x6058, (u8[]){0x01, 0x02, 0x03, 0x04}, 4);
|
|
+ i2c_write(0x62af, 0x01);
|
|
+ i2c_write(0x6207, 0x00);
|
|
+ i2c_write(0x621b, 0x00);
|
|
+ i2c_write(0x60e0, 0x1e);
|
|
+ i2c_write(0x603c, 0x04);
|
|
+ i2c_write(0x100e, 0x10);
|
|
+ i2c_end_block();
|
|
+ i2c_delay(2);
|
|
+ i2c_write(0x6005, 0x01);
|
|
+ i2c_delay(2);
|
|
+ i2c_write(0x6008, 0x00);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void configVSyncSettingFlava3(void)
|
|
+{
|
|
+ i2c_init(1);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x1047, 0x00);
|
|
+ i2c_write(0x6064, 0x01);
|
|
+ i2c_write_data(0x600c, (u8[]){0x01, 0x00}, 2);
|
|
+ i2c_write(0x6c07, 0x00);
|
|
+ i2c_write_data(0x7214, (u8[]){0x00, 0x00}, 2);
|
|
+ i2c_end_block();
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void configParamFlava3Pre(void)
|
|
+{
|
|
+ i2c_init(1);
|
|
+ i2c_begin_block();
|
|
+ i2c_write_data(0x6224, (u8[]){0x00, 0x01}, 2);
|
|
+ i2c_write_data(0x1047, (u8[]){0x00}, 1);
|
|
+ i2c_write_data(0x1050, (u8[]){0x00, 0x00, 0x00, 0x00}, 4);
|
|
+ i2c_write(0x7215, 0x00);
|
|
+ i2c_write(0x7077, 0x00);
|
|
+ i2c_write(0x7079, 0x80);
|
|
+ i2c_end_block();
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void configLinkTrainingFlava3(void)
|
|
+{
|
|
+ i2c_init(1);
|
|
+ i2c_begin_block();
|
|
+ i2c_write_data(0x600c, (u8[]){0x01, 0x00}, 2);
|
|
+ i2c_write_data(0x6c00, (u8[]){0x1e, 0x84, 0x00}, 3);
|
|
+ i2c_end_block();
|
|
+ i2c_mask(0x6005, 0x01, 0x01);
|
|
+ i2c_delay(2);
|
|
+ i2c_mask(0x6006, 0x04, 0x04);
|
|
+ i2c_delay(2);
|
|
+ i2c_write(0x6a03, 0x47);
|
|
+ i2c_delay(10);
|
|
+ i2c_waitset(0x60f8, 0xff);
|
|
+ i2c_waitset(0x60f9, 0x01);
|
|
+ i2c_write(0x6a01, 0x4d);
|
|
+ i2c_waitset(0x60f9, 0x1a);
|
|
+ i2c_waitset(0x6083, 0x02);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void initHdmiPhyForFlava3_1st(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7022, 0x03);
|
|
+ i2c_write(0x7030, 0x40);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x54}, 2);
|
|
+ i2c_write(0x7030, 0x41);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x54}, 2);
|
|
+ i2c_write(0x7030, 0x42);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x34}, 2);
|
|
+ i2c_write(0x7030, 0x43);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x34}, 2);
|
|
+ i2c_write(0x7030, 0x04);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x71}, 2);
|
|
+ i2c_write(0x7030, 0x14);
|
|
+ i2c_write(0x7025, 0x71);
|
|
+ i2c_write(0x7030, 0x24);
|
|
+ i2c_write(0x7025, 0x70);
|
|
+ i2c_write(0x7030, 0x34);
|
|
+ i2c_write(0x7025, 0x77);
|
|
+ i2c_write(0x7030, 0x05);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x71}, 2);
|
|
+ i2c_write(0x7030, 0x15);
|
|
+ i2c_write(0x7025, 0x71);
|
|
+ i2c_write(0x7030, 0x25);
|
|
+ i2c_write(0x7025, 0x70);
|
|
+ i2c_write(0x7030, 0x35);
|
|
+ i2c_write(0x7025, 0x77);
|
|
+ i2c_write(0x7030, 0x06);
|
|
+ i2c_write_data(0x7024, (u8[]){0x04, 0x71}, 2);
|
|
+ i2c_write(0x7030, 0x16);
|
|
+ i2c_write(0x7025, 0x70);
|
|
+ i2c_write(0x7030, 0x26);
|
|
+ i2c_write(0x7025, 0x70);
|
|
+ i2c_write(0x7030, 0x36);
|
|
+ i2c_write(0x7025, 0x77);
|
|
+ i2c_end_block();
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void initHdmiPhyForFlava3_2nd(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7030, 0x47);
|
|
+ i2c_write(0x7024, 0x04);
|
|
+ i2c_write(0x7025, 0x37);
|
|
+ i2c_write(0x7027, 0x17);
|
|
+ i2c_write(0x7030, 0x37);
|
|
+ i2c_write(0x7025, 0x71);
|
|
+ i2c_write(0x7027, 0x14);
|
|
+ i2c_write(0x7030, 0x48);
|
|
+ i2c_write(0x7024, 0x04);
|
|
+ i2c_write(0x7025, 0x37);
|
|
+ i2c_write(0x7027, 0x17);
|
|
+ i2c_write(0x7030, 0x38);
|
|
+ i2c_write(0x7025, 0x77);
|
|
+ i2c_write(0x7027, 0x14);
|
|
+ i2c_write(0x7030, 0x49);
|
|
+ i2c_write(0x7024, 0x01);
|
|
+ i2c_write(0x7030, 0x19);
|
|
+ i2c_write(0x7026, 0xcc);
|
|
+ i2c_write(0x7030, 0x29);
|
|
+ i2c_write(0x7026, 0xdd);
|
|
+ i2c_write(0x7030, 0x39);
|
|
+ i2c_write(0x7026, 0xef);
|
|
+ i2c_write(0x7030, 0x4a);
|
|
+ i2c_write(0x7024, 0x01);
|
|
+ i2c_write(0x7030, 0x1a);
|
|
+ i2c_write(0x7026, 0xcc);
|
|
+ i2c_write(0x7030, 0x2a);
|
|
+ i2c_write(0x7026, 0xdd);
|
|
+ i2c_write(0x7030, 0x3a);
|
|
+ i2c_write(0x7026, 0xef);
|
|
+ i2c_write(0x7030, 0x4b);
|
|
+ i2c_write(0x7024, 0x01);
|
|
+ i2c_write(0x7030, 0x1b);
|
|
+ i2c_write(0x7026, 0xcc);
|
|
+ i2c_write(0x7030, 0x2b);
|
|
+ i2c_write(0x7026, 0xdd);
|
|
+ i2c_write(0x7030, 0x3b);
|
|
+ i2c_write(0x7026, 0xef);
|
|
+ i2c_end_block();
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void mask_7203(void)
|
|
+{
|
|
+ i2c_init(1);
|
|
+ i2c_mask(0x7203, 0x00, 0x80);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setHdmiBasicVideoConfigFlava3(const struct drm_display_mode *mode)
|
|
+{
|
|
+ u8 vic = drm_match_cea_mode(mode);
|
|
+ int hz = drm_mode_vrefresh(mode);
|
|
+
|
|
+ i2c_init(1);
|
|
+ i2c_mask(0x7021, 0x00, 0xf0);
|
|
+ i2c_delay(500);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x100c, 0x01);
|
|
+ i2c_write_data(0x68a0, (u8[]){0x96, 0x04}, 2);
|
|
+ i2c_write(0x7005, 0x80);
|
|
+ i2c_write(0x7019, 0x00);
|
|
+ i2c_write(0x100c, 0x01);
|
|
+ i2c_write(0x7005, 0x80);
|
|
+ i2c_write(0x7009, 0x00);
|
|
+ i2c_write(0x7040, 0x42);
|
|
+ i2c_write(0x7225, 0x28);
|
|
+ if (vic == 16 || vic == 63) {
|
|
+ /* Set VIC and content type */
|
|
+ i2c_write_data(0x7227, (u8[]){vic, 0x00}, 2);
|
|
+ i2c_write_data(0x7070, (u8[]){vic, vic, 0x00, 0x00, 0x00, 0x00}, 6);
|
|
+ } else if (mode->hdisplay == 2560 && mode->vdisplay == 1440) {
|
|
+ /* Set VIC and content type */
|
|
+ i2c_write_data(0x7227, (u8[]){0x04, 0x00}, 2);
|
|
+ i2c_write_data(0x7070, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0xfb}, 6);
|
|
+ } else if (vic == 97) {
|
|
+ /* Set VIC and content type */
|
|
+ i2c_write_data(0x7227, (u8[]){0x06, 0x00}, 2);
|
|
+ i2c_write_data(0x7070, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 6);
|
|
+ }
|
|
+ i2c_write(0x70c0, 0xdc);
|
|
+ i2c_write(0x621b, 0x00);
|
|
+ i2c_write(0x629a, 0x00);
|
|
+ i2c_write_data(0x70c4, (u8[]){0x08, 0x08}, 2);
|
|
+ i2c_write(0x70c2, 0x00);
|
|
+ i2c_write(0x70fe, 0x02);
|
|
+ i2c_write(0x70c3, 0x00);
|
|
+ i2c_write(0x7018, (tmds_polarity == 1) << 1);
|
|
+ if (tmds_ch_swap == 1) {
|
|
+ i2c_end_block();
|
|
+ i2c_mask(0x701a, 0xb1, 0xff);
|
|
+ i2c_begin_block();
|
|
+ }
|
|
+ i2c_write(0x10e7, 0xff);
|
|
+ i2c_write(0x7202, 0x20);
|
|
+ if (vic == 97) {
|
|
+ i2c_write(0x7072, 0x01);
|
|
+ i2c_write(0x7074, 0x07);
|
|
+ i2c_write(0x7206, 0x80);
|
|
+ i2c_write(0x7203, 0x60);
|
|
+ i2c_write(0x7011, 0xff);
|
|
+ } else {
|
|
+ i2c_write(0x7203, 0x60);
|
|
+ i2c_write(0x7011, 0xd5);
|
|
+ }
|
|
+ i2c_end_block();
|
|
+ i2c_waitclear(0x7011, 0xff);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setVideoAdditionalConfigFlava3(const struct drm_display_mode *mode)
|
|
+{
|
|
+ u8 vic = drm_match_cea_mode(mode);
|
|
+
|
|
+ i2c_init(1);
|
|
+ i2c_waitset(0x10e7, 0x80);
|
|
+ if (vic == 97) {
|
|
+ /* Set VIC */
|
|
+ i2c_mask(0x7227, vic, 0xff);
|
|
+ }
|
|
+ /* Enable IT content */
|
|
+ i2c_mask(0x7226, 1 << 7, 0x80);
|
|
+ /* Set content type (game) */
|
|
+ i2c_mask(0x7228, 3 << 4, 0x30);
|
|
+ i2c_write(0x7204, 0x40);
|
|
+ i2c_waitclear(0x7204, 0x40);
|
|
+ i2c_delay(10);
|
|
+ i2c_begin_block();
|
|
+ if (vic == 97) {
|
|
+ i2c_write(0x7019, 0x01);
|
|
+ } else {
|
|
+ i2c_write(0x7019, 0x00);
|
|
+ }
|
|
+ i2c_write(0x7419, 0x05);
|
|
+ i2c_write(0x740a, 0xff);
|
|
+ i2c_end_block();
|
|
+ i2c_waitclear(0x740a, 0xff);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7404, 0x00);
|
|
+ i2c_write(0x7a88, 0xff);
|
|
+ i2c_end_block();
|
|
+ i2c_waitclear(0x7a84, 0x01);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7a8b, 0x05);
|
|
+ if (vic == 97) {
|
|
+ i2c_write(0x7c00, 0x03);
|
|
+ i2c_write_data(0x7a80, (u8[]){0xa8, 0x20, 0x00, 0x80}, 4);
|
|
+ }
|
|
+ i2c_end_block();
|
|
+ if (vic == 97) {
|
|
+ i2c_waitclear(0x7a84, 0x01);
|
|
+ }
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x7021, 0xff);
|
|
+ i2c_write(0x700a, 0x02);
|
|
+ i2c_write(0x705f, 0x80);
|
|
+ i2c_end_block();
|
|
+ i2c_delay(700);
|
|
+ i2c_write(0x7a8b, 0x00);
|
|
+ i2c_cmd_5_3(0x01);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setHdmiVideoConfigFlava3(const struct drm_display_mode *mode)
|
|
+{
|
|
+ configParamFlava3Pre();
|
|
+ configLinkTrainingFlava3();
|
|
+ setHdmiBasicVideoConfigFlava3(mode);
|
|
+ initHdmiPhyForFlava3_1st();
|
|
+ initHdmiPhyForFlava3_2nd();
|
|
+ mask_7203();
|
|
+ setVideoAdditionalConfigFlava3(mode);
|
|
+}
|
|
+
|
|
+static void setHdmiAudioConfigBasic(int channels)
|
|
+{
|
|
+ i2c_init(2);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x62a0, 0x06);
|
|
+ i2c_write(0x62a7, 0x13);
|
|
+ i2c_write(0x62ac, 0x82);
|
|
+ i2c_write(0x62cb, 0x02);
|
|
+ i2c_write(0x62cb, 0x03);
|
|
+ i2c_write(0x62cb, 0x00);
|
|
+ i2c_write(0x70ad, 0x00);
|
|
+ i2c_write(0x70af, 0x07);
|
|
+ i2c_write(0x70a9, 0x5e);
|
|
+ i2c_end_block();
|
|
+ i2c_mask(0x70af, 0x06, 0x06);
|
|
+ i2c_mask(0x70b3, 0x02, 0x0f);
|
|
+ i2c_mask(0x70ae, 0x80, 0xe0);
|
|
+ i2c_mask(0x70ae, max(1, channels - 1), 0x07);
|
|
+ i2c_mask(0x70ac, 0x01, 0x21);
|
|
+ i2c_mask(0x70ab, 0x81, 0x89);
|
|
+ i2c_mask(0x70a9, 0x08, 0x08);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setAudioConfigAdditional(int channels)
|
|
+{
|
|
+ u8 val = 0x00;
|
|
+ switch (channels) {
|
|
+ case 3: val = 0x02; break;
|
|
+ case 4: val = 0x03; break;
|
|
+ case 5: val = 0x09; break;
|
|
+ case 6: val = 0x0b; break;
|
|
+ case 7: val = 0x11; break;
|
|
+ case 8: val = 0x13; break;
|
|
+ }
|
|
+ i2c_init(2);
|
|
+ i2c_mask(0x70b0, 0x00, 0xff);
|
|
+ i2c_mask(0x70b1, 0x79, 0xff);
|
|
+ i2c_mask(0x70b2, 0x00, 0xff);
|
|
+ i2c_mask(0x70b3, 0x02, 0xff);
|
|
+ i2c_mask(0x70b4, 0x0b, 0x0f);
|
|
+ i2c_mask(0x70b5, 0x00, 0xff);
|
|
+ i2c_mask(0x70b6, 0x00, 0xff);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x10e7, 0xff);
|
|
+ i2c_write(0x7011, 0xa2);
|
|
+ i2c_end_block();
|
|
+ i2c_waitset(0x10e7, 0xa2);
|
|
+ i2c_mask(0x7267, val, 0xff);
|
|
+ i2c_write(0x7204, 0x10);
|
|
+ i2c_waitclear(0x7204, 0x10);
|
|
+ i2c_write(0x10e7, 0xff);
|
|
+ i2c_mask(0x7203, 0x10, 0x10);
|
|
+ i2c_delay(30);
|
|
+ i2c_write(0x70a8, 0xc0);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceHdmiSetAudioConfigFlava3(int channels)
|
|
+{
|
|
+ setHdmiAudioConfigBasic(channels);
|
|
+ setAudioConfigAdditional(channels);
|
|
+}
|
|
+
|
|
+static void initVerde(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_mask(0x105, 0x00, 0x03);
|
|
+ i2c_mask(0x104, 0x00, 0x02);
|
|
+ i2c_cmd_2_1(0x400, 0x00);
|
|
+ i2c_cmd_2_1(0x3261, 0x02);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x11, 0x03);
|
|
+ i2c_write(0x13, 0xff);
|
|
+ i2c_write_data(0x8000, (u8[]){0x00, 0x01, 0x07, 0x00}, 4);
|
|
+ i2c_write(0x108, 0x80);
|
|
+ i2c_write(0x9000, 0x01);
|
|
+ i2c_end_block();
|
|
+ i2c_cmd_5_3(0x00);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void linkConfigVerde(const struct drm_display_mode *mode)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_mask(0x105, 0x00, 0x40);
|
|
+ if (mode->hdisplay == 2560 && mode->vdisplay == 1440) {
|
|
+ i2c_cmd_2_1(0x400, 0x03);
|
|
+ }
|
|
+ i2c_begin_block();
|
|
+ i2c_write_data(0x9003, (u8[]){0x1e, 0x04, 0x01, 0x01, 0x00, 0x00, 0x00}, 7);
|
|
+ i2c_write(0x4002, 0x03);
|
|
+ i2c_end_block();
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void configDpLinkTrainingVerde(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_2_1(0x9002, 0x01);
|
|
+ i2c_waitset(0x4001, 0x02);
|
|
+ i2c_cmd_2_1(0x11, 0x01);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setTmdsConfigVerde(void)
|
|
+{
|
|
+ i2c_init(0);
|
|
+ i2c_cmd_2_1(0x9200, 0x00);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x9800, 0x00);
|
|
+ i2c_write(0x33b3, 0x1a);
|
|
+ i2c_write(0x9831, 0x80);
|
|
+ i2c_write_data(0x9810, (u8[]){0x00, 0x79, 0x00, 0x02, 0x0b, 0x00, 0x00}, 7);
|
|
+ i2c_end_block();
|
|
+ i2c_waitclear(0x9830, 0x80);
|
|
+ i2c_cmd_2_1(0x9830, 0x80);
|
|
+ i2c_waitclear(0x9830, 0x80);
|
|
+ i2c_cmd_2_1(0x9830, 0x81);
|
|
+ i2c_waitclear(0x9830, 0x80);
|
|
+ i2c_waitclear(0x108, 0x10);
|
|
+ i2c_mask(0x108, 0x11, 0x17);
|
|
+ i2c_waitset(0x13, 0x40);
|
|
+ i2c_cmd_2_1(0x13, 0x40);
|
|
+ i2c_cmd_3_5(0x4001, 0x03);
|
|
+ i2c_cmd_2_1(0x11, 0x01);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setHdmiBasicVideoConfigVerde(const struct drm_display_mode *mode)
|
|
+{
|
|
+ u8 vic = drm_match_cea_mode(mode);
|
|
+
|
|
+ i2c_init(0);
|
|
+ i2c_begin_block();
|
|
+ if (vic == 16) {
|
|
+ i2c_write_data(0x3058, (u8[]){0x02, 0x0d, 0xef, 0x00, 0x28, 0x08, 0x10, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 18);
|
|
+ } else if (mode->hdisplay == 2560 && mode->vdisplay == 1440) {
|
|
+ i2c_write_data(0x3058, (u8[]){0x02, 0x0d, 0xff, 0x00, 0x28, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 18);
|
|
+ } else if (vic == 97) {
|
|
+ i2c_write_data(0x3058, (u8[]){0x02, 0x0d, 0x9e, 0x00, 0x28, 0x08, 0x61, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 18);
|
|
+ }
|
|
+ i2c_write_data(0x31d0, (u8[]){0x01, 0x19, 0x5f, 0x53, 0x43, 0x45, 0x49, 0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, 28);
|
|
+ i2c_end_block();
|
|
+ i2c_begin_block();
|
|
+ i2c_write_data(0x3033, (u8[]){0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 9);
|
|
+ i2c_write_data(0x307a, (u8[]){0x01, 0x0a, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00}, 8);
|
|
+ i2c_write_data(0x3000, (u8[]){0x56, 0xaa, 0x00, 0x24, 0x00, 0xa8, 0x00, 0x00, 0x08, 0x80}, 10);
|
|
+ i2c_end_block();
|
|
+ i2c_cmd_2_1(0x300a, 0x01);
|
|
+ i2c_waitclear(0x300c, 0xf3);
|
|
+ i2c_waitclear(0x300d, 0xff);
|
|
+ i2c_waitclear(0x300e, 0xff);
|
|
+ i2c_waitclear(0x300f, 0xff);
|
|
+ i2c_waitclear(0x3010, 0xff);
|
|
+ i2c_waitclear(0x3011, 0xff);
|
|
+ i2c_waitset(0x13, 0x80);
|
|
+ i2c_cmd_2_1(0x13, 0x80);
|
|
+ i2c_mask(0x105, 0x01, 0x03);
|
|
+ i2c_delay(700);
|
|
+ i2c_cmd_5_3(0x01);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void setHdmiVideoConfigVerde(const struct drm_display_mode *mode)
|
|
+{
|
|
+ linkConfigVerde(mode);
|
|
+ configDpLinkTrainingVerde();
|
|
+ setTmdsConfigVerde();
|
|
+ setHdmiBasicVideoConfigVerde(mode);
|
|
+}
|
|
+
|
|
+static void setAudioConfigForVerde(int channels)
|
|
+{
|
|
+ i2c_init(2);
|
|
+ i2c_waitclear(0x102, 0x80);
|
|
+ i2c_cmd_2_1(0xf1, 0x01);
|
|
+ i2c_waitset(0xf2, 0x01);
|
|
+ i2c_waitset(0x16, 0x08);
|
|
+ i2c_cmd_2_1(0x16, 0x08);
|
|
+ i2c_begin_block();
|
|
+ i2c_write(0x9800, 0x00);
|
|
+ i2c_write(0x33b3, 0x1a);
|
|
+ i2c_write(0x9831, 0x80);
|
|
+ i2c_write_data(0x9810, (u8[]){0x00, 0x79, 0x00, 0x02, 0x0b, 0x00, 0x00}, 7);
|
|
+ i2c_end_block();
|
|
+ i2c_waitclear(0x9830, 0x80);
|
|
+ i2c_cmd_2_1(0x9830, 0x80);
|
|
+ i2c_waitclear(0x9830, 0x80);
|
|
+ i2c_cmd_2_1(0x9830, 0x81);
|
|
+ i2c_waitclear(0x9830, 0x80);
|
|
+ i2c_waitclear(0x108, 0x10);
|
|
+ i2c_mask(0x108, 0x10, 0x10);
|
|
+ i2c_waitclear(0x108, 0x10);
|
|
+ i2c_write_data(0x307a, (u8[]){0x01, 0x0a, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00}, 8);
|
|
+ i2c_mask(0x3000, 0x40, 0xc0);
|
|
+ i2c_mask(0x3005, 0x80, 0xc0);
|
|
+ i2c_cmd_2_1(0x300a, 0x01);
|
|
+ i2c_waitset(0x13, 0x80);
|
|
+ i2c_cmd_2_1(0x13, 0x80);
|
|
+ i2c_exec();
|
|
+}
|
|
+
|
|
+static void sceHdmiSetAudioConfigVerde(int channels)
|
|
+{
|
|
+ setAudioConfigForVerde(channels);
|
|
+}
|
|
+
|
|
+void sceHdmiInitVideoConfig(void)
|
|
+{
|
|
+ stopHdcpHw();
|
|
+ if (hdmi_ic_type == HDMI_IC_TYPE_FLAVA3) {
|
|
+ sceDisableEncode();
|
|
+ initIsrForFlava3();
|
|
+ initDpForFlava3();
|
|
+ configVSyncSettingFlava3();
|
|
+ } else {
|
|
+ initVerde();
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(sceHdmiInitVideoConfig);
|
|
+
|
|
+void sceHdmiSetVideoConfig(const struct drm_display_mode *mode)
|
|
+{
|
|
+ if (hdmi_ic_type == HDMI_IC_TYPE_FLAVA3) {
|
|
+ setHdmiVideoConfigFlava3(mode);
|
|
+ } else {
|
|
+ setHdmiVideoConfigVerde(mode);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(sceHdmiSetVideoConfig);
|
|
+
|
|
+void sceHdmiDeviceSetVideoMute(int mute)
|
|
+{
|
|
+ i2c_init(4);
|
|
+ if (hdmi_ic_type == HDMI_IC_TYPE_FLAVA3) {
|
|
+ i2c_write(0x705f, (mute == 1) << 7);
|
|
+ } else {
|
|
+ i2c_mask(0x105, mute != 1, 0x03);
|
|
+ }
|
|
+ i2c_exec();
|
|
+}
|
|
+EXPORT_SYMBOL(sceHdmiDeviceSetVideoMute);
|
|
+
|
|
+void sceHdmiSetAudioConfig(int channels)
|
|
+{
|
|
+ if (hdmi_ic_type == HDMI_IC_TYPE_FLAVA3) {
|
|
+ sceHdmiSetAudioConfigFlava3(channels);
|
|
+ } else {
|
|
+ sceHdmiSetAudioConfigVerde(channels);
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(sceHdmiSetAudioConfig);
|
|
+
|
|
+void sceHdmiSetAudioMute(int mute)
|
|
+{
|
|
+ i2c_init(4);
|
|
+ if (hdmi_ic_type == HDMI_IC_TYPE_FLAVA3) {
|
|
+ i2c_mask(0x70a8, (mute == 1) << 2, 0x04);
|
|
+ } else {
|
|
+ i2c_cmd_5_5(mute == 1);
|
|
+ }
|
|
+ i2c_exec();
|
|
+}
|
|
+EXPORT_SYMBOL(sceHdmiSetAudioMute);
|
|
+
|
|
+int isHdmiEnabled(void)
|
|
+{
|
|
+ return hdmi_is_enabled;
|
|
+}
|
|
+EXPORT_SYMBOL(isHdmiEnabled);
|
|
+
|
|
+int getHdmiConfiguration(void)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+ int ret;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_GENERAL;
|
|
+ msg->msg_type = 0x16;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = 0x10;
|
|
+
|
|
+ ret = icc_query(buf, buf);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ hdmi_ic_type = (msg->data[0x3] == 1) | 2;
|
|
+ tmds_polarity = msg->data[0x4] == 1;
|
|
+ tmds_ch_swap = msg->data[0x5] == 1;
|
|
+ dp_polarity = msg->data[0x6];
|
|
+ dp_ch_swap = msg->data[0x7];
|
|
+
|
|
+ pr_info("HDMI IC Type [%s]\n", hdmi_ic_type == HDMI_IC_TYPE_FLAVA3 ? "FLAVA3" : "VERDE");
|
|
+ pr_info("TMDS Polarity [%s]\n", tmds_polarity ? "Reverse" : "Normal");
|
|
+ pr_info("TMDS Ch Swap [%s]\n", tmds_ch_swap ? "Swap" : "Normal");
|
|
+ pr_info("DP Polarity [0x%x]\n", dp_polarity);
|
|
+ pr_info("DP Ch Swap [0x%x]\n", dp_ch_swap);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(getHdmiConfiguration);
|
|
+
|
|
+bool isHdmiModeValid(const struct drm_display_mode *mode, int force_1080p)
|
|
+{
|
|
+ u8 vic = drm_match_cea_mode(mode);
|
|
+
|
|
+ if (force_1080p)
|
|
+ return vic == 16; /* 1080p60 */
|
|
+
|
|
+ /* 1440p */
|
|
+ if (mode->hdisplay == 2560 && mode->vdisplay == 1440) {
|
|
+ return mode->clock == 241500 || mode->clock == 241700;
|
|
+ }
|
|
+
|
|
+ switch (vic) {
|
|
+ case 16: /* 1080p60 */
|
|
+ case 63: /* 1080p120 */
|
|
+ case 97: /* 2160p60 */
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+EXPORT_SYMBOL(isHdmiModeValid);
|
|
+
|
|
+const struct drm_edid *real_edid = NULL;
|
|
+
|
|
+static void fix_edid(u8 *edid)
|
|
+{
|
|
+ int i;
|
|
+ u32 sum = 0;
|
|
+
|
|
+ /* For some reason, PS5 sets 0x01. */
|
|
+ edid[0] = 0x00;
|
|
+ for (i = 0; i < 0x7f; i++)
|
|
+ sum += edid[i];
|
|
+ edid[0x7f] = -sum & 0xff;
|
|
+}
|
|
+
|
|
+void hdmi_notification_handler(struct icc_msg *msg)
|
|
+{
|
|
+ if (msg->data[1] == 0x02) {
|
|
+ fix_edid(&msg->data[4]);
|
|
+ real_edid = drm_edid_alloc(&msg->data[4], *(u16 *)&msg->data[2]);
|
|
+ pr_info("got real edid\n");
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(hdmi_notification_handler);
|
|
diff --git a/drivers/ps5/mp1.c b/drivers/ps5/mp1.c
|
|
new file mode 100644
|
|
index 000000000000..73bb7ca5d1d9
|
|
--- /dev/null
|
|
+++ b/drivers/ps5/mp1.c
|
|
@@ -0,0 +1,366 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/smp.h>
|
|
+#include <linux/ps5.h>
|
|
+#include <asm/amd/node.h>
|
|
+
|
|
+#define MP1_IOC_MAGIC 'M'
|
|
+
|
|
+#define MP1_BOOST_ENTER _IO(MP1_IOC_MAGIC, 1)
|
|
+#define MP1_BOOST_EXIT _IO(MP1_IOC_MAGIC, 2)
|
|
+
|
|
+#define CPU_MHZ_MIN 3200
|
|
+#define CPU_MHZ_MAX 3500
|
|
+
|
|
+#define GPU_MHZ_MIN 2000
|
|
+#define GPU_MHZ_MAX 2230
|
|
+
|
|
+#define MSR_ACCESS_DIS BIT_ULL(62)
|
|
+
|
|
+#define MSR_DPM_CFG 0xC0011074
|
|
+#define MSR_DPM_WAC_ACC_INDEX 0xC0011076
|
|
+#define MSR_DPM_WAC_DATA 0xC0011077
|
|
+
|
|
+#define MP1_C2PMSG_56 0x3b109e0
|
|
+#define MP1_C2PMSG_57 0x3b109e4
|
|
+#define MP1_C2PMSG_58 0x3b109e8
|
|
+#define MP1_C2PMSG_59 0x3b109ec
|
|
+#define MP1_C2PMSG_66 0x3b10a08
|
|
+#define MP1_C2PMSG_81 0x3b10a44
|
|
+#define MP1_C2PMSG_82 0x3b10a48
|
|
+#define MP1_C2PMSG_90 0x3b10a68
|
|
+
|
|
+#define PPSMC_MSG_ConfigureS3PwrOffRegisterAddressHigh 0x16
|
|
+#define PPSMC_MSG_ConfigureS3PwrOffRegisterAddressLow 0x17
|
|
+#define PPSMC_MSG_UniversalModeEntry 0x22
|
|
+#define PPSMC_MSG_UniversalModeExit 0x23
|
|
+#define PPSMC_MSG_SleepEntry 0x24
|
|
+#define PPSMC_MSG_GfxCacWeightOperation 0x2F
|
|
+#define PPSMC_MSG_L3CacWeightOperation 0x30
|
|
+
|
|
+struct mp1_msg {
|
|
+ u32 arg;
|
|
+ u32 resp;
|
|
+ u32 resp_val;
|
|
+ u32 extra_arg;
|
|
+ u32 extra;
|
|
+};
|
|
+
|
|
+struct bapm_param_set {
|
|
+ u64 dpm_wac[21];
|
|
+ u32 l3_cacw[80];
|
|
+ u32 gfx_cacw[88];
|
|
+ u64 gfx_cacw_len;
|
|
+ u32 bapm_param[176][2];
|
|
+};
|
|
+
|
|
+static const struct bapm_param_set bapm_param_set_1 = {
|
|
+ .dpm_wac = {
|
|
+ 0x0000bca79c8d0000, 0x0097000000000000, 0x007f0000000000ad,
|
|
+ 0x00930000c700bb00, 0x000000e0cad5ee00, 0x0000c60099ca00d1,
|
|
+ 0x00bab80000d8ff00, 0x000000000000a689, 0x0000b59d007300b1,
|
|
+ 0x0000009600ad0000, 0x00a000b1f8000000, 0x00000000d400b200,
|
|
+ 0x00a5000000980000, 0x0000969f00b49a00, 0xa48761a100b30000,
|
|
+ 0x8500869500008f00, 0x7a009700008400a1, 0x00000000a90031ac,
|
|
+ 0x00b7bd00a10000b5, 0xb000b2c8c3b1a500, 0xa600000000000000
|
|
+ },
|
|
+ .l3_cacw = {
|
|
+ [71] = 0x3ff80000
|
|
+ },
|
|
+ .gfx_cacw = {
|
|
+ [0] = 0x17, 0x0, 0xa1,
|
|
+ [5] = 0x3f0000,
|
|
+ [14] = 0x1c80000,
|
|
+ [19] = 0x22, 0x12,
|
|
+ [23] = 0x7019d, 0x28001e,
|
|
+ [28] = 0x35, 0x15d, 0x1ab, 0x7f0000,
|
|
+ [35] = 0xc4,
|
|
+ [49] = 0x310000,
|
|
+ [54] = 0x76
|
|
+ },
|
|
+ .gfx_cacw_len = 64,
|
|
+ .bapm_param = {
|
|
+ {1, 0x3d14a6f2}, {1, 0x00000000}, {1, 0x3ed4eb53}, {1, 0x3e16ae78},
|
|
+ {1, 0xbe4d4746}, {1, 0x3f34470b}, {1, 0x37178941}, {1, 0x3f726e59},
|
|
+ {1, 0x3f3ebeef}, {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6},
|
|
+ {1, 0x3d1582c2}, {1, 0x35937309}, {1, 0x3f0f45c5}, {1, 0x3ee17477},
|
|
+ {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2},
|
|
+ {1, 0x387ba882}, {1, 0x40b1089a}, {1, 0x41751b15}, {1, 0x4287d1bc},
|
|
+ {1, 0x3fb89949}, {1, 0x411eefdf}, {1, 0xbc816b59}, {1, 0x3d41513a},
|
|
+ [49] =
|
|
+ {1, 0x41751b15}, {1, 0x4287d1bc}, {1, 0x3fb89949}, {1, 0x412d973c},
|
|
+ {1, 0xbc816b59}, {1, 0x3d41513a}, {1, 0x3e0654c8}, {1, 0xbe27b465},
|
|
+ {1, 0x3f2d3daa}, {1, 0x41751b15}, {1, 0x4287d1bc}, {1, 0x3fb89949},
|
|
+ {1, 0x4061d4cc}, {1, 0xbc816b59}, {1, 0x3d41513a}, {1, 0x3e97c980},
|
|
+ {1, 0xbf1d9856}, {1, 0x3f828e45}, {1, 0x41751b15}, {1, 0x4287d1bc},
|
|
+ {1, 0x3fb89949}, {1, 0x409c4b3c}, {1, 0xbc816b59}, {1, 0x3d41513a},
|
|
+ {1, 0x3e7a6107}, {1, 0xbeedd766}, {1, 0x3f67aa70}, {1, 0x41751b15},
|
|
+ {1, 0x4287d1bc}, {1, 0x3fb89949}, {1, 0x40ccf558}, {1, 0xbc816b59},
|
|
+ {1, 0x3d41513a}, {1, 0x3e600a8a}, {1, 0xbecbe79c}, {1, 0x3f5b627c},
|
|
+ {1, 0x41751b15}, {1, 0x4287d1bc}, {1, 0x3fb89949}, {1, 0x41050555},
|
|
+ {1, 0xbc816b59}, {1, 0x3d41513a}, {1, 0x3e379205}, {1, 0xbe94367e},
|
|
+ {1, 0x3f46233f}, {1, 0x3f726e59}, {1, 0x3f3ebeef}, {1, 0x00000000},
|
|
+ {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2}, {1, 0x3d14a6f2},
|
|
+ {1, 0x00000000}, {1, 0x3ed4eb53}, {1, 0x3f726e59}, {1, 0x3f3ebeef},
|
|
+ {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2},
|
|
+ {1, 0x3d14a6f2}, {1, 0x00000000}, {1, 0x3ed4eb53}, {1, 0x3f726e59},
|
|
+ {1, 0x3f3ebeef}, {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6},
|
|
+ {1, 0x3d1582c2}, {1, 0x3d14a6f2}, {1, 0x00000000}, {1, 0x3ed4eb53},
|
|
+ {1, 0x3f726e59}, {1, 0x3f3ebeef}, {1, 0x00000000}, {1, 0x3f8e9ad0},
|
|
+ {1, 0xbc2c0ab6}, {1, 0x3d1582c2}, {1, 0x3d14a6f2}, {1, 0x00000000},
|
|
+ {1, 0x3ed4eb53}, {1, 0x3f726e59}, {1, 0x3f3ebeef}, {1, 0x00000000},
|
|
+ {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2}, {1, 0x3d14a6f2},
|
|
+ {1, 0x00000000}, {1, 0x3ed4eb53}, {1, 0x3f0f45c5}, {1, 0x3ee17477},
|
|
+ {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2},
|
|
+ {1, 0x3f0f45c5}, {1, 0x3ee17477}, {1, 0x00000000}, {1, 0x3f8e9ad0},
|
|
+ {1, 0xbc2c0ab6}, {1, 0x3d1582c2}, {1, 0x3f0f45c5}, {1, 0x3ee17477},
|
|
+ {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2},
|
|
+ {1, 0x3f0f45c5}, {1, 0x3ee17477}, {1, 0x00000000}, {1, 0x3f8e9ad0},
|
|
+ {1, 0xbc2c0ab6}, {1, 0x3d1582c2}, {1, 0x3f0f45c5}, {1, 0x3ee17477},
|
|
+ {1, 0x00000000}, {1, 0x3f8e9ad0}, {1, 0xbc2c0ab6}, {1, 0x3d1582c2},
|
|
+ {1, 0x3c437be5}, {1, 0x00000000}, {1, 0x00000000}, {1, 0x00000000},
|
|
+ {1, 0x00000000}, {1, 0x00000000}, {1, 0x00000000}
|
|
+ }
|
|
+};
|
|
+
|
|
+static u32 pcirc_smn_read(u32 reg)
|
|
+{
|
|
+ u32 val = 0;
|
|
+ if (amd_smn_read(0, reg, &val)) {
|
|
+ panic("amd_smn_read failed");
|
|
+ }
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void pcirc_smn_write(u32 reg, u32 val)
|
|
+{
|
|
+ if (amd_smn_write(0, reg, val)) {
|
|
+ panic("amd_smn_write failed");
|
|
+ }
|
|
+}
|
|
+
|
|
+static int mp1fw_waitmsg(u32 reg, u8 msgid)
|
|
+{
|
|
+ int timeout = 0;
|
|
+
|
|
+ while (!pcirc_smn_read(reg)) {
|
|
+ udelay(10);
|
|
+ if (++timeout == 200000) {
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (timeout >= 10000) {
|
|
+ pr_err("[MP1] wait %d msec (msgid=0x%X)\n", timeout / 100, msgid);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mp1fw_sendmsg(struct mp1_msg *msg, u8 msgid)
|
|
+{
|
|
+ if (mp1fw_waitmsg(MP1_C2PMSG_90, msgid)) {
|
|
+ pr_err("[MP1ERROR] : %s: Error : busy msgid=0x%X, arg=0x%08X\n", __func__, msgid, msg->arg);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ pcirc_smn_write(MP1_C2PMSG_90, 0);
|
|
+ pcirc_smn_write(MP1_C2PMSG_82, msg->arg);
|
|
+ if (msg->extra == 1)
|
|
+ pcirc_smn_write(MP1_C2PMSG_81, msg->extra_arg);
|
|
+ pcirc_smn_write(MP1_C2PMSG_66, msgid);
|
|
+
|
|
+ if (mp1fw_waitmsg(MP1_C2PMSG_90, msgid)) {
|
|
+ pr_err("[MP1ERROR] : %s: Error : retry over msgid=0x%X, arg=0x%08X\n", __func__, msgid, msg->arg);
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ u32 resp = pcirc_smn_read(MP1_C2PMSG_90);
|
|
+ u32 resp_val = pcirc_smn_read(MP1_C2PMSG_82);
|
|
+ if (resp != 1) {
|
|
+ pr_err("[MP1ERROR] : %s: invalid response msgid=0x%X, resp=0x%08X\n", __func__, msgid, resp);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ msg->resp = resp;
|
|
+ msg->resp_val = resp_val;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mp1_configure_s3_pwr_off_reg_addr_high(u64 addr)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ msg.arg = addr >> 32;
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_ConfigureS3PwrOffRegisterAddressHigh);
|
|
+}
|
|
+
|
|
+static int mp1_configure_s3_pwr_off_reg_addr_low(u64 addr)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ msg.arg = addr & 0xffffffff;
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_ConfigureS3PwrOffRegisterAddressLow);
|
|
+}
|
|
+
|
|
+void mp1_set_pm1_addr(u64 addr)
|
|
+{
|
|
+ mp1_configure_s3_pwr_off_reg_addr_high(addr);
|
|
+ mp1_configure_s3_pwr_off_reg_addr_low(addr);
|
|
+}
|
|
+EXPORT_SYMBOL(mp1_set_pm1_addr);
|
|
+
|
|
+int mp1_set_sleep_entry(void)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ msg.arg = 3; /* S3 */
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_SleepEntry);
|
|
+}
|
|
+EXPORT_SYMBOL(mp1_set_sleep_entry);
|
|
+
|
|
+static int mp1_universal_mode_enter(u32 *params)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+
|
|
+ pcirc_smn_write(MP1_C2PMSG_56, params[1]);
|
|
+ pcirc_smn_write(MP1_C2PMSG_57, params[2]);
|
|
+ pcirc_smn_write(MP1_C2PMSG_58, params[3]);
|
|
+ pcirc_smn_write(MP1_C2PMSG_59, params[4]);
|
|
+
|
|
+ msg.arg = params[0];
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_UniversalModeEntry);
|
|
+}
|
|
+
|
|
+static int mp1_universal_mode_exit(void)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_UniversalModeExit);
|
|
+}
|
|
+
|
|
+static int mp1_bapm_set_gfx_cacw(int index, int weight)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ msg.arg = index | 0x20000;
|
|
+ msg.extra_arg = weight;
|
|
+ msg.extra = 1;
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_GfxCacWeightOperation);
|
|
+}
|
|
+
|
|
+static int mp1_bapm_set_l3_cacw(int index, int weight)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ msg.arg = index | 0x20000;
|
|
+ msg.extra_arg = weight;
|
|
+ msg.extra = 1;
|
|
+ return mp1fw_sendmsg(&msg, PPSMC_MSG_L3CacWeightOperation);
|
|
+}
|
|
+
|
|
+static int mp1_bapm_write_param(int index, int param)
|
|
+{
|
|
+ struct mp1_msg msg = {};
|
|
+ msg.arg = index;
|
|
+ msg.extra_arg = param;
|
|
+ msg.extra = 1;
|
|
+ return mp1fw_sendmsg(&msg, 0x33);
|
|
+}
|
|
+
|
|
+static void update_bapm_weights_smp(void *info)
|
|
+{
|
|
+ u64 msr_dpm_cfg;
|
|
+ int i;
|
|
+
|
|
+ rdmsrq(MSR_DPM_CFG, msr_dpm_cfg);
|
|
+ wrmsrq(MSR_DPM_CFG, msr_dpm_cfg & ~MSR_ACCESS_DIS);
|
|
+
|
|
+ for (i = 0; i < 21; i++) {
|
|
+ wrmsrq(MSR_DPM_WAC_ACC_INDEX, i);
|
|
+ wrmsrq(MSR_DPM_WAC_DATA, bapm_param_set_1.dpm_wac[i]);
|
|
+ }
|
|
+
|
|
+ wrmsrq(MSR_DPM_CFG, msr_dpm_cfg);
|
|
+}
|
|
+
|
|
+static void mp1_set_bapm_param_set_all(void)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ on_each_cpu(update_bapm_weights_smp, NULL, 1);
|
|
+
|
|
+ for (i = 0; i < 80; i++) {
|
|
+ mp1_bapm_set_l3_cacw(i, bapm_param_set_1.l3_cacw[i]);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < bapm_param_set_1.gfx_cacw_len; i++) {
|
|
+ mp1_bapm_set_gfx_cacw(i, bapm_param_set_1.gfx_cacw[i]);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < 176; i++) {
|
|
+ if (bapm_param_set_1.bapm_param[i][0])
|
|
+ mp1_bapm_write_param(i, bapm_param_set_1.bapm_param[i][1]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static long mp1_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ switch (cmd) {
|
|
+ case MP1_BOOST_ENTER:
|
|
+ {
|
|
+ u32 params[5];
|
|
+ params[0] = 0x0;
|
|
+ params[1] = 0x449cd;
|
|
+ params[2] = (GPU_MHZ_MIN << 16) | CPU_MHZ_MIN;
|
|
+ params[3] = (GPU_MHZ_MAX << 16) | CPU_MHZ_MAX;
|
|
+ params[4] = 0x25c6a3c8;
|
|
+ return mp1_universal_mode_enter(params);
|
|
+ }
|
|
+
|
|
+ case MP1_BOOST_EXIT:
|
|
+ return mp1_universal_mode_exit();
|
|
+
|
|
+ default:
|
|
+ return -ENOTTY;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct file_operations mp1_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .unlocked_ioctl = mp1_ioctl,
|
|
+};
|
|
+
|
|
+static struct miscdevice mp1_misc_device = {
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .name = "mp1",
|
|
+ .fops = &mp1_fops,
|
|
+};
|
|
+
|
|
+static int __init mp1_init(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ mp1_universal_mode_exit();
|
|
+
|
|
+ pr_info("Set BAPM params\n");
|
|
+ mp1_set_bapm_param_set_all();
|
|
+
|
|
+ ret = misc_register(&mp1_misc_device);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit mp1_exit(void)
|
|
+{
|
|
+ misc_deregister(&mp1_misc_device);
|
|
+ mp1_universal_mode_exit();
|
|
+}
|
|
+
|
|
+module_init(mp1_init);
|
|
+module_exit(mp1_exit);
|
|
+
|
|
+MODULE_AUTHOR("Andy Nguyen");
|
|
+MODULE_DESCRIPTION("PlayStation 5 MP1 SMU driver");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/ps5/spcie.c b/drivers/ps5/spcie.c
|
|
new file mode 100644
|
|
index 000000000000..ecec0d6623b1
|
|
--- /dev/null
|
|
+++ b/drivers/ps5/spcie.c
|
|
@@ -0,0 +1,879 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/ps5.h>
|
|
+#include <linux/dmi.h>
|
|
+#include <linux/delay.h>
|
|
+
|
|
+#define PCI_DEVICE_ID_SPCIE 0x9107
|
|
+#define SPCIE_SUBFUNC_ICC 12
|
|
+#define NUM_IRQS 16
|
|
+
|
|
+#define ICC_QUERY_OFFSET 0
|
|
+#define ICC_REPLY_OFFSET 0x800
|
|
+
|
|
+#define ICC_DOORBELL_OFFSET 0x108000
|
|
+
|
|
+#define ICC_REG_SOW 0x7f0
|
|
+#define ICC_REG_SOR 0x7f4
|
|
+#define ICC_REG_EMW 0xff0
|
|
+#define ICC_REG_EMR 0xff4
|
|
+
|
|
+#define ICC_REG_DOORBELL 0x04
|
|
+#define ICC_REG_INTR_STATUS 0x14
|
|
+#define ICC_REG_INTR_MASK 0x24
|
|
+
|
|
+#define ICC_SEND 0x01
|
|
+#define ICC_ACK 0x02
|
|
+
|
|
+#define ICC_MSG_TYPE_REPLY 0x4000
|
|
+#define ICC_MSG_TYPE_NOTIF 0x8000
|
|
+
|
|
+#define ICC_TIMEOUT_MSECS 12000
|
|
+
|
|
+#define ICC_IOC_MAGIC 'I'
|
|
+
|
|
+#define ICC_FAN_CHANGE_SERVO_PATTERN _IOW(ICC_IOC_MAGIC, 1, u8)
|
|
+
|
|
+struct spcie_dev;
|
|
+
|
|
+struct spcie_icc_dev {
|
|
+ struct pci_dev *pdev;
|
|
+ struct spcie_dev *sdev;
|
|
+ struct input_dev *idev;
|
|
+ struct mutex lock;
|
|
+ wait_queue_head_t wq;
|
|
+ void __iomem *icc_doorbell_base;
|
|
+ void __iomem *icc_base;
|
|
+ u16 icc_send_xtn_id;
|
|
+ u8 notification[ICC_MSG_MAX_SIZE];
|
|
+ u16 notification_length;
|
|
+ u8 reply[ICC_MSG_MAX_SIZE];
|
|
+ u16 reply_length;
|
|
+ bool reply_ready;
|
|
+};
|
|
+
|
|
+struct spcie_dev {
|
|
+ struct pci_dev *pdev;
|
|
+ struct spcie_icc_dev *icc_dev;
|
|
+ void __iomem *bar2;
|
|
+ void __iomem *pervasive0;
|
|
+};
|
|
+
|
|
+static struct spcie_dev *sdev;
|
|
+static struct spcie_icc_dev *icc_dev;
|
|
+
|
|
+static u32 spcie_chip_revision_id = 0;
|
|
+
|
|
+static u8 indicator_white_dim[] = {
|
|
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x11,
|
|
+ 0x01, 0x00, 0x20, 0x00, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x02,
|
|
+ 0x00
|
|
+};
|
|
+
|
|
+static u8 indicator_white_medium[] = {
|
|
+ 0x03, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x11,
|
|
+ 0x01, 0x00, 0x5F, 0x00, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x02,
|
|
+ 0x00
|
|
+};
|
|
+
|
|
+static u8 indicator_white_bright[] = {
|
|
+ 0x03, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x11,
|
|
+ 0x01, 0x00, 0xFF, 0x00, 0x01, 0x00, 0x12, 0x01, 0x00, 0x00, 0x00, 0x02,
|
|
+ 0x00
|
|
+};
|
|
+
|
|
+static u16 icc_checksum(struct icc_msg *msg)
|
|
+{
|
|
+ u16 i;
|
|
+ u16 checksum = 0;
|
|
+ for (i = 0; i < msg->length; i++)
|
|
+ checksum += ((u8 *)msg)[i];
|
|
+ return checksum;
|
|
+}
|
|
+
|
|
+static int icc_send(u8 *query)
|
|
+{
|
|
+ struct icc_msg *msg;
|
|
+
|
|
+ msg = (struct icc_msg *)query;
|
|
+ if (msg->length < ICC_MSG_MIN_SIZE)
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->magic = 0x42;
|
|
+ msg->unk_04 = 3;
|
|
+ msg->id = icc_dev->icc_send_xtn_id++;
|
|
+ msg->checksum = icc_checksum(msg);
|
|
+
|
|
+ writew(0, icc_dev->icc_base + ICC_REG_SOR);
|
|
+
|
|
+ for (u16 i = 0; i < msg->length; i++)
|
|
+ writeb(query[i], icc_dev->icc_base + ICC_QUERY_OFFSET + i);
|
|
+
|
|
+ writew(1, icc_dev->icc_base + ICC_REG_SOW);
|
|
+
|
|
+ writel(ICC_SEND, icc_dev->icc_doorbell_base + ICC_REG_DOORBELL);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int icc_query(u8 *query, u8 *reply)
|
|
+{
|
|
+ struct icc_msg *msg;
|
|
+ u16 checksum, expected;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&icc_dev->lock);
|
|
+
|
|
+ icc_dev->reply_ready = false;
|
|
+
|
|
+ icc_send(query);
|
|
+
|
|
+ ret = wait_event_interruptible_timeout(icc_dev->wq, icc_dev->reply_ready,
|
|
+ msecs_to_jiffies(ICC_TIMEOUT_MSECS));
|
|
+ if (ret == 0) {
|
|
+ dev_err(&icc_dev->pdev->dev, "timeout\n");
|
|
+ mutex_unlock(&icc_dev->lock);
|
|
+ return -ETIMEDOUT;
|
|
+ } else if (ret < 0) {
|
|
+ dev_err(&icc_dev->pdev->dev, "interrupted\n");
|
|
+ mutex_unlock(&icc_dev->lock);
|
|
+ return -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ memcpy(reply, icc_dev->reply, icc_dev->reply_length);
|
|
+
|
|
+ msg = (struct icc_msg *)reply;
|
|
+ expected = msg->checksum;
|
|
+ msg->checksum = 0;
|
|
+ checksum = icc_checksum(msg);
|
|
+ if (checksum != expected) {
|
|
+ dev_err(&icc_dev->pdev->dev, "checksum mismatch\n");
|
|
+ mutex_unlock(&icc_dev->lock);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&icc_dev->lock);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(icc_query);
|
|
+
|
|
+int icc_nvs_write(u32 partition, u16 offset, u16 length, const void *data)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_NVS;
|
|
+ msg->msg_type = 0;
|
|
+ msg->length = sizeof(*msg) + 6 + length;
|
|
+ msg->data[0] = 0;
|
|
+ msg->data[1] = partition;
|
|
+ *(u16 *)&msg->data[2] = offset;
|
|
+ *(u16 *)&msg->data[4] = length;
|
|
+ memcpy(&msg->data[6], data, length);
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_nvs_write);
|
|
+
|
|
+int icc_nvs_read(u32 partition, u16 offset, u16 length, void *data)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+ int ret;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_NVS;
|
|
+ msg->msg_type = 1;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = 0;
|
|
+ msg->data[1] = partition;
|
|
+ *(u16 *)&msg->data[2] = offset;
|
|
+ *(u16 *)&msg->data[4] = length;
|
|
+
|
|
+ ret = icc_query(buf, buf);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ memcpy(data, &msg->data[2], length);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(icc_nvs_read);
|
|
+
|
|
+int icc_usbc_set_pdcon_op_mode(u8 PortId, u8 OpMode)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_USBC;
|
|
+ msg->msg_type = 0x10;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = PortId;
|
|
+ msg->data[1] = OpMode;
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_usbc_set_pdcon_op_mode);
|
|
+
|
|
+int icc_configuration_set_cpu_info_bit(u8 *bit)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_GENERAL;
|
|
+ msg->msg_type = 0x10;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = bit[1];
|
|
+ msg->data[1] = bit[0];
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_configuration_set_cpu_info_bit);
|
|
+
|
|
+int icc_configuration_clear_cpu_info_bit(void)
|
|
+{
|
|
+ u8 bit[2];
|
|
+ int ret;
|
|
+
|
|
+ bit[0] = 0;
|
|
+ bit[1] = 0;
|
|
+ ret = icc_configuration_set_cpu_info_bit(bit);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ bit[0] = 0;
|
|
+ bit[1] = 1;
|
|
+ ret = icc_configuration_set_cpu_info_bit(bit);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(icc_configuration_clear_cpu_info_bit);
|
|
+
|
|
+__noreturn void icc_power_suspend(int keep)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_POWER;
|
|
+ msg->msg_type = 1;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = 1; // with eap
|
|
+ msg->data[1] = 1;
|
|
+ msg->data[2] = 1;
|
|
+ msg->data[3] = 0;
|
|
+ msg->data[4] = 1;
|
|
+ *(u16 *)&msg->data[5] = (2 * (keep != 0)) | 0x30;
|
|
+ *(u16 *)&msg->data[7] = 0;
|
|
+
|
|
+ icc_query(buf, buf);
|
|
+ local_irq_disable();
|
|
+ wbinvd();
|
|
+ mp1_set_sleep_entry();
|
|
+ while (1);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_power_suspend);
|
|
+
|
|
+__noreturn void icc_power_shutdown(void)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ icc_configuration_clear_cpu_info_bit();
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_POWER;
|
|
+ msg->msg_type = 1;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = 0;
|
|
+ msg->data[1] = 0; // shutdown
|
|
+ msg->data[2] = 2; // depth
|
|
+ msg->data[3] = 0; // cause
|
|
+ msg->data[4] = 1; // hand
|
|
+ *(u32 *)&msg->data[5] = 0;
|
|
+
|
|
+ icc_query(buf, buf);
|
|
+ while (1);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_power_shutdown);
|
|
+
|
|
+__noreturn void icc_power_reboot(void)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ icc_configuration_clear_cpu_info_bit();
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_POWER;
|
|
+ msg->msg_type = 1;
|
|
+ msg->length = ICC_MSG_MIN_SIZE;
|
|
+ msg->data[0] = 0;
|
|
+ msg->data[1] = 1; // reboot
|
|
+ msg->data[2] = 2; // depth
|
|
+ msg->data[3] = 0; // cause
|
|
+ msg->data[4] = 1; // hand
|
|
+ *(u32 *)&msg->data[5] = 0;
|
|
+
|
|
+ icc_query(buf, buf);
|
|
+ while (1);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_power_reboot);
|
|
+
|
|
+int icc_button_enable_notification(u8 type, u8 enable)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_BUTTON;
|
|
+ msg->msg_type = 1;
|
|
+ msg->length = 0x20;
|
|
+ msg->data[0] = type;
|
|
+ msg->data[1] = enable;
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_button_enable_notification);
|
|
+
|
|
+int icc_button_enable_all_notifications(u8 enable)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ /* power button */
|
|
+ ret = icc_button_enable_notification(0, enable);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* eject button */
|
|
+ ret = icc_button_enable_notification(1, enable);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* reset button */
|
|
+ ret = icc_button_enable_notification(2, enable);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(icc_button_enable_all_notifications);
|
|
+
|
|
+int icc_thermal_enable_notification(u8 enable)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_THERMAL;
|
|
+ msg->msg_type = 2;
|
|
+ msg->length = 0x20;
|
|
+ msg->data[0] = enable;
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_thermal_enable_notification);
|
|
+
|
|
+int icc_indicator_set_led(u8 setting[], size_t setting_size)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_INDICATOR;
|
|
+ msg->msg_type = 0x20;
|
|
+ msg->length = sizeof(*msg) + setting_size;
|
|
+ memcpy(msg->data, setting, setting_size);
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_indicator_set_led);
|
|
+
|
|
+int icc_indicator_set_led_white(u8 level)
|
|
+{
|
|
+ switch (level) {
|
|
+ case 0:
|
|
+ return icc_indicator_set_led(indicator_white_dim, sizeof(indicator_white_dim));
|
|
+ case 1:
|
|
+ return icc_indicator_set_led(indicator_white_medium, sizeof(indicator_white_medium));
|
|
+ case 2:
|
|
+ return icc_indicator_set_led(indicator_white_bright, sizeof(indicator_white_bright));
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+EXPORT_SYMBOL(icc_indicator_set_led_white);
|
|
+
|
|
+int icc_fan_change_servo_pattern(u8 pattern)
|
|
+{
|
|
+ u8 buf[ICC_MSG_MAX_SIZE] = {};
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ msg->service_id = ICC_SERVICE_ID_FAN;
|
|
+ msg->msg_type = 0xb;
|
|
+ msg->length = 0x20;
|
|
+ msg->data[0] = pattern;
|
|
+ msg->data[1] = pattern;
|
|
+ msg->data[2] = pattern;
|
|
+ msg->data[3] = pattern;
|
|
+
|
|
+ return icc_query(buf, buf);
|
|
+}
|
|
+EXPORT_SYMBOL(icc_fan_change_servo_pattern);
|
|
+
|
|
+static void button_notification_handler(struct icc_msg *msg)
|
|
+{
|
|
+ if (msg->msg_type == 0x8010) { // power button down
|
|
+ input_report_key(icc_dev->idev, KEY_POWER, 1);
|
|
+ input_sync(icc_dev->idev);
|
|
+ } else if (msg->msg_type == 0x8011) { // power button up
|
|
+ input_report_key(icc_dev->idev, KEY_POWER, 0);
|
|
+ input_sync(icc_dev->idev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int icc_notification_handler(u8 service_id, u8 *buf)
|
|
+{
|
|
+ struct icc_msg *msg = (struct icc_msg *)buf;
|
|
+
|
|
+ if (service_id == ICC_SERVICE_ID_HDMI) {
|
|
+ hdmi_notification_handler(msg);
|
|
+ } else if (service_id == ICC_SERVICE_ID_BUTTON) {
|
|
+ button_notification_handler(msg);
|
|
+ } else {
|
|
+ pr_info("service id: %x\n", service_id);
|
|
+ print_hex_dump(KERN_INFO, "event: ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
+ msg->data, msg->length - sizeof(*msg), true);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static irqreturn_t icc_interrupt(int irq, void *dev)
|
|
+{
|
|
+ struct spcie_icc_dev *icc_dev = dev;
|
|
+ struct icc_msg *msg;
|
|
+ u32 intr_status;
|
|
+
|
|
+ while (1) {
|
|
+ intr_status = readl(icc_dev->icc_doorbell_base + ICC_REG_INTR_STATUS);
|
|
+ if (!intr_status)
|
|
+ break;
|
|
+
|
|
+ /* Ack the interrupt */
|
|
+ writel(intr_status, icc_dev->icc_doorbell_base + ICC_REG_INTR_STATUS);
|
|
+
|
|
+ if (intr_status & ICC_SEND) {
|
|
+ msg = (struct icc_msg *)(icc_dev->icc_base + ICC_REPLY_OFFSET);
|
|
+
|
|
+ if (msg->msg_type & ICC_MSG_TYPE_NOTIF) {
|
|
+ icc_dev->notification_length = min(msg->length, ICC_MSG_MAX_SIZE);
|
|
+ memcpy(icc_dev->notification, msg, icc_dev->notification_length);
|
|
+
|
|
+ writew(0, icc_dev->icc_base + ICC_REG_EMW);
|
|
+ writew(1, icc_dev->icc_base + ICC_REG_EMR);
|
|
+
|
|
+ writel(ICC_ACK, icc_dev->icc_doorbell_base + ICC_REG_DOORBELL);
|
|
+
|
|
+ icc_notification_handler(msg->service_id, icc_dev->notification);
|
|
+ } else if (msg->msg_type & ICC_MSG_TYPE_REPLY) {
|
|
+ icc_dev->reply_length = min(msg->length, ICC_MSG_MAX_SIZE);
|
|
+ memcpy(icc_dev->reply, msg, icc_dev->reply_length);
|
|
+
|
|
+ writew(0, icc_dev->icc_base + ICC_REG_EMW);
|
|
+ writew(1, icc_dev->icc_base + ICC_REG_EMR);
|
|
+
|
|
+ writel(ICC_ACK, icc_dev->icc_doorbell_base + ICC_REG_DOORBELL);
|
|
+
|
|
+ icc_dev->reply_ready = true;
|
|
+ wake_up_interruptible(&icc_dev->wq);
|
|
+ } else {
|
|
+ dev_err(&icc_dev->pdev->dev, "unknown query\n");
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static long icc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ switch (cmd) {
|
|
+ case ICC_FAN_CHANGE_SERVO_PATTERN:
|
|
+ {
|
|
+ u8 pattern;
|
|
+ if (copy_from_user(&pattern, (u8 __user *)arg, sizeof(u8)))
|
|
+ return -EFAULT;
|
|
+ return icc_fan_change_servo_pattern(pattern);
|
|
+ }
|
|
+
|
|
+ default:
|
|
+ return -ENOTTY;
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct file_operations icc_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .unlocked_ioctl = icc_ioctl,
|
|
+};
|
|
+
|
|
+static struct miscdevice icc_misc_device = {
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .name = "icc",
|
|
+ .fops = &icc_fops,
|
|
+};
|
|
+
|
|
+static int spcie_power_button_init(struct spcie_icc_dev *icc_dev)
|
|
+{
|
|
+ icc_dev->idev = input_allocate_device();
|
|
+ if (!icc_dev->idev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ icc_dev->idev->name = "Power Button";
|
|
+ icc_dev->idev->phys = "pm/button/input0";
|
|
+ icc_dev->idev->id.bustype = BUS_HOST;
|
|
+ icc_dev->idev->dev.parent = NULL;
|
|
+ input_set_capability(icc_dev->idev, EV_KEY, KEY_POWER);
|
|
+
|
|
+ return input_register_device(icc_dev->idev);
|
|
+}
|
|
+
|
|
+static void spcie_power_button_remove(struct spcie_icc_dev *icc_dev)
|
|
+{
|
|
+ input_free_device(icc_dev->idev);
|
|
+}
|
|
+
|
|
+static int spcie_icc_init(struct spcie_dev *sdev)
|
|
+{
|
|
+ int vector, ret;
|
|
+
|
|
+ icc_dev = devm_kzalloc(&sdev->pdev->dev, sizeof(*icc_dev), GFP_KERNEL);
|
|
+ if (!icc_dev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ icc_dev->pdev = sdev->pdev;
|
|
+ icc_dev->sdev = sdev;
|
|
+
|
|
+ sdev->icc_dev = icc_dev;
|
|
+
|
|
+ mutex_init(&icc_dev->lock);
|
|
+ init_waitqueue_head(&icc_dev->wq);
|
|
+
|
|
+ icc_dev->icc_doorbell_base = sdev->bar2 + ICC_DOORBELL_OFFSET;
|
|
+ icc_dev->icc_base = sdev->pervasive0;
|
|
+
|
|
+ ret = spcie_power_button_init(icc_dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Clear status */
|
|
+ writel(ICC_SEND | ICC_ACK, icc_dev->icc_doorbell_base + ICC_REG_INTR_STATUS);
|
|
+
|
|
+ /* Request IRQ for icc subfunc */
|
|
+ vector = pci_irq_vector(sdev->pdev, SPCIE_SUBFUNC_ICC);
|
|
+ ret = devm_request_irq(&sdev->pdev->dev, vector, icc_interrupt, 0, "icc", icc_dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* Enable IRQs */
|
|
+ writel(ICC_SEND | ICC_ACK, icc_dev->icc_doorbell_base + ICC_REG_INTR_MASK);
|
|
+
|
|
+ /* Set DMI info */
|
|
+ static char hw_model[32] = {0}, hw_info[32] = {0};
|
|
+ if (icc_nvs_read(2, 0x30, 0x20, hw_model) == 0) {
|
|
+ dmi_set_system_info(DMI_PRODUCT_VERSION, hw_model);
|
|
+ }
|
|
+ if (icc_nvs_read(2, 0x10, 0x11, hw_info) == 0) {
|
|
+ dmi_set_system_info(DMI_PRODUCT_SERIAL, hw_info);
|
|
+ }
|
|
+
|
|
+ /* Enable notifications */
|
|
+ icc_button_enable_all_notifications(1);
|
|
+ icc_thermal_enable_notification(1);
|
|
+
|
|
+ /* Set LED to white dim */
|
|
+ icc_indicator_set_led_white(0);
|
|
+
|
|
+ /* Enable SuperSpeed-USB-C port */
|
|
+ icc_usbc_set_pdcon_op_mode(0, 5);
|
|
+
|
|
+ /* Initialize HDMI */
|
|
+ getHdmiConfiguration();
|
|
+ hdmiSystemResume();
|
|
+ sceHdmiInitVideoConfig();
|
|
+
|
|
+ ret = misc_register(&icc_misc_device);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int spcie_icc_remove(struct spcie_dev *sdev)
|
|
+{
|
|
+ spcie_power_button_remove(sdev->icc_dev);
|
|
+ misc_deregister(&icc_misc_device);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+u32 spcie_get_chip_id(void)
|
|
+{
|
|
+ return spcie_chip_revision_id & 0xff0000;
|
|
+}
|
|
+EXPORT_SYMBOL(spcie_get_chip_id);
|
|
+
|
|
+u32 spcie_get_revision_id(void)
|
|
+{
|
|
+ return spcie_chip_revision_id & 0xffff;
|
|
+}
|
|
+EXPORT_SYMBOL(spcie_get_revision_id);
|
|
+
|
|
+u32 spcie_bar2_180000_read(u32 reg)
|
|
+{
|
|
+ return readl(sdev->bar2 + 0x180000 + reg);
|
|
+}
|
|
+EXPORT_SYMBOL(spcie_bar2_180000_read);
|
|
+
|
|
+void spcie_bar2_180000_write(u32 reg, u32 val)
|
|
+{
|
|
+ writel(val, sdev->bar2 + 0x180000 + reg);
|
|
+}
|
|
+EXPORT_SYMBOL(spcie_bar2_180000_write);
|
|
+
|
|
+u32 spcie_pervasive0_4000_read(u32 reg)
|
|
+{
|
|
+ return readl(sdev->pervasive0 + 0x4000 + reg);
|
|
+}
|
|
+EXPORT_SYMBOL(spcie_pervasive0_4000_read);
|
|
+
|
|
+static int spcie_init_dev(struct spcie_dev *sdev, int dev_ip)
|
|
+{
|
|
+ void __iomem *bar;
|
|
+ u32 offset0, offset1, offset2;
|
|
+ u32 val;
|
|
+ int retries = 10000;
|
|
+
|
|
+ switch (dev_ip) {
|
|
+ case 0:
|
|
+ bar = sdev->bar2;
|
|
+ offset0 = 0x142020;
|
|
+ offset1 = 0x142028;
|
|
+ offset2 = 0x180020;
|
|
+ break;
|
|
+ case 1:
|
|
+ bar = sdev->bar2;
|
|
+ offset0 = 0x143820;
|
|
+ offset1 = 0x143828;
|
|
+ offset2 = 0x18002c;
|
|
+ break;
|
|
+ case 2:
|
|
+ bar = sdev->bar2;
|
|
+ offset0 = 0x144820;
|
|
+ offset1 = 0x144828;
|
|
+ offset2 = 0x180030;
|
|
+ break;
|
|
+ case 3:
|
|
+ bar = sdev->bar2;
|
|
+ offset0 = 0x144020;
|
|
+ offset1 = 0x144028;
|
|
+ offset2 = 0x180024;
|
|
+ break;
|
|
+ case 4:
|
|
+ bar = sdev->pervasive0;
|
|
+ offset0 = 0x42420;
|
|
+ offset1 = 0x42428;
|
|
+ offset2 = 0x7010;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ writel(readl(bar + offset0) | 0x10, bar + offset0);
|
|
+
|
|
+ while (1) {
|
|
+ val = readl(bar + offset1);
|
|
+ if ((val & 0x10) == 0 && (val & 0x20) == 0 && (val & 0x40) == 0 && (val & 0x80) == 0)
|
|
+ break;
|
|
+ udelay(10);
|
|
+ if (!--retries) {
|
|
+ printk("[ERROR]: cannot stop the transaction(%X): %X\n", offset0, val);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ writel(readl(bar + offset0) | 0x1, bar + offset0);
|
|
+
|
|
+ if (dev_ip == 4) {
|
|
+ writel(1, sdev->pervasive0 + offset2);
|
|
+ writel(3, sdev->pervasive0 + 0x74b4);
|
|
+ } else {
|
|
+ writel(3, sdev->bar2 + offset2);
|
|
+
|
|
+ switch (dev_ip) {
|
|
+ case 0:
|
|
+ writel(1, sdev->pervasive0 + 0x74c8);
|
|
+ writel(0x1f, sdev->pervasive0 + 0x7c28);
|
|
+ break;
|
|
+ case 1:
|
|
+ writel(3, sdev->pervasive0 + 0x74c4);
|
|
+ writel(7, sdev->pervasive0 + 0x7c14);
|
|
+ break;
|
|
+ case 2:
|
|
+ writel(3, sdev->pervasive0 + 0x7498);
|
|
+ writel(7, sdev->pervasive0 + 0x7c18);
|
|
+ break;
|
|
+ case 3:
|
|
+ if (spcie_get_chip_id() == 0x110000) {
|
|
+ writel(7, sdev->pervasive0 + 0x74cc);
|
|
+ } else {
|
|
+ writel(1, sdev->pervasive0 + 0x74cc);
|
|
+ }
|
|
+ writel(0xf, sdev->pervasive0 + 0x7c1c);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ writel(readl(bar + offset0) & ~0x11, bar + offset0);
|
|
+
|
|
+ if (dev_ip == 4) {
|
|
+ writel(0, sdev->pervasive0 + offset2);
|
|
+ } else if (dev_ip == 0) {
|
|
+ writel(0, sdev->bar2 + offset2);
|
|
+
|
|
+ writel(readl(sdev->pervasive0 + 0x7c28) & ~8, sdev->pervasive0 + 0x7c28);
|
|
+ writel(readl(sdev->pervasive0 + 0x7c28) & ~1, sdev->pervasive0 + 0x7c28);
|
|
+
|
|
+ writel(1, sdev->bar2 + offset2);
|
|
+ writel(0, sdev->bar2 + offset2);
|
|
+
|
|
+ writel(readl(sdev->pervasive0 + 0x7c28) | 8, sdev->pervasive0 + 0x7c28);
|
|
+ writel(readl(sdev->pervasive0 + 0x7c28) | 1, sdev->pervasive0 + 0x7c28);
|
|
+
|
|
+ udelay(680);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int spcie_init(struct spcie_dev *sdev)
|
|
+{
|
|
+ writel(1, sdev->pervasive0 + 0x22004);
|
|
+ while ((readl(sdev->pervasive0 + 0x22000) & 7) != 4);
|
|
+
|
|
+ spcie_init_dev(sdev, 0);
|
|
+ if (spcie_get_chip_id() == 0x110000)
|
|
+ spcie_init_dev(sdev, 1);
|
|
+ spcie_init_dev(sdev, 2);
|
|
+ spcie_init_dev(sdev, 3);
|
|
+ spcie_init_dev(sdev, 4);
|
|
+
|
|
+ writel(1, sdev->bar2 + 0x18004c);
|
|
+ writel(1, sdev->pervasive0 + 0x7c08);
|
|
+
|
|
+ writel(0, sdev->bar2 + 0x18004c);
|
|
+ writel(0, sdev->pervasive0 + 0x22004);
|
|
+ while ((readl(sdev->pervasive0 + 0x22000) & 4) != 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int spcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
+{
|
|
+ u32 chip_revision, chip_id0, chip_id1;
|
|
+ char *chip_name, *rev_name;
|
|
+ int ret;
|
|
+
|
|
+ ret = pcim_enable_device(pdev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
|
|
+ if (!sdev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ sdev->pdev = pdev;
|
|
+
|
|
+ sdev->bar2 = pcim_iomap(pdev, 2, 0);
|
|
+ if (!sdev->bar2)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ sdev->pervasive0 = pcim_iomap(pdev, 4, 0);
|
|
+ if (!sdev->pervasive0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pci_set_master(pdev);
|
|
+
|
|
+ chip_revision = readl(sdev->pervasive0 + 0x4000);
|
|
+ dev_info(&sdev->pdev->dev, "Chip revision: %08x\n", chip_revision);
|
|
+
|
|
+ chip_id0 = readl(sdev->pervasive0 + 0x4008);
|
|
+ dev_info(&sdev->pdev->dev, "Chip ID0: %08x\n", chip_id0);
|
|
+
|
|
+ chip_id1 = readl(sdev->pervasive0 + 0x4004);
|
|
+ dev_info(&sdev->pdev->dev, "Chip ID1: %08x\n", chip_id1);
|
|
+
|
|
+ if ((chip_revision & 0xff000000) == 0x11000000) {
|
|
+ chip_name = "Salina";
|
|
+ spcie_chip_revision_id = 0x110000;
|
|
+ } else if ((chip_revision & 0xff000000) == 0x12000000) {
|
|
+ chip_name = "Salina2";
|
|
+ spcie_chip_revision_id = 0x120000;
|
|
+ } else {
|
|
+ panic("unknown subsys %08x\n", chip_revision);
|
|
+ }
|
|
+
|
|
+ switch (chip_revision & 0xffff) {
|
|
+ case 0x100: rev_name = "A0"; break;
|
|
+ case 0x101: rev_name = "A1"; break;
|
|
+ case 0x200: rev_name = "B0"; break;
|
|
+ case 0x201: rev_name = "B1"; break;
|
|
+ case 0x300: rev_name = "C0"; break;
|
|
+ default:
|
|
+ panic("unknown subsys %08x\n", chip_revision);
|
|
+ }
|
|
+
|
|
+ spcie_chip_revision_id |= (chip_revision & 0xffff);
|
|
+
|
|
+ dev_info(&sdev->pdev->dev, "%s %s\n", chip_name, rev_name);
|
|
+
|
|
+ ret = spcie_init(sdev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = pci_alloc_irq_vectors(pdev, NUM_IRQS, NUM_IRQS, PCI_IRQ_MSI);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ ret = spcie_icc_init(sdev);
|
|
+ if (ret) {
|
|
+ pci_free_irq_vectors(pdev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pci_set_drvdata(pdev, sdev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void spcie_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct spcie_dev *sdev = pci_get_drvdata(pdev);
|
|
+ if (sdev) {
|
|
+ spcie_icc_remove(sdev);
|
|
+ pci_free_irq_vectors(pdev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct pci_device_id spcie_ids[] = {
|
|
+ { PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SPCIE) },
|
|
+ { 0, }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(pci, spcie_ids);
|
|
+
|
|
+static struct pci_driver spcie_driver = {
|
|
+ .name = "spcie",
|
|
+ .id_table = spcie_ids,
|
|
+ .probe = spcie_probe,
|
|
+ .remove = spcie_remove,
|
|
+};
|
|
+
|
|
+module_pci_driver(spcie_driver);
|
|
+
|
|
+MODULE_AUTHOR("Andy Nguyen");
|
|
+MODULE_DESCRIPTION("PlayStation 5 Salina PCI Express glue driver");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/ps5/tpcie.c b/drivers/ps5/tpcie.c
|
|
new file mode 100644
|
|
index 000000000000..cfe53c9861c1
|
|
--- /dev/null
|
|
+++ b/drivers/ps5/tpcie.c
|
|
@@ -0,0 +1,294 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/console.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/msi.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/ps5.h>
|
|
+#include <linux/serial_core.h>
|
|
+#include <linux/tty_flip.h>
|
|
+
|
|
+#define PCI_DEVICE_ID_TPCIE 0x90ec
|
|
+#define TPCIE_SUBFUNC_UART 6
|
|
+#define NUM_IRQS 8
|
|
+
|
|
+#define UART_OFFSET 0x1010100
|
|
+
|
|
+struct tpcie_dev;
|
|
+
|
|
+struct tpcie_uart_dev {
|
|
+ struct pci_dev *pdev;
|
|
+ struct tpcie_dev *tdev;
|
|
+ void __iomem *uart_base;
|
|
+ struct tty_driver *tty_drv;
|
|
+ struct tty_port tty_port;
|
|
+};
|
|
+
|
|
+struct tpcie_dev {
|
|
+ struct pci_dev *pdev;
|
|
+ void __iomem *bar4;
|
|
+ struct tpcie_uart_dev *uart_dev;
|
|
+};
|
|
+
|
|
+static struct tpcie_uart_dev *uart_dev;
|
|
+
|
|
+static bool tpcie_uart_rx_ready(void)
|
|
+{
|
|
+ return (readl(uart_dev->uart_base + 0xc) & 0x10) != 0;
|
|
+}
|
|
+
|
|
+static bool tpcie_uart_tx_ready(void)
|
|
+{
|
|
+ return (readl(uart_dev->uart_base + 0xc) & 0x800) == 0;
|
|
+}
|
|
+
|
|
+static void tpcie_uart_putc(char c)
|
|
+{
|
|
+ unsigned timeout = 0xffff;
|
|
+ while (!tpcie_uart_tx_ready() && --timeout)
|
|
+ cpu_relax();
|
|
+ writel(c, uart_dev->uart_base + 0x4);
|
|
+}
|
|
+
|
|
+static void tpcie_uart_write(struct console *con, const char *s, unsigned int n)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < n; ++i) {
|
|
+ if (s[i] == '\n')
|
|
+ tpcie_uart_putc('\r');
|
|
+ tpcie_uart_putc(s[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static ssize_t tpcie_uart_tty_write(struct tty_struct *tty, const u8 *buf, size_t count)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < count; i++) {
|
|
+ tpcie_uart_putc(buf[i]);
|
|
+ }
|
|
+ tty_port_tty_wakeup(&uart_dev->tty_port);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static unsigned int tpcie_uart_tty_write_room(struct tty_struct *tty)
|
|
+{
|
|
+ return 2048;
|
|
+}
|
|
+
|
|
+static int tpcie_uart_tty_open(struct tty_struct *tty, struct file *file)
|
|
+{
|
|
+ return tty_port_open(&uart_dev->tty_port, tty, file);
|
|
+}
|
|
+
|
|
+static void tpcie_uart_tty_close(struct tty_struct *tty, struct file *file)
|
|
+{
|
|
+ tty_port_close(&uart_dev->tty_port, tty, file);
|
|
+}
|
|
+
|
|
+static void tpcie_uart_tty_hangup(struct tty_struct *tty)
|
|
+{
|
|
+ tty_port_hangup(&uart_dev->tty_port);
|
|
+}
|
|
+
|
|
+static struct tty_operations tpcie_uart_tty_ops = {
|
|
+ .open = tpcie_uart_tty_open,
|
|
+ .close = tpcie_uart_tty_close,
|
|
+ .write = tpcie_uart_tty_write,
|
|
+ .write_room = tpcie_uart_tty_write_room,
|
|
+ .hangup = tpcie_uart_tty_hangup,
|
|
+};
|
|
+
|
|
+static const struct tty_port_operations tpcie_uart_port_ops = {};
|
|
+
|
|
+static struct tty_driver *tpcie_uart_device(struct console *c, int *index)
|
|
+{
|
|
+ *index = 0;
|
|
+ return uart_dev->tty_drv;
|
|
+}
|
|
+
|
|
+static struct console tpcie_uart_console = {
|
|
+ .name = "ttyTitania",
|
|
+ .device = tpcie_uart_device,
|
|
+ .write = tpcie_uart_write,
|
|
+ .flags = CON_PRINTBUFFER,
|
|
+ .index = -1,
|
|
+};
|
|
+
|
|
+static irqreturn_t uart_interrupt(int irq, void *dev)
|
|
+{
|
|
+ u8 ch;
|
|
+ while (tpcie_uart_rx_ready()) {
|
|
+ ch = readl(uart_dev->uart_base + 0x0);
|
|
+ tty_insert_flip_char(&uart_dev->tty_port, ch, TTY_NORMAL);
|
|
+ }
|
|
+ tty_flip_buffer_push(&uart_dev->tty_port);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static void tcpie_config_msi(struct tpcie_dev *tdev, int subfunc, struct msi_msg *msg)
|
|
+{
|
|
+ writel(0xffffffff, tdev->bar4 + 0x120214);
|
|
+ writel(0, tdev->bar4 + 0x12020c);
|
|
+
|
|
+ writel(msg->address_lo, tdev->bar4 + 0x120204);
|
|
+ writel(msg->address_hi, tdev->bar4 + 0x120208);
|
|
+
|
|
+ writel(msg->data & ~0x1f, tdev->bar4 + 0x120210);
|
|
+ if (subfunc == TPCIE_SUBFUNC_UART) {
|
|
+ writel(msg->data & 0x1f, tdev->bar4 + 0x120230);
|
|
+ }
|
|
+ writel(msg->data & 0x1f, tdev->bar4 + 0x120210 + subfunc * 4);
|
|
+
|
|
+ writel(0x33, tdev->bar4 + 0x120200);
|
|
+}
|
|
+
|
|
+static int tpcie_uart_init(struct tpcie_dev *tdev)
|
|
+{
|
|
+ struct tty_driver *drv;
|
|
+ struct msi_msg msg;
|
|
+ int vector, ret;
|
|
+
|
|
+ uart_dev = devm_kzalloc(&tdev->pdev->dev, sizeof(*uart_dev), GFP_KERNEL);
|
|
+ if (!uart_dev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ uart_dev->pdev = tdev->pdev;
|
|
+ uart_dev->tdev = tdev;
|
|
+ tdev->uart_dev = uart_dev;
|
|
+ uart_dev->uart_base = tdev->bar4 + UART_OFFSET;
|
|
+
|
|
+ drv = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW);
|
|
+ if (IS_ERR(drv))
|
|
+ return PTR_ERR(drv);
|
|
+
|
|
+ drv->driver_name = "tpcie_uart";
|
|
+ drv->name = "ttyTitania";
|
|
+ drv->type = TTY_DRIVER_TYPE_SERIAL;
|
|
+ drv->major = 0;
|
|
+ drv->minor_start = 0;
|
|
+ drv->num = 1;
|
|
+ drv->subtype = SERIAL_TYPE_NORMAL;
|
|
+ drv->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
|
|
+ drv->init_termios = tty_std_termios;
|
|
+ tty_set_operations(drv, &tpcie_uart_tty_ops);
|
|
+ uart_dev->tty_drv = drv;
|
|
+
|
|
+ tty_port_init(&uart_dev->tty_port);
|
|
+ uart_dev->tty_port.ops = &tpcie_uart_port_ops;
|
|
+ tty_port_link_device(&uart_dev->tty_port, drv, 0);
|
|
+
|
|
+ ret = tty_register_driver(drv);
|
|
+ if (ret) {
|
|
+ tty_driver_kref_put(drv);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ register_console(&tpcie_uart_console);
|
|
+
|
|
+ /* Request IRQ for uart subfunc */
|
|
+ vector = pci_irq_vector(tdev->pdev, TPCIE_SUBFUNC_UART);
|
|
+ ret = devm_request_irq(&tdev->pdev->dev, vector, uart_interrupt, 0, "uart", uart_dev);
|
|
+ if (ret)
|
|
+ goto err_unreg_tty;
|
|
+
|
|
+ /* Special MSI configuration */
|
|
+ ret = irq_chip_compose_msi_msg(irq_get_irq_data(vector), &msg);
|
|
+ if (ret)
|
|
+ goto err_unreg_tty;
|
|
+
|
|
+ tcpie_config_msi(tdev, TPCIE_SUBFUNC_UART, &msg);
|
|
+
|
|
+ /* Enable IRQs */
|
|
+ writel(0x10, uart_dev->uart_base + 0x8);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_unreg_tty:
|
|
+ unregister_console(&tpcie_uart_console);
|
|
+ tty_unregister_driver(drv);
|
|
+ tty_driver_kref_put(drv);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void tpcie_uart_remove(struct tpcie_dev *tdev)
|
|
+{
|
|
+ if (!uart_dev)
|
|
+ return;
|
|
+
|
|
+ /* Disable IRQs */
|
|
+ writel(0, uart_dev->uart_base + 0x8);
|
|
+
|
|
+ unregister_console(&tpcie_uart_console);
|
|
+
|
|
+ if (uart_dev->tty_drv) {
|
|
+ tty_unregister_driver(uart_dev->tty_drv);
|
|
+ tty_driver_kref_put(uart_dev->tty_drv);
|
|
+ }
|
|
+
|
|
+ tty_port_destroy(&uart_dev->tty_port);
|
|
+}
|
|
+
|
|
+static int tpcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
+{
|
|
+ struct tpcie_dev *tdev;
|
|
+ int ret;
|
|
+
|
|
+ ret = pcim_enable_device(pdev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL);
|
|
+ if (!tdev)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ tdev->pdev = pdev;
|
|
+ tdev->bar4 = pcim_iomap(pdev, 4, 0);
|
|
+ if (!tdev->bar4)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ pci_set_master(pdev);
|
|
+
|
|
+ ret = pci_alloc_irq_vectors(pdev, NUM_IRQS, NUM_IRQS, PCI_IRQ_MSI);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ writel(0xf, tdev->bar4 + 0x1142000 + 0x100);
|
|
+ writel(0xf, tdev->bar4 + 0x1142000 + 0x104);
|
|
+
|
|
+ ret = tpcie_uart_init(tdev);
|
|
+ if (ret) {
|
|
+ pci_free_irq_vectors(pdev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pci_set_drvdata(pdev, tdev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void tpcie_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct tpcie_dev *tdev = pci_get_drvdata(pdev);
|
|
+ if (tdev) {
|
|
+ tpcie_uart_remove(tdev);
|
|
+ pci_free_irq_vectors(pdev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct pci_device_id tpcie_ids[] = {
|
|
+ { PCI_DEVICE(PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_TPCIE) },
|
|
+ { 0, }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(pci, tpcie_ids);
|
|
+
|
|
+static struct pci_driver tpcie_driver = {
|
|
+ .name = "tpcie",
|
|
+ .id_table = tpcie_ids,
|
|
+ .probe = tpcie_probe,
|
|
+ .remove = tpcie_remove,
|
|
+};
|
|
+
|
|
+module_pci_driver(tpcie_driver);
|
|
+
|
|
+MODULE_AUTHOR("Andy Nguyen");
|
|
+MODULE_DESCRIPTION("PlayStation 5 Titania PCI Express glue driver");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/include/linux/dmi.h b/include/linux/dmi.h
|
|
index 927f8a8b7a1d..474f1207e2f3 100644
|
|
--- a/include/linux/dmi.h
|
|
+++ b/include/linux/dmi.h
|
|
@@ -100,6 +100,9 @@ extern struct kobject *dmi_kobj;
|
|
extern int dmi_check_system(const struct dmi_system_id *list);
|
|
const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list);
|
|
extern const char * dmi_get_system_info(int field);
|
|
+#ifdef CONFIG_X86_PS5
|
|
+extern void dmi_set_system_info(int field, const char *str);
|
|
+#endif
|
|
extern const struct dmi_device * dmi_find_device(int type, const char *name,
|
|
const struct dmi_device *from);
|
|
extern void dmi_setup(void);
|
|
diff --git a/include/linux/ps5.h b/include/linux/ps5.h
|
|
new file mode 100644
|
|
index 000000000000..35ef3d05525b
|
|
--- /dev/null
|
|
+++ b/include/linux/ps5.h
|
|
@@ -0,0 +1,86 @@
|
|
+#ifndef _LINUX_PS5_H
|
|
+#define _LINUX_PS5_H
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <drm/drm_modes.h>
|
|
+
|
|
+#define ICC_MSG_MIN_SIZE 0x20
|
|
+#define ICC_MSG_MAX_SIZE 0x7f0
|
|
+
|
|
+struct icc_msg {
|
|
+ u8 magic;
|
|
+ u8 service_id;
|
|
+ u16 msg_type;
|
|
+ u16 unk_04;
|
|
+ u16 id;
|
|
+ u16 length;
|
|
+ u16 checksum;
|
|
+ u8 data[];
|
|
+};
|
|
+
|
|
+enum icc_service_id {
|
|
+ ICC_SERVICE_ID_CONFIGURATION = 0x01,
|
|
+ ICC_SERVICE_ID_GENERAL = 0x02,
|
|
+ ICC_SERVICE_ID_NVS = 0x03,
|
|
+ ICC_SERVICE_ID_POWER = 0x04,
|
|
+ ICC_SERVICE_ID_DEVICE = 0x05,
|
|
+ ICC_SERVICE_ID_UNKNOWN1 = 0x07,
|
|
+ ICC_SERVICE_ID_BUTTON = 0x08,
|
|
+ ICC_SERVICE_ID_INDICATOR = 0x09,
|
|
+ ICC_SERVICE_ID_FAN = 0x0a,
|
|
+ ICC_SERVICE_ID_THERMAL = 0x0b,
|
|
+ ICC_SERVICE_ID_HDMI = 0x10,
|
|
+ ICC_SERVICE_ID_USBC = 0x12,
|
|
+ ICC_SERVICE_ID_UNKNOWN3 = 0x13,
|
|
+ ICC_SERVICE_ID_CRASH_REPORT = 0x14,
|
|
+ ICC_SERVICE_ID_BDDRIVE = 0x15,
|
|
+ ICC_SERVICE_ID_UNKNOWN4 = 0x8c,
|
|
+ ICC_SERVICE_ID_UNKNOWN5 = 0x8d,
|
|
+ ICC_SERVICE_ID_SC_CONFIG = 0x8e,
|
|
+ ICC_SERVICE_ID_FLOYD = 0x9a,
|
|
+};
|
|
+
|
|
+u32 spcie_get_chip_id(void);
|
|
+u32 spcie_get_revision_id(void);
|
|
+u32 spcie_bar2_180000_read(u32 reg);
|
|
+void spcie_bar2_180000_write(u32 reg, u32 val);
|
|
+u32 spcie_pervasive0_4000_read(u32 reg);
|
|
+
|
|
+int icc_query(u8 *query, u8 *reply);
|
|
+
|
|
+void hdmiSystemResume(void);
|
|
+void sceHdmiInitVideoConfig(void);
|
|
+void sceHdmiSetVideoConfig(const struct drm_display_mode *mode);
|
|
+void sceHdmiSetAudioConfig(int channels);
|
|
+void sceHdmiDeviceSetVideoMute(int mute);
|
|
+void sceHdmiSetAudioMute(int mute);
|
|
+int getHdmiConfiguration(void);
|
|
+int isHdmiEnabled(void);
|
|
+bool isHdmiModeValid(const struct drm_display_mode *mode, int force_1080p);
|
|
+
|
|
+int icc_nvs_write(u32 partition, u16 offset, u16 length, const void *data);
|
|
+int icc_nvs_read(u32 partition, u16 offset, u16 length, void *data);
|
|
+
|
|
+int icc_usbc_set_pdcon_op_mode(u8 PortId, u8 OpMode);
|
|
+int icc_configuration_set_cpu_info_bit(u8 *bit);
|
|
+int icc_configuration_clear_cpu_info_bit(void);
|
|
+
|
|
+__noreturn void icc_power_suspend(int keep);
|
|
+void __noreturn icc_power_shutdown(void);
|
|
+void __noreturn icc_power_reboot(void);
|
|
+
|
|
+int icc_button_enable_notification(u8 type, u8 enable);
|
|
+int icc_button_enable_all_notifications(u8 enable);
|
|
+int icc_thermal_enable_notification(u8 enable);
|
|
+
|
|
+int icc_indicator_set_led(u8 setting[], size_t setting_size);
|
|
+int icc_indicator_set_led_white(u8 level);
|
|
+
|
|
+int icc_fan_change_servo_pattern(u8 pattern);
|
|
+
|
|
+void hdmi_notification_handler(struct icc_msg *msg);
|
|
+
|
|
+void mp1_set_pm1_addr(u64 addr);
|
|
+int mp1_set_sleep_entry(void);
|
|
+
|
|
+#endif /* _LINUX_PS5_H */
|
|
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
|
|
index 28169d7e143b..44f516daa21d 100755
|
|
--- a/scripts/setlocalversion
|
|
+++ b/scripts/setlocalversion
|
|
@@ -61,6 +61,9 @@ scm_version()
|
|
local no_dirty=false
|
|
local tag
|
|
|
|
+ # Do not add any suffix for ps5-linux.
|
|
+ return
|
|
+
|
|
while [ $# -gt 0 ];
|
|
do
|
|
case "$1" in
|
|
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
|
|
index a4104376523a..befee7ef6c94 100644
|
|
--- a/sound/hda/controllers/intel.c
|
|
+++ b/sound/hda/controllers/intel.c
|
|
@@ -2640,6 +2640,8 @@ static const struct pci_device_id azx_ids[] = {
|
|
AZX_DCAPS_PM_RUNTIME },
|
|
{ PCI_VDEVICE(ATI, 0x1308),
|
|
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
|
+ { PCI_VDEVICE(ATI, 0x13ea),
|
|
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
|
{ PCI_VDEVICE(ATI, 0x157a),
|
|
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
|
{ PCI_VDEVICE(ATI, 0x15b3),
|