diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e7212731cffb74..79d9c5c1282a2e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -522,6 +522,14 @@ config X86_NUMACHIP enable more than ~168 cores. If you don't have one of these, you should say N here. +config X86_PS4 + bool "Sony PlayStation 4" + depends on X86_64 + depends on X86_EXTENDED_PLATFORM + depends on PCI + ---help--- + Select to include support for the Sony PlayStation 4 game console. + config X86_VSMP bool "ScaleMP vSMP" select HYPERVISOR_GUEST diff --git a/arch/x86/include/asm/msi.h b/arch/x86/include/asm/msi.h index 25ddd0916bb2f4..eb7e220cfd7b68 100644 --- a/arch/x86/include/asm/msi.h +++ b/arch/x86/include/asm/msi.h @@ -10,5 +10,6 @@ int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *arg); void pci_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc); +void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg); #endif /* _ASM_X86_MSI_H */ diff --git a/arch/x86/include/asm/ps4.h b/arch/x86/include/asm/ps4.h new file mode 100644 index 00000000000000..60ee1c4f4f1841 --- /dev/null +++ b/arch/x86/include/asm/ps4.h @@ -0,0 +1,60 @@ +/* + * ps4.h: Sony PS4 platform setup code + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _ASM_X86_PS4_H +#define _ASM_X86_PS4_H + +#ifdef CONFIG_X86_PS4 + +#include + +#define PS4_DEFAULT_TSC_FREQ 1594000000 + +#define EMC_TIMER_BASE 0xd0281000 +#define EMC_TIMER_VALUE 0x28 + +extern unsigned long ps4_calibrate_tsc(void); + +/* + * The PS4 Aeolia southbridge device is a composite device containing some + * standard-ish, some not-so-standard, and some completely custom functions, + * all using special MSI handling. This function does the equivalent of + * pci_enable_msi_range and friends, for those devices. Only works after the + * Aeolia MSR routing function device (function 4) has been probed. + * Returns 1 or count, depending on IRQ allocation constraints, or negative on + * error. Assigned IRQ(s) start at dev->irq. + */ +extern int apcie_assign_irqs(struct pci_dev *dev, int nvec); +extern void apcie_free_irqs(unsigned int virq, unsigned int nr_irqs); + +extern int apcie_status(void); +extern int apcie_icc_cmd(u8 major, u16 minor, const void *data, + u16 length, void *reply, u16 reply_length); + + +#else + +static inline int apcie_assign_irqs(struct pci_dev *dev, int nvec) +{ + return -ENODEV; +} +static inline void apcie_free_irqs(unsigned int virq, unsigned int nvec) +{ +} +static inline int apcie_status(void) +{ + return -ENODEV; +} +static inline int apcie_icc_cmd(u8 major, u16 minor, const void *data, + u16 length, void *reply, u16 reply_length) +{ + return -ENODEV; +} + +#endif +#endif diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h index ed8ec011a9fdc6..4cb9306e05a4e7 100644 --- a/arch/x86/include/asm/setup.h +++ b/arch/x86/include/asm/setup.h @@ -62,6 +62,12 @@ extern void x86_ce4100_early_setup(void); static inline void x86_ce4100_early_setup(void) { } #endif +#ifdef CONFIG_X86_PS4 +extern void x86_ps4_early_setup(void); +#else +static inline void x86_ps4_early_setup(void) { } +#endif + #ifndef _SETUP #include diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index 60733f137e9a29..7c0d91b2dd2cd0 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -242,6 +242,7 @@ enum x86_hardware_subarch { X86_SUBARCH_XEN, X86_SUBARCH_INTEL_MID, X86_SUBARCH_CE4100, + X86_SUBARCH_PS4, X86_NR_SUBARCHS, }; diff --git a/arch/x86/kernel/apic/msi.c b/arch/x86/kernel/apic/msi.c index 72a94401f9e034..a5a648fbc6dc4a 100644 --- a/arch/x86/kernel/apic/msi.c +++ b/arch/x86/kernel/apic/msi.c @@ -26,7 +26,7 @@ static struct irq_domain *msi_default_domain; -static void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) +void irq_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) { struct irq_cfg *cfg = irqd_cfg(data); diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 16b1cbd3a61e27..fee5d3809c8dc9 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -467,5 +467,12 @@ void __init x86_64_start_reservations(char *real_mode_data) break; } + /* Call the subarch specific early setup function */ + switch (boot_params.hdr.hardware_subarch) { + case X86_SUBARCH_PS4: + x86_ps4_early_setup(); + break; + } + start_kernel(); } diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index d0e835470d01af..66bc0cf7ef25f5 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile @@ -10,6 +10,7 @@ obj-y += intel/ obj-y += intel-mid/ obj-y += intel-quark/ obj-y += olpc/ +obj-y += ps4/ obj-y += scx200/ obj-y += sfi/ obj-y += ts5500/ diff --git a/arch/x86/platform/PS4/Makefile b/arch/x86/platform/PS4/Makefile new file mode 100644 index 00000000000000..4d5fcde3a08077 --- /dev/null +++ b/arch/x86/platform/PS4/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_X86_PS4) += ps4.o calibrate.o diff --git a/arch/x86/platform/PS4/calibrate.c b/arch/x86/platform/PS4/calibrate.c new file mode 100644 index 00000000000000..a97c1b6c581366 --- /dev/null +++ b/arch/x86/platform/PS4/calibrate.c @@ -0,0 +1,116 @@ +/* + * calibrate.c: Sony PS4 TSC/LAPIC calibration + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + + #define pr_fmt(fmt) "ps4: " fmt + + #include +#include +#include +#include +#include +#include + + /* The PS4 southbridge (Aeolia) has an EMC timer that ticks at 32.768kHz, + * which seems to be an appropriate clock reference for calibration. Both TSC + * and the LAPIC timer are based on the core clock frequency and thus can be + * calibrated together. */ +static void __iomem *emc_timer = NULL; + + static __init inline u32 emctimer_read32(unsigned int reg) +{ + return ioread32(emc_timer + reg); +} + + static __init inline void emctimer_write32(unsigned int reg, u32 val) +{ + iowrite32(val, emc_timer + reg); +} + + static __init inline u32 emctimer_read(void) +{ + u32 t1, t2; + t1 = emctimer_read32(EMC_TIMER_VALUE); + while (1) { + t2 = emctimer_read32(EMC_TIMER_VALUE); + if (t1 == t2) + return t1; + t1 = t2; + } +} + + static __init unsigned long ps4_measure_tsc_freq(void) +{ + unsigned long ret = 0; + u32 t1, t2; + u64 tsc1, tsc2; + + // This is part of the Aeolia pcie device, but it's too early to + // do this in a driver. + emc_timer = ioremap(EMC_TIMER_BASE, 0x100); + if (!emc_timer) + goto fail; + + // reset/start the timer + emctimer_write32(0x84, emctimer_read32(0x84) & (~0x01)); + // udelay is not calibrated yet, so this is likely wildly off, but good + // enough to work. + udelay(300); + emctimer_write32(0x00, emctimer_read32(0x00) | 0x01); + emctimer_write32(0x84, emctimer_read32(0x84) | 0x01); + + t1 = emctimer_read(); + tsc1 = tsc2 = rdtsc(); + + while (emctimer_read() == t1) { + // 0.1s timeout should be enough + tsc2 = rdtsc(); + if ((tsc2 - tsc1) > (PS4_DEFAULT_TSC_FREQ/10)) { + pr_warn("EMC timer is broken.\n"); + goto fail; + } + } + pr_info("EMC timer started in %lld TSC ticks\n", tsc2 - tsc1); + + // Wait for a tick boundary + t1 = emctimer_read(); + while ((t2 = emctimer_read()) == t1); + tsc1 = rdtsc(); + + // Wait for 1024 ticks to elapse (31.25ms) + // We don't need to wait very long, as we are looking for transitions. + // At this value, a TSC uncertainty of ~50 ticks corresponds to 1ppm of + // clock accuracy. + while ((emctimer_read() - t2) < 1024); + tsc2 = rdtsc(); + + // TSC rate is 32 times the elapsed time + ret = (tsc2 - tsc1) * 32; + + pr_info("Calibrated TSC frequency: %ld kHz\n", ret); +fail: + if (emc_timer) { + iounmap(emc_timer); + emc_timer = NULL; + } + return ret; +} + + unsigned long __init ps4_calibrate_tsc(void) +{ + unsigned long tsc_freq = ps4_measure_tsc_freq(); + + if (!tsc_freq) { + pr_warn("Unable to measure TSC frequency, assuming default.\n"); + tsc_freq = PS4_DEFAULT_TSC_FREQ; + } + + lapic_timer_frequency = (tsc_freq + 8 * HZ) / (16 * HZ); + + return (tsc_freq + 500) / 1000; +} diff --git a/arch/x86/platform/PS4/ps4.c b/arch/x86/platform/PS4/ps4.c new file mode 100644 index 00000000000000..bc353035c1c3af --- /dev/null +++ b/arch/x86/platform/PS4/ps4.c @@ -0,0 +1,76 @@ +/* + * ps4.c: Sony PS4 platform setup code + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + + #define pr_fmt(fmt) "ps4: " fmt + + #include +#include +#include +#include +#include +#include +#include +#include + + #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + static bool is_ps4; +bool apcie_initialized; + + /* + * The RTC is part of the Aeolia PCI device and will be implemented there as + * an RTC class device; stub these out. + */ +static void dummy_get_wallclock(struct timespec *now) +{ + now->tv_sec = now->tv_nsec = 0; +} +static int dummy_set_wallclock(const struct timespec *now) +{ + return -ENODEV; +} + + /* + * Provide a way for generic drivers to query for the availability of the + * PS4 apcie driver/device, which is a dependency for them. + */ +int apcie_status(void) +{ + if (!is_ps4) + return -ENODEV; + return apcie_initialized; +} +EXPORT_SYMBOL_GPL(apcie_status); + + void icc_reboot(void); + + /* + * PS4 specific x86_init function overrides and early setup calls. + */ +void __init x86_ps4_early_setup(void) +{ + pr_info("x86_ps4_early_setup: PS4 early setup\n"); + is_ps4 = true; + x86_platform.calibrate_tsc = ps4_calibrate_tsc; + x86_platform.get_wallclock = dummy_get_wallclock; + x86_platform.set_wallclock = dummy_set_wallclock; + + legacy_pic = &null_legacy_pic; + machine_ops.emergency_restart = icc_reboot; +} diff --git a/drivers/Makefile b/drivers/Makefile index 28b030d7988d52..94f2a6f96a9269 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_SPMI) += spmi/ obj-$(CONFIG_HSI) += hsi/ obj-$(CONFIG_SLIMBUS) += slimbus/ +obj-$(CONFIG_X86_PS4) += ps4/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 021ce46e2e5734..2faaea778c9bdb 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -50,6 +50,10 @@ #include #include "ahci.h" +#ifdef CONFIG_X86_PS4 +#include +#endif + #define DRV_NAME "ahci" #define DRV_VERSION "3.0" @@ -579,6 +583,9 @@ static const struct pci_device_id ahci_pci_tbl[] = { /* Enmotus */ { PCI_DEVICE(0x1c44, 0x8000), board_ahci }, +/* Sony (PS4) */ + { PCI_VDEVICE(SONY, PCI_DEVICE_ID_SONY_AAHCI), board_ahci }, + /* Generic, PCI class code for AHCI */ { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci }, @@ -925,6 +932,22 @@ static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) */ if (pdev->dma_mask && pdev->dma_mask < DMA_BIT_MASK(32)) return 0; +#ifdef CONFIG_X86_PS4 + if (pdev->vendor == PCI_VENDOR_ID_SONY) { + rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(31)); + if (rc) { + dev_err(&pdev->dev, "31-bit DMA enable failed\n"); + return rc; + } + rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(31)); + if (rc) { + dev_err(&pdev->dev, + "31-bit consistent DMA enable failed\n"); + return rc; + } + return 0; + } +#endif if (using_dac && !dma_set_mask(&pdev->dev, DMA_BIT_MASK(64))) { @@ -1566,6 +1589,12 @@ static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, { int nvec; +#ifdef CONFIG_X86_PS4 + if (pdev->vendor == PCI_VENDOR_ID_SONY) { + return apcie_assign_irqs(pdev, 1); + } +#endif + if (hpriv->flags & AHCI_HFLAG_NO_MSI) return -ENODEV; @@ -1650,6 +1679,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) WARN_ON((int)ATA_MAX_QUEUE > AHCI_MAX_CMDS); +#ifdef CONFIG_X86_PS4 + /* This will return negative on non-PS4 platforms */ + if (apcie_status() == 0) + return -EPROBE_DEFER; +#endif + ata_print_version_once(&pdev->dev, DRV_VERSION); /* The AHCI driver can only drive the SATA ports, the PATA driver @@ -1877,6 +1912,11 @@ static void ahci_remove_one(struct pci_dev *pdev) { pm_runtime_get_noresume(&pdev->dev); ata_pci_remove_one(pdev); +#ifdef CONFIG_X86_PS4 + if (pdev->vendor == PCI_VENDOR_ID_SONY) { + apcie_free_irqs(pdev->irq, 1); + } +#endif } module_pci_driver(ahci_pci_driver); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 02f38cc9f4682b..d446acb258b46c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -255,3 +255,1086 @@ int drm_legacy_irq_control(struct drm_device *dev, void *data, } } #endif +/** + * drm_calc_timestamping_constants - calculate vblank timestamp constants + * @crtc: drm_crtc whose timestamp constants should be updated. + * @mode: display mode containing the scanout timings + * + * Calculate and store various constants which are later + * needed by vblank and swap-completion timestamping, e.g, + * by drm_calc_vbltimestamp_from_scanoutpos(). They are + * derived from CRTC's true scanout timing, so they take + * things like panel scaling or other adjustments into account. + */ +void drm_calc_timestamping_constants(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int linedur_ns = 0, framedur_ns = 0; + int dotclock = mode->crtc_clock; + if (!dev->num_crtcs) + return; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + /* Valid dotclock? */ + if (dotclock > 0) { + int frame_size = mode->crtc_htotal * mode->crtc_vtotal; + /* + * Convert scanline length in pixels and video + * dot clock to line duration and frame duration + * in nanoseconds: + */ + linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); + framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); + /* + * Fields of interlaced scanout modes are only half a frame duration. + */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + framedur_ns /= 2; + } else + DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n", + crtc->base.id); + vblank->linedur_ns = linedur_ns; + vblank->framedur_ns = framedur_ns; + DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n", + crtc->base.id, mode->crtc_htotal, + mode->crtc_vtotal, mode->crtc_vdisplay); + DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n", + crtc->base.id, dotclock, framedur_ns, linedur_ns); +} +EXPORT_SYMBOL(drm_calc_timestamping_constants); +/** + * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper + * @dev: DRM device + * @pipe: index of CRTC whose vblank timestamp to retrieve + * @max_error: Desired maximum allowable error in timestamps (nanosecs) + * On return contains true maximum error of timestamp + * @vblank_time: Pointer to struct timeval which should receive the timestamp + * @flags: Flags to pass to driver: + * 0 = Default, + * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler + * @mode: mode which defines the scanout timings + * + * Implements calculation of exact vblank timestamps from given drm_display_mode + * timings and current video scanout position of a CRTC. This can be called from + * within get_vblank_timestamp() implementation of a kms driver to implement the + * actual timestamping. + * + * Should return timestamps conforming to the OML_sync_control OpenML + * extension specification. The timestamp corresponds to the end of + * the vblank interval, aka start of scanout of topmost-leftmost display + * pixel in the following video frame. + * + * Requires support for optional dev->driver->get_scanout_position() + * in kms driver, plus a bit of setup code to provide a drm_display_mode + * that corresponds to the true scanout timing. + * + * The current implementation only handles standard video modes. It + * returns as no operation if a doublescan or interlaced video mode is + * active. Higher level code is expected to handle this. + * + * Returns: + * Negative value on error, failure or if not supported in current + * video mode: + * + * -EINVAL Invalid CRTC. + * -EAGAIN Temporary unavailable, e.g., called before initial modeset. + * -ENOTSUPP Function not supported in current display mode. + * -EIO Failed, e.g., due to failed scanout position query. + * + * Returns or'ed positive status flags on success: + * + * DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping. + * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval. + * + */ +int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, + unsigned int pipe, + int *max_error, + struct timeval *vblank_time, + unsigned flags, + const struct drm_display_mode *mode) +{ + struct timeval tv_etime; + ktime_t stime, etime; + unsigned int vbl_status; + int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD; + int vpos, hpos, i; + int delta_ns, duration_ns; + if (pipe >= dev->num_crtcs) { + DRM_ERROR("Invalid crtc %u\n", pipe); + return -EINVAL; + } + /* Scanout position query not supported? Should not happen. */ + if (!dev->driver->get_scanout_position) { + DRM_ERROR("Called from driver w/o get_scanout_position()!?\n"); + return -EIO; + } + /* If mode timing undefined, just return as no-op: + * Happens during initial modesetting of a crtc. + */ + if (mode->crtc_clock == 0) { + DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); + //DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); + return -EAGAIN; + } + + /* Get current scanout position with system timestamp. + * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times + * if single query takes longer than max_error nanoseconds. + * + * This guarantees a tight bound on maximum error if + * code gets preempted or delayed for some reason. + */ + for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) { + /* + * Get vertical and horizontal scanout position vpos, hpos, + * and bounding timestamps stime, etime, pre/post query. + */ + vbl_status = dev->driver->get_scanout_position(dev, pipe, flags, + &vpos, &hpos, + &stime, &etime, + mode); + /* Return as no-op if scanout query unsupported or failed. */ + if (!(vbl_status & DRM_SCANOUTPOS_VALID)) { + DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n", + pipe, vbl_status); + return -EIO; + } + /* Compute uncertainty in timestamp of scanout position query. */ + duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); + /* Accept result with < max_error nsecs timing uncertainty. */ + if (duration_ns <= *max_error) + break; + } + /* Noisy system timing? */ + if (i == DRM_TIMESTAMP_MAXRETRIES) { + DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n", + pipe, duration_ns/1000, *max_error/1000, i); + } + /* Return upper bound of timestamp precision error. */ + *max_error = duration_ns; + /* Check if in vblank area: + * vpos is >=0 in video scanout area, but negative + * within vblank area, counting down the number of lines until + * start of scanout. + */ + if (vbl_status & DRM_SCANOUTPOS_IN_VBLANK) + ret |= DRM_VBLANKTIME_IN_VBLANK; + /* Convert scanout position into elapsed time at raw_time query + * since start of scanout at first display scanline. delta_ns + * can be negative if start of scanout hasn't happened yet. + */ + delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos), + mode->crtc_clock); + if (!drm_timestamp_monotonic) + etime = ktime_mono_to_real(etime); + /* save this only for debugging purposes */ + tv_etime = ktime_to_timeval(etime); + /* Subtract time delta from raw timestamp to get final + * vblank_time timestamp for end of vblank. + */ + etime = ktime_sub_ns(etime, delta_ns); + *vblank_time = ktime_to_timeval(etime); + DRM_DEBUG_VBL("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", + pipe, vbl_status, hpos, vpos, + (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, + (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, + duration_ns/1000, i); + return ret; +} +EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); +static struct timeval get_drm_timestamp(void) +{ + ktime_t now; + now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); + return ktime_to_timeval(now); +} +/** + * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent + * vblank interval + * @dev: DRM device + * @pipe: index of CRTC whose vblank timestamp to retrieve + * @tvblank: Pointer to target struct timeval which should receive the timestamp + * @flags: Flags to pass to driver: + * 0 = Default, + * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler + * + * Fetches the system timestamp corresponding to the time of the most recent + * vblank interval on specified CRTC. May call into kms-driver to + * compute the timestamp with a high-precision GPU specific method. + * + * Returns zero if timestamp originates from uncorrected do_gettimeofday() + * call, i.e., it isn't very precisely locked to the true vblank. + * + * Returns: + * True if timestamp is considered to be very precise, false otherwise. + */ +static bool +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, + struct timeval *tvblank, unsigned flags) +{ + int ret; + /* Define requested maximum error on timestamps (nanoseconds). */ + int max_error = (int) drm_timestamp_precision * 1000; + /* Query driver if possible and precision timestamping enabled. */ + if (dev->driver->get_vblank_timestamp && (max_error > 0)) { + ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error, + tvblank, flags); + if (ret > 0) + return true; + } + /* GPU high precision timestamp query unsupported or failed. + * Return current monotonic/gettimeofday timestamp as best estimate. + */ + *tvblank = get_drm_timestamp(); + return false; +} +/** + * drm_vblank_count - retrieve "cooked" vblank counter value + * @dev: DRM device + * @pipe: index of CRTC for which to retrieve the counter + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. + * + * This is the legacy version of drm_crtc_vblank_count(). + * + * Returns: + * The software vblank counter. + */ +u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + if (WARN_ON(pipe >= dev->num_crtcs)) + return 0; + return vblank->count; +} +EXPORT_SYMBOL(drm_vblank_count); +/** + * drm_crtc_vblank_count - retrieve "cooked" vblank counter value + * @crtc: which counter to retrieve + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. + * + * This is the native KMS version of drm_vblank_count(). + * + * Returns: + * The software vblank counter. + */ +u32 drm_crtc_vblank_count(struct drm_crtc *crtc) +{ + return drm_vblank_count(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_count); +/** + * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the + * system timestamp corresponding to that vblank counter value. + * @dev: DRM device + * @pipe: index of CRTC whose counter to retrieve + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. Returns corresponding system timestamp of the time + * of the vblank interval that corresponds to the current vblank counter value. + * + * This is the legacy version of drm_crtc_vblank_count_and_time(). + */ +static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, + struct timeval *vblanktime) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + u32 vblank_count; + unsigned int seq; + if (WARN_ON(pipe >= dev->num_crtcs)) + return 0; + do { + seq = read_seqbegin(&vblank->seqlock); + vblank_count = vblank->count; + *vblanktime = vblank->time; + } while (read_seqretry(&vblank->seqlock, seq)); + return vblank_count; +} +/** + * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value + * and the system timestamp corresponding to that vblank counter value + * @crtc: which counter to retrieve + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. Returns corresponding system timestamp of the time + * of the vblank interval that corresponds to the current vblank counter value. + */ +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, + struct timeval *vblanktime) +{ + return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), + vblanktime); +} +EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); + + +static void send_vblank_event(struct drm_device *dev, + struct drm_pending_vblank_event *e, + unsigned long seq, struct timeval *now) +{ + e->event.sequence = seq; + e->event.tv_sec = now->tv_sec; + e->event.tv_usec = now->tv_usec; + trace_drm_vblank_event_delivered(e->base.pid, e->pipe, + e->event.sequence); + drm_send_event_locked(dev, &e->base); +} +/** + * drm_crtc_arm_vblank_event - arm vblank event after pageflip + * @crtc: the source CRTC of the vblank event + * @e: the event to send + * + * A lot of drivers need to generate vblank events for the very next vblank + * interrupt. For example when the page flip interrupt happens when the page + * flip gets armed, but not when it actually executes within the next vblank + * period. This helper function implements exactly the required vblank arming + * behaviour. + * + * NOTE: Drivers using this to send out the event in struct &drm_crtc_state + * as part of an atomic commit must ensure that the next vblank happens at + * exactly the same time as the atomic commit is committed to the hardware. This + * function itself does **not** protect again the next vblank interrupt racing + * with either this function call or the atomic commit operation. A possible + * sequence could be: + * + * 1. Driver commits new hardware state into vblank-synchronized registers. + * 2. A vblank happens, committing the hardware state. Also the corresponding + * vblank interrupt is fired off and fully processed by the interrupt + * handler. + * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event(). + * 4. The event is only send out for the next vblank, which is wrong. + * + * An equivalent race can happen when the driver calls + * drm_crtc_arm_vblank_event() before writing out the new hardware state. + * + * The only way to make this work safely is to prevent the vblank from firing + * (and the hardware from committing anything else) until the entire atomic + * commit sequence has run to completion. If the hardware does not have such a + * feature (e.g. using a "go" bit), then it is unsafe to use this functions. + * Instead drivers need to manually send out the event from their interrupt + * handler by calling drm_crtc_send_vblank_event() and make sure that there's no + * possible race with the hardware committing the atomic update. + * + * Caller must hold event lock. Caller must also hold a vblank reference for + * the event @e, which will be dropped when the next vblank arrives. + */ +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + assert_spin_locked(&dev->event_lock); + e->pipe = pipe; + e->event.sequence = drm_vblank_count(dev, pipe); + list_add_tail(&e->base.link, &dev->vblank_event_list); +} +EXPORT_SYMBOL(drm_crtc_arm_vblank_event); +/** + * drm_crtc_send_vblank_event - helper to send vblank event after pageflip + * @crtc: the source CRTC of the vblank event + * @e: the event to send + * + * Updates sequence # and timestamp on event for the most recently processed + * vblank, and sends it to userspace. Caller must hold event lock. + * + * See drm_crtc_arm_vblank_event() for a helper which can be used in certain + * situation, especially to send out events for atomic commit operations. + */ +void drm_crtc_send_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e) +{ + struct drm_device *dev = crtc->dev; + unsigned int seq, pipe = drm_crtc_index(crtc); + struct timeval now; + if (dev->num_crtcs > 0) { + seq = drm_vblank_count_and_time(dev, pipe, &now); + } else { + seq = 0; + now = get_drm_timestamp(); + } + e->pipe = pipe; + send_vblank_event(dev, e, seq, &now); +} +EXPORT_SYMBOL(drm_crtc_send_vblank_event); +/** + * drm_vblank_enable - enable the vblank interrupt on a CRTC + * @dev: DRM device + * @pipe: CRTC index + * + * Returns: + * Zero on success or a negative error code on failure. + */ +static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int ret = 0; + assert_spin_locked(&dev->vbl_lock); + spin_lock(&dev->vblank_time_lock); + if (!vblank->enabled) { + /* + * Enable vblank irqs under vblank_time_lock protection. + * All vblank count & timestamp updates are held off + * until we are done reinitializing master counter and + * timestamps. Filtercode in drm_handle_vblank() will + * prevent double-accounting of same vblank interval. + */ + ret = dev->driver->enable_vblank(dev, pipe); + DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret); + if (ret) + atomic_dec(&vblank->refcount); + else { + vblank->enabled = true; + drm_update_vblank_count(dev, pipe, 0); + } + } + spin_unlock(&dev->vblank_time_lock); + return ret; +} +/** + * drm_vblank_get - get a reference count on vblank events + * @dev: DRM device + * @pipe: index of CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. + * + * This is the legacy version of drm_crtc_vblank_get(). + * + * Returns: + * Zero on success or a negative error code on failure. + */ +static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + int ret = 0; + if (!dev->num_crtcs) + return -EINVAL; + if (WARN_ON(pipe >= dev->num_crtcs)) + return -EINVAL; + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* Going from 0->1 means we have to enable interrupts again */ + if (atomic_add_return(1, &vblank->refcount) == 1) { + ret = drm_vblank_enable(dev, pipe); + } else { + if (!vblank->enabled) { + atomic_dec(&vblank->refcount); + ret = -EINVAL; + } + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + return ret; +} +/** + * drm_crtc_vblank_get - get a reference count on vblank events + * @crtc: which CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_crtc_vblank_get(struct drm_crtc *crtc) +{ + return drm_vblank_get(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_get); +/** + * drm_vblank_put - release ownership of vblank events + * @dev: DRM device + * @pipe: index of CRTC to release + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + * + * This is the legacy version of drm_crtc_vblank_put(). + */ +static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + if (WARN_ON(atomic_read(&vblank->refcount) == 0)) + return; + /* Last user schedules interrupt disable */ + if (atomic_dec_and_test(&vblank->refcount)) { + if (drm_vblank_offdelay == 0) + return; + else if (dev->vblank_disable_immediate || drm_vblank_offdelay < 0) + vblank_disable_fn((unsigned long)vblank); + else + mod_timer(&vblank->disable_timer, + jiffies + ((drm_vblank_offdelay * HZ)/1000)); + } +} +/** + * drm_crtc_vblank_put - give up ownership of vblank events + * @crtc: which counter to give up + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + */ +void drm_crtc_vblank_put(struct drm_crtc *crtc) +{ + drm_vblank_put(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_put); +/** + * drm_wait_one_vblank - wait for one vblank + * @dev: DRM device + * @pipe: CRTC index + * + * This waits for one vblank to pass on @pipe, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @pipe is disabled, e.g. + * due to lack of driver support or because the crtc is off. + */ +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int ret; + u32 last; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + ret = drm_vblank_get(dev, pipe); + if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret)) + return; + last = drm_vblank_count(dev, pipe); + ret = wait_event_timeout(vblank->queue, + last != drm_vblank_count(dev, pipe), + msecs_to_jiffies(100)); + WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe); + drm_vblank_put(dev, pipe); +} +EXPORT_SYMBOL(drm_wait_one_vblank); +/** + * drm_crtc_wait_one_vblank - wait for one vblank + * @crtc: DRM crtc + * + * This waits for one vblank to pass on @crtc, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. + * due to lack of driver support or because the crtc is off. + */ +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc) +{ + drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_wait_one_vblank); +/** + * drm_vblank_off - disable vblank events on a CRTC + * @dev: DRM device + * @pipe: CRTC index + * + * Drivers can use this function to shut down the vblank interrupt handling when + * disabling a crtc. This function ensures that the latest vblank frame count is + * stored so that drm_vblank_on() can restore it again. + * + * Drivers must use this function when the hardware vblank counter can get + * reset, e.g. when suspending. + * + * This is the legacy version of drm_crtc_vblank_off(). + */ +void drm_vblank_off(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned long irqflags; + unsigned int seq; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + spin_lock_irqsave(&dev->event_lock, irqflags); + spin_lock(&dev->vbl_lock); + DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", + pipe, vblank->enabled, vblank->inmodeset); + /* Avoid redundant vblank disables without previous drm_vblank_on(). */ + if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset) + vblank_disable_and_save(dev, pipe); + wake_up(&vblank->queue); + /* + * Prevent subsequent drm_vblank_get() from re-enabling + * the vblank interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock(&dev->vbl_lock); + /* Send any queued vblank events, lest the natives grow disquiet */ + seq = drm_vblank_count_and_time(dev, pipe, &now); + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != pipe) + continue; + DRM_DEBUG("Sending premature vblank event on disable: " + "wanted %u, current %u\n", + e->event.sequence, seq); + list_del(&e->base.link); + drm_vblank_put(dev, pipe); + send_vblank_event(dev, e, seq, &now); + } + spin_unlock_irqrestore(&dev->event_lock, irqflags); +} +EXPORT_SYMBOL(drm_vblank_off); +/** + * drm_crtc_vblank_off - disable vblank events on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to shut down the vblank interrupt handling when + * disabling a crtc. This function ensures that the latest vblank frame count is + * stored so that drm_vblank_on can restore it again. + * + * Drivers must use this function when the hardware vblank counter can get + * reset, e.g. when suspending. + * + * This is the native kms version of drm_vblank_off(). + */ +void drm_crtc_vblank_off(struct drm_crtc *crtc) +{ + drm_vblank_off(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_off); +/** + * drm_crtc_vblank_reset - reset vblank state to off on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to reset the vblank state to off at load time. + * Drivers should use this together with the drm_crtc_vblank_off() and + * drm_crtc_vblank_on() functions. The difference compared to + * drm_crtc_vblank_off() is that this function doesn't save the vblank counter + * and hence doesn't need to call any driver hooks. + */ +void drm_crtc_vblank_reset(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned long irqflags; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* + * Prevent subsequent drm_vblank_get() from enabling the vblank + * interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + WARN_ON(!list_empty(&dev->vblank_event_list)); +} +EXPORT_SYMBOL(drm_crtc_vblank_reset); +/** + * drm_vblank_on - enable vblank events on a CRTC + * @dev: DRM device + * @pipe: CRTC index + * + * This functions restores the vblank interrupt state captured with + * drm_vblank_off() again. Note that calls to drm_vblank_on() and + * drm_vblank_off() can be unbalanced and so can also be unconditionally called + * in driver load code to reflect the current hardware state of the crtc. + * + * This is the legacy version of drm_crtc_vblank_on(). + */ +void drm_vblank_on(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + spin_lock_irqsave(&dev->vbl_lock, irqflags); + DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", + pipe, vblank->enabled, vblank->inmodeset); + /* Drop our private "prevent drm_vblank_get" refcount */ + if (vblank->inmodeset) { + atomic_dec(&vblank->refcount); + vblank->inmodeset = 0; + } + drm_reset_vblank_timestamp(dev, pipe); + /* + * re-enable interrupts if there are users left, or the + * user wishes vblank interrupts to be enabled all the time. + */ + if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) + WARN_ON(drm_vblank_enable(dev, pipe)); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); +} +EXPORT_SYMBOL(drm_vblank_on); +/** + * drm_crtc_vblank_on - enable vblank events on a CRTC + * @crtc: CRTC in question + * + * This functions restores the vblank interrupt state captured with + * drm_vblank_off() again. Note that calls to drm_vblank_on() and + * drm_vblank_off() can be unbalanced and so can also be unconditionally called + * in driver load code to reflect the current hardware state of the crtc. + * + * This is the native kms version of drm_vblank_on(). + */ +void drm_crtc_vblank_on(struct drm_crtc *crtc) +{ + drm_vblank_on(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_on); +/** + * drm_vblank_pre_modeset - account for vblanks across mode sets + * @dev: DRM device + * @pipe: CRTC index + * + * Account for vblank events across mode setting events, which will likely + * reset the hardware frame counter. + * + * This is done by grabbing a temporary vblank reference to ensure that the + * vblank interrupt keeps running across the modeset sequence. With this the + * software-side vblank frame counting will ensure that there are no jumps or + * discontinuities. + * + * Unfortunately this approach is racy and also doesn't work when the vblank + * interrupt stops running, e.g. across system suspend resume. It is therefore + * highly recommended that drivers use the newer drm_vblank_off() and + * drm_vblank_on() instead. drm_vblank_pre_modeset() only works correctly when + * using "cooked" software vblank frame counters and not relying on any hardware + * counters. + * + * Drivers must call drm_vblank_post_modeset() when re-enabling the same crtc + * again. + */ +void drm_vblank_pre_modeset(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + /* vblank is not initialized (IRQ not installed ?), or has been freed */ + if (!dev->num_crtcs) + return; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + /* + * To avoid all the problems that might happen if interrupts + * were enabled/disabled around or between these calls, we just + * have the kernel take a reference on the CRTC (just once though + * to avoid corrupting the count if multiple, mismatch calls occur), + * so that interrupts remain enabled in the interim. + */ + if (!vblank->inmodeset) { + vblank->inmodeset = 0x1; + if (drm_vblank_get(dev, pipe) == 0) + vblank->inmodeset |= 0x2; + } +} +EXPORT_SYMBOL(drm_vblank_pre_modeset); +/** + * drm_vblank_post_modeset - undo drm_vblank_pre_modeset changes + * @dev: DRM device + * @pipe: CRTC index + * + * This function again drops the temporary vblank reference acquired in + * drm_vblank_pre_modeset. + */ +void drm_vblank_post_modeset(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + /* vblank is not initialized (IRQ not installed ?), or has been freed */ + if (!dev->num_crtcs) + return; + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + if (vblank->inmodeset) { + spin_lock_irqsave(&dev->vbl_lock, irqflags); + drm_reset_vblank_timestamp(dev, pipe); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + if (vblank->inmodeset & 0x2) + drm_vblank_put(dev, pipe); + vblank->inmodeset = 0; + } +} +EXPORT_SYMBOL(drm_vblank_post_modeset); +/* + * drm_modeset_ctl - handle vblank event counter changes across mode switch + * @DRM_IOCTL_ARGS: standard ioctl arguments + * + * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET + * ioctls around modesetting so that any lost vblank events are accounted for. + * + * Generally the counter will reset across mode sets. If interrupts are + * enabled around this call, we don't have to do anything since the counter + * will have already been incremented. + */ +int drm_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_modeset_ctl *modeset = data; + unsigned int pipe; + /* If drm_vblank_init() hasn't been called yet, just no-op */ + if (!dev->num_crtcs) + return 0; + /* KMS drivers handle this internally */ + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) + return 0; + pipe = modeset->crtc; + if (pipe >= dev->num_crtcs) + return -EINVAL; + switch (modeset->cmd) { + case _DRM_PRE_MODESET: + drm_vblank_pre_modeset(dev, pipe); + break; + case _DRM_POST_MODESET: + drm_vblank_post_modeset(dev, pipe); + break; + default: + return -EINVAL; + } + return 0; +} +static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, + union drm_wait_vblank *vblwait, + struct drm_file *file_priv) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct drm_pending_vblank_event *e; + struct timeval now; + unsigned long flags; + unsigned int seq; + int ret; + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (e == NULL) { + ret = -ENOMEM; + goto err_put; + } + e->pipe = pipe; + e->base.pid = current->pid; + e->event.base.type = DRM_EVENT_VBLANK; + e->event.base.length = sizeof(e->event); + e->event.user_data = vblwait->request.signal; + spin_lock_irqsave(&dev->event_lock, flags); + /* + * drm_vblank_off() might have been called after we called + * drm_vblank_get(). drm_vblank_off() holds event_lock + * around the vblank disable, so no need for further locking. + * The reference from drm_vblank_get() protects against + * vblank disable from another source. + */ + if (!vblank->enabled) { + ret = -EINVAL; + goto err_unlock; + } + ret = drm_event_reserve_init_locked(dev, file_priv, &e->base, + &e->event.base); + if (ret) + goto err_unlock; + seq = drm_vblank_count_and_time(dev, pipe, &now); + DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", + vblwait->request.sequence, seq, pipe); + trace_drm_vblank_event_queued(current->pid, pipe, + vblwait->request.sequence); + e->event.sequence = vblwait->request.sequence; + if ((seq - vblwait->request.sequence) <= (1 << 23)) { + drm_vblank_put(dev, pipe); + send_vblank_event(dev, e, seq, &now); + vblwait->reply.sequence = seq; + } else { + /* drm_handle_vblank_events will call drm_vblank_put */ + list_add_tail(&e->base.link, &dev->vblank_event_list); + vblwait->reply.sequence = vblwait->request.sequence; + } + spin_unlock_irqrestore(&dev->event_lock, flags); + return 0; +err_unlock: + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); +err_put: + drm_vblank_put(dev, pipe); + return ret; +} +/* + * Wait for VBLANK. + * + * \param inode device inode. + * \param file_priv DRM file private. + * \param cmd command. + * \param data user argument, pointing to a drm_wait_vblank structure. + * \return zero on success or a negative number on failure. + * + * This function enables the vblank interrupt on the pipe requested, then + * sleeps waiting for the requested sequence number to occur, and drops + * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that + * after a timeout with no further vblank waits scheduled). + */ +int drm_wait_vblank(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vblank_crtc *vblank; + union drm_wait_vblank *vblwait = data; + int ret; + unsigned int flags, seq, pipe, high_pipe; + if (!dev->irq_enabled) + return -EINVAL; + if (vblwait->request.type & _DRM_VBLANK_SIGNAL) + return -EINVAL; + if (vblwait->request.type & + ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | + _DRM_VBLANK_HIGH_CRTC_MASK)) { + DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", + vblwait->request.type, + (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | + _DRM_VBLANK_HIGH_CRTC_MASK)); + return -EINVAL; + } + flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; + high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK); + if (high_pipe) + pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT; + else + pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; + if (pipe >= dev->num_crtcs) + return -EINVAL; + vblank = &dev->vblank[pipe]; + ret = drm_vblank_get(dev, pipe); + if (ret) { + DRM_DEBUG("failed to acquire vblank counter, %d\n", ret); + return ret; + } + seq = drm_vblank_count(dev, pipe); + switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { + case _DRM_VBLANK_RELATIVE: + vblwait->request.sequence += seq; + vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; + case _DRM_VBLANK_ABSOLUTE: + break; + default: + ret = -EINVAL; + goto done; + } + if ((flags & _DRM_VBLANK_NEXTONMISS) && + (seq - vblwait->request.sequence) <= (1 << 23)) { + vblwait->request.sequence = seq + 1; + } + if (flags & _DRM_VBLANK_EVENT) { + /* must hold on to the vblank ref until the event fires + * drm_vblank_put will be called asynchronously + */ + return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); + } + DRM_DEBUG("waiting on vblank count %u, crtc %u\n", + vblwait->request.sequence, pipe); + DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, + (((drm_vblank_count(dev, pipe) - + vblwait->request.sequence) <= (1 << 23)) || + !vblank->enabled || + !dev->irq_enabled)); + if (ret != -EINTR) { + struct timeval now; + vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now); + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + DRM_DEBUG("returning %u to client\n", + vblwait->reply.sequence); + } else { + DRM_DEBUG("vblank wait interrupted by signal\n"); + } +done: + drm_vblank_put(dev, pipe); + return ret; +} +static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) +{ + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned int seq; + assert_spin_locked(&dev->event_lock); + seq = drm_vblank_count_and_time(dev, pipe, &now); + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != pipe) + continue; + if ((seq - e->event.sequence) > (1<<23)) + continue; + DRM_DEBUG("vblank event on %u, current %u\n", + e->event.sequence, seq); + list_del(&e->base.link); + drm_vblank_put(dev, pipe); + send_vblank_event(dev, e, seq, &now); + } + trace_drm_vblank_event(pipe, seq); +} +/** + * drm_handle_vblank - handle a vblank event + * @dev: DRM device + * @pipe: index of CRTC where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + * + * This is the legacy version of drm_crtc_handle_vblank(). + */ +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + if (WARN_ON_ONCE(!dev->num_crtcs)) + return false; + if (WARN_ON(pipe >= dev->num_crtcs)) + return false; + spin_lock_irqsave(&dev->event_lock, irqflags); + /* Need timestamp lock to prevent concurrent execution with + * vblank enable/disable, as this would cause inconsistent + * or corrupted timestamps and vblank counts. + */ + spin_lock(&dev->vblank_time_lock); + /* Vblank irq handling disabled. Nothing to do. */ + if (!vblank->enabled) { + spin_unlock(&dev->vblank_time_lock); + spin_unlock_irqrestore(&dev->event_lock, irqflags); + return false; + } + drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ); + spin_unlock(&dev->vblank_time_lock); + wake_up(&vblank->queue); + drm_handle_vblank_events(dev, pipe); + spin_unlock_irqrestore(&dev->event_lock, irqflags); + return true; +} +EXPORT_SYMBOL(drm_handle_vblank); +/** + * drm_crtc_handle_vblank - handle a vblank event + * @crtc: where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + * + * This is the native KMS version of drm_handle_vblank(). + * + * Returns: + * True if the event was successfully handled, false on failure. + */ +bool drm_crtc_handle_vblank(struct drm_crtc *crtc) +{ + return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_handle_vblank); +/** + * drm_vblank_no_hw_counter - "No hw counter" implementation of .get_vblank_counter() + * @dev: DRM device + * @pipe: CRTC for which to read the counter + * + * Drivers can plug this into the .get_vblank_counter() function if + * there is no useable hardware frame counter available. + * + * Returns: + * 0 + */ +u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) +{ + WARN_ON_ONCE(dev->max_vblank_count != 0); + return 0; +} +EXPORT_SYMBOL(drm_vblank_no_hw_counter); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 693748ad8b881a..ec2c24596f463d 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -188,6 +188,8 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, if (WARN_ON(!dev->pdev)) return -EINVAL; + root = dev->pdev->bus->self; + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EOPNOTSUPP; @@ -324,9 +326,60 @@ int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) } return 0; } -EXPORT_SYMBOL(drm_legacy_pci_init); +int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) +{ + struct pci_dev *root; + u32 lnkcap, lnkcap2; + + *mask = 0; + if (!dev->pdev) + if (!dev->pdev || pci_is_root_bus(dev->pdev->bus)) + return -EINVAL; + + root = dev->pdev->bus->self; + /* we've been informed via and serverworks don't make the cut */ + if (root->vendor == PCI_VENDOR_ID_VIA || + root->vendor == PCI_VENDOR_ID_SERVERWORKS) + return -EINVAL; + pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); + pcie_capability_read_dword(root, PCI_EXP_LNKCAP2, &lnkcap2); + if (lnkcap2) { /* PCIe r3.0-compliant */ + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) + *mask |= DRM_PCIE_SPEED_25; + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) + *mask |= DRM_PCIE_SPEED_50; + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) + *mask |= DRM_PCIE_SPEED_80; + } else { /* pre-r3.0 */ + if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) + *mask |= DRM_PCIE_SPEED_25; + if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) + *mask |= (DRM_PCIE_SPEED_25 | DRM_PCIE_SPEED_50); + } + DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", root->vendor, root->device, lnkcap, lnkcap2); + return 0; +} +EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); +int drm_pcie_get_max_link_width(struct drm_device *dev, u32 *mlw) +{ + struct pci_dev *root; + u32 lnkcap; + *mlw = 0; + if (!dev->pdev) + return -EINVAL; + root = dev->pdev->bus->self; + pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); + *mlw = (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4; + DRM_INFO("probing mlw for device %x:%x = %x\n", root->vendor, root->device, lnkcap); + return 0; +} +EXPORT_SYMBOL(drm_pcie_get_max_link_width); #else +int drm_legacy_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) +{ + return -1; +} void drm_pci_agp_destroy(struct drm_device *dev) {} @@ -337,6 +390,8 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, } #endif +EXPORT_SYMBOL(drm_legacy_pci_init); + /** * drm_legacy_pci_exit - unregister shadow-attach legacy DRM driver * @driver: DRM device driver diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 92ccd7aed0d44e..8c312aeb44f557 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -82,6 +82,8 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ radeon-$(CONFIG_MMU_NOTIFIER) += radeon_mn.o +radeon-$(CONFIG_X86_PS4) += ps4_bridge.o + # add async DMA block radeon-y += \ r600_dma.o \ diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 3e798593e04252..c47a413b7c0dd3 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -325,7 +325,7 @@ static int radeon_dp_get_dp_link_config(struct drm_connector *connector, } } else { for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) { - for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) { + for (lane_num = 4; lane_num <= max_lane_num; lane_num <<= 1) { max_pix_clock = (lane_num * link_rates[i] * 8) / bpp; if (max_pix_clock >= pix_clock) { *dp_lanes = lane_num; diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index e67ed383e11b12..4f56eb62a49888 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -689,7 +689,8 @@ atombios_get_encoder_mode(struct drm_encoder *encoder) if (radeon_encoder->is_mst_encoder || radeon_encoder->offset) return ATOM_ENCODER_MODE_DP_MST; /* dp bridges are always DP */ - if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE) + if if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE || + rdev->family == CHIP_LIVERPOOL) return ATOM_ENCODER_MODE_DP; /* DVO is always DVO */ diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index ab7b4e2ffcd21c..aaf29929ea7b20 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -120,6 +120,13 @@ MODULE_FIRMWARE("radeon/mullins_mec.bin"); MODULE_FIRMWARE("radeon/mullins_rlc.bin"); MODULE_FIRMWARE("radeon/mullins_sdma.bin"); +MODULE_FIRMWARE("radeon/LIVERPOOL_pfp.bin"); +MODULE_FIRMWARE("radeon/LIVERPOOL_me.bin"); +MODULE_FIRMWARE("radeon/LIVERPOOL_ce.bin"); +MODULE_FIRMWARE("radeon/LIVERPOOL_mec.bin"); +MODULE_FIRMWARE("radeon/LIVERPOOL_rlc.bin"); +MODULE_FIRMWARE("radeon/LIVERPOOL_sdma.bin"); + extern int r600_ih_ring_alloc(struct radeon_device *rdev); extern void r600_ih_ring_fini(struct radeon_device *rdev); extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); @@ -1038,6 +1045,422 @@ static const u32 kalindi_rlc_save_restore_register_list[] = (0x0e00 << 16) | (0x8c30 >> 2), (0x0e00 << 16) | (0x8c34 >> 2), (0x0e00 << 16) | (0x9600 >> 2), + +}; + +static const u32 liverpool_rlc_save_restore_register_list[] = +{ + (0x0600 << 16) | 0x263d, + 0x00000000, + (0x0680 << 16) | 0x263d, + 0x00000000, + (0x0e00 << 16) | 0x263e, + 0x00000000, + (0x0e00 << 16) | 0x2640, + 0x00000000, + (0x0e00 << 16) | 0x3098, + 0x00000000, + (0x0e00 << 16) | 0x243a, + 0x00000000, + (0x0e00 << 16) | 0xf000, + 0x00000000, + (0x0e00 << 16) | 0xf003, + 0x00000000, + (0x0e00 << 16) | 0x2307, + 0x00000000, + (0x0e00 << 16) | 0x25c0, + 0x00000000, + (0x0e00 << 16) | 0x3348, + 0x00000000, + (0x1e00 << 16) | 0x3348, + 0x00000000, + (0x4e00 << 16) | 0x3348, + 0x00000000, + (0x5e00 << 16) | 0x3348, + 0x00000000, + (0x6e00 << 16) | 0x3348, + 0x00000000, + (0x7e00 << 16) | 0x3348, + 0x00000000, + (0x8e00 << 16) | 0x3348, + 0x00000000, + (0x9e00 << 16) | 0x3348, + 0x00000000, + (0xae00 << 16) | 0x3348, + 0x00000000, + (0xbe00 << 16) | 0x3348, + 0x00000000, + (0x0400 << 16) | 0x226f, + 0x00000000, + (0x0480 << 16) | 0x226f, + 0x00000000, + (0x0e00 << 16) | 0x2240, + 0x00000000, + 0x3, + (0x0e00 << 16) | 0x260d, + 0x00000000, + (0x0000 << 16) | 0xc3c0, + 0x00000000, + (0x0001 << 16) | 0xc3c0, + 0x00000000, + (0x0002 << 16) | 0xc3c0, + 0x00000000, + (0x0003 << 16) | 0xc3c0, + 0x00000000, + (0x0080 << 16) | 0xc3c0, + 0x00000000, + (0x0081 << 16) | 0xc3c0, + 0x00000000, + (0x0082 << 16) | 0xc3c0, + 0x00000000, + (0x0083 << 16) | 0xc3c0, + 0x00000000, + (0x0000 << 16) | 0xc3c1, + 0x00000000, + (0x0001 << 16) | 0xc3c1, + 0x00000000, + (0x0002 << 16) | 0xc3c1, + 0x00000000, + (0x0003 << 16) | 0xc3c1, + 0x00000000, + (0x0080 << 16) | 0xc3c1, + 0x00000000, + (0x0081 << 16) | 0xc3c1, + 0x00000000, + (0x0082 << 16) | 0xc3c1, + 0x00000000, + (0x0083 << 16) | 0xc3c1, + 0x00000000, + (0x0000 << 16) | 0xc3c2, + 0x00000000, + (0x0001 << 16) | 0xc3c2, + 0x00000000, + (0x0002 << 16) | 0xc3c2, + 0x00000000, + (0x0003 << 16) | 0xc3c2, + 0x00000000, + (0x0080 << 16) | 0xc3c2, + 0x00000000, + (0x0081 << 16) | 0xc3c2, + 0x00000000, + (0x0082 << 16) | 0xc3c2, + 0x00000000, + (0x0083 << 16) | 0xc3c2, + 0x00000000, + (0x0000 << 16) | 0xc3c3, + 0x00000000, + (0x0001 << 16) | 0xc3c3, + 0x00000000, + (0x0002 << 16) | 0xc3c3, + 0x00000000, + (0x0003 << 16) | 0xc3c3, + 0x00000000, + (0x0080 << 16) | 0xc3c3, + 0x00000000, + (0x0081 << 16) | 0xc3c3, + 0x00000000, + (0x0082 << 16) | 0xc3c3, + 0x00000000, + (0x0083 << 16) | 0xc3c3, + 0x00000000, + (0x0600 << 16) | 0x26df, + 0x00000000, + (0x0680 << 16) | 0x26df, + 0x00000000, + (0x0e00 << 16) | 0x2285, + 0x00000000, + (0x0e00 << 16) | 0x2286, + 0x00000000, + (0x0600 << 16) | 0xc280, + 0x00000000, + (0x0680 << 16) | 0xc280, + 0x00000000, + (0x1600 << 16) | 0xc280, + 0x00000000, + (0x1680 << 16) | 0xc280, + 0x00000000, + (0x0e00 << 16) | 0x22fc, + 0x00000000, + (0x0e00 << 16) | 0x22f3, + 0x00000000, + (0x0e00 << 16) | 0x22c9, + 0x00000000, + (0x0e00 << 16) | 0xc281, + 0x00000000, + (0x1e00 << 16) | 0xc281, + 0x00000000, + (0x0600 << 16) | 0xc284, + 0x00000000, + (0x0680 << 16) | 0xc284, + 0x00000000, + (0x1600 << 16) | 0xc284, + 0x00000000, + (0x1680 << 16) | 0xc284, + 0x00000000, + (0x0600 << 16) | 0xc285, + 0x00000000, + (0x0680 << 16) | 0xc285, + 0x00000000, + (0x1600 << 16) | 0xc285, + 0x00000000, + (0x1680 << 16) | 0xc285, + 0x00000000, + (0x0600 << 16) | 0xc286, + 0x00000000, + (0x0680 << 16) | 0xc286, + 0x00000000, + (0x1600 << 16) | 0xc286, + 0x00000000, + (0x1680 << 16) | 0xc286, + 0x00000000, + (0x0600 << 16) | 0xc28b, + 0x00000000, + (0x0680 << 16) | 0xc28b, + 0x00000000, + (0x1600 << 16) | 0xc28b, + 0x00000000, + (0x1680 << 16) | 0xc28b, + 0x00000000, + (0x0e00 << 16) | 0x31c0, + 0x00000000, + (0x0e00 << 16) | 0x31c1, + 0x00000000, + (0x0e00 << 16) | 0x31c2, + 0x00000000, + (0x0e00 << 16) | 0x31da, + 0x00000000, + (0x0400 << 16) | 0x31dc, + 0x00000000, + (0x0480 << 16) | 0x31dc, + 0x00000000, + (0x0400 << 16) | 0x31dd, + 0x00000000, + (0x0480 << 16) | 0x31dd, + 0x00000000, + (0x0400 << 16) | 0x31de, + 0x00000000, + (0x0480 << 16) | 0x31de, + 0x00000000, + (0x0400 << 16) | 0x31df, + 0x00000000, + (0x0480 << 16) | 0x31df, + 0x00000000, + (0x0400 << 16) | 0x31e0, + 0x00000000, + (0x0480 << 16) | 0x31e0, + 0x00000000, + (0x0400 << 16) | 0x31e1, + 0x00000000, + (0x0480 << 16) | 0x31e1, + 0x00000000, + (0x0400 << 16) | 0x31e2, + 0x00000000, + (0x0480 << 16) | 0x31e2, + 0x00000000, + (0x0400 << 16) | 0x31e3, + 0x00000000, + (0x0480 << 16) | 0x31e3, + 0x00000000, + (0x0400 << 16) | 0x31e4, + 0x00000000, + (0x0480 << 16) | 0x31e4, + 0x00000000, + (0x0400 << 16) | 0x31e5, + 0x00000000, + (0x0480 << 16) | 0x31e5, + 0x00000000, + (0x0400 << 16) | 0x31e6, + 0x00000000, + (0x0480 << 16) | 0x31e6, + 0x00000000, + (0x0400 << 16) | 0x31e7, + 0x00000000, + (0x0480 << 16) | 0x31e7, + 0x00000000, + (0x0400 << 16) | 0x31e8, + 0x00000000, + (0x0480 << 16) | 0x31e8, + 0x00000000, + (0x0400 << 16) | 0x31e9, + 0x00000000, + (0x0480 << 16) | 0x31e9, + 0x00000000, + (0x0400 << 16) | 0x31ea, + 0x00000000, + (0x0480 << 16) | 0x31ea, + 0x00000000, + (0x0400 << 16) | 0x31eb, + 0x00000000, + (0x0480 << 16) | 0x31eb, + 0x00000000, + (0x0400 << 16) | 0x31ec, + 0x00000000, + (0x0480 << 16) | 0x31ec, + 0x00000000, + (0x0400 << 16) | 0x31ed, + 0x00000000, + (0x0480 << 16) | 0x31ed, + 0x00000000, + (0x0400 << 16) | 0x31ee, + 0x00000000, + (0x0480 << 16) | 0x31ee, + 0x00000000, + (0x0400 << 16) | 0x31ef, + 0x00000000, + (0x0480 << 16) | 0x31ef, + 0x00000000, + (0x0e00 << 16) | 0x2440, + 0x00000000, + (0x0e00 << 16) | 0xf004, + 0x00000000, + (0x0e00 << 16) | 0x24aa, + 0x00000000, + (0x0e00 << 16) | 0x24ab, + 0x00000000, + (0x0e00 << 16) | 0x24ac, + 0x00000000, + (0x0e00 << 16) | 0x24ad, + 0x00000000, + (0x0e00 << 16) | 0x24ae, + 0x00000000, + (0x0e00 << 16) | 0x24af, + 0x00000000, + (0x0e00 << 16) | 0x24b0, + 0x00000000, + (0x0e00 << 16) | 0x24b1, + 0x00000000, + (0x0e00 << 16) | 0x24b2, + 0x00000000, + (0x0e00 << 16) | 0x24b3, + 0x00000000, + (0x0e00 << 16) | 0x24b4, + 0x00000000, + (0x0e00 << 16) | 0x2300, + 0x00000000, + (0x0e00 << 16) | 0x2301, + 0x00000000, + (0x0e00 << 16) | 0x2308, + 0x00000000, + (0x0e00 << 16) | 0x230e, + 0x00000000, + (0x0e00 << 16) | 0x230f, + 0x00000000, + (0x0e00 << 16) | 0x2b80, + 0x00000000, + (0x0e00 << 16) | 0x2581, + 0x00000000, + (0x0e00 << 16) | 0x2b02, + 0x00000000, + (0x0e00 << 16) | 0x2b03, + 0x00000000, + (0x0e00 << 16) | 0x2b04, + 0x00000000, + (0x0e00 << 16) | 0x2b05, + 0x00000000, + (0x0e00 << 16) | 0x2b16, + 0x00000000, + (0x0e00 << 16) | 0x2b1a, + 0x00000000, + (0x0e00 << 16) | 0x2b1b, + 0x00000000, + (0x0e00 << 16) | 0x2b1c, + 0x00000000, + (0x0e00 << 16) | 0x2b1d, + 0x00000000, + (0x0e00 << 16) | 0x2b1e, + 0x00000000, + (0x0e00 << 16) | 0x2b1f, + 0x00000000, + (0x0e00 << 16) | 0x2b20, + 0x00000000, + (0x0e00 << 16) | 0x2b21, + 0x00000000, + (0x0e00 << 16) | 0x2b22, + 0x00000000, + (0x0e00 << 16) | 0x2b23, + 0x00000000, + (0x0e00 << 16) | 0x25c3, + 0x00000000, + (0x0e00 << 16) | 0x25c5, + 0x00000000, + (0x0e00 << 16) | 0x25c6, + 0x00000000, + (0x0e00 << 16) | 0x25c7, + 0x00000000, + (0x0e00 << 16) | 0xc41a, + 0x00000000, + (0x1e00 << 16) | 0xc41a, + 0x00000000, + (0x4e00 << 16) | 0xc41a, + 0x00000000, + (0x5e00 << 16) | 0xc41a, + 0x00000000, + (0x6e00 << 16) | 0xc41a, + 0x00000000, + (0x7e00 << 16) | 0xc41a, + 0x00000000, + (0x8e00 << 16) | 0xc41a, + 0x00000000, + (0x9e00 << 16) | 0xc41a, + 0x00000000, + (0xae00 << 16) | 0xc41a, + 0x00000000, + (0xbe00 << 16) | 0xc41a, + 0x00000000, + (0x0e00 << 16) | 0x3344, + 0x00000000, + (0x0e00 << 16) | 0x3345, + 0x00000000, + (0x0e00 << 16) | 0x222c, + 0x00000000, + (0x0e00 << 16) | 0x222d, + 0x00000000, + (0x0e00 << 16) | 0x222e, + 0x00000000, + (0x0e00 << 16) | 0x222f, + 0x00000000, + (0x0400 << 16) | 0x2270, + 0x00000000, + (0x0480 << 16) | 0x2270, + 0x00000000, + (0x0e00 << 16) | 0x2231, + 0x00000000, + (0x0e00 << 16) | 0x2274, + 0x00000000, + (0x0e00 << 16) | 0x2234, + 0x00000000, + (0x0e00 << 16) | 0x2235, + 0x00000000, + (0x0e00 << 16) | 0x2236, + 0x00000000, + (0x0e00 << 16) | 0x2260, + 0x00000000, + (0x0e00 << 16) | 0x2262, + 0x00000000, + (0x0e00 << 16) | 0x226c, + 0x00000000, + (0x0e00 << 16) | 0x226e, + 0x00000000, + (0x0e00 << 16) | 0x2268, + 0x00000000, + (0x0e00 << 16) | 0x2232, + 0x00000000, + (0x0e00 << 16) | 0x2233, + 0x00000000, + (0x0e00 << 16) | 0x226d, + 0x00000000, + (0x0e00 << 16) | 0xf87f, + 0x00000000, + (0x0e00 << 16) | 0xf084, + 0x00000000, + (0x0e00 << 16) | 0xf085, + 0x00000000, + (0x0e00 << 16) | 0xf086, + 0x00000000, + (0x0e00 << 16) | 0x2241, + 0x00000000, + 1, + (0x0e00 << 16) | 0x230d, }; static const u32 bonaire_golden_spm_registers[] = @@ -1184,6 +1607,155 @@ static const u32 bonaire_mgcg_cgcg_init[] = 0xd80c, 0xff000ff0, 0x00000100 }; +static const u32 liverpool_golden_common_registers[] = +{ + 0xc770, 0xffffffff, 0x00000800, /* SPI_RESOURCE_RESERVE_CU_0 */ + 0xc774, 0xffffffff, 0x00000800, /* SPI_RESOURCE_RESERVE_CU_1 */ + 0xc798, 0xffffffff, 0x00ffffbf, /* SPI_RESOURCE_RESERVE_EN_CU_0 */ + 0xc79c, 0xffffffff, 0x00ffffaf, /* SPI_RESOURCE_RESERVE_EN_CU_1 */ + 0xc7a0, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_2 */ + 0xc7a4, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_3*/ + 0xc7a8, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_4 */ + 0xc7ac, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_5 */ + 0xc7b0, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_6 */ + 0xc7b4, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_7 */ + 0xc7b8, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_8 */ + 0xc7bc, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_9 */ + 0x28350, 0xffffffff, 0x2a00161a, /* PA_SC_RASTER_CONFIG */ + 0x28354, 0xffffffff, 0x00000000, /* PA_SC_RASTER_CONFIG_1 */ + 0x5004, 0x00002000, 0x00002000, /* GARLIC_FLUSH_CNTL */ +}; + +static const u32 liverpool_golden_registers[] = +{ + 0xc420, 0xffffffff, 0xfffffffc, /* RLC_CGTT_MGCG_OVERRIDE */ + 0x30800, 0xffffffff, 0xe0000000, /* GRBM_GFX_INDEX */ + /* These are all setting OFF_HYSTERESIS = 0x10 */ + 0x3c2a0, 0xffffffff, 0x00000100, /* CB_CGTT_SCLK_CTRL */ + 0x3c208, 0xffffffff, 0x00000100, /* CGTT_BCI_CLK_CTRL */ + 0x3c2c0, 0xffffffff, 0x00000100, /* CGTT_CP_CLK_CTRL */ + 0x3c2c8, 0xffffffff, 0x00000100, /* CGTT_CPC_CLK_CTRL */ + 0x3c2c4, 0xffffffff, 0x00000100, /* CGTT_CPF_CLK_CTRL */ + 0x55e4, 0xffffffff, 0x00600100, /* CGTT_DRM_CLK_CTRL0 */ + 0x3c280, 0xffffffff, 0x00000100, /* CGTT_GDS_CLK_CTRL */ + 0x3c214, 0xffffffff, 0x06000100, /* CGTT_IA_CLK_CTRL */ + 0x3c220, 0xffffffff, 0x00000100, /* CGTT_PA_CLK_CTRL */ + 0x3c218, 0xffffffff, 0x06000100, /* CGTT_WD_CLK_CTRL */ + 0x3c204, 0xffffffff, 0x00000100, /* CGTT_PC_CLK_CTRL */ + 0x3c2e0, 0xffffffff, 0x00000100, /* CGTT_RLC_CLK_CTRL */ + 0x3c224, 0xffffffff, 0x00000100, /* CGTT_SC_CLK_CTRL */ + 0x3c200, 0xffffffff, 0x00000100, /* CGTT_SPI_CLK_CTRL */ + 0x3c230, 0xffffffff, 0x00000100, /* CGTT_SQ_CLK_CTRL */ + 0x3c234, 0xffffffff, 0x00000100, /* CGTT_SQG_CLK_CTRL */ + 0x3c250, 0xffffffff, 0x00000100, /* CGTT_SX_CLK_CTRL0 */ + 0x3c254, 0xffffffff, 0x00000100, /* CGTT_SX_CLK_CTRL1 */ + 0x3c258, 0xffffffff, 0x00000100, /* CGTT_SX_CLK_CTRL2 */ + 0x3c25c, 0xffffffff, 0x00000100, /* CGTT_SX_CLK_CTRL3 */ + 0x3c260, 0xffffffff, 0x00000100, /* CGTT_SX_CLK_CTRL4 */ + 0x3c27c, 0xffffffff, 0x00000100, /* CGTT_TCI_CLK_CTRL */ + 0x3c278, 0xffffffff, 0x00000100, /* CGTT_TCP_CLK_CTRL */ + 0x3c210, 0xffffffff, 0x06000100, /* CGTT_VGT_CLK_CTRL */ + 0x3c290, 0xffffffff, 0x00000100, /* DB_CGTT_CLK_CTRL_0 */ + 0x3c274, 0xffffffff, 0x00000100, /* TA_CGTT_CTRL */ + 0x3c2b4, 0xffffffff, 0x00000100, /* TCA_CGTT_SCLK_CTRL */ + 0x3c2b0, 0xffffffff, 0x00000100, /* TCC_CGTT_SCLK_CTRL */ + 0x3c270, 0xffffffff, 0x00000100, /* TD_CGTT_CTRL */ + /* */ + 0x30800, 0xffffffff, 0xe0000000, /* GRBM_GFX_INDEX */ + 0x3c020, 0xffffffff, 0x00010000, /* CGTS_CU0_SP0_CTRL_REG */ + 0x3c024, 0xffffffff, 0x00030002, /* CGTS_CU0_LDS_SQ_CTRL_REG */ + 0x3c028, 0xffffffff, 0x00040007, /* CGTS_CU0_TA_SQC_CTRL_REG */ + 0x3c02c, 0xffffffff, 0x00060005, /* CGTS_CU0_SP1_CTRL_REG */ + 0x3c030, 0xffffffff, 0x00090008, /* CGTS_CU0_TD_TCP_CTRL_REG */ + 0x3c034, 0xffffffff, 0x00010000, /* CGTS_CU1_SP0_CTRL_REG */ + 0x3c038, 0xffffffff, 0x00030002, /* CGTS_CU1_LDS_SQ_CTRL_REG */ + 0x3c03c, 0xffffffff, 0x00040007, /* CGTS_CU1_TA_CTRL_REG */ + 0x3c040, 0xffffffff, 0x00060005, /* CGTS_CU1_SP1_CTRL_REG */ + 0x3c044, 0xffffffff, 0x00090008, /* CGTS_CU1_TD_TCP_CTRL_REG */ + 0x3c048, 0xffffffff, 0x00010000, /* CGTS_CU2_SP0_CTRL_REG */ + 0x3c04c, 0xffffffff, 0x00030002, /* CGTS_CU2_LDS_SQ_CTRL_REG */ + 0x3c050, 0xffffffff, 0x00040007, /* CGTS_CU2_TA_CTRL_REG */ + 0x3c054, 0xffffffff, 0x00060005, /* CGTS_CU2_SP1_CTRL_REG */ + 0x3c058, 0xffffffff, 0x00090008, /* CGTS_CU2_TD_TCP_CTRL_REG */ + 0x3c05c, 0xffffffff, 0x00010000, /* CGTS_CU3_SP0_CTRL_REG */ + 0x3c060, 0xffffffff, 0x00030002, /* CGTS_CU3_LDS_SQ_CTRL_REG */ + 0x3c064, 0xffffffff, 0x00040007, /* CGTS_CU3_TA_SQC_CTRL_REG */ + 0x3c068, 0xffffffff, 0x00060005, /* CGTS_CU3_SP1_CTRL_REG */ + 0x3c06c, 0xffffffff, 0x00090008, /* CGTS_CU3_TD_TCP_CTRL_REG */ + 0x3c070, 0xffffffff, 0x00010000, /* CGTS_CU4_SP0_CTRL_REG */ + 0x3c074, 0xffffffff, 0x00030002, /* CGTS_CU4_LDS_SQ_CTRL_REG */ + 0x3c078, 0xffffffff, 0x00040007, /* CGTS_CU4_TA_CTRL_REG */ + 0x3c07c, 0xffffffff, 0x00060005, /* CGTS_CU4_SP1_CTRL_REG */ + 0x3c080, 0xffffffff, 0x00090008, /* CGTS_CU4_TD_TCP_CTRL_REG */ + 0x3c084, 0xffffffff, 0x00010000, /* CGTS_CU5_SP0_CTRL_REG */ + 0x3c088, 0xffffffff, 0x00030002, /* CGTS_CU5_LDS_SQ_CTRL_REG */ + 0x3c08c, 0xffffffff, 0x00040007, /* CGTS_CU5_TA_CTRL_REG */ + 0x3c090, 0xffffffff, 0x00060005, /* CGTS_CU5_SP1_CTRL_REG */ + 0x3c094, 0xffffffff, 0x00090008, /* CGTS_CU5_TD_TCP_CTRL_REG */ + 0x3c098, 0xffffffff, 0x00010000, /* CGTS_CU6_SP0_CTRL_REG */ + 0x3c09c, 0xffffffff, 0x00030002, /* CGTS_CU6_LDS_SQ_CTRL_REG */ + 0x3c0a0, 0xffffffff, 0x00040007, /* CGTS_CU6_TA_SQC_CTRL_REG */ + 0x3c0a4, 0xffffffff, 0x00060005, /* CGTS_CU6_SP1_CTRL_REG */ + 0x3c0a8, 0xffffffff, 0x00090008, /* CGTS_CU6_TD_TCP_CTRL_REG */ + 0x3c0ac, 0xffffffff, 0x00010000, /* CGTS_CU7_SP0_CTRL_REG */ + 0x3c0b0, 0xffffffff, 0x00030002, /* CGTS_CU7_LDS_SQ_CTRL_REG */ + 0x3c0b4, 0xffffffff, 0x00040007, /* CGTS_CU7_TA_SQC_CTRL_REG */ + 0x3c0b8, 0xffffffff, 0x00060005, /* CGTS_CU7_SP1_CTRL_REG */ + 0x3c0bc, 0xffffffff, 0x00090008, /* CGTS_CU7_TD_TCP_CTRL_REG */ + 0x3c0c0, 0xffffffff, 0x00010000, /* CGTS_CU8_SP0_CTRL_REG */ + 0x3c0c4, 0xffffffff, 0x00030002, /* CGTS_CU8_LDS_SQ_CTRL_REG */ + 0x3c0c8, 0xffffffff, 0x00040007, /* CGTS_CU8_TA_CTRL_REG */ + 0x3c0cc, 0xffffffff, 0x00060005, /* CGTS_CU8_SP1_CTRL_REG */ + 0x3c0d0, 0xffffffff, 0x00090008, /* CGTS_CU8_TD_TCP_CTRL_REG */ + 0x3c0d4, 0xffffffff, 0x00010000, /* CGTS_CU9_SP0_CTRL_REG */ + 0x3c0d8, 0xffffffff, 0x00030002, /* CGTS_CU9_LDS_SQ_CTRL_REG */ + 0x3c0dc, 0xffffffff, 0x00040007, /* CGTS_CU9_TA_CTRL_REG */ + 0x3c0e0, 0xffffffff, 0x00060005, /* CGTS_CU9_SP1_CTRL_REG */ + 0x3c0e4, 0xffffffff, 0x00090008, /* CGTS_CU9_TD_TCP_CTRL_REG */ + 0x3c000, 0xffffffff, 0x96940200, /* CGTS_SM_CTRL_REG */ + 0x8708, 0xffffffff, 0x00900100, /* CP_RB_WPTR_POLL_CNTL */ + 0xc424, 0xffffffff, 0x0020003f, /* RLC_CGCG_CGLS_CTRL */ + 0x9a10, 0x00210000, 0x00018208, /* CB_HW_CONTROL */ + 0x3c000, 0xffff1fff, 0x96940200, /* CGTS_SM_CTRL_REG */ + 0x3c00c, 0xffff0001, 0xff000000, /* CGTS_TCC_DISABLE */ + 0x3c010, 0xffff0000, 0xff000000, /* CGTS_USER_TCC_DISABLE */ + 0x55e4, 0xff607fff, 0xfc000100, /* CGTT_DRM_CLK_CTRL0 */ + 0x3c200, 0xfdfc0fff, 0x00000100, /* CGTT_SPI_CLK_CTRL */ + 0x6ed8, 0x00010000, 0x00010000, /* CRTC_DOUBLE_BUFFER_CONTROL */ + 0x9834, 0xf00fffff, 0x00004400, /* DB_DEBUG2 */ + 0x5bb0, 0x000000f0, 0x00000070, /* FBC_DEBUG_COMP */ + 0x98f8, 0x73773777, 0x12011003, /* GB_ADDR_CONFIG */ + 0x2f48, 0x73773777, 0x12010001, /* HDP_ADDR_CONFIG */ + 0x8a14, 0xf000003f, 0x00000007, /* PA_CL_ENHANCE */ + 0x8bf0, 0x00000001, 0x00000001, /* PA_SC_ENHANCE */ + 0x8b24, 0xffffffff, 0x00ffffff, /* PA_SC_FORCE_EOV_MAX_CNTS */ + 0x30a04, 0x0000ff0f, 0x00000000, /* PA_SC_LINE_STIPPLE_STATE */ + 0x28a4c, 0x07ffffff, 0x06000000, /* PA_SC_MODE_CNTL_1 */ + 0xc37c, 0xffffffff, 0x00000b00, /* RLC_PG_DELAY_2 */ + 0x4d8, 0x00000fff, 0x00000100, /* SCLK_CGTT_BLK_CTRL_REG */ + 0x3e78, 0x00000001, 0x00000002, /* SEM_CHICKEN_BITS */ + 0xc768, 0x00000008, 0x00000008, /* SPI_RESET_DEBUG */ + 0x8c00, 0x000000ff, 0x00000001, /* SQ_CONFIG */ + 0x9508, 0x00010000, 0x00010000, /* TA_CNTL_AUX */ + 0xac0c, 0xffffffff, 0x76325410, /* TCP_CHAN_STEER_LO */ + 0xc770, 0xffffffff, 0x00000800, /* SPI_RESOURCE_RESERVE_CU_0 */ + 0xc774, 0xffffffff, 0x00000800, /* SPI_RESOURCE_RESERVE_CU_1 */ + 0xc798, 0xffffffff, 0x00ffffbf, /* SPI_RESOURCE_RESERVE_EN_CU_0 */ + 0xc79c, 0xffffffff, 0x00ffffaf, /* SPI_RESOURCE_RESERVE_EN_CU_1 */ + 0xc7a0, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_2 */ + 0xc7a4, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_3 */ + 0xc7a8, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_4 */ + 0xc7ac, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_5*/ + 0xc7b0, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_6 */ + 0xc7b4, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_7 */ + 0xc7b8, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_8 */ + 0xc7bc, 0xffffffff, 0x00fffffe, /* SPI_RESOURCE_RESERVE_EN_CU_9 */ + 0x28350, 0xffffffff, 0x2a00161a, /* PA_SC_RASTER_CONFIG */ + 0x28354, 0xffffffff, 0x00000000, /* PA_SC_RASTER_CONFIG_1 */ + 0x5004, 0x00002000, 0x00002000, /* GARLIC_FLUSH_CNTL */ + 0x14d4, 0xffffffff, 0x00000000, /* VM_CONTEXTS_DISABLE */ +}; + static const u32 spectre_golden_spm_registers[] = { 0x30800, 0xe0ffffff, 0xe0000000 @@ -1642,6 +2214,20 @@ static void cik_init_golden_registers(struct radeon_device *rdev) bonaire_golden_spm_registers, (const u32)ARRAY_SIZE(bonaire_golden_spm_registers)); break; + case CHIP_LIVERPOOL: + /*radeon_program_register_sequence(rdev, + liverpool_mgcg_cgcg_init, + (const u32)ARRAY_SIZE(liverpool_mgcg_cgcg_init));*/ + radeon_program_register_sequence(rdev, + liverpool_golden_registers, + (const u32)ARRAY_SIZE(liverpool_golden_registers)); + radeon_program_register_sequence(rdev, + liverpool_golden_common_registers, + (const u32)ARRAY_SIZE(liverpool_golden_common_registers)); + /*radeon_program_register_sequence(rdev, + liverpool_golden_spm_registers, + (const u32)ARRAY_SIZE(liverpool_golden_spm_registers));*/ + break; case CHIP_KABINI: radeon_program_register_sequence(rdev, kalindi_mgcg_cgcg_init, @@ -2055,6 +2641,17 @@ static int cik_init_microcode(struct radeon_device *rdev) sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; num_fw = 6; break; +case CHIP_LIVERPOOL: + chip_name = "LIVERPOOL"; + new_chip_name = "liverpool"; + pfp_req_size = LIVERPOOL_PFP_UCODE_SIZE * 4; + me_req_size = LIVERPOOL_ME_UCODE_SIZE * 4; + ce_req_size = CIK_CE_UCODE_SIZE * 4; + mec_req_size = CIK_MEC_UCODE_SIZE * 4; + rlc_req_size = LIVERPOOL_RLC_UCODE_SIZE * 4; + sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; + num_fw = 6; + break; default: BUG(); } @@ -3182,10 +3779,12 @@ static void cik_gpu_init(struct radeon_device *rdev) u32 hdp_host_path_cntl; u32 tmp; int i, j; - + + printk("gb_addr_config=%08x\n", gb_addr_config); switch (rdev->family) { case CHIP_BONAIRE: - rdev->config.cik.max_shader_engines = 2; + gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; + rdev->config.cik.max_shader_engines = 2; rdev->config.cik.max_tile_pipes = 4; rdev->config.cik.max_cu_per_sh = 7; rdev->config.cik.max_sh_per_se = 1; @@ -3199,7 +3798,6 @@ static void cik_gpu_init(struct radeon_device *rdev) rdev->config.cik.sc_prim_fifo_size_backend = 0x100; rdev->config.cik.sc_hiz_tile_fifo_size = 0x30; rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130; - gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; break; case CHIP_HAWAII: rdev->config.cik.max_shader_engines = 4; @@ -3235,6 +3833,24 @@ static void cik_gpu_init(struct radeon_device *rdev) rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130; gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; break; +case CHIP_LIVERPOOL: + rdev->config.cik.max_shader_engines = 2; // VERIFIED + rdev->config.cik.max_tile_pipes = 8; // VERIFIED + rdev->config.cik.max_cu_per_sh = 10; // PROBABLY OK + rdev->config.cik.max_sh_per_se = 1; // VERIFIED + rdev->config.cik.max_backends_per_se = 2; // PROBABLY OK, >1? + rdev->config.cik.max_texture_channel_caches = 4; // ?? + rdev->config.cik.max_gprs = 256; + rdev->config.cik.max_gs_threads = 32; // ?? + rdev->config.cik.max_hw_contexts = 8; + + rdev->config.cik.sc_prim_fifo_size_frontend = 0x20; + rdev->config.cik.sc_prim_fifo_size_backend = 0x100; + rdev->config.cik.sc_hiz_tile_fifo_size = 0x30; + rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130; + + gb_addr_config = HAWAII_GB_ADDR_CONFIG_GOLDEN; + break; case CHIP_KABINI: case CHIP_MULLINS: default: @@ -3947,21 +4563,21 @@ static int cik_cp_gfx_load_microcode(struct radeon_device *rdev) /* PFP */ fw_data = (const __be32 *)rdev->pfp_fw->data; WREG32(CP_PFP_UCODE_ADDR, 0); - for (i = 0; i < CIK_PFP_UCODE_SIZE; i++) + for (i = 0; i < rdev->pfp_fw->size/4; i++) WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); WREG32(CP_PFP_UCODE_ADDR, 0); /* CE */ fw_data = (const __be32 *)rdev->ce_fw->data; WREG32(CP_CE_UCODE_ADDR, 0); - for (i = 0; i < CIK_CE_UCODE_SIZE; i++) + for (i = 0; i < rdev->ce_fw->size/4; i++) WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++)); WREG32(CP_CE_UCODE_ADDR, 0); /* ME */ fw_data = (const __be32 *)rdev->me_fw->data; WREG32(CP_ME_RAM_WADDR, 0); - for (i = 0; i < CIK_ME_UCODE_SIZE; i++) + for (i = 0; i < rdev->me_fw->size/4; i++) WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); WREG32(CP_ME_RAM_WADDR, 0); } @@ -5483,6 +6099,14 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) rdev->vm_manager.saved_table_addr[i]); } +if (rdev->family == CHIP_LIVERPOOL) { + for (i = 2; i < 8; i++) { + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR + (i << 2), 0); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR + (i << 2), + rdev->vm_manager.max_pfn - 1); + } + } + /* enable context1-15 */ WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR, (u32)(rdev->dummy_page.addr >> 12)); @@ -5978,6 +6602,8 @@ static int cik_rlc_resume(struct radeon_device *rdev) case CHIP_MULLINS: size = ML_RLC_UCODE_SIZE; break; + case CHIP_LIVERPOOL: + size = LIVERPOOL_RLC_UCODE_SIZE; } fw_data = (const __be32 *)rdev->rlc_fw->data; @@ -6759,6 +7385,10 @@ void cik_get_csb_buffer(struct radeon_device *rdev, volatile u32 *buffer) buffer[count++] = cpu_to_le32(0x3a00161a); buffer[count++] = cpu_to_le32(0x0000002e); break; + case CHIP_LIVERPOOL: + buffer[count++] = cpu_to_le32(0x2a00161a); + buffer[count++] = cpu_to_le32(0x00000000); + break; default: buffer[count++] = cpu_to_le32(0x00000000); buffer[count++] = cpu_to_le32(0x00000000); @@ -7572,7 +8202,7 @@ int cik_irq_process(struct radeon_device *rdev) return IRQ_NONE; rptr = rdev->ih.rptr; - DRM_DEBUG("cik_irq_process start: rptr %d, wptr %d\n", rptr, wptr); + //DRM_DEBUG("cik_irq_process start: rptr %d, wptr %d\n", rptr, wptr); /* Order reading of wptr vs. reading of IH ring data */ rmb(); @@ -7603,7 +8233,7 @@ int cik_irq_process(struct radeon_device *rdev) if (atomic_read(&rdev->irq.pflip[0])) radeon_crtc_handle_vblank(rdev, 0); rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VBLANK_INTERRUPT; - DRM_DEBUG("IH: D1 vblank\n"); + //DRM_DEBUG("IH: D1 vblank\n"); break; case 1: /* D1 vline */ @@ -7611,7 +8241,7 @@ int cik_irq_process(struct radeon_device *rdev) DRM_DEBUG("IH: IH event w/o asserted irq bit?\n"); rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VLINE_INTERRUPT; - DRM_DEBUG("IH: D1 vline\n"); + //DRM_DEBUG("IH: D1 vline\n"); break; default: @@ -7633,7 +8263,7 @@ int cik_irq_process(struct radeon_device *rdev) if (atomic_read(&rdev->irq.pflip[1])) radeon_crtc_handle_vblank(rdev, 1); rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; - DRM_DEBUG("IH: D2 vblank\n"); + //DRM_DEBUG("IH: D2 vblank\n"); break; case 1: /* D2 vline */ @@ -7641,7 +8271,7 @@ int cik_irq_process(struct radeon_device *rdev) DRM_DEBUG("IH: IH event w/o asserted irq bit?\n"); rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT; - DRM_DEBUG("IH: D2 vline\n"); + //DRM_DEBUG("IH: D2 vline\n"); break; default: @@ -7663,7 +8293,7 @@ int cik_irq_process(struct radeon_device *rdev) if (atomic_read(&rdev->irq.pflip[2])) radeon_crtc_handle_vblank(rdev, 2); rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; - DRM_DEBUG("IH: D3 vblank\n"); + //DRM_DEBUG("IH: D3 vblank\n"); break; case 1: /* D3 vline */ @@ -7671,7 +8301,7 @@ int cik_irq_process(struct radeon_device *rdev) DRM_DEBUG("IH: IH event w/o asserted irq bit?\n"); rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT; - DRM_DEBUG("IH: D3 vline\n"); + //DRM_DEBUG("IH: D3 vline\n"); break; default: @@ -7693,7 +8323,7 @@ int cik_irq_process(struct radeon_device *rdev) if (atomic_read(&rdev->irq.pflip[3])) radeon_crtc_handle_vblank(rdev, 3); rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; - DRM_DEBUG("IH: D4 vblank\n"); + //DRM_DEBUG("IH: D4 vblank\n"); break; case 1: /* D4 vline */ @@ -7701,7 +8331,7 @@ int cik_irq_process(struct radeon_device *rdev) DRM_DEBUG("IH: IH event w/o asserted irq bit?\n"); rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT; - DRM_DEBUG("IH: D4 vline\n"); + //DRM_DEBUG("IH: D4 vline\n"); break; default: @@ -7723,7 +8353,7 @@ int cik_irq_process(struct radeon_device *rdev) if (atomic_read(&rdev->irq.pflip[4])) radeon_crtc_handle_vblank(rdev, 4); rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; - DRM_DEBUG("IH: D5 vblank\n"); + //DRM_DEBUG("IH: D5 vblank\n"); break; case 1: /* D5 vline */ @@ -7731,7 +8361,7 @@ int cik_irq_process(struct radeon_device *rdev) DRM_DEBUG("IH: IH event w/o asserted irq bit?\n"); rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT; - DRM_DEBUG("IH: D5 vline\n"); + //DRM_DEBUG("IH: D5 vline\n"); break; default: @@ -7753,7 +8383,7 @@ int cik_irq_process(struct radeon_device *rdev) if (atomic_read(&rdev->irq.pflip[5])) radeon_crtc_handle_vblank(rdev, 5); rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; - DRM_DEBUG("IH: D6 vblank\n"); + //DRM_DEBUG("IH: D6 vblank\n"); break; case 1: /* D6 vline */ @@ -7761,7 +8391,7 @@ int cik_irq_process(struct radeon_device *rdev) DRM_DEBUG("IH: IH event w/o asserted irq bit?\n"); rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT; - DRM_DEBUG("IH: D6 vline\n"); + //DRM_DEBUG("IH: D6 vline\n"); break; default: @@ -7775,7 +8405,7 @@ int cik_irq_process(struct radeon_device *rdev) case 14: /* D4 page flip */ case 16: /* D5 page flip */ case 18: /* D6 page flip */ - DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); + //DRM_DEBUG("IH: D%d flip\n", ((src_id - 8) >> 1) + 1); if (radeon_use_pflipirq > 0) radeon_crtc_handle_flip(rdev, (src_id - 8) >> 1); break; @@ -8088,7 +8718,7 @@ int cik_irq_process(struct radeon_device *rdev) } break; default: - DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + //DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); break; } @@ -8325,6 +8955,11 @@ static int cik_startup(struct radeon_device *rdev) rdev->rlc.reg_list = spectre_rlc_save_restore_register_list; rdev->rlc.reg_list_size = (u32)ARRAY_SIZE(spectre_rlc_save_restore_register_list); + } else + if (rdev->family == CHIP_LIVERPOOL) { + rdev->rlc.reg_list = liverpool_rlc_save_restore_register_list; + rdev->rlc.reg_list_size = + (u32)ARRAY_SIZE(liverpool_rlc_save_restore_register_list); } else { rdev->rlc.reg_list = kalindi_rlc_save_restore_register_list; rdev->rlc.reg_list_size = @@ -8459,8 +9094,10 @@ static int cik_startup(struct radeon_device *rdev) if (r) return r; +#if 0 cik_uvd_resume(rdev); cik_vce_resume(rdev); +#endif r = radeon_ib_pool_init(rdev); if (r) { @@ -9384,6 +10021,8 @@ void dce8_bandwidth_update(struct radeon_device *rdev) struct drm_display_mode *mode = NULL; u32 num_heads = 0, lb_size; int i; + // FIXME PS4: this stuff is broken + return; if (!rdev->mode_info.mode_config_initialized) return; diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index 9c351dc8a9e048..b5a6d7b0ca5724 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -141,11 +141,23 @@ void cik_sdma_ring_ib_execute(struct radeon_device *rdev, while ((next_rptr & 7) != 4) next_rptr++; next_rptr += 4; - radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); - radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); - radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr)); - radeon_ring_write(ring, 1); /* number of DWs to follow */ - radeon_ring_write(ring, next_rptr); + if (rdev->family == CHIP_LIVERPOOL) { + /* SDMA_OPCODE_WRITE is broken on Liverpool when used + * in the ring (works in IBs) */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_CONSTANT_FILL, 0, + SDMA_CONSTANT_FILL_EXTRA_SIZE(2))); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr)); + radeon_ring_write(ring, next_rptr); + radeon_ring_write(ring, 4); + } else { + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, + SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr)); + radeon_ring_write(ring, 1); /* number of DWs to follow */ + radeon_ring_write(ring, next_rptr); + } } /* IB packet must end on a 8 DW boundary */ @@ -666,11 +678,26 @@ int cik_sdma_ring_test(struct radeon_device *rdev, DRM_ERROR("radeon: dma failed to lock ring %d (%d).\n", ring->idx, r); return r; } - radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); - radeon_ring_write(ring, lower_32_bits(gpu_addr)); - radeon_ring_write(ring, upper_32_bits(gpu_addr)); - radeon_ring_write(ring, 1); /* number of DWs to follow */ - radeon_ring_write(ring, 0xDEADBEEF); + + if (rdev->family == CHIP_LIVERPOOL) { + /* SDMA_OPCODE_WRITE is broken on Liverpool when used in the + * ring (works in IBs) */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_CONSTANT_FILL, 0, + SDMA_CONSTANT_FILL_EXTRA_SIZE(2))); + radeon_ring_write(ring, lower_32_bits(gpu_addr)); + radeon_ring_write(ring, upper_32_bits(gpu_addr)); + radeon_ring_write(ring, 0xDEADBEEF); /* Fill value */ + radeon_ring_write(ring, 4); /* number of bytes */ + } else { + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, + SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); + radeon_ring_write(ring, lower_32_bits(gpu_addr)); + radeon_ring_write(ring, upper_32_bits(gpu_addr)); + radeon_ring_write(ring, 1); /* number of DWs to follow */ + radeon_ring_write(ring, 0xDEADBEEF); + } + + radeon_ring_unlock_commit(rdev, ring, false); for (i = 0; i < rdev->usec_timeout; i++) { diff --git a/drivers/gpu/drm/radeon/ps4_bridge.c b/drivers/gpu/drm/radeon/ps4_bridge.c new file mode 100644 index 00000000000000..a2702d8057a10a --- /dev/null +++ b/drivers/gpu/drm/radeon/ps4_bridge.c @@ -0,0 +1,583 @@ +/* + * Panasonic MN86471A DP->HDMI bridge driver (via PS4 Aeolia ICC interface) + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "drm_atomic_helper.h" +#include "drm_edid.h" +#include "drmP.h" + +#include "radeon_mode.h" +#include "ObjectID.h" + +#define CMD_READ 1, 1 +#define CMD_WRITE 2, 2 +#define CMD_MASK 2, 3 +#define CMD_DELAY 3, 1 +#define CMD_WAIT_SET 3, 2 +#define CMD_WAIT_CLEAR 3, 3 + +#define TSYSCTRL 0x7005 +# define TSYSCTRL_HDMI BIT(7) + +#define TSRST 0x7006 +# define TSRST_AVCSRST BIT(0) +# define TSRST_ENCSRST BIT(1) +# define TSRST_FIFOSRST BIT(2) +# define TSRST_CCSRST BIT(3) +# define TSRST_HDCPSRST BIT(4) +# define TSRST_AUDSRST BIT(6) +# define TSRST_VIFSRST BIT(7) + +#define TMONREG 0x7008 +# define TMONREG_HPD BIT(3) + +#define TDPCMODE 0x7009 + + +#define UPDCTRL 0x7011 +# define UPDCTRL_ALLUPD BIT(7) +# define UPDCTRL_AVIIUPD BIT(6) +# define UPDCTRL_AUDIUPD BIT(5) +# define UPDCTRL_CLKUPD BIT(4) +# define UPDCTRL_HVSIUPD BIT(3) +# define UPDCTRL_VIFUPD BIT(2) +# define UPDCTRL_AUDUPD BIT(1) +# define UPDCTRL_CSCUPD BIT(0) + + +#define VINCNT 0x7040 +# define VINCNT_VIF_FILEN BIT(6) + +#define VMUTECNT 0x705f +# define VMUTECNT_CCVMUTE BIT(7) +# define VMUTECNT_DUMON BIT(6) +# define VMUTECNT_LINEWIDTH_80 (0<<4) +# define VMUTECNT_LINEWIDTH_90 (1<<4) +# define VMUTECNT_LINEWIDTH_180 (2<<4) +# define VMUTECNT_LINEWIDTH_360 (3<<4) +# define VMUTECNT_VMUTE_MUTE_ASYNC 1 +# define VMUTECNT_VMUTE_MUTE_NORMAL 2 +# define VMUTECNT_VMUTE_MUTE_RAMPA 4 +# define VMUTECNT_VMUTE_MUTE_RAMPB 8 +# define VMUTECNT_VMUTE_MUTE_COLORBAR_RGB 10 +# define VMUTECNT_VMUTE_MUTE_TOGGLE 12 +# define VMUTECNT_VMUTE_MUTE_COLORBAR_YCBCR 14 + +#define CSCMOD 0x70c0 +#define C420SET 0x70c2 +#define OUTWSET 0x70c3 + +#define PKTENA 0x7202 + +#define INFENA 0x7203 +# define INFENA_AVIEN BIT(6) + +#define AKESTA 0x7a84 +# define AKESTA_BUSY BIT(0) + +#define AKESRST 0x7a88 + +#define HDCPEN 0x7a8b +# define HDCPEN_NONE 0x00 +# define HDCPEN_ENC_EN 0x03 +# define HDCPEN_ENC_DIS 0x05 + + +struct i2c_cmd_hdr { + u8 major; + u8 length; + u8 minor; + u8 count; +} __packed; + +struct i2c_cmdqueue { + struct { + u8 code; + u16 length; + u8 count; + u8 cmdbuf[0x7ec]; + } __packed req; + struct { + u8 res1, res2; + u8 unk1, unk2; + u8 count; + u8 databuf[0x7eb]; + } __packed reply; + + u8 *p; + struct i2c_cmd_hdr *cmd; +}; + +struct mn86471a_bridge { + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_bridge bridge; + struct i2c_cmdqueue cq; + struct mutex mutex; + + int mode; +}; + +/* this should really be taken care of by the connector, but that is currently + * contained/owned by radeon_connector so just use a global for now */ +static struct mn86471a_bridge g_bridge = { + .mutex = __MUTEX_INITIALIZER(g_bridge.mutex) +}; + +static void cq_init(struct i2c_cmdqueue *q, u8 code) +{ + q->req.code = code; + q->req.count = 0; + q->p = q->req.cmdbuf; + q->cmd = NULL; +} + +static void cq_cmd(struct i2c_cmdqueue *q, u8 major, u8 minor) +{ + if (!q->cmd || q->cmd->major != major || q->cmd->minor != minor) { + if (q->cmd) + q->cmd->length = q->p - (u8 *)q->cmd; + q->cmd = (struct i2c_cmd_hdr *)q->p; + q->cmd->major = major; + q->cmd->minor = minor; + q->cmd->length = 0; + q->cmd->count = 1; + q->req.count += 1; + q->p += sizeof(*q->cmd); + } else { + q->cmd->count += 1; + } +} + +static int cq_exec(struct i2c_cmdqueue *q) +{ + int res; + + if (!q->cmd) + return 0; + + q->cmd->length = q->p - (u8 *)q->cmd; + q->req.length = q->p - (u8 *)&q->req; + + res = apcie_icc_cmd(0x10, 0, &q->req, q->req.length, + &q->reply, sizeof(q->reply)); + + if (res < 5) { + DRM_ERROR("icc i2c commandqueue failed: %d\n", res); + return -EIO; + } + if (q->reply.res1 != 0 || q->reply.res2) { + DRM_ERROR("icc i2c commandqueue failed: %d, %d\n", + q->reply.res1, q->reply.res2); + return -EIO; + } + + return res; +} + +static void cq_read(struct i2c_cmdqueue *q, u16 addr, u8 count) +{ + cq_cmd(q, CMD_READ); + *q->p++ = count; + *q->p++ = addr >> 8; + *q->p++ = addr & 0xff; + *q->p++ = 0; +} + +static void cq_writereg(struct i2c_cmdqueue *q, u16 addr, u8 data) +{ + cq_cmd(q, CMD_WRITE); + *q->p++ = 1; + *q->p++ = addr >> 8; + *q->p++ = addr & 0xff; + *q->p++ = data; +} + +#if 0 +static void cq_write(struct i2c_cmdqueue *q, u16 addr, u8 *data, u8 count) +{ + cq_cmd(q, CMD_WRITE); + *q->p++ = count; + *q->p++ = addr >> 8; + *q->p++ = addr & 0xff; + while (count--) + *q->p++ = *data++; +} +#endif + +static void cq_mask(struct i2c_cmdqueue *q, u16 addr, u8 value, u8 mask) +{ + cq_cmd(q, CMD_MASK); + *q->p++ = 1; + *q->p++ = addr >> 8; + *q->p++ = addr & 0xff; + *q->p++ = value; + *q->p++ = mask; +} + +#if 0 +static void cq_delay(struct i2c_cmdqueue *q, u16 time) +{ + cq_cmd(q, CMD_DELAY); + *q->p++ = 0; + *q->p++ = time & 0xff; + *q->p++ = time>>8; + *q->p++ = 0; +} +#endif + +static void cq_wait_set(struct i2c_cmdqueue *q, u16 addr, u8 mask) +{ + cq_cmd(q, CMD_WAIT_SET); + *q->p++ = 0; + *q->p++ = addr >> 8; + *q->p++ = addr & 0xff; + *q->p++ = mask; +} + +static void cq_wait_clear(struct i2c_cmdqueue *q, u16 addr, u8 mask) +{ + cq_cmd(q, CMD_WAIT_CLEAR); + *q->p++ = 0; + *q->p++ = addr >> 8; + *q->p++ = addr & 0xff; + *q->p++ = mask; +} + +static inline struct mn86471a_bridge * + bridge_to_mn86471a(struct drm_bridge *bridge) +{ + return container_of(bridge, struct mn86471a_bridge, bridge); +} + +static void mn86471a_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mn86471a_bridge *mn_bridge = bridge_to_mn86471a(bridge); + + /* This gets called before pre_enable/enable, so we just stash + * the vic ID for later */ + mn_bridge->mode = drm_match_cea_mode(adjusted_mode); + DRM_DEBUG_KMS("vic mode: %d\n", mn_bridge->mode); + if (!mn_bridge->mode) { + DRM_ERROR("attempted to set non-CEA mode\n"); + } +} + +static void mn86471a_pre_enable(struct drm_bridge *bridge) +{ + struct mn86471a_bridge *mn_bridge = bridge_to_mn86471a(bridge); + DRM_DEBUG_KMS("mn86471a_pre_enable\n"); + + mutex_lock(&mn_bridge->mutex); + cq_init(&mn_bridge->cq, 4); + +#if 0 + /* No idea. DP stuff probably. This borks for some reason. Meh. */ + cq_writereg(&mn_bridge->cq, 0x7657,0xff); + cq_writereg(&mn_bridge->cq, 0x76a5,0x80); + cq_writereg(&mn_bridge->cq, 0x76a6,0x04); + cq_writereg(&mn_bridge->cq, 0x7601,0x0a); + cq_writereg(&mn_bridge->cq, 0x7602,0x84); + cq_writereg(&mn_bridge->cq, 0x7603,0x00); + cq_writereg(&mn_bridge->cq, 0x76a8,0x09); + cq_writereg(&mn_bridge->cq, 0x76ae,0xd1); + cq_writereg(&mn_bridge->cq, 0x76af,0x50); + cq_writereg(&mn_bridge->cq, 0x76b0,0x70); + cq_writereg(&mn_bridge->cq, 0x76b1,0xb0); + cq_writereg(&mn_bridge->cq, 0x76b2,0xf0); + cq_writereg(&mn_bridge->cq, 0x76db,0x00); + cq_writereg(&mn_bridge->cq, 0x76dc,0x64); + cq_writereg(&mn_bridge->cq, 0x76dd,0x22); + cq_writereg(&mn_bridge->cq, 0x76e4,0x00); + cq_writereg(&mn_bridge->cq, 0x76e6,0x1e); /* 0 for (DP?) scramble off */ + cq_writereg(&mn_bridge->cq, 0x7670,0xff); + cq_writereg(&mn_bridge->cq, 0x7671,0xff); + cq_writereg(&mn_bridge->cq, 0x7672,0xff); + cq_writereg(&mn_bridge->cq, 0x7673,0xff); + cq_writereg(&mn_bridge->cq, 0x7668,0xff); + cq_writereg(&mn_bridge->cq, 0x7669,0xff); + cq_writereg(&mn_bridge->cq, 0x766a,0xff); + cq_writereg(&mn_bridge->cq, 0x766b,0xff); + cq_writereg(&mn_bridge->cq, 0x7655,0x04); + cq_writereg(&mn_bridge->cq, 0x7007,0xff); + cq_writereg(&mn_bridge->cq, 0x7098,0xff); + cq_writereg(&mn_bridge->cq, 0x7099,0x00); + cq_writereg(&mn_bridge->cq, 0x709a,0x0f); + cq_writereg(&mn_bridge->cq, 0x709b,0x00); + cq_writereg(&mn_bridge->cq, 0x709c,0x50); + cq_writereg(&mn_bridge->cq, 0x709d,0x00); + cq_writereg(&mn_bridge->cq, 0x709e,0x00); + cq_writereg(&mn_bridge->cq, 0x709f,0xd0); + cq_writereg(&mn_bridge->cq, 0x7a9c,0x2e); + cq_writereg(&mn_bridge->cq, 0x7021,0x04); + cq_writereg(&mn_bridge->cq, 0x7028,0x00); + cq_writereg(&mn_bridge->cq, 0x7030,0xa3); + cq_writereg(&mn_bridge->cq, 0x7016,0x04); +#endif + + /* Disable InfoFrames */ + cq_writereg(&mn_bridge->cq, INFENA, 0x00); + /* Reset HDCP */ + cq_writereg(&mn_bridge->cq, TSRST, TSRST_ENCSRST | TSRST_HDCPSRST); + /* Disable HDCP flag */ + cq_writereg(&mn_bridge->cq, TSRST, HDCPEN_ENC_DIS); + /* HDCP AKE reset */ + cq_writereg(&mn_bridge->cq, AKESRST, 0xff); + /* Wait AKE busy */ + cq_wait_clear(&mn_bridge->cq, AKESTA, AKESTA_BUSY); + + if (cq_exec(&mn_bridge->cq) < 0) { + DRM_ERROR("failed to run pre-enable sequence"); + } + mutex_unlock(&mn_bridge->mutex); +} + +static void mn86471a_enable(struct drm_bridge *bridge) +{ + struct mn86471a_bridge *mn_bridge = bridge_to_mn86471a(bridge); + u8 dp[3]; + + if (!mn_bridge->mode) { + DRM_ERROR("mode not available\n"); + return; + } + + DRM_DEBUG_KMS("mn86471a_enable (mode: %d)\n", mn_bridge->mode); + + /* Here come the dragons */ + + mutex_lock(&mn_bridge->mutex); + cq_init(&mn_bridge->cq, 4); + /* Read DisplayPort status (?) */ + cq_read(&mn_bridge->cq, 0x76e1, 3); + if (cq_exec(&mn_bridge->cq) < 11) { + mutex_unlock(&mn_bridge->mutex); + DRM_ERROR("could not read DP status"); + return; + } + memcpy(dp, &mn_bridge->cq.reply.databuf[3], 3); + + cq_init(&mn_bridge->cq, 4); + + /* Wait for DP lane status */ + cq_wait_set(&mn_bridge->cq, 0x761e, 0x77); + cq_wait_set(&mn_bridge->cq, 0x761f, 0x77); + /* Wait for ?? */ + cq_wait_set(&mn_bridge->cq, 0x7669, 0x01); + + cq_writereg(&mn_bridge->cq, 0x76d9, (dp[0] & 0x1f) | (dp[0] << 5)); + cq_writereg(&mn_bridge->cq, 0x76da, (dp[1] & 0x7c) | ((dp[0] >> 3) & 3) | ((dp[1] << 5) & 0x80)); + cq_writereg(&mn_bridge->cq, 0x76db, 0x80 | ((dp[1] >> 3) & 0xf)); + cq_writereg(&mn_bridge->cq, 0x76e4, 0x01); + cq_writereg(&mn_bridge->cq, TSYSCTRL, TSYSCTRL_HDMI); + cq_writereg(&mn_bridge->cq, VINCNT, VINCNT_VIF_FILEN); + cq_writereg(&mn_bridge->cq, 0x7071, 0); + cq_writereg(&mn_bridge->cq, 0x7062, mn_bridge->mode); + cq_writereg(&mn_bridge->cq, 0x765a, 0); + cq_writereg(&mn_bridge->cq, 0x7062, mn_bridge->mode | 0x80); + cq_writereg(&mn_bridge->cq, 0x7215, 0x28); /* aspect */ + cq_writereg(&mn_bridge->cq, 0x7217, mn_bridge->mode); + cq_writereg(&mn_bridge->cq, 0x7218, 0); + cq_writereg(&mn_bridge->cq, CSCMOD, 0xdc); + cq_writereg(&mn_bridge->cq, C420SET, 0xaa); + cq_writereg(&mn_bridge->cq, TDPCMODE, 0x4a); + cq_writereg(&mn_bridge->cq, OUTWSET, 0x00); + cq_writereg(&mn_bridge->cq, 0x70c4, 0x08); + cq_writereg(&mn_bridge->cq, 0x70c5, 0x08); + cq_writereg(&mn_bridge->cq, 0x7096, 0xff); + cq_writereg(&mn_bridge->cq, 0x7027, 0x00); + cq_writereg(&mn_bridge->cq, 0x7020, 0x20); + cq_writereg(&mn_bridge->cq, 0x700b, 0x01); + cq_writereg(&mn_bridge->cq, PKTENA, 0x20); + cq_writereg(&mn_bridge->cq, 0x7096, 0xff); + cq_writereg(&mn_bridge->cq, INFENA, INFENA_AVIEN); + cq_writereg(&mn_bridge->cq, UPDCTRL, UPDCTRL_ALLUPD | UPDCTRL_AVIIUPD | + UPDCTRL_CLKUPD | UPDCTRL_VIFUPD | + UPDCTRL_CSCUPD); + cq_wait_set(&mn_bridge->cq, 0x7096, 0x80); + + cq_mask(&mn_bridge->cq, 0x7216, 0x00, 0x80); + cq_writereg(&mn_bridge->cq, 0x7218, 0x00); + + cq_writereg(&mn_bridge->cq, 0x7096, 0xff); + cq_writereg(&mn_bridge->cq, VMUTECNT, VMUTECNT_LINEWIDTH_90 | VMUTECNT_VMUTE_MUTE_NORMAL); + cq_writereg(&mn_bridge->cq, 0x7016, 0x04); + cq_writereg(&mn_bridge->cq, 0x7a88, 0xff); + cq_writereg(&mn_bridge->cq, 0x7a83, 0x88); + cq_writereg(&mn_bridge->cq, 0x7204, 0x40); + + cq_wait_set(&mn_bridge->cq, 0x7096, 0x80); + + cq_writereg(&mn_bridge->cq, 0x7006, 0x02); + cq_writereg(&mn_bridge->cq, 0x7020, 0x21); + cq_writereg(&mn_bridge->cq, 0x7a8b, 0x00); + cq_writereg(&mn_bridge->cq, 0x7020, 0x21); + + cq_writereg(&mn_bridge->cq, VMUTECNT, VMUTECNT_LINEWIDTH_90); + if (cq_exec(&mn_bridge->cq) < 0) { + DRM_ERROR("Failed to configure bridge mode\n"); + } + + mutex_unlock(&mn_bridge->mutex); +} + +static void mn86471a_disable(struct drm_bridge *bridge) +{ + struct mn86471a_bridge *mn_bridge = bridge_to_mn86471a(bridge); + DRM_DEBUG_KMS("mn86471a_disable\n"); + + mutex_lock(&mn_bridge->mutex); + cq_init(&mn_bridge->cq, 4); + cq_writereg(&mn_bridge->cq, VMUTECNT, VMUTECNT_LINEWIDTH_90 | VMUTECNT_VMUTE_MUTE_NORMAL); + cq_writereg(&mn_bridge->cq, INFENA, 0x00); + if (cq_exec(&mn_bridge->cq) < 0) { + DRM_ERROR("Failed to disable bridge\n"); + } + mutex_unlock(&mn_bridge->mutex); +} + +static void mn86471a_post_disable(struct drm_bridge *bridge) +{ + /* struct mn86471a_bridge *mn_bridge = bridge_to_mn86471a(bridge); */ + DRM_DEBUG_KMS("mn86471a_post_disable\n"); +} + +/* Hardcoded modes, since we don't really know how to do custom modes yet. + * Other CEA modes *should* work (and are allowed if externally added) */ + +/* 1 - 640x480@60Hz */ +static const struct drm_display_mode mode_480p = { + DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3 +}; +/* 4 - 1280x720@60Hz */ +static const struct drm_display_mode mode_720p = { + DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390, + 1430, 1650, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9 +}; +/* 16 - 1920x1080@60Hz */ +static const struct drm_display_mode mode_1080p = { + DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9 +}; + +int mn86471a_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *newmode; + DRM_DEBUG_KMS("mn86471a_get_modes\n"); + + newmode = drm_mode_duplicate(dev, &mode_1080p); + drm_mode_probed_add(connector, newmode); + //newmode = drm_mode_duplicate(dev, &mode_720p); + //drm_mode_probed_add(connector, newmode); + //newmode = drm_mode_duplicate(dev, &mode_480p); + //drm_mode_probed_add(connector, newmode); + + drm_mode_connector_update_edid_property(connector, NULL); + + return 0; +} + +enum drm_connector_status mn86471a_detect(struct drm_connector *connector, + bool force) +{ + struct mn86471a_bridge *mn_bridge = &g_bridge; + u8 reg; + + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; + + radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT; + radeon_dp_getdpcd(radeon_connector); + + mutex_lock(&mn_bridge->mutex); + cq_init(&mn_bridge->cq, 4); + cq_read(&mn_bridge->cq, TMONREG, 1); + if (cq_exec(&mn_bridge->cq) < 9) { + mutex_unlock(&mn_bridge->mutex); + DRM_ERROR("could not read TMONREG"); + return connector_status_disconnected; + } + reg = mn_bridge->cq.reply.databuf[3]; + mutex_unlock(&mn_bridge->mutex); + + DRM_DEBUG_KMS("TMONREG=0x%02x\n", reg); + + if (reg & TMONREG_HPD) + return connector_status_connected; + else + return connector_status_disconnected; +} + +int mn86471a_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + int vic = drm_match_cea_mode(mode); + + /* Allow anything that we can match up to a VIC (CEA modes) */ + if (!vic || vic != 16) { + return MODE_BAD; + } + + return MODE_OK; +} + +static int mn86471a_bridge_attach(struct drm_bridge *bridge) +{ + /* struct mn86471a_bridge *mn_bridge = bridge_to_mn86471a(bridge); */ + + return 0; +} + +static struct drm_bridge_funcs mn86471a_bridge_funcs = { + .pre_enable = mn86471a_pre_enable, + .enable = mn86471a_enable, + .disable = mn86471a_disable, + .post_disable = mn86471a_post_disable, + .attach = mn86471a_bridge_attach, + .mode_set = mn86471a_mode_set +}; + +int mn86471a_bridge_register(struct drm_connector *connector, + struct drm_encoder *encoder) +{ + int ret; + struct mn86471a_bridge *mn_bridge = &g_bridge; + struct drm_device *dev = connector->dev; + + mn_bridge->encoder = encoder; + mn_bridge->connector = connector; + mn_bridge->bridge.funcs = &mn86471a_bridge_funcs; + ret = drm_bridge_attach(dev, &mn_bridge->bridge); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return -EINVAL; + } + + encoder->bridge = &mn_bridge->bridge; + + return 0; +} + diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 32808e50be12f8..8043e8760f30b4 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -2651,7 +2651,8 @@ void r100_pll_errata_after_index(struct radeon_device *rdev); #define ASIC_IS_DCE81(rdev) ((rdev->family == CHIP_KAVERI)) #define ASIC_IS_DCE82(rdev) ((rdev->family == CHIP_BONAIRE)) #define ASIC_IS_DCE83(rdev) ((rdev->family == CHIP_KABINI) || \ - (rdev->family == CHIP_MULLINS)) + (rdev->family == CHIP_MULLINS) || \ + (rdev->family == CHIP_LIVERPOOL)) #define ASIC_IS_LOMBOK(rdev) ((rdev->ddev->pdev->device == 0x6849) || \ (rdev->ddev->pdev->device == 0x6850) || \ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index bc5121d1a7bc56..4cd323906a67de 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -2242,12 +2242,18 @@ static struct radeon_asic kv_asic = { .get_backlight_level = &atombios_get_backlight_level, }, .copy = { - .blit = &cik_copy_cpdma, - .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .blit = &cik_copy_dma, + .blit_ring_index = R600_RING_TYPE_DMA_INDEX, .dma = &cik_copy_dma, .dma_ring_index = R600_RING_TYPE_DMA_INDEX, .copy = &cik_copy_dma, .copy_ring_index = R600_RING_TYPE_DMA_INDEX, +// .blit = &cik_copy_cpdma, +// .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, +// .dma = &cik_copy_cpdma, +// .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX, +// .copy = &cik_copy_cpdma, +// .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX, }, .surface = { .set_reg = r600_set_surface_reg, @@ -2626,6 +2632,7 @@ int radeon_asic_init(struct radeon_device *rdev) case CHIP_KAVERI: case CHIP_KABINI: case CHIP_MULLINS: + case CHIP_LIVERPOOL: rdev->asic = &kv_asic; /* set num crtcs */ if (rdev->family == CHIP_KAVERI) { @@ -2670,7 +2677,7 @@ int radeon_asic_init(struct radeon_device *rdev) RADEON_CG_SUPPORT_SDMA_LS | RADEON_CG_SUPPORT_BIF_LS | RADEON_CG_SUPPORT_VCE_MGCG | - RADEON_CG_SUPPORT_UVD_MGCG | + /*RADEON_CG_SUPPORT_UVD_MGCG |*/ RADEON_CG_SUPPORT_HDP_LS | RADEON_CG_SUPPORT_HDP_MGCG; rdev->pg_flags = 0; @@ -2683,8 +2690,8 @@ int radeon_asic_init(struct radeon_device *rdev) RADEON_PG_SUPPORT_RLC_SMU_HS | RADEON_PG_SUPPORT_SAMU;*/ } - rdev->has_uvd = true; - rdev->has_vce = true; + rdev->has_uvd = false; + rdev->has_vce = false; break; default: /* FIXME: not supported yet */ diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c index 96f71114237a86..5a9358ee3b4e1b 100644 --- a/drivers/gpu/drm/radeon/radeon_audio.c +++ b/drivers/gpu/drm/radeon/radeon_audio.c @@ -338,6 +338,11 @@ int radeon_audio_init(struct radeon_device *rdev) for (i = 0; i < rdev->audio.num_pins; i++) radeon_audio_enable(rdev, &rdev->audio.pin[i], 0); + /* LVP has standalone S/PDIF on the third pin, always enable */ + if (rdev->family == CHIP_LIVERPOOL) { + radeon_audio_enable(rdev, &rdev->audio.pin[2], 0xf); + } + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index de1745adccccb8..1293143c0bb2d5 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -1833,6 +1833,29 @@ static const struct drm_connector_funcs radeon_dp_connector_funcs = { .force = radeon_dvi_force, }; +#ifdef CONFIG_X86_PS4 +int mn86471a_get_modes(struct drm_connector *connector); +enum drm_connector_status mn86471a_detect(struct drm_connector *connector, + bool force); +int mn86471a_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); + + static const struct drm_connector_helper_funcs radeon_ps4_dp_connector_helper_funcs = { + .get_modes = mn86471a_get_modes, + .mode_valid = mn86471a_mode_valid, + .best_encoder = radeon_dvi_encoder, +}; + + static const struct drm_connector_funcs radeon_ps4_dp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = mn86471a_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + //.set_property = radeon_connector_set_property, + .destroy = radeon_connector_destroy, + .force = radeon_dvi_force, +}; +#endif + static const struct drm_connector_funcs radeon_edp_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_dp_detect, @@ -1873,6 +1896,7 @@ radeon_add_atom_connector(struct drm_device *dev, uint32_t subpixel_order = SubPixelNone; bool shared_ddc = false; bool is_dp_bridge = false; + bool is_ps4_bridge = false; bool has_aux = false; if (connector_type == DRM_MODE_CONNECTOR_Unknown) @@ -1924,6 +1948,17 @@ radeon_add_atom_connector(struct drm_device *dev, if (!radeon_connector) return; + /* Liverpool (PS4) has an DP bridge which needs a special driver, and + * a fake HDMI port that doesn't really exist. */ + if (rdev->family == CHIP_LIVERPOOL) { + if (connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + connector_type = DRM_MODE_CONNECTOR_HDMIA; + is_dp_bridge = true; + } else { + return; + } + } + connector = &radeon_connector->base; radeon_connector->connector_id = connector_id; @@ -1979,10 +2014,18 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIB: case DRM_MODE_CONNECTOR_DisplayPort: + if (is_ps4_bridge) { + &radeon_dp_connector_funcs, connector_type); drm_connector_init(dev, &radeon_connector->base, + drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + &radeon_dp_connector_helper_funcs); drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); + } else { drm_connector_init(dev, &radeon_connector->base, - &radeon_dp_connector_funcs, connector_type); + &radeon_ps4_dp_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, - &radeon_dp_connector_helper_funcs); + &radeon_ps4_dp_connector_helper_funcs); + } + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 0a9312ea250a8b..da3a55e12e7189 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -101,6 +101,7 @@ static const char radeon_family_name[][16] = { "BONAIRE", "KAVERI", "KABINI", + "LIVERPOOL", "HAWAII", "MULLINS", "LAST", diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index aa898c699101c9..5edd91d8e04fa6 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -674,13 +674,13 @@ static void radeon_crtc_init(struct drm_device *dev, int index) return; drm_crtc_init(dev, &radeon_crtc->base, &radeon_crtc_funcs); - drm_mode_crtc_set_gamma_size(&radeon_crtc->base, 256); radeon_crtc->crtc_id = index; + radeon_crtc->flip_queue = alloc_workqueue("radeon-crtc", WQ_HIGHPRI, 0); rdev->mode_info.crtcs[index] = radeon_crtc; - if (rdev->family >= CHIP_BONAIRE) { + if (rdev->family >= CHIP_BONAIRE && rdev->family != CHIP_LIVERPOOL) { radeon_crtc->max_cursor_width = CIK_CURSOR_WIDTH; radeon_crtc->max_cursor_height = CIK_CURSOR_HEIGHT; } else { diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 2e96c886392bd1..beeeca9a9ffd44 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -45,6 +45,10 @@ #include #include +#ifdef CONFIG_X86_PS4 +#include +#endif + /* * KMS wrapper. * - 2.0.0 - initial interface @@ -330,6 +334,16 @@ static int radeon_pci_probe(struct pci_dev *pdev, if (ret) return ret; +#ifdef CONFIG_X86_PS4 + /* On the PS4 (Liverpool graphics) we have a hard dependency on the + * Aeolia driver to set up the HDMI encoder which is connected to it, + * so defer probe until it is ready. This test passes if this isn't + * a PS4 (returns -ENODEV). + */ + if (apcie_status() == 0) + return -EPROBE_DEFER; +#endif + return drm_get_pci_dev(pdev, ent, &kms_driver); } diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index c341fb2a5b5624..9c0333828d390a 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -197,6 +197,22 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder, } } +#ifdef CONFIG_X86_PS4 +int mn86471a_bridge_register(struct drm_connector *connector, + struct drm_encoder *encoder); + + static void radeon_maybe_add_bridge(struct drm_connector *connector, + struct drm_encoder *encoder) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + + if (rdev->family == CHIP_LIVERPOOL) { + mn86471a_bridge_register(connector, encoder); + } +} +#endif + void radeon_link_encoder_connector(struct drm_device *dev) { @@ -211,6 +227,10 @@ radeon_link_encoder_connector(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { radeon_encoder = to_radeon_encoder(encoder); if (radeon_encoder->devices & radeon_connector->devices) { + #ifdef CONFIG_X86_PS4 + radeon_maybe_add_bridge(connector, encoder); + #endif + drm_connector_attach_encoder(connector, encoder); if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) radeon_encoder_add_backlight(radeon_encoder, connector); diff --git a/drivers/gpu/drm/radeon/radeon_family.h b/drivers/gpu/drm/radeon/radeon_family.h index 4b7b87f71a6371..afb9e6f9ff46a0 100644 --- a/drivers/gpu/drm/radeon/radeon_family.h +++ b/drivers/gpu/drm/radeon/radeon_family.h @@ -96,6 +96,7 @@ enum radeon_family { CHIP_BONAIRE, CHIP_KAVERI, CHIP_KABINI, + CHIP_LIVERPOOL, CHIP_HAWAII, CHIP_MULLINS, CHIP_LAST, diff --git a/drivers/gpu/drm/radeon/radeon_ib.c b/drivers/gpu/drm/radeon/radeon_ib.c index 92ce0e533bc01b..bbe651e86150cc 100644 --- a/drivers/gpu/drm/radeon/radeon_ib.c +++ b/drivers/gpu/drm/radeon/radeon_ib.c @@ -259,7 +259,7 @@ int radeon_ib_ring_tests(struct radeon_device *rdev) unsigned i; int r; - for (i = 0; i < RADEON_NUM_RINGS; ++i) { + for (i = 0; i < 1; ++i) { struct radeon_ring *ring = &rdev->ring[i]; if (!ring->ready) diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 4b6542538ff915..c0f3203ba36ef7 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1503,6 +1503,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_BONAIRE: case CHIP_KABINI: case CHIP_KAVERI: + case CHIP_LIVERPOOL: case CHIP_HAWAII: case CHIP_MULLINS: /* DPM requires the RLC, RV770+ dGPU requires SMC */ diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index dc4576e4d8ad16..4896ea0d25a40b 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -38,6 +38,8 @@ #define CIK_PFP_UCODE_SIZE 2144 #define CIK_ME_UCODE_SIZE 2144 #define CIK_CE_UCODE_SIZE 2144 +#define LIVERPOOL_PFP_UCODE_SIZE 4192 +#define LIVERPOOL_ME_UCODE_SIZE 4192 /* MEC */ #define CIK_MEC_UCODE_SIZE 4192 @@ -53,6 +55,7 @@ #define KB_RLC_UCODE_SIZE 2560 #define KV_RLC_UCODE_SIZE 2560 #define ML_RLC_UCODE_SIZE 2560 +#define LIVERPOOL_RLC_UCODE_SIZE 1536 /* MC */ #define BTC_MC_UCODE_SIZE 6024 diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index 95f4db70dd2232..e413cc7b65eedf 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -124,6 +124,7 @@ int radeon_uvd_init(struct radeon_device *rdev) case CHIP_BONAIRE: case CHIP_KABINI: case CHIP_KAVERI: + case CHIP_LIVERPOOL: case CHIP_HAWAII: case CHIP_MULLINS: legacy_fw_name = FIRMWARE_BONAIRE_LEGACY; diff --git a/drivers/gpu/drm/radeon/radeon_vce.c b/drivers/gpu/drm/radeon/radeon_vce.c index c1c619facb476a..cd92c6ee4e675d 100644 --- a/drivers/gpu/drm/radeon/radeon_vce.c +++ b/drivers/gpu/drm/radeon/radeon_vce.c @@ -76,6 +76,7 @@ int radeon_vce_init(struct radeon_device *rdev) case CHIP_BONAIRE: case CHIP_KAVERI: case CHIP_KABINI: + case CHIP_LIVERPOOL: case CHIP_HAWAII: case CHIP_MULLINS: fw_name = FIRMWARE_BONAIRE; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index f977df90d2a491..5d2b9269ab42f1 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -2532,10 +2532,11 @@ static int __init early_amd_iommu_init(void) if (!is_kdump_kernel() || amd_iommu_disabled) disable_iommus(); - if (amd_iommu_irq_remap) +/*. if (amd_iommu_irq_remap) amd_iommu_irq_remap = check_ioapic_information(); +*/ - if (amd_iommu_irq_remap) { + if (amd_iommu_irq_remap) { /* * Interrupt remapping enabled, create kmem_cache for the * remapping tables. diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index ab9e2b9010940b..5460b7244ac711 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -31,8 +31,8 @@ #include #include -#ifdef CONFIG_X86 -#include +#ifdef CONFIG_X86_PS4 +#include #endif #include "cqhci.h" @@ -315,6 +315,54 @@ static const struct sdhci_pci_fixes sdhci_intel_qrk = { .quirks = SDHCI_QUIRK_NO_HISPD_BIT, }; +#ifdef CONFIG_X86_PS4 +static int aeolia_probe(struct sdhci_pci_chip *chip) +{ + chip->num_slots = 1; + chip->first_bar = 0; + if (apcie_status() == 0) + return -EPROBE_DEFER; + + chip->pdev->class &= ~0x0000FF; + chip->pdev->class |= PCI_SDHCI_IFDMA; + return 0; +} + + static int aeolia_probe_slot(struct sdhci_pci_slot *slot) +{ + int err = apcie_assign_irqs(slot->chip->pdev, 1); + if (err <= 0) { + dev_err(&slot->chip->pdev->dev, "failed to get IRQ: %d\n", err); + return -ENODEV; + } + slot->host->irq = slot->chip->pdev->irq; + return 0; +} + + static void aeolia_remove_slot(struct sdhci_pci_slot *slot, int dead) +{ + apcie_free_irqs(slot->chip->pdev->irq, 1); +} + + static int aeolia_enable_dma(struct sdhci_pci_slot *slot) +{ + if (pci_set_dma_mask(slot->chip->pdev, DMA_BIT_MASK(31))) { + return -EINVAL; + } + if (pci_set_consistent_dma_mask(slot->chip->pdev, DMA_BIT_MASK(31))) { + return -EINVAL; + } + return 0; +} + + static const struct sdhci_pci_fixes sdhci_aeolia = { + .probe = aeolia_probe, + .probe_slot = aeolia_probe_slot, + .remove_slot = aeolia_remove_slot, + .enable_dma = aeolia_enable_dma, +}; +#endif + static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; @@ -1686,6 +1734,9 @@ static const struct pci_device_id pci_ids[] = { SDHCI_PCI_DEVICE(O2, SEABIRD1, o2), SDHCI_PCI_DEVICE(ARASAN, PHY_EMMC, arasan), SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps), +#ifdef CONFIG_X86_PS4 + SDHCI_PCI_DEVICE(SONY, ASDHCI, aeolia), +#endif SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd), /* Generic SD host controller */ {PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)}, @@ -1717,6 +1768,10 @@ int sdhci_pci_enable_dma(struct sdhci_host *host) pci_set_master(pdev); + if (slot->chip->fixes && slot->chip->fixes->enable_dma) { + return slot->chip->fixes->enable_dma(slot); + } + return 0; } @@ -1831,12 +1886,11 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = { \*****************************************************************************/ static struct sdhci_pci_slot *sdhci_pci_probe_slot( - struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar, - int slotno) + struct pci_dev *pdev, struct sdhci_pci_chip *chip, int slotno) { struct sdhci_pci_slot *slot; struct sdhci_host *host; - int ret, bar = first_bar + slotno; + int ret, bar = chip->first_bar + slotno; size_t priv_size = chip->fixes ? chip->fixes->priv_size : 0; if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { @@ -2076,6 +2130,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev, chip->allow_runtime_pm = chip->fixes->allow_runtime_pm; } chip->num_slots = slots; + chip->first_bar = first_bar; chip->pm_retune = true; chip->rpm_retune = true; @@ -2090,7 +2145,7 @@ static int sdhci_pci_probe(struct pci_dev *pdev, slots = chip->num_slots; /* Quirk may have changed this */ for (i = 0; i < slots; i++) { - slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); + slot = sdhci_pci_probe_slot(pdev, chip, i); if (IS_ERR(slot)) { for (i--; i >= 0; i--) sdhci_pci_remove_slot(chip->slots[i]); diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index e5dc6e44c7a4f4..7f91306c4ea9e9 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -128,6 +128,7 @@ struct sdhci_pci_fixes { int (*probe_slot) (struct sdhci_pci_slot *); int (*add_host) (struct sdhci_pci_slot *); void (*remove_slot) (struct sdhci_pci_slot *, int); + int (*enable_dma) (struct sdhci_pci_slot *); #ifdef CONFIG_PM_SLEEP int (*suspend) (struct sdhci_pci_chip *); @@ -169,6 +170,7 @@ struct sdhci_pci_chip { const struct sdhci_pci_fixes *fixes; int num_slots; /* Slots on controller */ + int first_bar; /* First valid BAR */ struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */ };