diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-07-01 22:53:43 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-07-01 22:53:43 +0300 |
commit | e058a84bfddc42ba356a2316f2cf1141974625c9 (patch) | |
tree | e6a02dd913e83f44ea9f5a779f9b9bd56d06a9e3 /drivers/gpu/drm/vc4 | |
parent | c288d9cd710433e5991d58a0764c4d08a933b871 (diff) | |
parent | 8a02ea42bc1d4c448caf1bab0e05899dad503f74 (diff) | |
download | linux-e058a84bfddc42ba356a2316f2cf1141974625c9.tar.xz |
Merge tag 'drm-next-2021-07-01' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"Highlights:
- AMD enables two more GPUs, with resulting header files
- i915 has started to move to TTM for discrete GPU and enable DG1
discrete GPU support (not by default yet)
- new HyperV drm driver
- vmwgfx adds arm64 support
- TTM refactoring ongoing
- 16bpc display support for AMD hw
Otherwise it's just the usual insane amounts of work all over the
place in lots of drivers and the core, as mostly summarised below:
Core:
- mark AGP ioctls as legacy
- disable force probing for non-master clients
- HDR metadata property helpers
- HDMI infoframe signal colorimetry support
- remove drm_device.pdev pointer
- remove DRM_KMS_FB_HELPER config option
- remove drm_pci_alloc/free
- drm_err_*/drm_dbg_* helpers
- use drm driver names for fbdev
- leaked DMA handle fix
- 16bpc fixed point format fourcc
- add prefetching memcpy for WC
- Documentation fixes
aperture:
- add aperture ownership helpers
dp:
- aux fixes
- downstream 0 port handling
- use extended base receiver capability DPCD
- Rename DP_PSR_SELECTIVE_UPDATE to better mach eDP spec
- mst: use khz as link rate during init
- VCPI fixes for StarTech hub
ttm:
- provide tt_shrink file via debugfs
- warn about freeing pinned BOs
- fix swapping error handling
- move page alignment into BO
- cleanup ttm_agp_backend
- add ttm_sys_manager
- don't override vm_ops
- ttm_bo_mmap removed
- make ttm_resource base of all managers
- remove VM_MIXEDMAP usage
panel:
- sysfs_emit support
- simple: runtime PM support
- simple: power up panel when reading EDID + caching
bridge:
- MHDP8546: HDCP support + DT bindings
- MHDP8546: Register DP AUX channel with userspace
- TI SN65DSI83 + SN65DSI84: add driver
- Sil8620: Fix module dependencies
- dw-hdmi: make CEC driver loading optional
- Ti-sn65dsi86: refclk fixes, subdrivers, runtime pm
- It66121: Add driver + DT bindings
- Adv7511: Support I2S IEC958 encoding
- Anx7625: fix power-on delay
- Nwi-dsi: Modesetting fixes; Cleanups
- lt6911: add missing MODULE_DEVICE_TABLE
- cdns: fix PM reference leak
hyperv:
- add new DRM driver for HyperV graphics
efifb:
- non-PCI device handling fixes
i915:
- refactor IP/device versioning
- XeLPD Display IP preperation work
- ADL-P enablement patches
- DG1 uAPI behind BROKEN
- disable mmap ioctl for discerte GPUs
- start enabling HuC loading for Gen12+
- major GuC backend rework for new platforms
- initial TTM support for Discrete GPUs
- locking rework for TTM prep
- use correct max source link rate for eDP
- %p4cc format printing
- GLK display fixes
- VLV DSI panel power fixes
- PSR2 disabled for RKL and ADL-S
- ACPI _DSM invalid access fixed
- DMC FW path abstraction
- ADL-S PCI ID update
- uAPI headers converted to kerneldoc
- initial LMEM support for DG1
- x86/gpu: add Jasperlake to gen11 early quirks
amdgpu:
- Aldebaran updates + initial SR-IOV
- new GPU: Beige Goby and Yellow Carp support
- more LTTPR display work
- Vangogh updates
- SDMA 5.x GCR fixes
- PCIe ASPM support
- Renoir TMZ enablement
- initial multiple eDP panel support
- use fdinfo to track devices/process info
- pin/unpin TTM fixes
- free resource on fence usage query
- fix fence calculation
- fix hotunplug/suspend issues
- GC/MM register access macro cleanup for SR-IOV
- W=1 fixes
- ACPI ATCS/ATIF handling rework
- 16bpc fixed point format support
- Initial smartshift support
- RV/PCO power tuning fixes
- new INFO query for additional vbios info
amdkfd:
- SR-IOV aldebaran support
- HMM SVM support
radeon:
- SMU regression fixes
- Oland flickering fix
vmwgfx:
- enable console with fbdev emulation
- fix cpu updates of coherent multisample surfaces
- remove reservation semaphore
- add initial SVGA3 support
- support arm64
msm:
- devcoredump support for display errors
- dpu/dsi: yaml bindings conversion
- mdp5: alpha/blend_mode/zpos support
- a6xx: cached coherent buffer support
- gpu iova fault improvement
- a660 support
rockchip:
- RK3036 win1 scaling support
- RK3066/3188 missing register support
- RK3036/3066/3126/3188 alpha support
mediatek:
- MT8167 HDMI support
- MT8183 DPI dual edge support
tegra:
- fixed YUV support/scaling on Tegra186+
ast:
- use pcim_iomap
- fix DP501 EDID
bochs:
- screen blanking support
etnaviv:
- export more GPU ID values to userspace
- add HWDB entry for GPU on i.MX8MP
- rework linear window calcs
exynos:
- pm runtime changes
imx:
- Annotate dma_fence critical section
- fix PRG modifiers after drmm conversion
- Add 8 pixel alignment fix for 1366x768
- fix YUV advertising
- add color properties
ingenic:
- IPU planes fix
panfrost:
- Mediatek MT8183 support + DT bindings
- export AFBC_FEATURES register to userspace
simpledrm:
- %pr for printing resources
nouveau:
- pin/unpin TTM fixes
qxl:
- unpin shadow BO
virtio:
- create dumb BOs as guest blob
vkms:
- drmm_universal_plane_alloc
- add XRGB plane composition
- overlay support"
* tag 'drm-next-2021-07-01' of git://anongit.freedesktop.org/drm/drm: (1570 commits)
drm/i915: Reinstate the mmap ioctl for some platforms
drm/i915/dsc: abstract helpers to get bigjoiner primary/secondary crtc
Revert "drm/msm/mdp5: provide dynamic bandwidth management"
drm/msm/mdp5: provide dynamic bandwidth management
drm/msm/mdp5: add perf blocks for holding fudge factors
drm/msm/mdp5: switch to standard zpos property
drm/msm/mdp5: add support for alpha/blend_mode properties
drm/msm/mdp5: use drm_plane_state for pixel blend mode
drm/msm/mdp5: use drm_plane_state for storing alpha value
drm/msm/mdp5: use drm atomic helpers to handle base drm plane state
drm/msm/dsi: do not enable PHYs when called for the slave DSI interface
drm/msm: Add debugfs to trigger shrinker
drm/msm/dpu: Avoid ABBA deadlock between IRQ modules
drm/msm: devcoredump iommu fault support
iommu/arm-smmu-qcom: Add stall support
drm/msm: Improve the a6xx page fault handler
iommu/arm-smmu-qcom: Add an adreno-smmu-priv callback to get pagefault info
iommu/arm-smmu: Add support for driver IOMMU fault handlers
drm/msm: export hangcheck_period in debugfs
drm/msm/a6xx: add support for Adreno 660 GPU
...
Diffstat (limited to 'drivers/gpu/drm/vc4')
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_crtc.c | 68 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi.c | 234 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi.h | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_kms.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_txp.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_vec.c | 27 |
9 files changed, 318 insertions, 55 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 76657dcdf9b0..18f5009ce90e 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -279,14 +279,22 @@ static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc, * allows drivers to push pixels to more than one encoder from the * same CRTC. */ -static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) +static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, + struct drm_atomic_state *state, + struct drm_connector_state *(*get_state)(struct drm_atomic_state *state, + struct drm_connector *connector)) { struct drm_connector *connector; struct drm_connector_list_iter conn_iter; drm_connector_list_iter_begin(crtc->dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->state->crtc == crtc) { + struct drm_connector_state *conn_state = get_state(state, connector); + + if (!conn_state) + continue; + + if (conn_state->crtc == crtc) { drm_connector_list_iter_end(&conn_iter); return connector->encoder; } @@ -305,16 +313,17 @@ static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc) CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR); } -static void vc4_crtc_config_pv(struct drm_crtc *crtc) +static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_new_connector_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); - struct drm_crtc_state *state = crtc->state; - struct drm_display_mode *mode = &state->adjusted_mode; + struct drm_crtc_state *crtc_state = crtc->state; + struct drm_display_mode *mode = &crtc_state->adjusted_mode; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1; bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 || @@ -421,10 +430,10 @@ static void require_hvs_enabled(struct drm_device *dev) } static int vc4_crtc_disable(struct drm_crtc *crtc, + struct drm_encoder *encoder, struct drm_atomic_state *state, unsigned int channel) { - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -465,10 +474,29 @@ static int vc4_crtc_disable(struct drm_crtc *crtc, return 0; } +static struct drm_encoder *vc4_crtc_get_encoder_by_type(struct drm_crtc *crtc, + enum vc4_encoder_type type) +{ + struct drm_encoder *encoder; + + drm_for_each_encoder(encoder, crtc->dev) { + struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); + + if (vc4_encoder->type == type) + return encoder; + } + + return NULL; +} + int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) { struct drm_device *drm = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + enum vc4_encoder_type encoder_type; + const struct vc4_pv_data *pv_data; + struct drm_encoder *encoder; + unsigned encoder_sel; int channel; if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node, @@ -487,7 +515,17 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) if (channel < 0) return 0; - return vc4_crtc_disable(crtc, NULL, channel); + encoder_sel = VC4_GET_FIELD(CRTC_READ(PV_CONTROL), PV_CONTROL_CLK_SELECT); + if (WARN_ON(encoder_sel != 0)) + return 0; + + pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc); + encoder_type = pv_data->encoder_types[encoder_sel]; + encoder = vc4_crtc_get_encoder_by_type(crtc, encoder_type); + if (WARN_ON(!encoder)) + return 0; + + return vc4_crtc_disable(crtc, encoder, NULL, channel); } static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, @@ -496,6 +534,8 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc); struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_old_connector_state); struct drm_device *dev = crtc->dev; require_hvs_enabled(dev); @@ -503,7 +543,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, /* Disable vblank irq handling before crtc is disabled. */ drm_crtc_vblank_off(crtc); - vc4_crtc_disable(crtc, state, old_vc4_state->assigned_channel); + vc4_crtc_disable(crtc, encoder, state, old_vc4_state->assigned_channel); /* * Make sure we issue a vblank event after disabling the CRTC if @@ -524,7 +564,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); - struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc); + struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state, + drm_atomic_get_new_connector_state); struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); require_hvs_enabled(dev); @@ -539,7 +580,7 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, if (vc4_encoder->pre_crtc_configure) vc4_encoder->pre_crtc_configure(encoder, state); - vc4_crtc_config_pv(crtc); + vc4_crtc_config_pv(crtc, state); CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN); @@ -994,7 +1035,7 @@ static const struct vc4_pv_data bcm2711_pv3_data = { .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { - [0] = VC4_ENCODER_TYPE_VEC, + [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC, }, }; @@ -1035,6 +1076,9 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct vc4_encoder *vc4_encoder; int i; + if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) + continue; + vc4_encoder = to_vc4_encoder(encoder); for (i = 0; i < ARRAY_SIZE(pv_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) { diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 556ad0f02a0d..8a60fb8ad370 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -30,6 +30,7 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <drm/drm_aperture.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> #include <drm/drm_fb_cma_helper.h> @@ -56,10 +57,8 @@ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index) res = platform_get_resource(dev, IORESOURCE_MEM, index); map = devm_ioremap_resource(&dev->dev, res); - if (IS_ERR(map)) { - DRM_ERROR("Failed to map registers: %ld\n", PTR_ERR(map)); + if (IS_ERR(map)) return map; - } return map; } @@ -266,7 +265,9 @@ static int vc4_drm_bind(struct device *dev) if (ret) goto unbind_all; - drm_fb_helper_remove_conflicting_framebuffers(NULL, "vc4drmfb", false); + ret = drm_aperture_remove_framebuffers(false, "vc4drmfb"); + if (ret) + goto unbind_all; ret = vc4_kms_load(drm); if (ret < 0) @@ -303,12 +304,21 @@ static const struct component_master_ops vc4_drm_ops = { .unbind = vc4_drm_unbind, }; +/* + * This list determines the binding order of our components, and we have + * a few constraints: + * - The TXP driver needs to be bound before the PixelValves (CRTC) + * but after the HVS to set the possible_crtc field properly + * - The HDMI driver needs to be bound after the HVS so that we can + * lookup the HVS maximum core clock rate and figure out if we + * support 4kp60 or not. + */ static struct platform_driver *const component_drivers[] = { + &vc4_hvs_driver, &vc4_hdmi_driver, &vc4_vec_driver, &vc4_dpi_driver, &vc4_dsi_driver, - &vc4_hvs_driver, &vc4_txp_driver, &vc4_crtc_driver, &vc4_v3d_driver, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index a7500716cf3f..5dceadc61600 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -825,7 +825,7 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc, void vc4_crtc_reset(struct drm_crtc *crtc); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); void vc4_crtc_get_margins(struct drm_crtc_state *state, - unsigned int *right, unsigned int *left, + unsigned int *left, unsigned int *right, unsigned int *top, unsigned int *bottom); /* vc4_debugfs.c */ diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 8106b5634fe1..aab1b36ceb3c 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -35,6 +35,7 @@ #include <drm/drm_edid.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> +#include <drm/drm_scdc_helper.h> #include <linux/clk.h> #include <linux/component.h> #include <linux/i2c.h> @@ -76,6 +77,8 @@ #define VC5_HDMI_VERTB_VSPO_SHIFT 16 #define VC5_HDMI_VERTB_VSPO_MASK VC4_MASK(29, 16) +#define VC5_HDMI_SCRAMBLER_CTL_ENABLE BIT(0) + #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_SHIFT 8 #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK VC4_MASK(10, 8) @@ -91,10 +94,14 @@ # define VC4_HD_M_ENABLE BIT(0) #define CEC_CLOCK_FREQ 40000 -#define VC4_HSM_MID_CLOCK 149985000 #define HDMI_14_MAX_TMDS_CLK (340 * 1000 * 1000) +static bool vc4_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode) +{ + return (mode->clock * 1000) > HDMI_14_MAX_TMDS_CLK; +} + static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; @@ -161,10 +168,9 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev)); - if (vc4_hdmi->hpd_gpio) { - if (gpio_get_value_cansleep(vc4_hdmi->hpd_gpio) ^ - vc4_hdmi->hpd_active_low) - connected = true; + if (vc4_hdmi->hpd_gpio && + gpiod_get_value_cansleep(vc4_hdmi->hpd_gpio)) { + connected = true; } else if (drm_probe_ddc(vc4_hdmi->ddc)) { connected = true; } else if (HDMI_READ(HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) { @@ -215,9 +221,47 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) ret = drm_add_edid_modes(connector, edid); kfree(edid); + if (vc4_hdmi->disable_4kp60) { + struct drm_device *drm = connector->dev; + struct drm_display_mode *mode; + + list_for_each_entry(mode, &connector->probed_modes, head) { + if (vc4_hdmi_mode_needs_scrambling(mode)) { + drm_warn_once(drm, "The core clock cannot reach frequencies high enough to support 4k @ 60Hz."); + drm_warn_once(drm, "Please change your config.txt file to add hdmi_enable_4kp60."); + } + } + } + return ret; } +static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct drm_connector_state *old_state = + drm_atomic_get_old_connector_state(state, connector); + struct drm_connector_state *new_state = + drm_atomic_get_new_connector_state(state, connector); + struct drm_crtc *crtc = new_state->crtc; + + if (!crtc) + return 0; + + if (old_state->colorspace != new_state->colorspace || + !drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->mode_changed = true; + } + + return 0; +} + static void vc4_hdmi_connector_reset(struct drm_connector *connector) { struct vc4_hdmi_connector_state *old_state = @@ -267,6 +311,7 @@ static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { .get_modes = vc4_hdmi_connector_get_modes, + .atomic_check = vc4_hdmi_connector_atomic_check, }; static int vc4_hdmi_connector_init(struct drm_device *dev, @@ -294,6 +339,11 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, if (ret) return ret; + ret = drm_mode_create_hdmi_colorspace_property(connector); + if (ret) + return ret; + + drm_connector_attach_colorspace_property(connector); drm_connector_attach_tv_margin_properties(connector); drm_connector_attach_max_bpc_property(connector, 8, 12); @@ -303,6 +353,9 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, connector->interlace_allowed = 1; connector->doublescan_allowed = 0; + if (vc4_hdmi->variant->supports_hdr) + drm_connector_attach_hdr_output_metadata_property(connector); + drm_connector_attach_encoder(connector, encoder); return 0; @@ -399,7 +452,7 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) vc4_encoder->limited_rgb_range ? HDMI_QUANTIZATION_RANGE_LIMITED : HDMI_QUANTIZATION_RANGE_FULL); - + drm_hdmi_avi_infoframe_colorspace(&frame.avi, cstate); drm_hdmi_avi_infoframe_bars(&frame.avi, cstate); vc4_hdmi_write_infoframe(encoder, &frame); @@ -436,6 +489,25 @@ static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) vc4_hdmi_write_infoframe(encoder, &frame); } +static void vc4_hdmi_set_hdr_infoframe(struct drm_encoder *encoder) +{ + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_connector *connector = &vc4_hdmi->connector; + struct drm_connector_state *conn_state = connector->state; + union hdmi_infoframe frame; + + if (!vc4_hdmi->variant->supports_hdr) + return; + + if (!conn_state->hdr_output_metadata) + return; + + if (drm_hdmi_infoframe_set_hdr_metadata(&frame.drm, conn_state)) + return; + + vc4_hdmi_write_infoframe(encoder, &frame); +} + static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); @@ -448,6 +520,90 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) */ if (vc4_hdmi->audio.streaming) vc4_hdmi_set_audio_infoframe(encoder); + + vc4_hdmi_set_hdr_infoframe(encoder); +} + +static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_display_info *display = &vc4_hdmi->connector.display_info; + + if (!vc4_encoder->hdmi_monitor) + return false; + + if (!display->hdmi.scdc.supported || + !display->hdmi.scdc.scrambling.supported) + return false; + + return true; +} + +#define SCRAMBLING_POLLING_DELAY_MS 1000 + +static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) +{ + struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + + if (!vc4_hdmi_supports_scrambling(encoder, mode)) + return; + + if (!vc4_hdmi_mode_needs_scrambling(mode)) + return; + + drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true); + drm_scdc_set_scrambling(vc4_hdmi->ddc, true); + + HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) | + VC5_HDMI_SCRAMBLER_CTL_ENABLE); + + queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work, + msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS)); +} + +static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder) +{ + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_crtc *crtc = encoder->crtc; + + /* + * At boot, encoder->crtc will be NULL. Since we don't know the + * state of the scrambler and in order to avoid any + * inconsistency, let's disable it all the time. + */ + if (crtc && !vc4_hdmi_supports_scrambling(encoder, &crtc->mode)) + return; + + if (crtc && !vc4_hdmi_mode_needs_scrambling(&crtc->mode)) + return; + + if (delayed_work_pending(&vc4_hdmi->scrambling_work)) + cancel_delayed_work_sync(&vc4_hdmi->scrambling_work); + + HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) & + ~VC5_HDMI_SCRAMBLER_CTL_ENABLE); + + drm_scdc_set_scrambling(vc4_hdmi->ddc, false); + drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false); +} + +static void vc4_hdmi_scrambling_wq(struct work_struct *work) +{ + struct vc4_hdmi *vc4_hdmi = container_of(to_delayed_work(work), + struct vc4_hdmi, + scrambling_work); + + if (drm_scdc_get_scrambling_status(vc4_hdmi->ddc)) + return; + + drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true); + drm_scdc_set_scrambling(vc4_hdmi->ddc, true); + + queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work, + msecs_to_jiffies(SCRAMBLING_POLLING_DELAY_MS)); } static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, @@ -462,6 +618,8 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX); + + vc4_hdmi_disable_scrambling(encoder); } static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, @@ -742,10 +900,10 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, conn_state_to_vc4_hdmi_conn_state(conn_state); struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); - unsigned long pixel_rate, hsm_rate; + unsigned long bvb_rate, pixel_rate, hsm_rate; int ret; - ret = pm_runtime_get_sync(&vc4_hdmi->pdev->dev); + ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to retain power domain: %d\n", ret); return; @@ -789,12 +947,14 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, vc4_hdmi_cec_update_clk_div(vc4_hdmi); - /* - * FIXME: When the pixel freq is 594MHz (4k60), this needs to be setup - * at 300MHz. - */ - ret = clk_set_min_rate(vc4_hdmi->pixel_bvb_clock, - (hsm_rate > VC4_HSM_MID_CLOCK ? 150000000 : 75000000)); + if (pixel_rate > 297000000) + bvb_rate = 300000000; + else if (pixel_rate > 148500000) + bvb_rate = 150000000; + else + bvb_rate = 75000000; + + ret = clk_set_min_rate(vc4_hdmi->pixel_bvb_clock, bvb_rate); if (ret) { DRM_ERROR("Failed to set pixel bvb clock rate: %d\n", ret); clk_disable_unprepare(vc4_hdmi->pixel_clock); @@ -900,6 +1060,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, } vc4_hdmi_recenter_fifo(vc4_hdmi); + vc4_hdmi_enable_scrambling(encoder); } static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) @@ -952,6 +1113,9 @@ static int vc4_hdmi_encoder_atomic_check(struct drm_encoder *encoder, if (pixel_rate > vc4_hdmi->variant->max_pixel_clock) return -EINVAL; + if (vc4_hdmi->disable_4kp60 && (pixel_rate > HDMI_14_MAX_TMDS_CLK)) + return -EINVAL; + vc4_state->pixel_rate = pixel_rate; return 0; @@ -971,6 +1135,9 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder, if ((mode->clock * 1000) > vc4_hdmi->variant->max_pixel_clock) return MODE_CLOCK_HIGH; + if (vc4_hdmi->disable_4kp60 && vc4_hdmi_mode_needs_scrambling(mode)) + return MODE_CLOCK_HIGH; + return MODE_OK; } @@ -1954,12 +2121,12 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) struct vc4_hdmi *vc4_hdmi; struct drm_encoder *encoder; struct device_node *ddc_node; - u32 value; int ret; vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); if (!vc4_hdmi) return -ENOMEM; + INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq); dev_set_drvdata(dev, vc4_hdmi); encoder = &vc4_hdmi->encoder.base.base; @@ -1992,26 +2159,34 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) /* Only use the GPIO HPD pin if present in the DT, otherwise * we'll use the HDMI core's register. */ - if (of_find_property(dev->of_node, "hpd-gpios", &value)) { - enum of_gpio_flags hpd_gpio_flags; - - vc4_hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node, - "hpd-gpios", 0, - &hpd_gpio_flags); - if (vc4_hdmi->hpd_gpio < 0) { - ret = vc4_hdmi->hpd_gpio; - goto err_unprepare_hsm; - } - - vc4_hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW; + vc4_hdmi->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); + if (IS_ERR(vc4_hdmi->hpd_gpio)) { + ret = PTR_ERR(vc4_hdmi->hpd_gpio); + goto err_put_ddc; } vc4_hdmi->disable_wifi_frequencies = of_property_read_bool(dev->of_node, "wifi-2.4ghz-coexistence"); + if (variant->max_pixel_clock == 600000000) { + struct vc4_dev *vc4 = to_vc4_dev(drm); + long max_rate = clk_round_rate(vc4->hvs->core_clk, 550000000); + + if (max_rate < 550000000) + vc4_hdmi->disable_4kp60 = true; + } + if (vc4_hdmi->variant->reset) vc4_hdmi->variant->reset(vc4_hdmi); + if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") || + of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) && + HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) { + clk_prepare_enable(vc4_hdmi->pixel_clock); + clk_prepare_enable(vc4_hdmi->hsm_clock); + clk_prepare_enable(vc4_hdmi->pixel_bvb_clock); + } + pm_runtime_enable(dev); drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); @@ -2041,8 +2216,8 @@ err_destroy_conn: vc4_hdmi_connector_destroy(&vc4_hdmi->connector); err_destroy_encoder: drm_encoder_cleanup(encoder); -err_unprepare_hsm: pm_runtime_disable(dev); +err_put_ddc: put_device(&vc4_hdmi->ddc->dev); return ret; @@ -2119,6 +2294,7 @@ static const struct vc4_hdmi_variant bcm2835_variant = { .phy_rng_enable = vc4_hdmi_phy_rng_enable, .phy_rng_disable = vc4_hdmi_phy_rng_disable, .channel_map = vc4_hdmi_channel_map, + .supports_hdr = false, }; static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { @@ -2146,6 +2322,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi0_variant = { .phy_rng_enable = vc5_hdmi_phy_rng_enable, .phy_rng_disable = vc5_hdmi_phy_rng_disable, .channel_map = vc5_hdmi_channel_map, + .supports_hdr = true, }; static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { @@ -2173,6 +2350,7 @@ static const struct vc4_hdmi_variant bcm2711_hdmi1_variant = { .phy_rng_enable = vc5_hdmi_phy_rng_enable, .phy_rng_disable = vc5_hdmi_phy_rng_disable, .channel_map = vc5_hdmi_channel_map, + .supports_hdr = true, }; static const struct of_device_id vc4_hdmi_dt_match[] = { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index 3cebd1fd00fc..884d245507a9 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -99,6 +99,9 @@ struct vc4_hdmi_variant { /* Callback to get channel map */ u32 (*channel_map)(struct vc4_hdmi *vc4_hdmi, u32 channel_mask); + + /* Enables HDR metadata */ + bool supports_hdr; }; /* HDMI audio information */ @@ -126,6 +129,8 @@ struct vc4_hdmi { struct vc4_hdmi_encoder encoder; struct drm_connector connector; + struct delayed_work scrambling_work; + struct i2c_adapter *ddc; void __iomem *hdmicore_regs; void __iomem *hd_regs; @@ -143,8 +148,7 @@ struct vc4_hdmi { /* VC5 Only */ void __iomem *rm_regs; - int hpd_gpio; - bool hpd_active_low; + struct gpio_desc *hpd_gpio; /* * On some systems (like the RPi4), some modes are in the same @@ -154,6 +158,14 @@ struct vc4_hdmi { */ bool disable_wifi_frequencies; + /* + * Even if HDMI0 on the RPi4 can output modes requiring a pixel + * rate higher than 297MHz, it needs some adjustments in the + * config.txt file to be able to do so and thus won't always be + * available. + */ + bool disable_4kp60; + struct cec_adapter *cec_adap; struct cec_msg cec_rx_msg; bool cec_tx_ok; diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h index e1b58eac766f..19d2fdc446bc 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h @@ -100,6 +100,7 @@ enum vc4_hdmi_field { HDMI_RM_FORMAT, HDMI_RM_OFFSET, HDMI_SCHEDULER_CONTROL, + HDMI_SCRAMBLER_CTL, HDMI_SW_RESET_CONTROL, HDMI_TX_PHY_CHANNEL_SWAP, HDMI_TX_PHY_CLK_DIV, @@ -238,6 +239,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi0_fields[] = { VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x178), VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x17c), VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8), + VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), @@ -317,6 +319,7 @@ static const struct vc4_hdmi_register __maybe_unused vc5_hdmi_hdmi1_fields[] = { VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x178), VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x17c), VC4_HDMI_REG(HDMI_HOTPLUG, 0x1a8), + VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1c4), VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc), VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f0), diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 948b3a58aad1..6a1a9e1d72ce 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -899,7 +899,6 @@ int vc4_kms_load(struct drm_device *dev) dev->mode_config.helper_private = &vc4_mode_config_helpers; dev->mode_config.preferred_depth = 24; dev->mode_config.async_page_flip = true; - dev->mode_config.allow_fb_modifiers = true; ret = vc4_ctm_obj_init(vc4); if (ret) diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index c0122d83b651..2fc7f4b5fa09 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -507,7 +507,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) return ret; encoder = &txp->connector.encoder; - encoder->possible_crtcs |= drm_crtc_mask(crtc); + encoder->possible_crtcs = drm_crtc_mask(crtc); ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0, dev_name(dev), txp); diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 090529d0d5dc..11fc3d6f66b1 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -154,9 +154,14 @@ #define VEC_DAC_MISC_DAC_RST_N BIT(0) +struct vc4_vec_variant { + u32 dac_config; +}; + /* General VEC hardware state. */ struct vc4_vec { struct platform_device *pdev; + const struct vc4_vec_variant *variant; struct drm_encoder *encoder; struct drm_connector *connector; @@ -445,10 +450,7 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) VEC_WRITE(VEC_CONFIG2, VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS); VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD); - VEC_WRITE(VEC_DAC_CONFIG, - VEC_DAC_CONFIG_DAC_CTRL(0xc) | - VEC_DAC_CONFIG_DRIVER_CTRL(0xc) | - VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x46)); + VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config); /* Mask all interrupts. */ VEC_WRITE(VEC_MASK0, 0); @@ -501,8 +503,21 @@ static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { .atomic_mode_set = vc4_vec_encoder_atomic_mode_set, }; +static const struct vc4_vec_variant bcm2835_vec_variant = { + .dac_config = VEC_DAC_CONFIG_DAC_CTRL(0xc) | + VEC_DAC_CONFIG_DRIVER_CTRL(0xc) | + VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x46) +}; + +static const struct vc4_vec_variant bcm2711_vec_variant = { + .dac_config = VEC_DAC_CONFIG_DAC_CTRL(0x0) | + VEC_DAC_CONFIG_DRIVER_CTRL(0x80) | + VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x61) +}; + static const struct of_device_id vc4_vec_dt_match[] = { - { .compatible = "brcm,bcm2835-vec", .data = NULL }, + { .compatible = "brcm,bcm2835-vec", .data = &bcm2835_vec_variant }, + { .compatible = "brcm,bcm2711-vec", .data = &bcm2711_vec_variant }, { /* sentinel */ }, }; @@ -540,6 +555,8 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) vec->encoder = &vc4_vec_encoder->base.base; vec->pdev = pdev; + vec->variant = (const struct vc4_vec_variant *) + of_device_get_match_data(dev); vec->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(vec->regs)) return PTR_ERR(vec->regs); |