summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2017-05-18 12:57:06 +1000
committerDave Airlie <airlied@redhat.com>2017-05-18 12:57:06 +1000
commite98c58e55f68f8785aebfab1f8c9a03d8de0afe1 (patch)
tree8357e8fda6efb0867ac39fc6b9211a579721d00a /drivers/gpu/drm
parent2ea659a9ef488125eb46da6eb571de5eae5c43f6 (diff)
parent9cf8f5802f39d9991158b29033c852bccfc3a4d4 (diff)
Merge tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc into drm-next
UAPI Changes: - Return -ENODEV instead of -ENXIO when creating cma fb w/o valid gem (Daniel) - Add aspect ratio and custom scaling propertis to connector state (Maarten) Cross-subsystem Changes: - None Core Changes: - Add Laurent as bridge reviewer and Andrzej as bridge maintainer (Archit) - Maintain new STM driver through -misc (Yannick) - Misc doc improvements (as is tradition) (Daniel) - Add driver-private objects to atomic state (Dhinakaran) - Deprecate preclose hook in modern drivers (use postclose) (Daniel) - Add hwmode to vblank struct. This fixes mode access in irq context and reduced a bunch of boilerplate (Daniel) Driver Changes: - vc4: Add out-fence support to vc4 V3D rendering (Eric) - stm: Add stm32f429 display hw and am-480272h3tmqw-t01h panel support (Yannick) - vc4: Remove 256MB cma limit from vc4 (Eric) - dw-hdmi: Disable audio when inactive, instead of always enabled (Romain) - zte: Add support for VGA to the ZTE driver (Shawn) - i915: Track DP MST bandwidth and check it in atomic_check (Dhinakaran) - vgem: Enable gem dmabuf import iface to facilitate ion testing (Laura) - vc4: Add support for Cygnus (new dt compat string + couple bug fixes) (Eric) - pl111: Add driver for pl111 CLCD display controller (Eric/Tom) - vgem: Subclass drm_device instead of standalone platform device (Chris) Cc: Archit Taneja <architt@codeaurora.org> Cc: Eric Anholt <eric@anholt.net> Cc: Yannick Fertre <yannick.fertre@st.com> Cc: Romain Perier <romain.perier@collabora.com> Cc: Navare, Manasi D <manasi.d.navare@intel.com> Cc: Shawn Guo <shawn.guo@linaro.org> Cc: Dhinakaran Pandiyan <dhinakaran.pandiyan@intel.com> Cc: Laura Abbott <labbott@redhat.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Tom Cooksey <tom.cooksey@arm.com> Cc: Daniel Vetter <daniel.vetter@intel.com> Cc: Chris Wilson <chris@chris-wilson.co.uk> * tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc: (72 commits) drm: add missing declaration to drm_blend.h drm/dp: Wait up all outstanding tx waiters drm/dp: Read the tx msg state once after checking for an event drm/prime: Forward declare struct device drm/vblank: Lock down vblank->hwmode more drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos drm/vblank: Add FIXME comments about moving the vblank ts hooks drm/vblank: Switch to bool in_vblank_irq in get_vblank_timestamp drm/vblank: Switch drm_driver->get_vblank_timestamp to return a bool drm/vgem: Convert to a struct drm_device subclass gpu: drm: gma500: remove dead code drm/sti: Adjust two checks for null pointers in sti_hqvdp_probe() drm/sti: Fix typos in a comment line drm/sti: Fix a typo in a comment line drm/sti: Replace 17 seq_puts() calls by seq_putc() drm/sti: Reduce function calls for sequence output at five places drm/sti: use seq_puts to display a string drm: Nerf the preclose callback for modern drivers drm/exynos: Merge pre/postclose hooks drm/tegra: switch to postclose ...
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/Kconfig4
-rw-r--r--drivers/gpu/drm/Makefile2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c41
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h3
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c6
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c46
-rw-r--r--drivers/gpu/drm/drm_atomic.c87
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c12
-rw-r--r--drivers/gpu/drm/drm_color_mgmt.c9
-rw-r--r--drivers/gpu/drm/drm_connector.c64
-rw-r--r--drivers/gpu/drm/drm_dp_mst_topology.c164
-rw-r--r--drivers/gpu/drm/drm_fb_cma_helper.c29
-rw-r--r--drivers/gpu/drm/drm_file.c8
-rw-r--r--drivers/gpu/drm/drm_irq.c129
-rw-r--r--drivers/gpu/drm/drm_plane_helper.c11
-rw-r--r--drivers/gpu/drm/drm_prime.c30
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c8
-rw-r--r--drivers/gpu/drm/gma500/mdfld_tpo_vid.c51
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c62
-rw-r--r--drivers/gpu/drm/i915/intel_display.c11
-rw-r--r--drivers/gpu/drm/i915/intel_dp_mst.c48
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h1
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c18
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c23
-rw-r--r--drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c45
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c39
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c11
-rw-r--r--drivers/gpu/drm/pl111/Kconfig12
-rw-r--r--drivers/gpu/drm/pl111/Makefile5
-rw-r--r--drivers/gpu/drm/pl111/pl111_connector.c127
-rw-r--r--drivers/gpu/drm/pl111/pl111_display.c344
-rw-r--r--drivers/gpu/drm/pl111/pl111_drm.h56
-rw-r--r--drivers/gpu/drm/pl111/pl111_drv.c272
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c18
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c37
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h3
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c14
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.h3
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c20
-rw-r--r--drivers/gpu/drm/selftests/test-drm_mm.c28
-rw-r--r--drivers/gpu/drm/sti/sti_cursor.c5
-rw-r--r--drivers/gpu/drm/sti/sti_dvo.c3
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.c5
-rw-r--r--drivers/gpu/drm/sti/sti_hda.c9
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c23
-rw-r--r--drivers/gpu/drm/sti/sti_hqvdp.c7
-rw-r--r--drivers/gpu/drm/sti/sti_mixer.c3
-rw-r--r--drivers/gpu/drm/sti/sti_tvout.c7
-rw-r--r--drivers/gpu/drm/sti/sti_vid.c5
-rw-r--r--drivers/gpu/drm/stm/Kconfig16
-rw-r--r--drivers/gpu/drm/stm/Makefile7
-rw-r--r--drivers/gpu/drm/stm/drv.c221
-rw-r--r--drivers/gpu/drm/stm/ltdc.c1160
-rw-r--r--drivers/gpu/drm/stm/ltdc.h40
-rw-r--r--drivers/gpu/drm/tegra/drm.c4
-rw-r--r--drivers/gpu/drm/vc4/Makefile1
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c37
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c34
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c6
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h70
-rw-r--r--drivers/gpu/drm/vc4/vc4_fence.c56
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c161
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c174
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c65
-rw-r--r--drivers/gpu/drm/vc4/vc4_kms.c10
-rw-r--r--drivers/gpu/drm/vc4/vc4_render_cl.c3
-rw-r--r--drivers/gpu/drm/vc4/vc4_v3d.c180
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate.c54
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c194
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.h2
-rw-r--r--drivers/gpu/drm/zte/Makefile1
-rw-r--r--drivers/gpu/drm/zte/zx_common_regs.h31
-rw-r--r--drivers/gpu/drm/zte/zx_drm_drv.c1
-rw-r--r--drivers/gpu/drm/zte/zx_drm_drv.h1
-rw-r--r--drivers/gpu/drm/zte/zx_plane.c1
-rw-r--r--drivers/gpu/drm/zte/zx_plane_regs.h18
-rw-r--r--drivers/gpu/drm/zte/zx_vga.c531
-rw-r--r--drivers/gpu/drm/zte/zx_vga_regs.h36
-rw-r--r--drivers/gpu/drm/zte/zx_vou.c36
-rw-r--r--drivers/gpu/drm/zte/zx_vou_regs.h12
83 files changed, 4396 insertions, 761 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 78d7fc0ebb57..83cb2a88c204 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -246,6 +246,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
+source "drivers/gpu/drm/stm/Kconfig"
+
source "drivers/gpu/drm/panel/Kconfig"
source "drivers/gpu/drm/bridge/Kconfig"
@@ -274,6 +276,8 @@ source "drivers/gpu/drm/meson/Kconfig"
source "drivers/gpu/drm/tinydrm/Kconfig"
+source "drivers/gpu/drm/pl111/Kconfig"
+
# Keep legacy drivers last
menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 59f0f9b696eb..c156fecfb362 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/
obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STM) += stm/
obj-$(CONFIG_DRM_STI) += sti/
obj-$(CONFIG_DRM_IMX) += imx/
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
@@ -96,3 +97,4 @@ obj-y += hisilicon/
obj-$(CONFIG_DRM_ZTE) += zte/
obj-$(CONFIG_DRM_MXSFB) += mxsfb/
obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
+obj-$(CONFIG_DRM_PL111) += pl111/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 833c3c16501a..67cdab9241a4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -1912,10 +1912,6 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon);
u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
-int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
- int *max_error,
- struct timeval *vblank_time,
- unsigned flags);
long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index f2d705e6a75a..5cb8f3e68447 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -715,6 +715,16 @@ static const struct file_operations amdgpu_driver_kms_fops = {
#endif
};
+static bool
+amdgpu_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
+{
+ return amdgpu_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
+ stime, etime, mode);
+}
+
static struct drm_driver kms_driver = {
.driver_features =
DRIVER_USE_AGP |
@@ -729,8 +739,8 @@ static struct drm_driver kms_driver = {
.get_vblank_counter = amdgpu_get_vblank_counter_kms,
.enable_vblank = amdgpu_enable_vblank_kms,
.disable_vblank = amdgpu_disable_vblank_kms,
- .get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms,
- .get_scanout_position = amdgpu_get_crtc_scanoutpos,
+ .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
+ .get_scanout_position = amdgpu_get_crtc_scanout_position,
#if defined(CONFIG_DEBUG_FS)
.debugfs_init = amdgpu_debugfs_init,
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 96c341670782..dca4be970d13 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -945,47 +945,6 @@ void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe)
amdgpu_irq_put(adev, &adev->crtc_irq, idx);
}
-/**
- * amdgpu_get_vblank_timestamp_kms - get vblank timestamp
- *
- * @dev: drm dev pointer
- * @crtc: crtc to get the timestamp for
- * @max_error: max error
- * @vblank_time: time value
- * @flags: flags passed to the driver
- *
- * Gets the timestamp on the requested crtc based on the
- * scanout position. (all asics).
- * Returns postive status flags on success, negative error on failure.
- */
-int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
- int *max_error,
- struct timeval *vblank_time,
- unsigned flags)
-{
- struct drm_crtc *crtc;
- struct amdgpu_device *adev = dev->dev_private;
-
- if (pipe >= dev->num_crtcs) {
- DRM_ERROR("Invalid crtc %u\n", pipe);
- return -EINVAL;
- }
-
- /* Get associated drm_crtc: */
- crtc = &adev->mode_info.crtcs[pipe]->base;
- if (!crtc) {
- /* This can occur on driver load if some component fails to
- * initialize completely and driver is unloaded */
- DRM_ERROR("Uninitialized crtc %d\n", pipe);
- return -EINVAL;
- }
-
- /* Helper routine in DRM core does all the work: */
- return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
- vblank_time, flags,
- &crtc->hwmode);
-}
-
const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index dbd10618ec20..43a9d3aec6c4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -534,6 +534,9 @@ struct amdgpu_framebuffer {
((em) == ATOM_ENCODER_MODE_DP_MST))
/* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
+#define DRM_SCANOUTPOS_VALID (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE (1 << 2)
#define USE_REAL_VBLANKSTART (1 << 30)
#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index 9126d0306ab5..9b87067c022c 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -160,7 +160,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
time_before(jiffies, timeout));
if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
- dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus");
+ dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus\n");
return -ETIMEDOUT;
}
@@ -202,7 +202,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
- dev_err(&sii902x->i2c->dev, "failed to release the i2c bus");
+ dev_err(&sii902x->i2c->dev, "failed to release the i2c bus\n");
return -ETIMEDOUT;
}
@@ -298,7 +298,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge)
if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
dev_err(&sii902x->i2c->dev,
- "sii902x driver is only compatible with DRM devices supporting atomic updates");
+ "sii902x driver is only compatible with DRM devices supporting atomic updates\n");
return -ENOTSUPP;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 4e1f54a675d8..8737de8c1c52 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -173,6 +173,8 @@ struct dw_hdmi {
unsigned int reg_shift;
struct regmap *regm;
+ void (*enable_audio)(struct dw_hdmi *hdmi);
+ void (*disable_audio)(struct dw_hdmi *hdmi);
};
#define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -542,13 +544,41 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
+static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
+{
+ hdmi_modb(hdmi, enable ? 0 : HDMI_MC_CLKDIS_AUDCLK_DISABLE,
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
+{
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+}
+
+static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
+{
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+}
+
+static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
+{
+ hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+ hdmi_enable_audio_clk(hdmi, true);
+}
+
+static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
+{
+ hdmi_enable_audio_clk(hdmi, false);
+}
+
void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
{
unsigned long flags;
spin_lock_irqsave(&hdmi->audio_lock, flags);
hdmi->audio_enable = true;
- hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+ if (hdmi->enable_audio)
+ hdmi->enable_audio(hdmi);
spin_unlock_irqrestore(&hdmi->audio_lock, flags);
}
EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
@@ -559,7 +589,8 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
spin_lock_irqsave(&hdmi->audio_lock, flags);
hdmi->audio_enable = false;
- hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+ if (hdmi->disable_audio)
+ hdmi->disable_audio(hdmi);
spin_unlock_irqrestore(&hdmi->audio_lock, flags);
}
EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
@@ -1573,11 +1604,6 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
HDMI_MC_FLOWCTRL);
}
-static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
-{
- hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
-}
-
/* Workaround to clear the overflow condition */
static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
{
@@ -1691,7 +1717,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
/* HDMI Initialization Step E - Configure audio */
hdmi_clk_regenerator_update_pixel_clock(hdmi);
- hdmi_enable_audio_clk(hdmi);
+ hdmi_enable_audio_clk(hdmi, true);
}
/* not for DVI mode */
@@ -2403,6 +2429,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
audio.irq = irq;
audio.hdmi = hdmi;
audio.eld = hdmi->connector.eld;
+ hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
+ hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
pdevinfo.name = "dw-hdmi-ahb-audio";
pdevinfo.data = &audio;
@@ -2415,6 +2443,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
audio.hdmi = hdmi;
audio.write = hdmi_writeb;
audio.read = hdmi_readb;
+ hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
+ hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
pdevinfo.name = "dw-hdmi-i2s-audio";
pdevinfo.data = &audio;
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index f32506a7c1d6..cdec19a86af3 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -57,6 +57,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state)
kfree(state->connectors);
kfree(state->crtcs);
kfree(state->planes);
+ kfree(state->private_objs);
}
EXPORT_SYMBOL(drm_atomic_state_default_release);
@@ -184,6 +185,17 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
state->planes[i].ptr = NULL;
state->planes[i].state = NULL;
}
+
+ for (i = 0; i < state->num_private_objs; i++) {
+ void *obj_state = state->private_objs[i].obj_state;
+
+ state->private_objs[i].funcs->destroy_state(obj_state);
+ state->private_objs[i].obj = NULL;
+ state->private_objs[i].obj_state = NULL;
+ state->private_objs[i].funcs = NULL;
+ }
+ state->num_private_objs = 0;
+
}
EXPORT_SYMBOL(drm_atomic_state_default_clear);
@@ -425,7 +437,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob,
}
static int
-drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
+drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
struct drm_property_blob **blob,
uint64_t blob_id,
ssize_t expected_size,
@@ -434,7 +446,7 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
struct drm_property_blob *new_blob = NULL;
if (blob_id != 0) {
- new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
+ new_blob = drm_property_lookup_blob(dev, blob_id);
if (new_blob == NULL)
return -EINVAL;
@@ -483,7 +495,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
drm_property_blob_put(mode);
return ret;
} else if (property == config->degamma_lut_property) {
- ret = drm_atomic_replace_property_blob_from_id(crtc,
+ ret = drm_atomic_replace_property_blob_from_id(dev,
&state->degamma_lut,
val,
-1,
@@ -491,7 +503,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
state->color_mgmt_changed |= replaced;
return ret;
} else if (property == config->ctm_property) {
- ret = drm_atomic_replace_property_blob_from_id(crtc,
+ ret = drm_atomic_replace_property_blob_from_id(dev,
&state->ctm,
val,
sizeof(struct drm_color_ctm),
@@ -499,7 +511,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
state->color_mgmt_changed |= replaced;
return ret;
} else if (property == config->gamma_lut_property) {
- ret = drm_atomic_replace_property_blob_from_id(crtc,
+ ret = drm_atomic_replace_property_blob_from_id(dev,
&state->gamma_lut,
val,
-1,
@@ -978,6 +990,59 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
}
/**
+ * drm_atomic_get_private_obj_state - get private object state
+ * @state: global atomic state
+ * @obj: private object to get the state for
+ * @funcs: pointer to the struct of function pointers that identify the object
+ * type
+ *
+ * This function returns the private object state for the given private object,
+ * allocating the state if needed. It does not grab any locks as the caller is
+ * expected to care of any required locking.
+ *
+ * RETURNS:
+ *
+ * Either the allocated state or the error code encoded into a pointer.
+ */
+void *
+drm_atomic_get_private_obj_state(struct drm_atomic_state *state, void *obj,
+ const struct drm_private_state_funcs *funcs)
+{
+ int index, num_objs, i;
+ size_t size;
+ struct __drm_private_objs_state *arr;
+
+ for (i = 0; i < state->num_private_objs; i++)
+ if (obj == state->private_objs[i].obj &&
+ state->private_objs[i].obj_state)
+ return state->private_objs[i].obj_state;
+
+ num_objs = state->num_private_objs + 1;
+ size = sizeof(*state->private_objs) * num_objs;
+ arr = krealloc(state->private_objs, size, GFP_KERNEL);
+ if (!arr)
+ return ERR_PTR(-ENOMEM);
+
+ state->private_objs = arr;
+ index = state->num_private_objs;
+ memset(&state->private_objs[index], 0, sizeof(*state->private_objs));
+
+ state->private_objs[index].obj_state = funcs->duplicate_state(state, obj);
+ if (!state->private_objs[index].obj_state)
+ return ERR_PTR(-ENOMEM);
+
+ state->private_objs[index].obj = obj;
+ state->private_objs[index].funcs = funcs;
+ state->num_private_objs = num_objs;
+
+ DRM_DEBUG_ATOMIC("Added new private object state %p to %p\n",
+ state->private_objs[index].obj_state, state);
+
+ return state->private_objs[index].obj_state;
+}
+EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
+
+/**
* drm_atomic_get_connector_state - get connector state
* @state: global atomic state object
* @connector: connector to get state object for
@@ -1123,6 +1188,10 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
*/
if (state->link_status != DRM_LINK_STATUS_GOOD)
state->link_status = val;
+ } else if (property == config->aspect_ratio_property) {
+ state->picture_aspect_ratio = val;
+ } else if (property == connector->scaling_mode_property) {
+ state->scaling_mode = val;
} else if (connector->funcs->atomic_set_property) {
return connector->funcs->atomic_set_property(connector,
state, property, val);
@@ -1199,6 +1268,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
*val = state->tv.hue;
} else if (property == config->link_status_property) {
*val = state->link_status;
+ } else if (property == config->aspect_ratio_property) {
+ *val = state->picture_aspect_ratio;
+ } else if (property == connector->scaling_mode_property) {
+ *val = state->scaling_mode;
} else if (connector->funcs->atomic_get_property) {
return connector->funcs->atomic_get_property(connector,
state, property, val);
@@ -1618,7 +1691,7 @@ int drm_atomic_commit(struct drm_atomic_state *state)
if (ret)
return ret;
- DRM_DEBUG_ATOMIC("commiting %p\n", state);
+ DRM_DEBUG_ATOMIC("committing %p\n", state);
return config->funcs->atomic_commit(state->dev, state, false);
}
@@ -1647,7 +1720,7 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state)
if (ret)
return ret;
- DRM_DEBUG_ATOMIC("commiting %p nonblocking\n", state);
+ DRM_DEBUG_ATOMIC("committing %p nonblocking\n", state);
return config->funcs->atomic_commit(state->dev, state, true);
}
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 8be9719284b0..6426339427a4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1070,8 +1070,8 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
*
* Note that @pre_swap is needed since the point where we block for fences moves
* around depending upon whether an atomic commit is blocking or
- * non-blocking. For async commit all waiting needs to happen after
- * drm_atomic_helper_swap_state() is called, but for synchronous commits we want
+ * non-blocking. For non-blocking commit all waiting needs to happen after
+ * drm_atomic_helper_swap_state() is called, but for blocking commits we want
* to wait **before** we do anything that can't be easily rolled back. That is
* before we call drm_atomic_helper_swap_state().
*
@@ -2032,6 +2032,8 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
struct drm_plane *plane;
struct drm_plane_state *old_plane_state, *new_plane_state;
struct drm_crtc_commit *commit;
+ void *obj, *obj_state;
+ const struct drm_private_state_funcs *funcs;
if (stall) {
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
@@ -2092,6 +2094,9 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
state->planes[i].state = old_plane_state;
plane->state = new_plane_state;
}
+
+ __for_each_private_obj(state, obj, obj_state, i, funcs)
+ funcs->swap_state(obj, &state->private_objs[i].obj_state);
}
EXPORT_SYMBOL(drm_atomic_helper_swap_state);
@@ -3517,7 +3522,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
*
* Implements support for legacy gamma correction table for drivers
* that support color management through the DEGAMMA_LUT/GAMMA_LUT
- * properties.
+ * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
+ * how the atomic color management and gamma tables work.
*/
int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
u16 *red, u16 *green, u16 *blue,
diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c
index 533f3a3e6877..3eda500fc005 100644
--- a/drivers/gpu/drm/drm_color_mgmt.c
+++ b/drivers/gpu/drm/drm_color_mgmt.c
@@ -43,7 +43,8 @@
*
* Setting this to NULL (blob property value set to 0) means a
* linear/pass-thru gamma table should be used. This is generally the
- * driver boot-up state too.
+ * driver boot-up state too. Drivers can access this blob through
+ * &drm_crtc_state.degamma_lut.
*
* “DEGAMMA_LUT_SIZE”:
* Unsinged range property to give the size of the lookup table to be set
@@ -60,7 +61,8 @@
*
* Setting this to NULL (blob property value set to 0) means a
* unit/pass-thru matrix should be used. This is generally the driver
- * boot-up state too.
+ * boot-up state too. Drivers can access the blob for the color conversion
+ * matrix through &drm_crtc_state.ctm.
*
* “GAMMA_LUT”:
* Blob property to set the gamma lookup table (LUT) mapping pixel data
@@ -72,7 +74,8 @@
*
* Setting this to NULL (blob property value set to 0) means a
* linear/pass-thru gamma table should be used. This is generally the
- * driver boot-up state too.
+ * driver boot-up state too. Drivers can access this blob through
+ * &drm_crtc_state.gamma_lut.
*
* “GAMMA_LUT_SIZE”:
* Unsigned range property to give the size of the lookup table to be set
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 9f847615ac74..5cd61aff7857 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -941,6 +941,10 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
*
* Called by a driver the first time it's needed, must be attached to desired
* connectors.
+ *
+ * Atomic drivers should use drm_connector_attach_scaling_mode_property()
+ * instead to correctly assign &drm_connector_state.picture_aspect_ratio
+ * in the atomic state.
*/
int drm_mode_create_scaling_mode_property(struct drm_device *dev)
{
@@ -961,6 +965,66 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)
EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
/**
+ * drm_connector_attach_scaling_mode_property - attach atomic scaling mode property
+ * @connector: connector to attach scaling mode property on.
+ * @scaling_mode_mask: or'ed mask of BIT(%DRM_MODE_SCALE_\*).
+ *
+ * This is used to add support for scaling mode to atomic drivers.
+ * The scaling mode will be set to &drm_connector_state.picture_aspect_ratio
+ * and can be used from &drm_connector_helper_funcs->atomic_check for validation.
+ *
+ * This is the atomic version of drm_mode_create_scaling_mode_property().
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
+ u32 scaling_mode_mask)
+{
+ struct drm_device *dev = connector->dev;
+ struct drm_property *scaling_mode_property;
+ int i, j = 0;
+ const unsigned valid_scaling_mode_mask =
+ (1U << ARRAY_SIZE(drm_scaling_mode_enum_list)) - 1;
+
+ if (WARN_ON(hweight32(scaling_mode_mask) < 2 ||
+ scaling_mode_mask & ~valid_scaling_mode_mask))
+ return -EINVAL;
+
+ scaling_mode_property =
+ drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+ hweight32(scaling_mode_mask));
+
+ if (!scaling_mode_property)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) {
+ int ret;
+
+ if (!(BIT(i) & scaling_mode_mask))
+ continue;
+
+ ret = drm_property_add_enum(scaling_mode_property, j++,
+ drm_scaling_mode_enum_list[i].type,
+ drm_scaling_mode_enum_list[i].name);
+
+ if (ret) {
+ drm_property_destroy(dev, scaling_mode_property);
+
+ return ret;
+ }
+ }
+
+ drm_object_attach_property(&connector->base,
+ scaling_mode_property, 0);
+
+ connector->scaling_mode_property = scaling_mode_property;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
+
+/**
* drm_mode_create_aspect_ratio_property - create aspect ratio property
* @dev: DRM device
*
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index d3fc7e4e85b7..222eb1a8549b 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -737,16 +737,16 @@ static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_sideband_msg_tx *txmsg)
{
- bool ret;
+ unsigned int state;
/*
* All updates to txmsg->state are protected by mgr->qlock, and the two
* cases we check here are terminal states. For those the barriers
* provided by the wake_up/wait_event pair are enough.
*/
- ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
- txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
- return ret;
+ state = READ_ONCE(txmsg->state);
+ return (state == DRM_DP_SIDEBAND_TX_RX ||
+ state == DRM_DP_SIDEBAND_TX_TIMEOUT);
}
static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
@@ -855,7 +855,7 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
mutex_unlock(&mstb->mgr->qlock);
if (wake_tx)
- wake_up(&mstb->mgr->tx_waitq);
+ wake_up_all(&mstb->mgr->tx_waitq);
kref_put(kref, drm_dp_free_mst_branch_device);
}
@@ -1510,7 +1510,7 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
if (txmsg->seqno != -1)
txmsg->dst->tx_slots[txmsg->seqno] = NULL;
txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
- wake_up(&mgr->tx_waitq);
+ wake_up_all(&mgr->tx_waitq);
}
}
@@ -2258,7 +2258,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
mstb->tx_slots[slot] = NULL;
mutex_unlock(&mgr->qlock);
- wake_up(&mgr->tx_waitq);
+ wake_up_all(&mgr->tx_waitq);
}
return ret;
}
@@ -2498,6 +2498,81 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
}
/**
+ * drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @port: port to find vcpi slots for
+ * @pbn: bandwidth required for the mode in PBN
+ *
+ * RETURNS:
+ * Total slots in the atomic state assigned for this port or error
+ */
+int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port, int pbn)
+{
+ struct drm_dp_mst_topology_state *topology_state;
+ int req_slots;
+
+ topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+ if (topology_state == NULL)
+ return -ENOMEM;
+
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (port == NULL)
+ return -EINVAL;
+ req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+ DRM_DEBUG_KMS("vcpi slots req=%d, avail=%d\n",
+ req_slots, topology_state->avail_slots);
+
+ if (req_slots > topology_state->avail_slots) {
+ drm_dp_put_port(port);
+ return -ENOSPC;
+ }
+
+ topology_state->avail_slots -= req_slots;
+ DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+
+ drm_dp_put_port(port);
+ return req_slots;
+}
+EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
+
+/**
+ * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @slots: number of vcpi slots to release
+ *
+ * RETURNS:
+ * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
+ * negative error code
+ */
+int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_mgr *mgr,
+ int slots)
+{
+ struct drm_dp_mst_topology_state *topology_state;
+
+ topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+ if (topology_state == NULL)
+ return -ENOMEM;
+
+ /* We cannot rely on port->vcpi.num_slots to update
+ * topology_state->avail_slots as the port may not exist if the parent
+ * branch device was unplugged. This should be fixed by tracking
+ * per-port slot allocation in drm_dp_mst_topology_state instead of
+ * depending on the caller to tell us how many slots to release.
+ */
+ topology_state->avail_slots += slots;
+ DRM_DEBUG_KMS("vcpi slots released=%d, avail=%d\n",
+ slots, topology_state->avail_slots);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
+
+/**
* drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
* @mgr: manager for this port
* @port: port to allocate a virtual channel for.
@@ -2936,6 +3011,69 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
(*mgr->cbs->hotplug)(mgr);
}
+void *drm_dp_mst_duplicate_state(struct drm_atomic_state *state, void *obj)
+{
+ struct drm_dp_mst_topology_mgr *mgr = obj;
+ struct drm_dp_mst_topology_state *new_mst_state;
+
+ if (WARN_ON(!mgr->state))
+ return NULL;
+
+ new_mst_state = kmemdup(mgr->state, sizeof(*new_mst_state), GFP_KERNEL);
+ if (new_mst_state)
+ new_mst_state->state = state;
+ return new_mst_state;
+}
+
+void drm_dp_mst_swap_state(void *obj, void **obj_state_ptr)
+{
+ struct drm_dp_mst_topology_mgr *mgr = obj;
+ struct drm_dp_mst_topology_state **topology_state_ptr;
+
+ topology_state_ptr = (struct drm_dp_mst_topology_state **)obj_state_ptr;
+
+ mgr->state->state = (*topology_state_ptr)->state;
+ swap(*topology_state_ptr, mgr->state);
+ mgr->state->state = NULL;
+}
+
+void drm_dp_mst_destroy_state(void *obj_state)
+{
+ kfree(obj_state);
+}
+
+static const struct drm_private_state_funcs mst_state_funcs = {
+ .duplicate_state = drm_dp_mst_duplicate_state,
+ .swap_state = drm_dp_mst_swap_state,
+ .destroy_state = drm_dp_mst_destroy_state,
+};
+
+/**
+ * drm_atomic_get_mst_topology_state: get MST topology state
+ *
+ * @state: global atomic state
+ * @mgr: MST topology manager, also the private object in this case
+ *
+ * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
+ * state vtable so that the private object state returned is that of a MST
+ * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
+ * to care of the locking, so warn if don't hold the connection_mutex.
+ *
+ * RETURNS:
+ *
+ * The MST topology state or error pointer.
+ */
+struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct drm_device *dev = mgr->dev;
+
+ WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+ return drm_atomic_get_private_obj_state(state, mgr,
+ &mst_state_funcs);
+}
+EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
+
/**
* drm_dp_mst_topology_mgr_init - initialise a topology manager
* @mgr: manager struct to initialise
@@ -2980,6 +3118,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
if (test_calc_pbn_mode() < 0)
DRM_ERROR("MST PBN self-test failed\n");
+ mgr->state = kzalloc(sizeof(*mgr->state), GFP_KERNEL);
+ if (mgr->state == NULL)
+ return -ENOMEM;
+ mgr->state->mgr = mgr;
+
+ /* max. time slots - one slot for MTP header */
+ mgr->state->avail_slots = 63;
+ mgr->funcs = &mst_state_funcs;
+
return 0;
}
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
@@ -3000,6 +3147,9 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
mutex_unlock(&mgr->payload_lock);
mgr->dev = NULL;
mgr->aux = NULL;
+ kfree(mgr->state);
+ mgr->state = NULL;
+ mgr->funcs = NULL;
}
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
index 50abd1faf38f..53f9bdf470d7 100644
--- a/drivers/gpu/drm/drm_fb_cma_helper.c
+++ b/drivers/gpu/drm/drm_fb_cma_helper.c
@@ -189,7 +189,7 @@ struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev,
obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
if (!obj) {
dev_err(dev->dev, "Failed to lookup GEM object\n");
- ret = -ENXIO;
+ ret = -ENOENT;
goto err_gem_object_put;
}
@@ -260,6 +260,33 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
/**
+ * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
+ * @fb: The framebuffer
+ * @state: Which state of drm plane
+ * @plane: Which plane
+ * Return the CMA GEM address for given framebuffer.
+ *
+ * This function will usually be called from the PLANE callback functions.
+ */
+dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
+ struct drm_plane_state *state,
+ unsigned int plane)
+{
+ struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+ dma_addr_t paddr;
+
+ if (plane >= 4)
+ return 0;
+
+ paddr = fb_cma->obj[plane]->paddr + fb->offsets[plane];
+ paddr += fb->format->cpp[plane] * (state->src_x >> 16);
+ paddr += fb->pitches[plane] * (state->src_y >> 16);
+
+ return paddr;
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
+
+/**
* drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
* @plane: Which plane
* @state: Plane state attach fence to
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 3783b659cd38..caad93dab54b 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -351,9 +351,8 @@ void drm_lastclose(struct drm_device * dev)
*
* This function must be used by drivers as their &file_operations.release
* method. It frees any resources associated with the open file, and calls the
- * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is
- * the last open file for the DRM device also proceeds to call the
- * &drm_driver.lastclose driver callback.
+ * &drm_driver.postclose driver callback. If this is the last open file for the
+ * DRM device also proceeds to call the &drm_driver.lastclose driver callback.
*
* RETURNS:
*
@@ -373,7 +372,8 @@ int drm_release(struct inode *inode, struct file *filp)
list_del(&file_priv->lhead);
mutex_unlock(&dev->filelist_mutex);
- if (dev->driver->preclose)
+ if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
+ dev->driver->preclose)
dev->driver->preclose(dev, file_priv);
/* ========================================================
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 8c866cac62dd..c7debaad67f8 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -54,7 +54,7 @@
static bool
drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
- struct timeval *tvblank, unsigned flags);
+ struct timeval *tvblank, bool in_vblank_irq);
static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
@@ -138,7 +138,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
*/
do {
cur_vblank = __get_vblank_counter(dev, pipe);
- rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0);
+ rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
/*
@@ -171,7 +171,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
* device vblank fields.
*/
static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
- unsigned long flags)
+ bool in_vblank_irq)
{
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
u32 cur_vblank, diff;
@@ -194,7 +194,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
*/
do {
cur_vblank = __get_vblank_counter(dev, pipe);
- rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags);
+ rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
if (dev->max_vblank_count != 0) {
@@ -214,13 +214,13 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
*/
diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
- if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ)
+ if (diff == 0 && in_vblank_irq)
DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
" diff_ns = %lld, framedur_ns = %d)\n",
pipe, (long long) diff_ns, framedur_ns);
} else {
/* some kind of default for drivers w/o accurate vbl timestamping */
- diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0;
+ diff = in_vblank_irq ? 1 : 0;
}
/*
@@ -253,7 +253,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
* Otherwise reinitialize delayed at next vblank interrupt and assign 0
* for now, to mark the vblanktimestamp as invalid.
*/
- if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0)
+ if (!rc && in_vblank_irq)
t_vblank = (struct timeval) {0, 0};
store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
@@ -291,7 +291,7 @@ u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
spin_lock_irqsave(&dev->vblank_time_lock, flags);
- drm_update_vblank_count(dev, pipe, 0);
+ drm_update_vblank_count(dev, pipe, false);
vblank = drm_vblank_count(dev, pipe);
spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
@@ -349,7 +349,7 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
* this time. This makes the count account for the entire time
* between drm_crtc_vblank_on() and drm_crtc_vblank_off().
*/
- drm_update_vblank_count(dev, pipe, 0);
+ drm_update_vblank_count(dev, pipe, false);
spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
}
@@ -684,6 +684,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
vblank->linedur_ns = linedur_ns;
vblank->framedur_ns = framedur_ns;
+ vblank->hwmode = *mode;
DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
crtc->base.id, mode->crtc_htotal,
@@ -700,10 +701,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* @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
+ * @in_vblank_irq:
+ * True when called from drm_crtc_handle_vblank(). Some drivers
+ * need to apply some workarounds for gpu-specific vblank irq quirks
+ * if flag is set.
*
* 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
@@ -723,52 +724,62 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* 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.
+ * This function can be used to implement the &drm_driver.get_vblank_timestamp
+ * directly, if the driver implements the &drm_driver.get_scanout_position hook.
*
- * Returns or'ed positive status flags on success:
+ * Note that atomic drivers must call drm_calc_timestamping_constants() before
+ * enabling a CRTC. The atomic helpers already take care of that in
+ * drm_atomic_helper_update_legacy_modeset_state().
*
- * DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping.
- * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
+ * Returns:
*
+ * Returns true on success, and false on failure, i.e. when no accurate
+ * timestamp could be acquired.
*/
-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)
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+ unsigned int pipe,
+ int *max_error,
+ struct timeval *vblank_time,
+ bool in_vblank_irq)
{
struct timeval tv_etime;
ktime_t stime, etime;
- unsigned int vbl_status;
- int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
+ bool vbl_status;
+ struct drm_crtc *crtc;
+ const struct drm_display_mode *mode;
+ struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
int vpos, hpos, i;
int delta_ns, duration_ns;
- if (pipe >= dev->num_crtcs) {
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return false;
+
+ crtc = drm_crtc_from_index(dev, pipe);
+
+ if (pipe >= dev->num_crtcs || !crtc) {
DRM_ERROR("Invalid crtc %u\n", pipe);
- return -EINVAL;
+ return false;
}
/* 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;
+ return false;
}
+ if (drm_drv_uses_atomic_modeset(dev))
+ mode = &vblank->hwmode;
+ else
+ mode = &crtc->hwmode;
+
/* 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);
- return -EAGAIN;
+ WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
+
+ return false;
}
/* Get current scanout position with system timestamp.
@@ -783,16 +794,17 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
* 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,
+ vbl_status = dev->driver->get_scanout_position(dev, pipe,
+ in_vblank_irq,
&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;
+ if (!vbl_status) {
+ DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
+ pipe);
+ return false;
}
/* Compute uncertainty in timestamp of scanout position query. */
@@ -830,13 +842,13 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
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,
+ DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+ pipe, 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;
+ return true;
}
EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
@@ -854,9 +866,10 @@ static struct timeval get_drm_timestamp(void)
* @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
+ * @in_vblank_irq:
+ * True when called from drm_crtc_handle_vblank(). Some drivers
+ * need to apply some workarounds for gpu-specific vblank irq quirks
+ * if flag is set.
*
* Fetches the system timestamp corresponding to the time of the most recent
* vblank interval on specified CRTC. May call into kms-driver to
@@ -870,27 +883,25 @@ static struct timeval get_drm_timestamp(void)
*/
static bool
drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
- struct timeval *tvblank, unsigned flags)
+ struct timeval *tvblank, bool in_vblank_irq)
{
- int ret;
+ bool ret = false;
/* 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)) {
+ 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;
- }
+ tvblank, in_vblank_irq);
/* GPU high precision timestamp query unsupported or failed.
* Return current monotonic/gettimeofday timestamp as best estimate.
*/
- *tvblank = get_drm_timestamp();
+ if (!ret)
+ *tvblank = get_drm_timestamp();
- return false;
+ return ret;
}
/**
@@ -1329,6 +1340,10 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
send_vblank_event(dev, e, seq, &now);
}
spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+ /* Will be reset by the modeset helpers when re-enabling the crtc by
+ * calling drm_calc_timestamping_constants(). */
+ vblank->hwmode.crtc_clock = 0;
}
EXPORT_SYMBOL(drm_crtc_vblank_off);
@@ -1760,7 +1775,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
return false;
}
- drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ);
+ drm_update_vblank_count(dev, pipe, true);
spin_unlock(&dev->vblank_time_lock);
diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c
index b84a295230fc..2c27f6f5a668 100644
--- a/drivers/gpu/drm/drm_plane_helper.c
+++ b/drivers/gpu/drm/drm_plane_helper.c
@@ -381,6 +381,7 @@ EXPORT_SYMBOL(drm_primary_helper_update);
/**
* drm_primary_helper_disable() - Helper for primary plane disable
* @plane: plane to disable
+ * @ctx: lock acquire context, not used here
*
* Provides a default plane disable handler for primary planes. This is handler
* is called in response to a userspace SetPlane operation on the plane with a
@@ -510,12 +511,10 @@ int drm_plane_helper_commit(struct drm_plane *plane,
if (plane_funcs->cleanup_fb)
plane_funcs->cleanup_fb(plane, plane_state);
out:
- if (plane_state) {
- if (plane->funcs->atomic_destroy_state)
- plane->funcs->atomic_destroy_state(plane, plane_state);
- else
- drm_atomic_helper_plane_destroy_state(plane, plane_state);
- }
+ if (plane->funcs->atomic_destroy_state)
+ plane->funcs->atomic_destroy_state(plane, plane_state);
+ else
+ drm_atomic_helper_plane_destroy_state(plane, plane_state);
return ret;
}
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 954eb848b5e2..22408badc617 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -595,15 +595,18 @@ out_unlock:
EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
/**
- * drm_gem_prime_import - helper library implementation of the import callback
+ * drm_gem_prime_import_dev - core implementation of the import callback
* @dev: drm_device to import into
* @dma_buf: dma-buf object to import
+ * @attach_dev: struct device to dma_buf attach
*
- * This is the implementation of the gem_prime_import functions for GEM drivers
- * using the PRIME helpers.
+ * This is the core of drm_gem_prime_import. It's designed to be called by
+ * drivers who want to use a different device structure than dev->dev for
+ * attaching via dma_buf.
*/
-struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
- struct dma_buf *dma_buf)
+struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
+ struct dma_buf *dma_buf,
+ struct device *attach_dev)
{
struct dma_buf_attachment *attach;
struct sg_table *sgt;
@@ -625,7 +628,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
if (!dev->driver->gem_prime_import_sg_table)
return ERR_PTR(-EINVAL);
- attach = dma_buf_attach(dma_buf, dev->dev);
+ attach = dma_buf_attach(dma_buf, attach_dev);
if (IS_ERR(attach))
return ERR_CAST(attach);
@@ -655,6 +658,21 @@ fail_detach:
return ERR_PTR(ret);
}
+EXPORT_SYMBOL(drm_gem_prime_import_dev);
+
+/**
+ * drm_gem_prime_import - helper library implementation of the import callback
+ * @dev: drm_device to import into
+ * @dma_buf: dma-buf object to import
+ *
+ * This is the implementation of the gem_prime_import functions for GEM drivers
+ * using the PRIME helpers.
+ */
+struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ return drm_gem_prime_import_dev(dev, dma_buf, dev->dev);
+}
EXPORT_SYMBOL(drm_gem_prime_import);
/**
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 09d3c4c3c858..50294a7bd29d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -82,14 +82,9 @@ err_file_priv_free:
return ret;
}
-static void exynos_drm_preclose(struct drm_device *dev,
- struct drm_file *file)
-{
- exynos_drm_subdrv_close(dev, file);
-}
-
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
{
+ exynos_drm_subdrv_close(dev, file);
kfree(file->driver_priv);
file->driver_priv = NULL;
}
@@ -145,7 +140,6 @@ static struct drm_driver exynos_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
| DRIVER_ATOMIC | DRIVER_RENDER,
.open = exynos_drm_open,
- .preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose,
.gem_free_object_unlocked = exynos_drm_gem_free_object,
diff --git a/drivers/gpu/drm/gma500/mdfld_tpo_vid.c b/drivers/gpu/drm/gma500/mdfld_tpo_vid.c
index d8d4170725b2..d40628e6810d 100644
--- a/drivers/gpu/drm/gma500/mdfld_tpo_vid.c
+++ b/drivers/gpu/drm/gma500/mdfld_tpo_vid.c
@@ -32,53 +32,20 @@ static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev)
struct drm_display_mode *mode;
struct drm_psb_private *dev_priv = dev->dev_private;
struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
- bool use_gct = false;
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode)
return NULL;
- if (use_gct) {
- mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
- mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
- mode->hsync_start = mode->hdisplay +
- ((ti->hsync_offset_hi << 8) |
- ti->hsync_offset_lo);
- mode->hsync_end = mode->hsync_start +
- ((ti->hsync_pulse_width_hi << 8) |
- ti->hsync_pulse_width_lo);
- mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) |
- ti->hblank_lo);
- mode->vsync_start =
- mode->vdisplay + ((ti->vsync_offset_hi << 8) |
- ti->vsync_offset_lo);
- mode->vsync_end =
- mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) |
- ti->vsync_pulse_width_lo);
- mode->vtotal = mode->vdisplay +
- ((ti->vblank_hi << 8) | ti->vblank_lo);
- mode->clock = ti->pixel_clock * 10;
-
- dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
- dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
- dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
- dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
- dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
- dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
- dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
- dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
- dev_dbg(dev->dev, "clock is %d\n", mode->clock);
- } else {
- mode->hdisplay = 864;
- mode->vdisplay = 480;
- mode->hsync_start = 873;
- mode->hsync_end = 876;
- mode->htotal = 887;
- mode->vsync_start = 487;
- mode->vsync_end = 490;
- mode->vtotal = 499;
- mode->clock = 33264;
- }
+ mode->hdisplay = 864;
+ mode->vdisplay = 480;
+ mode->hsync_start = 873;
+ mode->hsync_end = 876;
+ mode->htotal = 887;
+ mode->vsync_start = 487;
+ mode->vsync_end = 490;
+ mode->vtotal = 499;
+ mode->clock = 33264;
drm_mode_set_name(mode);
drm_mode_set_crtcinfo(mode, 0);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index fd97fe00cd0d..04493ef1d2f7 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -720,9 +720,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
struct drm_i915_private *dev_priv = to_i915(dev);
i915_reg_t high_frame, low_frame;
u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
- struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
- pipe);
- const struct drm_display_mode *mode = &intel_crtc->base.hwmode;
+ const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode;
unsigned long irqflags;
htotal = mode->crtc_htotal;
@@ -779,13 +777,17 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = to_i915(dev);
- const struct drm_display_mode *mode = &crtc->base.hwmode;
+ const struct drm_display_mode *mode;
+ struct drm_vblank_crtc *vblank;
enum pipe pipe = crtc->pipe;
int position, vtotal;
if (!crtc->active)
return -1;
+ vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
+ mode = &vblank->hwmode;
+
vtotal = mode->crtc_vtotal;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
vtotal /= 2;
@@ -827,10 +829,10 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
return (position + crtc->scanline_offset) % vtotal;
}
-static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
- unsigned int flags, int *vpos, int *hpos,
- ktime_t *stime, ktime_t *etime,
- const struct drm_display_mode *mode)
+static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
@@ -838,13 +840,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
int position;
int vbl_start, vbl_end, hsync_start, htotal, vtotal;
bool in_vbl = true;
- int ret = 0;
unsigned long irqflags;
if (WARN_ON(!mode->crtc_clock)) {
DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
"pipe %c\n", pipe_name(pipe));
- return 0;
+ return false;
}
htotal = mode->crtc_htotal;
@@ -859,8 +860,6 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
vtotal /= 2;
}
- ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
/*
* Lock uncore.lock, as we will do multiple timing critical raw
* register reads, potentially with preemption disabled, so the
@@ -944,11 +943,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
*hpos = position - (*vpos * htotal);
}
- /* In vblank? */
- if (in_vbl)
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
-
- return ret;
+ return true;
}
int intel_get_crtc_scanline(struct intel_crtc *crtc)
@@ -964,37 +959,6 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc)
return position;
}
-static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
- int *max_error,
- struct timeval *vblank_time,
- unsigned flags)
-{
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct intel_crtc *crtc;
-
- if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
- DRM_ERROR("Invalid crtc %u\n", pipe);
- return -EINVAL;
- }
-
- /* Get drm_crtc to timestamp: */
- crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
- if (crtc == NULL) {
- DRM_ERROR("Invalid crtc %u\n", pipe);
- return -EINVAL;
- }
-
- if (!crtc->base.hwmode.crtc_clock) {
- DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
- return -EBUSY;
- }
-
- /* Helper routine in DRM core does all the work: */
- return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
- vblank_time, flags,
- &crtc->base.hwmode);
-}
-
static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
{
u32 busy_up, busy_down, max_avg, min_avg;
@@ -4294,7 +4258,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
- dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+ dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
if (IS_CHERRYVIEW(dev_priv)) {
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3617927af269..2f2bb623cf5f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -11444,12 +11444,6 @@ intel_modeset_update_crtc_state(struct drm_atomic_state *state)
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state);
- /* Update hwmode for vblank functions */
- if (new_crtc_state->active)
- crtc->hwmode = new_crtc_state->adjusted_mode;
- else
- crtc->hwmode.crtc_clock = 0;
-
/*
* Update legacy state to satisfy fbc code. This can
* be removed when fbc uses the atomic state.
@@ -15425,8 +15419,6 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
to_intel_crtc_state(crtc->base.state);
int pixclk = 0;
- crtc->base.hwmode = crtc_state->base.adjusted_mode;
-
memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
if (crtc_state->base.active) {
intel_mode_from_pipe_config(&crtc->base.mode, crtc_state);
@@ -15456,7 +15448,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled)
pixclk = DIV_ROUND_UP(pixclk * 100, 95);
- drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode);
+ drm_calc_timestamping_constants(&crtc->base,
+ &crtc_state->base.adjusted_mode);
update_scanline_offset(crtc);
}
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index c1f62eb07c07..1dee9933005f 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -39,7 +39,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
struct intel_dp *intel_dp = &intel_dig_port->dp;
struct intel_connector *connector =
to_intel_connector(conn_state->connector);
- struct drm_atomic_state *state;
+ struct drm_atomic_state *state = pipe_config->base.state;
int bpp;
int lane_count, slots;
const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
@@ -57,20 +57,24 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
* seem to suggest we should do otherwise.
*/
lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
-
pipe_config->lane_count = lane_count;
pipe_config->pipe_bpp = bpp;
- pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
- state = pipe_config->base.state;
+ pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port))
pipe_config->has_audio = true;
- mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
+ mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
pipe_config->pbn = mst_pbn;
- slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+ slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
+ connector->port, mst_pbn);
+ if (slots < 0) {
+ DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots);
+ return false;
+ }
intel_link_compute_m_n(bpp, lane_count,
adjusted_mode->crtc_clock,
@@ -80,7 +84,38 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
pipe_config->dp_m_n.tu = slots;
return true;
+}
+static int intel_dp_mst_atomic_check(struct drm_connector *connector,
+ struct drm_connector_state *new_conn_state)
+{
+ struct drm_atomic_state *state = new_conn_state->state;
+ struct drm_connector_state *old_conn_state;
+ struct drm_crtc *old_crtc;
+ struct drm_crtc_state *crtc_state;
+ int slots, ret = 0;
+
+ old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+ old_crtc = old_conn_state->crtc;
+ if (!old_crtc)
+ return ret;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
+ slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu;
+ if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) {
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_encoder *old_encoder;
+
+ old_encoder = old_conn_state->best_encoder;
+ mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
+
+ ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots);
+ if (ret)
+ DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret);
+ else
+ to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
+ }
+ return ret;
}
static void intel_mst_disable_dp(struct intel_encoder *encoder,
@@ -387,6 +422,7 @@ static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_fun
.mode_valid = intel_dp_mst_mode_valid,
.atomic_best_encoder = intel_mst_atomic_best_encoder,
.best_encoder = intel_mst_best_encoder,
+ .atomic_check = intel_dp_mst_atomic_check,
};
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index aaee3949a422..48ea8d9d49fe 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -869,7 +869,6 @@ struct intel_hdmi {
bool has_audio;
enum hdmi_force_audio force_audio;
bool rgb_quant_range_selectable;
- enum hdmi_picture_aspect aspect_ratio;
struct intel_connector *attached_connector;
void (*write_infoframe)(struct drm_encoder *encoder,
const struct intel_crtc_state *crtc_state,
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 1d623b5e09d6..c6b8207724fa 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1403,7 +1403,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
}
/* Set user selected PAR to incoming mode's member */
- adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio;
+ adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
pipe_config->lane_count = 4;
@@ -1649,19 +1649,7 @@ intel_hdmi_set_property(struct drm_connector *connector,
}
if (property == connector->dev->mode_config.aspect_ratio_property) {
- switch (val) {
- case DRM_MODE_PICTURE_ASPECT_NONE:
- intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
- break;
- case DRM_MODE_PICTURE_ASPECT_4_3:
- intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
- break;
- case DRM_MODE_PICTURE_ASPECT_16_9:
- intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
- break;
- default:
- return -EINVAL;
- }
+ connector->state->picture_aspect_ratio = val;
goto done;
}
@@ -1823,7 +1811,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
intel_attach_broadcast_rgb_property(connector);
intel_hdmi->color_range_auto = true;
intel_attach_aspect_ratio_property(connector);
- intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+ connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
}
/*
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 816a6f5a3fd9..ef6fa87b2f8a 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -107,11 +107,6 @@ struct intel_sdvo {
bool color_range_auto;
/**
- * HDMI user specified aspect ratio
- */
- enum hdmi_picture_aspect aspect_ratio;
-
- /**
* This is set if we're going to treat the device as TV-out.
*
* While we have these nice friendly flags for output types that ought
@@ -1186,7 +1181,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
/* Set user selected PAR to incoming mode's member */
if (intel_sdvo->is_hdmi)
- adjusted_mode->picture_aspect_ratio = intel_sdvo->aspect_ratio;
+ adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
return true;
}
@@ -2067,19 +2062,7 @@ intel_sdvo_set_property(struct drm_connector *connector,
}
if (property == connector->dev->mode_config.aspect_ratio_property) {
- switch (val) {
- case DRM_MODE_PICTURE_ASPECT_NONE:
- intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
- break;
- case DRM_MODE_PICTURE_ASPECT_4_3:
- intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
- break;
- case DRM_MODE_PICTURE_ASPECT_16_9:
- intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
- break;
- default:
- return -EINVAL;
- }
+ connector->state->picture_aspect_ratio = val;
goto done;
}
@@ -2418,7 +2401,7 @@ intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
intel_sdvo->color_range_auto = true;
}
intel_attach_aspect_ratio_property(&connector->base.base);
- intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+ connector->base.base.state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
}
static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index d3d6b4cae1e6..e2b3346ead48 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -527,31 +527,28 @@ static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
return NULL;
}
-static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
- unsigned int flags, int *vpos, int *hpos,
- ktime_t *stime, ktime_t *etime,
- const struct drm_display_mode *mode)
+static bool mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
{
struct msm_drm_private *priv = dev->dev_private;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
- int ret = 0;
crtc = priv->crtcs[pipe];
if (!crtc) {
DRM_ERROR("Invalid crtc %d\n", pipe);
- return 0;
+ return false;
}
encoder = get_encoder_from_crtc(crtc);
if (!encoder) {
DRM_ERROR("no encoder found for crtc %d\n", pipe);
- return 0;
+ return false;
}
- ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
@@ -575,10 +572,8 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
if (line < vactive_start) {
line -= vactive_start;
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
} else if (line > vactive_end) {
line = line - vfp_end - vactive_start;
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
} else {
line -= vactive_start;
}
@@ -589,31 +584,7 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
if (etime)
*etime = ktime_get();
- return ret;
-}
-
-static int mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
- int *max_error,
- struct timeval *vblank_time,
- unsigned flags)
-{
- struct msm_drm_private *priv = dev->dev_private;
- struct drm_crtc *crtc;
-
- if (pipe < 0 || pipe >= priv->num_crtcs) {
- DRM_ERROR("Invalid crtc %d\n", pipe);
- return -EINVAL;
- }
-
- crtc = priv->crtcs[pipe];
- if (!crtc) {
- DRM_ERROR("Invalid crtc %d\n", pipe);
- return -EINVAL;
- }
-
- return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
- vblank_time, flags,
- &crtc->mode);
+ return true;
}
static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
@@ -725,7 +696,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
dev->mode_config.max_width = 0xffff;
dev->mode_config.max_height = 0xffff;
- dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
+ dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
dev->driver->get_scanout_position = mdp5_get_scanoutpos;
dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
dev->max_vblank_count = 0xffffffff;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 21b10f9840c9..6718c84fb862 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -98,7 +98,7 @@ calc(int blanks, int blanke, int total, int line)
return line;
}
-static int
+static bool
nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime)
{
@@ -111,16 +111,16 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
};
struct nouveau_display *disp = nouveau_display(crtc->dev);
struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
- int ret, retry = 20;
+ int retry = 20;
+ bool ret = false;
do {
ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
if (ret != 0)
- return 0;
+ return false;
if (args.scan.vline) {
- ret |= DRM_SCANOUTPOS_ACCURATE;
- ret |= DRM_SCANOUTPOS_VALID;
+ ret = true;
break;
}
@@ -133,14 +133,12 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
if (stime) *stime = ns_to_ktime(args.scan.time[0]);
if (etime) *etime = ns_to_ktime(args.scan.time[1]);
- if (*vpos < 0)
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
return ret;
}
-int
+bool
nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
- unsigned int flags, int *vpos, int *hpos,
+ bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
{
@@ -153,28 +151,7 @@ nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
}
}
- return 0;
-}
-
-int
-nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe,
- int *max_error, struct timeval *time, unsigned flags)
-{
- struct drm_crtc *crtc;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (nouveau_crtc(crtc)->index == pipe) {
- struct drm_display_mode *mode;
- if (drm_drv_uses_atomic_modeset(dev))
- mode = &crtc->state->adjusted_mode;
- else
- mode = &crtc->hwmode;
- return drm_calc_vbltimestamp_from_scanoutpos(dev,
- pipe, max_error, time, flags, mode);
- }
- }
-
- return -EINVAL;
+ return false;
}
static void
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h
index e1d772d39488..201aec2ea5b8 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.h
+++ b/drivers/gpu/drm/nouveau/nouveau_display.h
@@ -68,11 +68,9 @@ int nouveau_display_suspend(struct drm_device *dev, bool runtime);
void nouveau_display_resume(struct drm_device *dev, bool runtime);
int nouveau_display_vblank_enable(struct drm_device *, unsigned int);
void nouveau_display_vblank_disable(struct drm_device *, unsigned int);
-int nouveau_display_scanoutpos(struct drm_device *, unsigned int,
- unsigned int, int *, int *, ktime_t *,
- ktime_t *, const struct drm_display_mode *);
-int nouveau_display_vblstamp(struct drm_device *, unsigned int, int *,
- struct timeval *, unsigned);
+bool nouveau_display_scanoutpos(struct drm_device *, unsigned int,
+ bool, int *, int *, ktime_t *,
+ ktime_t *, const struct drm_display_mode *);
int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 2b6ac24ce690..1f751a3f570c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -881,7 +881,7 @@ done:
}
static void
-nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
+nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
{
struct nouveau_cli *cli = nouveau_cli(fpriv);
struct nouveau_drm *drm = nouveau_drm(dev);
@@ -897,12 +897,6 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
list_del(&cli->head);
mutex_unlock(&drm->client.mutex);
-}
-
-static void
-nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
-{
- struct nouveau_cli *cli = nouveau_cli(fpriv);
nouveau_cli_fini(cli);
kfree(cli);
pm_runtime_mark_last_busy(dev->dev);
@@ -974,7 +968,6 @@ driver_stub = {
.load = nouveau_drm_load,
.unload = nouveau_drm_unload,
.open = nouveau_drm_open,
- .preclose = nouveau_drm_preclose,
.postclose = nouveau_drm_postclose,
.lastclose = nouveau_vga_lastclose,
@@ -985,7 +978,7 @@ driver_stub = {
.enable_vblank = nouveau_display_vblank_enable,
.disable_vblank = nouveau_display_vblank_disable,
.get_scanout_position = nouveau_display_scanoutpos,
- .get_vblank_timestamp = nouveau_display_vblstamp,
+ .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
.ioctls = nouveau_ioctls,
.num_ioctls = ARRAY_SIZE(nouveau_ioctls),
diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig
new file mode 100644
index 000000000000..ede49efd531f
--- /dev/null
+++ b/drivers/gpu/drm/pl111/Kconfig
@@ -0,0 +1,12 @@
+config DRM_PL111
+ tristate "DRM Support for PL111 CLCD Controller"
+ depends on DRM
+ depends on ARM || ARM64 || COMPILE_TEST
+ select DRM_KMS_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_GEM_CMA_HELPER
+ select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+ help
+ Choose this option for DRM support for the PL111 CLCD controller.
+ If M is selected the module will be called pl111_drm.
+
diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
new file mode 100644
index 000000000000..01caee727c13
--- /dev/null
+++ b/drivers/gpu/drm/pl111/Makefile
@@ -0,0 +1,5 @@
+pl111_drm-y += pl111_connector.o \
+ pl111_display.o \
+ pl111_drv.o
+
+obj-$(CONFIG_DRM_PL111) += pl111_drm.o
diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
new file mode 100644
index 000000000000..3f213d7e7692
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_connector.c
@@ -0,0 +1,127 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_connector.c
+ * Implementation of the connector functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+static void pl111_connector_destroy(struct drm_connector *connector)
+{
+ struct pl111_drm_connector *pl111_connector =
+ to_pl111_connector(connector);
+
+ if (pl111_connector->panel)
+ drm_panel_detach(pl111_connector->panel);
+
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status pl111_connector_detect(struct drm_connector
+ *connector, bool force)
+{
+ struct pl111_drm_connector *pl111_connector =
+ to_pl111_connector(connector);
+
+ return (pl111_connector->panel ?
+ connector_status_connected :
+ connector_status_disconnected);
+}
+
+static int pl111_connector_helper_get_modes(struct drm_connector *connector)
+{
+ struct pl111_drm_connector *pl111_connector =
+ to_pl111_connector(connector);
+
+ if (!pl111_connector->panel)
+ return 0;
+
+ return drm_panel_get_modes(pl111_connector->panel);
+}
+
+const struct drm_connector_funcs connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = pl111_connector_destroy,
+ .detect = pl111_connector_detect,
+ .dpms = drm_atomic_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+const struct drm_connector_helper_funcs connector_helper_funcs = {
+ .get_modes = pl111_connector_helper_get_modes,
+};
+
+/* Walks the OF graph to find the panel node and then asks DRM to look
+ * up the panel.
+ */
+static struct drm_panel *pl111_get_panel(struct device *dev)
+{
+ struct device_node *endpoint, *panel_node;
+ struct device_node *np = dev->of_node;
+ struct drm_panel *panel;
+
+ endpoint = of_graph_get_next_endpoint(np, NULL);
+ if (!endpoint) {
+ dev_err(dev, "no endpoint to fetch panel\n");
+ return NULL;
+ }
+
+ /* don't proceed if we have an endpoint but no panel_node tied to it */
+ panel_node = of_graph_get_remote_port_parent(endpoint);
+ of_node_put(endpoint);
+ if (!panel_node) {
+ dev_err(dev, "no valid panel node\n");
+ return NULL;
+ }
+
+ panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+
+ return panel;
+}
+
+int pl111_connector_init(struct drm_device *dev)
+{
+ struct pl111_drm_dev_private *priv = dev->dev_private;
+ struct pl111_drm_connector *pl111_connector = &priv->connector;
+ struct drm_connector *connector = &pl111_connector->connector;
+
+ drm_connector_init(dev, connector, &connector_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ drm_connector_helper_add(connector, &connector_helper_funcs);
+
+ pl111_connector->panel = pl111_get_panel(dev->dev);
+ if (pl111_connector->panel)
+ drm_panel_attach(pl111_connector->panel, connector);
+
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
new file mode 100644
index 000000000000..39a5c33bce7d
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_display.c
@@ -0,0 +1,344 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/amba/clcd-regs.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/dma-buf.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+irqreturn_t pl111_irq(int irq, void *data)
+{
+ struct pl111_drm_dev_private *priv = data;
+ u32 irq_stat;
+ irqreturn_t status = IRQ_NONE;
+
+ irq_stat = readl(priv->regs + CLCD_PL111_MIS);
+
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
+ drm_crtc_handle_vblank(&priv->pipe.crtc);
+
+ status = IRQ_HANDLED;
+ }
+
+ /* Clear the interrupt once done */
+ writel(irq_stat, priv->regs + CLCD_PL111_ICR);
+
+ return status;
+}
+
+static u32 pl111_get_fb_offset(struct drm_plane_state *pstate)
+{
+ struct drm_framebuffer *fb = pstate->fb;
+ struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
+
+ return (obj->paddr +
+ fb->offsets[0] +
+ fb->format->cpp[0] * pstate->src_x +
+ fb->pitches[0] * pstate->src_y);
+}
+
+static int pl111_display_check(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *pstate,
+ struct drm_crtc_state *cstate)
+{
+ const struct drm_display_mode *mode = &cstate->mode;
+ struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+ struct drm_framebuffer *fb = pstate->fb;
+
+ if (mode->hdisplay % 16)
+ return -EINVAL;
+
+ if (fb) {
+ u32 offset = pl111_get_fb_offset(pstate);
+
+ /* FB base address must be dword aligned. */
+ if (offset & 3)
+ return -EINVAL;
+
+ /* There's no pitch register -- the mode's hdisplay
+ * controls it.
+ */
+ if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0])
+ return -EINVAL;
+
+ /* We can't change the FB format in a flicker-free
+ * manner (and only update it during CRTC enable).
+ */
+ if (old_fb && old_fb->format != fb->format)
+ cstate->mode_changed = true;
+ }
+
+ return 0;
+}
+
+static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
+ struct drm_crtc_state *cstate)
+{
+ struct drm_crtc *crtc = &pipe->crtc;
+ struct drm_plane *plane = &pipe->plane;
+ struct drm_device *drm = crtc->dev;
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+ const struct drm_display_mode *mode = &cstate->mode;
+ struct drm_framebuffer *fb = plane->state->fb;
+ struct drm_connector *connector = &priv->connector.connector;
+ u32 cntl;
+ u32 ppl, hsw, hfp, hbp;
+ u32 lpp, vsw, vfp, vbp;
+ u32 cpl;
+ int ret;
+
+ ret = clk_set_rate(priv->clk, mode->clock * 1000);
+ if (ret) {
+ dev_err(drm->dev,
+ "Failed to set pixel clock rate to %d: %d\n",
+ mode->clock * 1000, ret);
+ }
+
+ clk_prepare_enable(priv->clk);
+
+ ppl = (mode->hdisplay / 16) - 1;
+ hsw = mode->hsync_end - mode->hsync_start - 1;
+ hfp = mode->hsync_start - mode->hdisplay - 1;
+ hbp = mode->htotal - mode->hsync_end - 1;
+
+ lpp = mode->vdisplay - 1;
+ vsw = mode->vsync_end - mode->vsync_start - 1;
+ vfp = mode->vsync_start - mode->vdisplay;
+ vbp = mode->vtotal - mode->vsync_end;
+
+ cpl = mode->hdisplay - 1;
+
+ writel((ppl << 2) |
+ (hsw << 8) |
+ (hfp << 16) |
+ (hbp << 24),
+ priv->regs + CLCD_TIM0);
+ writel(lpp |
+ (vsw << 10) |
+ (vfp << 16) |
+ (vbp << 24),
+ priv->regs + CLCD_TIM1);
+ /* XXX: We currently always use CLCDCLK with no divisor. We
+ * could probably reduce power consumption by using HCLK
+ * (apb_pclk) with a divisor when it gets us near our target
+ * pixel clock.
+ */
+ writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
+ ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
+ ((connector->display_info.bus_flags &
+ DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
+ ((connector->display_info.bus_flags &
+ DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
+ TIM2_BCD |
+ (cpl << 16),
+ priv->regs + CLCD_TIM2);
+ writel(0, priv->regs + CLCD_TIM3);
+
+ drm_panel_prepare(priv->connector.panel);
+
+ /* Enable and Power Up */
+ cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
+
+ /* Note that the the hardware's format reader takes 'r' from
+ * the low bit, while DRM formats list channels from high bit
+ * to low bit as you read left to right.
+ */
+ switch (fb->format->format) {
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ cntl |= CNTL_LCDBPP24;
+ break;
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ cntl |= CNTL_LCDBPP24 | CNTL_BGR;
+ break;
+ case DRM_FORMAT_BGR565:
+ cntl |= CNTL_LCDBPP16_565;
+ break;
+ case DRM_FORMAT_RGB565:
+ cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
+ break;
+ case DRM_FORMAT_ABGR1555:
+ case DRM_FORMAT_XBGR1555:
+ cntl |= CNTL_LCDBPP16;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_XRGB1555:
+ cntl |= CNTL_LCDBPP16 | CNTL_BGR;
+ break;
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_XBGR4444:
+ cntl |= CNTL_LCDBPP16_444;
+ break;
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_XRGB4444:
+ cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
+ break;
+ default:
+ WARN_ONCE(true, "Unknown FB format 0x%08x\n",
+ fb->format->format);
+ break;
+ }
+
+ writel(cntl, priv->regs + CLCD_PL111_CNTL);
+
+ drm_panel_enable(priv->connector.panel);
+
+ drm_crtc_vblank_on(crtc);
+}
+
+void pl111_display_disable(struct drm_simple_display_pipe *pipe)
+{
+ struct drm_crtc *crtc = &pipe->crtc;
+ struct drm_device *drm = crtc->dev;
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+
+ drm_crtc_vblank_off(crtc);
+
+ drm_panel_disable(priv->connector.panel);
+
+ /* Disable and Power Down */
+ writel(0, priv->regs + CLCD_PL111_CNTL);
+
+ drm_panel_unprepare(priv->connector.panel);
+
+ clk_disable_unprepare(priv->clk);
+}
+
+static void pl111_display_update(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *old_pstate)
+{
+ struct drm_crtc *crtc = &pipe->crtc;
+ struct drm_device *drm = crtc->dev;
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+ struct drm_pending_vblank_event *event = crtc->state->event;
+ struct drm_plane *plane = &pipe->plane;
+ struct drm_plane_state *pstate = plane->state;
+ struct drm_framebuffer *fb = pstate->fb;
+
+ if (fb) {
+ u32 addr = pl111_get_fb_offset(pstate);
+
+ writel(addr, priv->regs + CLCD_UBAS);
+ }
+
+ if (event) {
+ crtc->state->event = NULL;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+
+ writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
+
+ return 0;
+}
+
+void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+
+ writel(0, priv->regs + CLCD_PL111_IENB);
+}
+
+static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *plane_state)
+{
+ return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
+}
+
+const struct drm_simple_display_pipe_funcs pl111_display_funcs = {
+ .check = pl111_display_check,
+ .enable = pl111_display_enable,
+ .disable = pl111_display_disable,
+ .update = pl111_display_update,
+ .prepare_fb = pl111_display_prepare_fb,
+};
+
+int pl111_display_init(struct drm_device *drm)
+{
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+ struct device *dev = drm->dev;
+ struct device_node *endpoint;
+ u32 tft_r0b0g0[3];
+ int ret;
+ static const u32 formats[] = {
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_ABGR1555,
+ DRM_FORMAT_XBGR1555,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ABGR4444,
+ DRM_FORMAT_XBGR4444,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_XRGB4444,
+ };
+
+ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (!endpoint)
+ return -ENODEV;
+
+ if (of_property_read_u32_array(endpoint,
+ "arm,pl11x,tft-r0g0b0-pads",
+ tft_r0b0g0,
+ ARRAY_SIZE(tft_r0b0g0)) != 0) {
+ dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
+ of_node_put(endpoint);
+ return -ENOENT;
+ }
+ of_node_put(endpoint);
+
+ if (tft_r0b0g0[0] != 0 ||
+ tft_r0b0g0[1] != 8 ||
+ tft_r0b0g0[2] != 16) {
+ dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
+ return -EINVAL;
+ }
+
+ ret = drm_simple_display_pipe_init(drm, &priv->pipe,
+ &pl111_display_funcs,
+ formats, ARRAY_SIZE(formats),
+ &priv->connector.connector);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
new file mode 100644
index 000000000000..f381593921b7
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_drm.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#ifndef _PL111_DRM_H_
+#define _PL111_DRM_H_
+
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
+
+struct pl111_drm_connector {
+ struct drm_connector connector;
+ struct drm_panel *panel;
+};
+
+struct pl111_drm_dev_private {
+ struct drm_device *drm;
+
+ struct pl111_drm_connector connector;
+ struct drm_simple_display_pipe pipe;
+ struct drm_fbdev_cma *fbdev;
+
+ void *regs;
+ struct clk *clk;
+};
+
+#define to_pl111_connector(x) \
+ container_of(x, struct pl111_drm_connector, connector)
+
+int pl111_display_init(struct drm_device *dev);
+int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc);
+irqreturn_t pl111_irq(int irq, void *data);
+int pl111_connector_init(struct drm_device *dev);
+int pl111_encoder_init(struct drm_device *dev);
+int pl111_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+
+#endif /* _PL111_DRM_H_ */
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
new file mode 100644
index 000000000000..936403f65508
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -0,0 +1,272 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * DOC: ARM PrimeCell PL111 CLCD Driver
+ *
+ * The PL111 is a simple LCD controller that can support TFT and STN
+ * displays. This driver exposes a standard KMS interface for them.
+ *
+ * This driver uses the same Device Tree binding as the fbdev CLCD
+ * driver. While the fbdev driver supports panels that may be
+ * connected to the CLCD internally to the CLCD driver, in DRM the
+ * panels get split out to drivers/gpu/drm/panels/. This means that,
+ * in converting from using fbdev to using DRM, you also need to write
+ * a panel driver (which may be as simple as an entry in
+ * panel-simple.c).
+ *
+ * The driver currently doesn't expose the cursor. The DRM API for
+ * cursors requires support for 64x64 ARGB8888 cursor images, while
+ * the hardware can only support 64x64 monochrome with masking
+ * cursors. While one could imagine trying to hack something together
+ * to look at the ARGB8888 and program reasonable in monochrome, we
+ * just don't expose the cursor at all instead, and leave cursor
+ * support to the X11 software cursor layer.
+ *
+ * TODO:
+ *
+ * - Fix race between setting plane base address and getting IRQ for
+ * vsync firing the pageflip completion.
+ *
+ * - Expose the correct set of formats we can support based on the
+ * "arm,pl11x,tft-r0g0b0-pads" DT property.
+ *
+ * - Use the "max-memory-bandwidth" DT property to filter the
+ * supported formats.
+ *
+ * - Read back hardware state at boot to skip reprogramming the
+ * hardware when doing a no-op modeset.
+ *
+ * - Use the internal clock divisor to reduce power consumption by
+ * using HCLK (apb_pclk) when appropriate.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+#define DRIVER_DESC "DRM module for PL111"
+
+struct drm_mode_config_funcs mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static int pl111_modeset_init(struct drm_device *dev)
+{
+ struct drm_mode_config *mode_config;
+ struct pl111_drm_dev_private *priv = dev->dev_private;
+ int ret = 0;
+
+ drm_mode_config_init(dev);
+ mode_config = &dev->mode_config;
+ mode_config->funcs = &mode_config_funcs;
+ mode_config->min_width = 1;
+ mode_config->max_width = 1024;
+ mode_config->min_height = 1;
+ mode_config->max_height = 768;
+
+ ret = pl111_connector_init(dev);
+ if (ret) {
+ dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
+ goto out_config;
+ }
+
+ /* Don't actually attach if we didn't find a drm_panel
+ * attached to us. This will allow a kernel to include both
+ * the fbdev pl111 driver and this one, and choose between
+ * them based on which subsystem has support for the panel.
+ */
+ if (!priv->connector.panel) {
+ dev_info(dev->dev,
+ "Disabling due to lack of DRM panel device.\n");
+ ret = -ENODEV;
+ goto out_config;
+ }
+
+ ret = pl111_display_init(dev);
+ if (ret != 0) {
+ dev_err(dev->dev, "Failed to init display\n");
+ goto out_config;
+ }
+
+ ret = drm_vblank_init(dev, 1);
+ if (ret != 0) {
+ dev_err(dev->dev, "Failed to init vblank\n");
+ goto out_config;
+ }
+
+ drm_mode_config_reset(dev);
+
+ priv->fbdev = drm_fbdev_cma_init(dev, 32,
+ dev->mode_config.num_connector);
+
+ drm_kms_helper_poll_init(dev);
+
+ goto finish;
+
+out_config:
+ drm_mode_config_cleanup(dev);
+finish:
+ return ret;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
+
+static void pl111_lastclose(struct drm_device *dev)
+{
+ struct pl111_drm_dev_private *priv = dev->dev_private;
+
+ drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static struct drm_driver pl111_drm_driver = {
+ .driver_features =
+ DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
+ .lastclose = pl111_lastclose,
+ .ioctls = NULL,
+ .fops = &drm_fops,
+ .name = "pl111",
+ .desc = DRIVER_DESC,
+ .date = "20170317",
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+
+ .enable_vblank = pl111_enable_vblank,
+ .disable_vblank = pl111_disable_vblank,
+
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+};
+
+#ifdef CONFIG_ARM_AMBA
+static int pl111_amba_probe(struct amba_device *amba_dev,
+ const struct amba_id *id)
+{
+ struct device *dev = &amba_dev->dev;
+ struct pl111_drm_dev_private *priv;
+ struct drm_device *drm;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ drm = drm_dev_alloc(&pl111_drm_driver, dev);
+ if (IS_ERR(drm))
+ return PTR_ERR(drm);
+ amba_set_drvdata(amba_dev, drm);
+ priv->drm = drm;
+ drm->dev_private = priv;
+
+ priv->clk = devm_clk_get(dev, "clcdclk");
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "CLCD: unable to get clk.\n");
+ ret = PTR_ERR(priv->clk);
+ goto dev_unref;
+ }
+
+ priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
+ if (!priv->regs) {
+ dev_err(dev, "%s failed mmio\n", __func__);
+ return -EINVAL;
+ }
+
+ /* turn off interrupts before requesting the irq */
+ writel(0, priv->regs + CLCD_PL111_IENB);
+
+ ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
+ "pl111", priv);
+ if (ret != 0) {
+ dev_err(dev, "%s failed irq %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = pl111_modeset_init(drm);
+ if (ret != 0)
+ goto dev_unref;
+
+ ret = drm_dev_register(drm, 0);
+ if (ret < 0)
+ goto dev_unref;
+
+ return 0;
+
+dev_unref:
+ drm_dev_unref(drm);
+ return ret;
+}
+
+static int pl111_amba_remove(struct amba_device *amba_dev)
+{
+ struct drm_device *drm = amba_get_drvdata(amba_dev);
+ struct pl111_drm_dev_private *priv = drm->dev_private;
+
+ drm_dev_unregister(drm);
+ if (priv->fbdev)
+ drm_fbdev_cma_fini(priv->fbdev);
+ drm_mode_config_cleanup(drm);
+ drm_dev_unref(drm);
+
+ return 0;
+}
+
+static struct amba_id pl111_id_table[] = {
+ {
+ .id = 0x00041111,
+ .mask = 0x000fffff,
+ },
+ {0, 0},
+};
+
+static struct amba_driver pl111_amba_driver = {
+ .drv = {
+ .name = "drm-clcd-pl111",
+ },
+ .probe = pl111_amba_probe,
+ .remove = pl111_amba_remove,
+ .id_table = pl111_id_table,
+};
+
+module_amba_driver(pl111_amba_driver);
+#endif /* CONFIG_ARM_AMBA */
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 93d45aa5c3d4..ef8a75940980 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -115,10 +115,6 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
int radeon_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
void radeon_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
-int radeon_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
- int *max_error,
- struct timeval *vblank_time,
- unsigned flags);
void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
@@ -530,6 +526,16 @@ static const struct file_operations radeon_driver_kms_fops = {
#endif
};
+static bool
+radeon_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
+{
+ return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
+ stime, etime, mode);
+}
+
static struct drm_driver kms_driver = {
.driver_features =
DRIVER_USE_AGP |
@@ -544,8 +550,8 @@ static struct drm_driver kms_driver = {
.get_vblank_counter = radeon_get_vblank_counter_kms,
.enable_vblank = radeon_enable_vblank_kms,
.disable_vblank = radeon_disable_vblank_kms,
- .get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
- .get_scanout_position = radeon_get_crtc_scanoutpos,
+ .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
+ .get_scanout_position = radeon_get_crtc_scanout_position,
.irq_preinstall = radeon_driver_irq_preinstall_kms,
.irq_postinstall = radeon_driver_irq_postinstall_kms,
.irq_uninstall = radeon_driver_irq_uninstall_kms,
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index e3e7cb1d10a2..6a68d440bc44 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -858,43 +858,6 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc)
spin_unlock_irqrestore(&rdev->irq.lock, irqflags);
}
-/**
- * radeon_get_vblank_timestamp_kms - get vblank timestamp
- *
- * @dev: drm dev pointer
- * @crtc: crtc to get the timestamp for
- * @max_error: max error
- * @vblank_time: time value
- * @flags: flags passed to the driver
- *
- * Gets the timestamp on the requested crtc based on the
- * scanout position. (all asics).
- * Returns postive status flags on success, negative error on failure.
- */
-int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
- int *max_error,
- struct timeval *vblank_time,
- unsigned flags)
-{
- struct drm_crtc *drmcrtc;
- struct radeon_device *rdev = dev->dev_private;
-
- if (crtc < 0 || crtc >= dev->num_crtcs) {
- DRM_ERROR("Invalid crtc %d\n", crtc);
- return -EINVAL;
- }
-
- /* Get associated drm_crtc: */
- drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
- if (!drmcrtc)
- return -EINVAL;
-
- /* Helper routine in DRM core does all the work: */
- return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
- vblank_time, flags,
- &drmcrtc->hwmode);
-}
-
const struct drm_ioctl_desc radeon_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF_DRV(RADEON_CP_START, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index ad282648fc8b..00f5ec5c12c7 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -691,6 +691,9 @@ struct atom_voltage_table
};
/* Driver internal use only flags of radeon_get_crtc_scanoutpos() */
+#define DRM_SCANOUTPOS_VALID (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE (1 << 2)
#define USE_REAL_VBLANKSTART (1 << 30)
#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index d8fa7a9c9240..1bccd827d2e4 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -104,26 +104,18 @@ static void analogix_dp_psr_work(struct work_struct *work)
{
struct rockchip_dp_device *dp =
container_of(work, typeof(*dp), psr_work);
- struct drm_crtc *crtc = dp->encoder.crtc;
- int psr_state = dp->psr_state;
- int vact_end;
int ret;
unsigned long flags;
- if (!crtc)
- return;
-
- vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
-
- ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
- PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+ ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
+ PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
if (ret) {
dev_err(dp->dev, "line flag interrupt did not arrive\n");
return;
}
spin_lock_irqsave(&dp->psr_lock, flags);
- if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+ if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE)
analogix_dp_enable_psr(dp->dev);
else
analogix_dp_disable_psr(dp->dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index a48fcce3f5f6..47905faf5586 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -62,8 +62,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
struct device *dev);
void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev);
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
- unsigned int mstimeout);
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
extern struct platform_driver cdn_dp_driver;
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 3f7a82d1e095..40a5e6ef6f2c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -468,7 +468,7 @@ static bool vop_line_flag_irq_is_enabled(struct vop *vop)
return !!line_flag_irq;
}
-static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+static void vop_line_flag_irq_enable(struct vop *vop)
{
unsigned long flags;
@@ -477,7 +477,6 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
spin_lock_irqsave(&vop->irq_lock, flags);
- VOP_CTRL_SET(vop, line_flag_num[0], line_num);
VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
@@ -981,6 +980,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
VOP_CTRL_SET(vop, vact_st_end, val);
VOP_CTRL_SET(vop, vpost_st_end, val);
+ VOP_CTRL_SET(vop, line_flag_num[0], vact_end);
+
clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
VOP_CTRL_SET(vop, standby, 0);
@@ -1507,19 +1508,16 @@ static void vop_win_init(struct vop *vop)
}
/**
- * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * rockchip_drm_wait_vact_end
* @crtc: CRTC to enable line flag
- * @line_num: interested line number
* @mstimeout: millisecond for timeout
*
- * Driver would hold here until the interested line flag interrupt have
- * happened or timeout to wait.
+ * Wait for vact_end line flag irq or timeout.
*
* Returns:
* Zero on success, negative errno on failure.
*/
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
- unsigned int mstimeout)
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
{
struct vop *vop = to_vop(crtc);
unsigned long jiffies_left;
@@ -1527,14 +1525,14 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
if (!crtc || !vop->is_enabled)
return -ENODEV;
- if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+ if (mstimeout <= 0)
return -EINVAL;
if (vop_line_flag_irq_is_enabled(vop))
return -EBUSY;
reinit_completion(&vop->line_flag_completion);
- vop_line_flag_irq_enable(vop, line_num);
+ vop_line_flag_irq_enable(vop);
jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
msecs_to_jiffies(mstimeout));
@@ -1547,7 +1545,7 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
return 0;
}
-EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+EXPORT_SYMBOL(rockchip_drm_wait_vact_end);
static int vop_bind(struct device *dev, struct device *master, void *data)
{
diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c
index fa356f5dae27..dfdd858eda0a 100644
--- a/drivers/gpu/drm/selftests/test-drm_mm.c
+++ b/drivers/gpu/drm/selftests/test-drm_mm.c
@@ -514,6 +514,8 @@ static int igt_reserve(void *ignored)
ret = __igt_reserve(count, size + 1);
if (ret)
return ret;
+
+ cond_resched();
}
return 0;
@@ -712,6 +714,10 @@ static int igt_insert(void *ignored)
return ret;
ret = __igt_insert(count, size + 1, false);
+ if (ret)
+ return ret;
+
+ cond_resched();
}
return 0;
@@ -741,6 +747,10 @@ static int igt_replace(void *ignored)
return ret;
ret = __igt_insert(count, size + 1, true);
+ if (ret)
+ return ret;
+
+ cond_resched();
}
return 0;
@@ -1011,6 +1021,8 @@ static int igt_insert_range(void *ignored)
ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
if (ret)
return ret;
+
+ cond_resched();
}
return 0;
@@ -1056,6 +1068,7 @@ static int igt_align(void *ignored)
drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node);
DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+ cond_resched();
}
ret = 0;
@@ -1097,6 +1110,8 @@ static int igt_align_pot(int max)
align, bit);
goto out;
}
+
+ cond_resched();
}
ret = 0;
@@ -1471,6 +1486,8 @@ static int igt_evict(void *ignored)
goto out;
}
}
+
+ cond_resched();
}
ret = 0;
@@ -1566,6 +1583,8 @@ static int igt_evict_range(void *ignored)
goto out;
}
}
+
+ cond_resched();
}
ret = 0;
@@ -1683,6 +1702,7 @@ static int igt_topdown(void *ignored)
drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node);
DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+ cond_resched();
}
ret = 0;
@@ -1783,6 +1803,7 @@ static int igt_bottomup(void *ignored)
drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node);
DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+ cond_resched();
}
ret = 0;
@@ -1970,6 +1991,8 @@ static int igt_color(void *ignored)
drm_mm_remove_node(node);
kfree(node);
}
+
+ cond_resched();
}
ret = 0;
@@ -2047,6 +2070,7 @@ static int evict_color(struct drm_mm *mm,
}
}
+ cond_resched();
return 0;
}
@@ -2132,6 +2156,8 @@ static int igt_color_evict(void *ignored)
goto out;
}
}
+
+ cond_resched();
}
ret = 0;
@@ -2231,6 +2257,8 @@ static int igt_color_evict_range(void *ignored)
goto out;
}
}
+
+ cond_resched();
}
ret = 0;
diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c
index cca75bddb9ad..5b3a41f74f21 100644
--- a/drivers/gpu/drm/sti/sti_cursor.c
+++ b/drivers/gpu/drm/sti/sti_cursor.c
@@ -33,7 +33,7 @@
#define STI_CURS_MAX_SIZE 128
/*
- * pixmap dma buffer stucture
+ * pixmap dma buffer structure
*
* @paddr: physical address
* @size: buffer size
@@ -121,8 +121,7 @@ static int cursor_dbg_show(struct seq_file *s, void *data)
cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
DBGFS_DUMP(CUR_AWS);
DBGFS_DUMP(CUR_AWE);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index bb23318a44b7..24ebc6b2f34d 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -186,8 +186,7 @@ static int dvo_dbg_show(struct seq_file *s, void *data)
DBGFS_DUMP(DVO_LUT_PROG_MID);
DBGFS_DUMP(DVO_LUT_PROG_HIGH);
dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
index 88f16cdf6a4b..5ee0503945c8 100644
--- a/drivers/gpu/drm/sti/sti_gdp.c
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -149,7 +149,7 @@ static void gdp_dbg_ctl(struct seq_file *s, int val)
seq_puts(s, "\tColor:");
for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) {
if (gdp_format_to_str[i].format == (val & 0x1F)) {
- seq_printf(s, gdp_format_to_str[i].name);
+ seq_puts(s, gdp_format_to_str[i].name);
break;
}
}
@@ -266,8 +266,7 @@ static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node)
seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2);
seq_printf(s, "\n\tPPT 0x%08X", node->gam_gdp_ppt);
gdp_dbg_ppt(s, node->gam_gdp_ppt);
- seq_printf(s, "\n\tCML 0x%08X", node->gam_gdp_cml);
- seq_puts(s, "\n");
+ seq_printf(s, "\n\tCML 0x%08X\n", node->gam_gdp_cml);
}
static int gdp_node_dbg_show(struct seq_file *s, void *arg)
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index 0c0a75bc8bc3..d6ed909d9d75 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -320,8 +320,7 @@ static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
{
unsigned int i;
- seq_puts(s, "\n\n");
- seq_puts(s, " HDA AWG microcode:");
+ seq_puts(s, "\n\n HDA AWG microcode:");
for (i = 0; i < AWG_MAX_INST; i++) {
if (i % 8 == 0)
seq_printf(s, "\n %04X:", i);
@@ -333,8 +332,7 @@ static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg)
{
u32 val = readl(reg);
- seq_puts(s, "\n");
- seq_printf(s, "\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
+ seq_printf(s, "\n\n %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
seq_puts(s, "\tHD DACs ");
seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled");
}
@@ -356,8 +354,7 @@ static int hda_dbg_show(struct seq_file *s, void *data)
hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI);
if (hda->video_dacs_ctrl)
hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index 243905b6ae59..a59c95a8081b 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -592,7 +592,7 @@ static void hdmi_dbg_cfg(struct seq_file *s, int val)
{
int tmp;
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
tmp = val & HDMI_CFG_HDMI_NOT_DVI;
DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI");
seq_puts(s, "\t\t\t\t\t");
@@ -616,7 +616,7 @@ static void hdmi_dbg_sta(struct seq_file *s, int val)
{
int tmp;
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
tmp = (val & HDMI_STA_DLL_LCK);
DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked");
seq_puts(s, "\t\t\t\t\t");
@@ -632,7 +632,7 @@ static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val)
"once every field",
"once every frame"};
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1));
DBGFS_PRINT_STR("Data island 1:", en_di[tmp]);
seq_puts(s, "\t\t\t\t\t");
@@ -664,16 +664,16 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
DBGFS_DUMP("\n", HDMI_STA);
hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA));
DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN);
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN));
DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX);
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX));
DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN);
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN));
DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX);
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX));
DBGFS_DUMP("", HDMI_SW_DI_CFG);
hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG));
@@ -692,8 +692,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI);
- seq_puts(s, "\n");
- seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):",
+ seq_printf(s, "\n\n AUDIO Infoframe (Data Island slot N=%d):",
HDMI_IFRAME_SLOT_AUDIO);
DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO);
@@ -703,8 +702,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO);
- seq_puts(s, "\n");
- seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
+ seq_printf(s, "\n\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
HDMI_IFRAME_SLOT_VENDOR);
DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR);
@@ -714,8 +712,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR);
DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c
index 66f843148ef7..a1c161f77804 100644
--- a/drivers/gpu/drm/sti/sti_hqvdp.c
+++ b/drivers/gpu/drm/sti/sti_hqvdp.c
@@ -625,8 +625,7 @@ static int hqvdp_dbg_show(struct seq_file *s, void *data)
hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
}
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
@@ -1357,12 +1356,12 @@ static int sti_hqvdp_probe(struct platform_device *pdev)
/* Get Memory resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
+ if (!res) {
DRM_ERROR("Get memory resource failed\n");
return -ENXIO;
}
hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res));
- if (hqvdp->regs == NULL) {
+ if (!hqvdp->regs) {
DRM_ERROR("Register mapping failed\n");
return -ENXIO;
}
diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
index 4ddc58f7fe2e..2bd1d46fe1cd 100644
--- a/drivers/gpu/drm/sti/sti_mixer.c
+++ b/drivers/gpu/drm/sti/sti_mixer.c
@@ -162,8 +162,7 @@ static int mixer_dbg_show(struct seq_file *s, void *arg)
DBGFS_DUMP(GAM_MIXER_MBP);
DBGFS_DUMP(GAM_MIXER_MX0);
mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
index 8b8ea717c121..8959fcc743a8 100644
--- a/drivers/gpu/drm/sti/sti_tvout.c
+++ b/drivers/gpu/drm/sti/sti_tvout.c
@@ -459,7 +459,7 @@ static void tvout_dbg_vip(struct seq_file *s, int val)
"Aux (color matrix by-passed)",
"", "", "", "", "", "Force value"};
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
@@ -558,8 +558,7 @@ static int tvout_dbg_show(struct seq_file *s, void *data)
DBGFS_DUMP(TVO_CSC_AUX_M6);
DBGFS_DUMP(TVO_CSC_AUX_M7);
DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
@@ -847,7 +846,7 @@ static int sti_tvout_probe(struct platform_device *pdev)
tvout->dev = dev;
- /* get Memory ressources */
+ /* get memory resources */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
if (!res) {
DRM_ERROR("Invalid glue resource\n");
diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c
index 2ad59892b57e..577a3341d3c1 100644
--- a/drivers/gpu/drm/sti/sti_vid.c
+++ b/drivers/gpu/drm/sti/sti_vid.c
@@ -61,7 +61,7 @@
static void vid_dbg_ctl(struct seq_file *s, int val)
{
val = val >> 30;
- seq_puts(s, "\t");
+ seq_putc(s, '\t');
if (!(val & 1))
seq_puts(s, "NOT ");
@@ -114,8 +114,7 @@ static int vid_dbg_show(struct seq_file *s, void *arg)
DBGFS_DUMP(VID_BC);
DBGFS_DUMP(VID_TINT);
DBGFS_DUMP(VID_CSAT);
- seq_puts(s, "\n");
-
+ seq_putc(s, '\n');
return 0;
}
diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig
new file mode 100644
index 000000000000..2c4817fb0890
--- /dev/null
+++ b/drivers/gpu/drm/stm/Kconfig
@@ -0,0 +1,16 @@
+config DRM_STM
+ tristate "DRM Support for STMicroelectronics SoC Series"
+ depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM)
+ select DRM_KMS_HELPER
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ select DRM_PANEL
+ select VIDEOMODE_HELPERS
+ select FB_PROVIDE_GET_FB_UNMAPPED_AREA
+ default y
+
+ help
+ Enable support for the on-chip display controller on
+ STMicroelectronics STM32 MCUs.
+ To compile this driver as a module, choose M here: the module
+ will be called stm-drm.
diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile
new file mode 100644
index 000000000000..e114d45dbd42
--- /dev/null
+++ b/drivers/gpu/drm/stm/Makefile
@@ -0,0 +1,7 @@
+ccflags-y := -Iinclude/drm
+
+stm-drm-y := \
+ drv.o \
+ ltdc.o
+
+obj-$(CONFIG_DRM_STM) += stm-drm.o
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c
new file mode 100644
index 000000000000..83ab48f1fd00
--- /dev/null
+++ b/drivers/gpu/drm/stm/drv.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ * Yannick Fertre <yannick.fertre@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "ltdc.h"
+
+#define DRIVER_NAME "stm"
+#define DRIVER_DESC "STMicroelectronics SoC DRM"
+#define DRIVER_DATE "20170330"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+#define DRIVER_PATCH_LEVEL 0
+
+#define STM_MAX_FB_WIDTH 2048
+#define STM_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */
+
+static void drv_output_poll_changed(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ drm_fbdev_cma_hotplug_event(ldev->fbdev);
+}
+
+static const struct drm_mode_config_funcs drv_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+ .output_poll_changed = drv_output_poll_changed,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void drv_lastclose(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ DRM_DEBUG("%s\n", __func__);
+
+ drm_fbdev_cma_restore_mode(ldev->fbdev);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
+
+static struct drm_driver drv_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
+ DRIVER_ATOMIC,
+ .lastclose = drv_lastclose,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCH_LEVEL,
+ .fops = &drv_driver_fops,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_free_object_unlocked = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = drm_gem_prime_import,
+ .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+ .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_vmap = drm_gem_cma_prime_vmap,
+ .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+ .gem_prime_mmap = drm_gem_cma_prime_mmap,
+ .enable_vblank = ltdc_crtc_enable_vblank,
+ .disable_vblank = ltdc_crtc_disable_vblank,
+};
+
+static int drv_load(struct drm_device *ddev)
+{
+ struct platform_device *pdev = to_platform_device(ddev->dev);
+ struct drm_fbdev_cma *fbdev;
+ struct ltdc_device *ldev;
+ int ret;
+
+ DRM_DEBUG("%s\n", __func__);
+
+ ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
+ if (!ldev)
+ return -ENOMEM;
+
+ ddev->dev_private = (void *)ldev;
+
+ drm_mode_config_init(ddev);
+
+ /*
+ * set max width and height as default value.
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ ddev->mode_config.min_width = 0;
+ ddev->mode_config.min_height = 0;
+ ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
+ ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;
+ ddev->mode_config.funcs = &drv_mode_config_funcs;
+
+ ret = ltdc_load(ddev);
+ if (ret)
+ goto err;
+
+ drm_mode_config_reset(ddev);
+ drm_kms_helper_poll_init(ddev);
+
+ if (ddev->mode_config.num_connector) {
+ ldev = ddev->dev_private;
+ fbdev = drm_fbdev_cma_init(ddev, 16,
+ ddev->mode_config.num_connector);
+ if (IS_ERR(fbdev)) {
+ DRM_DEBUG("Warning: fails to create fbdev\n");
+ fbdev = NULL;
+ }
+ ldev->fbdev = fbdev;
+ }
+
+ platform_set_drvdata(pdev, ddev);
+
+ return 0;
+err:
+ drm_mode_config_cleanup(ddev);
+ return ret;
+}
+
+static void drv_unload(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ DRM_DEBUG("%s\n", __func__);
+
+ if (ldev->fbdev) {
+ drm_fbdev_cma_fini(ldev->fbdev);
+ ldev->fbdev = NULL;
+ }
+ drm_kms_helper_poll_fini(ddev);
+ ltdc_unload(ddev);
+ drm_mode_config_cleanup(ddev);
+}
+
+static int stm_drm_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct drm_device *ddev;
+ int ret;
+
+ DRM_DEBUG("%s\n", __func__);
+
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+ ddev = drm_dev_alloc(&drv_driver, dev);
+ if (IS_ERR(ddev))
+ return PTR_ERR(ddev);
+
+ ret = drv_load(ddev);
+ if (ret)
+ goto err_unref;
+
+ ret = drm_dev_register(ddev, 0);
+ if (ret)
+ goto err_unref;
+
+ return 0;
+
+err_unref:
+ drm_dev_unref(ddev);
+
+ return ret;
+}
+
+static int stm_drm_platform_remove(struct platform_device *pdev)
+{
+ struct drm_device *ddev = platform_get_drvdata(pdev);
+
+ DRM_DEBUG("%s\n", __func__);
+
+ drm_dev_unregister(ddev);
+ drv_unload(ddev);
+ drm_dev_unref(ddev);
+
+ return 0;
+}
+
+static const struct of_device_id drv_dt_ids[] = {
+ { .compatible = "st,stm32-ltdc"},
+ { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, drv_dt_ids);
+
+static struct platform_driver stm_drm_platform_driver = {
+ .probe = stm_drm_platform_probe,
+ .remove = stm_drm_platform_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = drv_dt_ids,
+ },
+};
+
+module_platform_driver(stm_drm_platform_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
new file mode 100644
index 000000000000..a40418cda74a
--- /dev/null
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ * Yannick Fertre <yannick.fertre@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_plane_helper.h>
+
+#include <video/videomode.h>
+
+#include "ltdc.h"
+
+#define NB_CRTC 1
+#define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
+
+#define MAX_IRQ 4
+
+#define HWVER_10200 0x010200
+#define HWVER_10300 0x010300
+#define HWVER_20101 0x020101
+
+/*
+ * The address of some registers depends on the HW version: such registers have
+ * an extra offset specified with reg_ofs.
+ */
+#define REG_OFS_NONE 0
+#define REG_OFS_4 4 /* Insertion of "Layer Configuration 2" reg */
+#define REG_OFS (ldev->caps.reg_ofs)
+#define LAY_OFS 0x80 /* Register Offset between 2 layers */
+
+/* Global register offsets */
+#define LTDC_IDR 0x0000 /* IDentification */
+#define LTDC_LCR 0x0004 /* Layer Count */
+#define LTDC_SSCR 0x0008 /* Synchronization Size Configuration */
+#define LTDC_BPCR 0x000C /* Back Porch Configuration */
+#define LTDC_AWCR 0x0010 /* Active Width Configuration */
+#define LTDC_TWCR 0x0014 /* Total Width Configuration */
+#define LTDC_GCR 0x0018 /* Global Control */
+#define LTDC_GC1R 0x001C /* Global Configuration 1 */
+#define LTDC_GC2R 0x0020 /* Global Configuration 2 */
+#define LTDC_SRCR 0x0024 /* Shadow Reload Configuration */
+#define LTDC_GACR 0x0028 /* GAmma Correction */
+#define LTDC_BCCR 0x002C /* Background Color Configuration */
+#define LTDC_IER 0x0034 /* Interrupt Enable */
+#define LTDC_ISR 0x0038 /* Interrupt Status */
+#define LTDC_ICR 0x003C /* Interrupt Clear */
+#define LTDC_LIPCR 0x0040 /* Line Interrupt Position Configuration */
+#define LTDC_CPSR 0x0044 /* Current Position Status */
+#define LTDC_CDSR 0x0048 /* Current Display Status */
+
+/* Layer register offsets */
+#define LTDC_L1LC1R (0x0080) /* L1 Layer Configuration 1 */
+#define LTDC_L1LC2R (0x0084) /* L1 Layer Configuration 2 */
+#define LTDC_L1CR (0x0084 + REG_OFS) /* L1 Control */
+#define LTDC_L1WHPCR (0x0088 + REG_OFS) /* L1 Window Hor Position Config */
+#define LTDC_L1WVPCR (0x008C + REG_OFS) /* L1 Window Vert Position Config */
+#define LTDC_L1CKCR (0x0090 + REG_OFS) /* L1 Color Keying Configuration */
+#define LTDC_L1PFCR (0x0094 + REG_OFS) /* L1 Pixel Format Configuration */
+#define LTDC_L1CACR (0x0098 + REG_OFS) /* L1 Constant Alpha Config */
+#define LTDC_L1DCCR (0x009C + REG_OFS) /* L1 Default Color Configuration */
+#define LTDC_L1BFCR (0x00A0 + REG_OFS) /* L1 Blend Factors Configuration */
+#define LTDC_L1FBBCR (0x00A4 + REG_OFS) /* L1 FrameBuffer Bus Control */
+#define LTDC_L1AFBCR (0x00A8 + REG_OFS) /* L1 AuxFB Control */
+#define LTDC_L1CFBAR (0x00AC + REG_OFS) /* L1 Color FrameBuffer Address */
+#define LTDC_L1CFBLR (0x00B0 + REG_OFS) /* L1 Color FrameBuffer Length */
+#define LTDC_L1CFBLNR (0x00B4 + REG_OFS) /* L1 Color FrameBuffer Line Nb */
+#define LTDC_L1AFBAR (0x00B8 + REG_OFS) /* L1 AuxFB Address */
+#define LTDC_L1AFBLR (0x00BC + REG_OFS) /* L1 AuxFB Length */
+#define LTDC_L1AFBLNR (0x00C0 + REG_OFS) /* L1 AuxFB Line Number */
+#define LTDC_L1CLUTWR (0x00C4 + REG_OFS) /* L1 CLUT Write */
+#define LTDC_L1YS1R (0x00E0 + REG_OFS) /* L1 YCbCr Scale 1 */
+#define LTDC_L1YS2R (0x00E4 + REG_OFS) /* L1 YCbCr Scale 2 */
+
+/* Bit definitions */
+#define SSCR_VSH GENMASK(10, 0) /* Vertical Synchronization Height */
+#define SSCR_HSW GENMASK(27, 16) /* Horizontal Synchronization Width */
+
+#define BPCR_AVBP GENMASK(10, 0) /* Accumulated Vertical Back Porch */
+#define BPCR_AHBP GENMASK(27, 16) /* Accumulated Horizontal Back Porch */
+
+#define AWCR_AAH GENMASK(10, 0) /* Accumulated Active Height */
+#define AWCR_AAW GENMASK(27, 16) /* Accumulated Active Width */
+
+#define TWCR_TOTALH GENMASK(10, 0) /* TOTAL Height */
+#define TWCR_TOTALW GENMASK(27, 16) /* TOTAL Width */
+
+#define GCR_LTDCEN BIT(0) /* LTDC ENable */
+#define GCR_DEN BIT(16) /* Dither ENable */
+#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity */
+#define GCR_DEPOL BIT(29) /* Data Enable POLarity */
+#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity */
+#define GCR_HSPOL BIT(31) /* Horizontal Synchro POLarity */
+
+#define GC1R_WBCH GENMASK(3, 0) /* Width of Blue CHannel output */
+#define GC1R_WGCH GENMASK(7, 4) /* Width of Green Channel output */
+#define GC1R_WRCH GENMASK(11, 8) /* Width of Red Channel output */
+#define GC1R_PBEN BIT(12) /* Precise Blending ENable */
+#define GC1R_DT GENMASK(15, 14) /* Dithering Technique */
+#define GC1R_GCT GENMASK(19, 17) /* Gamma Correction Technique */
+#define GC1R_SHREN BIT(21) /* SHadow Registers ENabled */
+#define GC1R_BCP BIT(22) /* Background Colour Programmable */
+#define GC1R_BBEN BIT(23) /* Background Blending ENabled */
+#define GC1R_LNIP BIT(24) /* Line Number IRQ Position */
+#define GC1R_TP BIT(25) /* Timing Programmable */
+#define GC1R_IPP BIT(26) /* IRQ Polarity Programmable */
+#define GC1R_SPP BIT(27) /* Sync Polarity Programmable */
+#define GC1R_DWP BIT(28) /* Dither Width Programmable */
+#define GC1R_STREN BIT(29) /* STatus Registers ENabled */
+#define GC1R_BMEN BIT(31) /* Blind Mode ENabled */
+
+#define GC2R_EDCA BIT(0) /* External Display Control Ability */
+#define GC2R_STSAEN BIT(1) /* Slave Timing Sync Ability ENabled */
+#define GC2R_DVAEN BIT(2) /* Dual-View Ability ENabled */
+#define GC2R_DPAEN BIT(3) /* Dual-Port Ability ENabled */
+#define GC2R_BW GENMASK(6, 4) /* Bus Width (log2 of nb of bytes) */
+#define GC2R_EDCEN BIT(7) /* External Display Control ENabled */
+
+#define SRCR_IMR BIT(0) /* IMmediate Reload */
+#define SRCR_VBR BIT(1) /* Vertical Blanking Reload */
+
+#define BCCR_BCBLACK 0x00 /* Background Color BLACK */
+#define BCCR_BCBLUE GENMASK(7, 0) /* Background Color BLUE */
+#define BCCR_BCGREEN GENMASK(15, 8) /* Background Color GREEN */
+#define BCCR_BCRED GENMASK(23, 16) /* Background Color RED */
+#define BCCR_BCWHITE GENMASK(23, 0) /* Background Color WHITE */
+
+#define IER_LIE BIT(0) /* Line Interrupt Enable */
+#define IER_FUIE BIT(1) /* Fifo Underrun Interrupt Enable */
+#define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */
+#define IER_RRIE BIT(3) /* Register Reload Interrupt enable */
+
+#define ISR_LIF BIT(0) /* Line Interrupt Flag */
+#define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */
+#define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
+#define ISR_RRIF BIT(3) /* Register Reload Interrupt Flag */
+
+#define LXCR_LEN BIT(0) /* Layer ENable */
+#define LXCR_COLKEN BIT(1) /* Color Keying Enable */
+#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
+
+#define LXWHPCR_WHSTPOS GENMASK(11, 0) /* Window Horizontal StarT POSition */
+#define LXWHPCR_WHSPPOS GENMASK(27, 16) /* Window Horizontal StoP POSition */
+
+#define LXWVPCR_WVSTPOS GENMASK(10, 0) /* Window Vertical StarT POSition */
+#define LXWVPCR_WVSPPOS GENMASK(26, 16) /* Window Vertical StoP POSition */
+
+#define LXPFCR_PF GENMASK(2, 0) /* Pixel Format */
+
+#define LXCACR_CONSTA GENMASK(7, 0) /* CONSTant Alpha */
+
+#define LXBFCR_BF2 GENMASK(2, 0) /* Blending Factor 2 */
+#define LXBFCR_BF1 GENMASK(10, 8) /* Blending Factor 1 */
+
+#define LXCFBLR_CFBLL GENMASK(12, 0) /* Color Frame Buffer Line Length */
+#define LXCFBLR_CFBP GENMASK(28, 16) /* Color Frame Buffer Pitch in bytes */
+
+#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
+
+#define HSPOL_AL 0 /* Horizontal Sync POLarity Active Low */
+#define VSPOL_AL 0 /* Vertical Sync POLarity Active Low */
+#define DEPOL_AL 0 /* Data Enable POLarity Active Low */
+#define PCPOL_IPC 0 /* Input Pixel Clock */
+#define HSPOL_AH GCR_HSPOL /* Horizontal Sync POLarity Active High */
+#define VSPOL_AH GCR_VSPOL /* Vertical Sync POLarity Active High */
+#define DEPOL_AH GCR_DEPOL /* Data Enable POLarity Active High */
+#define PCPOL_IIPC GCR_PCPOL /* Inverted Input Pixel Clock */
+#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */
+#define BF1_PAXCA 0x600 /* Pixel Alpha x Constant Alpha */
+#define BF1_CA 0x400 /* Constant Alpha */
+#define BF2_1PAXCA 0x007 /* 1 - (Pixel Alpha x Constant Alpha) */
+#define BF2_1CA 0x005 /* 1 - Constant Alpha */
+
+#define NB_PF 8 /* Max nb of HW pixel format */
+
+enum ltdc_pix_fmt {
+ PF_NONE,
+ /* RGB formats */
+ PF_ARGB8888, /* ARGB [32 bits] */
+ PF_RGBA8888, /* RGBA [32 bits] */
+ PF_RGB888, /* RGB [24 bits] */
+ PF_RGB565, /* RGB [16 bits] */
+ PF_ARGB1555, /* ARGB A:1 bit RGB:15 bits [16 bits] */
+ PF_ARGB4444, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
+ /* Indexed formats */
+ PF_L8, /* Indexed 8 bits [8 bits] */
+ PF_AL44, /* Alpha:4 bits + indexed 4 bits [8 bits] */
+ PF_AL88 /* Alpha:8 bits + indexed 8 bits [16 bits] */
+};
+
+/* The index gives the encoding of the pixel format for an HW version */
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
+ PF_ARGB8888, /* 0x00 */
+ PF_RGB888, /* 0x01 */
+ PF_RGB565, /* 0x02 */
+ PF_ARGB1555, /* 0x03 */
+ PF_ARGB4444, /* 0x04 */
+ PF_L8, /* 0x05 */
+ PF_AL44, /* 0x06 */
+ PF_AL88 /* 0x07 */
+};
+
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
+ PF_ARGB8888, /* 0x00 */
+ PF_RGB888, /* 0x01 */
+ PF_RGB565, /* 0x02 */
+ PF_RGBA8888, /* 0x03 */
+ PF_AL44, /* 0x04 */
+ PF_L8, /* 0x05 */
+ PF_ARGB1555, /* 0x06 */
+ PF_ARGB4444 /* 0x07 */
+};
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+ return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+ writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+ reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
+ u32 val)
+{
+ reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
+}
+
+static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
+{
+ return (struct ltdc_device *)crtc->dev->dev_private;
+}
+
+static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
+{
+ return (struct ltdc_device *)plane->dev->dev_private;
+}
+
+static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
+{
+ return (struct ltdc_device *)enc->dev->dev_private;
+}
+
+static inline struct ltdc_device *connector_to_ltdc(struct drm_connector *con)
+{
+ return (struct ltdc_device *)con->dev->dev_private;
+}
+
+static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
+{
+ enum ltdc_pix_fmt pf;
+
+ switch (drm_fmt) {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ pf = PF_ARGB8888;
+ break;
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBX8888:
+ pf = PF_RGBA8888;
+ break;
+ case DRM_FORMAT_RGB888:
+ pf = PF_RGB888;
+ break;
+ case DRM_FORMAT_RGB565:
+ pf = PF_RGB565;
+ break;
+ case DRM_FORMAT_ARGB1555:
+ case DRM_FORMAT_XRGB1555:
+ pf = PF_ARGB1555;
+ break;
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_XRGB4444:
+ pf = PF_ARGB4444;
+ break;
+ case DRM_FORMAT_C8:
+ pf = PF_L8;
+ break;
+ default:
+ pf = PF_NONE;
+ break;
+ /* Note: There are no DRM_FORMAT for AL44 and AL88 */
+ }
+
+ return pf;
+}
+
+static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
+{
+ switch (pf) {
+ case PF_ARGB8888:
+ return DRM_FORMAT_ARGB8888;
+ case PF_RGBA8888:
+ return DRM_FORMAT_RGBA8888;
+ case PF_RGB888:
+ return DRM_FORMAT_RGB888;
+ case PF_RGB565:
+ return DRM_FORMAT_RGB565;
+ case PF_ARGB1555:
+ return DRM_FORMAT_ARGB1555;
+ case PF_ARGB4444:
+ return DRM_FORMAT_ARGB4444;
+ case PF_L8:
+ return DRM_FORMAT_C8;
+ case PF_AL44: /* No DRM support */
+ case PF_AL88: /* No DRM support */
+ case PF_NONE:
+ default:
+ return 0;
+ }
+}
+
+static irqreturn_t ltdc_irq_thread(int irq, void *arg)
+{
+ struct drm_device *ddev = arg;
+ struct ltdc_device *ldev = ddev->dev_private;
+ struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
+
+ /* Line IRQ : trigger the vblank event */
+ if (ldev->irq_status & ISR_LIF)
+ drm_crtc_handle_vblank(crtc);
+
+ /* Save FIFO Underrun & Transfer Error status */
+ mutex_lock(&ldev->err_lock);
+ if (ldev->irq_status & ISR_FUIF)
+ ldev->error_status |= ISR_FUIF;
+ if (ldev->irq_status & ISR_TERRIF)
+ ldev->error_status |= ISR_TERRIF;
+ mutex_unlock(&ldev->err_lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ltdc_irq(int irq, void *arg)
+{
+ struct drm_device *ddev = arg;
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ /* Read & Clear the interrupt status */
+ ldev->irq_status = reg_read(ldev->regs, LTDC_ISR);
+ reg_write(ldev->regs, LTDC_ICR, ldev->irq_status);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/*
+ * DRM_CRTC
+ */
+
+static void ltdc_crtc_load_lut(struct drm_crtc *crtc)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ unsigned int i, lay;
+
+ for (lay = 0; lay < ldev->caps.nb_layers; lay++)
+ for (i = 0; i < 256; i++)
+ reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS,
+ ldev->clut[i]);
+}
+
+static void ltdc_crtc_enable(struct drm_crtc *crtc)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /* Sets the background color value */
+ reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
+
+ /* Enable IRQ */
+ reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+ /* Immediately commit the planes */
+ reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+
+ /* Enable LTDC */
+ reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+ drm_crtc_vblank_on(crtc);
+}
+
+static void ltdc_crtc_disable(struct drm_crtc *crtc)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_crtc_vblank_off(crtc);
+
+ /* disable LTDC */
+ reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+ /* disable IRQ */
+ reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+ /* immediately commit disable of layers before switching off LTDC */
+ reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+}
+
+static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+ struct videomode vm;
+ int rate = mode->clock * 1000;
+ u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
+ u32 total_width, total_height;
+ u32 val;
+
+ drm_display_mode_to_videomode(mode, &vm);
+
+ DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
+ DRM_DEBUG_DRIVER("Video mode: %dx%d", vm.hactive, vm.vactive);
+ DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
+ vm.hfront_porch, vm.hback_porch, vm.hsync_len,
+ vm.vfront_porch, vm.vback_porch, vm.vsync_len);
+
+ /* Convert video timings to ltdc timings */
+ hsync = vm.hsync_len - 1;
+ vsync = vm.vsync_len - 1;
+ accum_hbp = hsync + vm.hback_porch;
+ accum_vbp = vsync + vm.vback_porch;
+ accum_act_w = accum_hbp + vm.hactive;
+ accum_act_h = accum_vbp + vm.vactive;
+ total_width = accum_act_w + vm.hfront_porch;
+ total_height = accum_act_h + vm.vfront_porch;
+
+ clk_disable(ldev->pixel_clk);
+
+ if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
+ DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
+ return;
+ }
+
+ clk_enable(ldev->pixel_clk);
+
+ /* Configures the HS, VS, DE and PC polarities. */
+ val = HSPOL_AL | HSPOL_AL | DEPOL_AL | PCPOL_IPC;
+
+ if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)
+ val |= HSPOL_AH;
+
+ if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+ val |= VSPOL_AH;
+
+ if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
+ val |= DEPOL_AH;
+
+ if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+ val |= PCPOL_IIPC;
+
+ reg_update_bits(ldev->regs, LTDC_GCR,
+ GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+
+ /* Set Synchronization size */
+ val = (hsync << 16) | vsync;
+ reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+
+ /* Set Accumulated Back porch */
+ val = (accum_hbp << 16) | accum_vbp;
+ reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+
+ /* Set Accumulated Active Width */
+ val = (accum_act_w << 16) | accum_act_h;
+ reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+
+ /* Set total width & height */
+ val = (total_width << 16) | total_height;
+ reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+
+ reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1));
+}
+
+static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_crtc_state *old_crtc_state)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ struct drm_pending_vblank_event *event = crtc->state->event;
+
+ DRM_DEBUG_ATOMIC("\n");
+
+ /* Commit shadow registers = update planes at next vblank */
+ reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+
+ if (event) {
+ crtc->state->event = NULL;
+
+ spin_lock_irq(&crtc->dev->event_lock);
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ }
+}
+
+static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
+ .load_lut = ltdc_crtc_load_lut,
+ .enable = ltdc_crtc_enable,
+ .disable = ltdc_crtc_disable,
+ .mode_set_nofb = ltdc_crtc_mode_set_nofb,
+ .atomic_flush = ltdc_crtc_atomic_flush,
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ DRM_DEBUG_DRIVER("\n");
+ reg_set(ldev->regs, LTDC_IER, IER_LIE);
+
+ return 0;
+}
+
+void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ DRM_DEBUG_DRIVER("\n");
+ reg_clear(ldev->regs, LTDC_IER, IER_LIE);
+}
+
+static struct drm_crtc_funcs ltdc_crtc_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+/*
+ * DRM_PLANE
+ */
+
+static int ltdc_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct drm_framebuffer *fb = state->fb;
+ u32 src_x, src_y, src_w, src_h;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (!fb)
+ return 0;
+
+ /* convert src_ from 16:16 format */
+ src_x = state->src_x >> 16;
+ src_y = state->src_y >> 16;
+ src_w = state->src_w >> 16;
+ src_h = state->src_h >> 16;
+
+ /* Reject scaling */
+ if ((src_w != state->crtc_w) || (src_h != state->crtc_h)) {
+ DRM_ERROR("Scaling is not supported");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ltdc_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *oldstate)
+{
+ struct ltdc_device *ldev = plane_to_ltdc(plane);
+ struct drm_plane_state *state = plane->state;
+ struct drm_framebuffer *fb = state->fb;
+ u32 lofs = plane->index * LAY_OFS;
+ u32 x0 = state->crtc_x;
+ u32 x1 = state->crtc_x + state->crtc_w - 1;
+ u32 y0 = state->crtc_y;
+ u32 y1 = state->crtc_y + state->crtc_h - 1;
+ u32 src_x, src_y, src_w, src_h;
+ u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
+ enum ltdc_pix_fmt pf;
+
+ if (!state->crtc || !fb) {
+ DRM_DEBUG_DRIVER("fb or crtc NULL");
+ return;
+ }
+
+ /* convert src_ from 16:16 format */
+ src_x = state->src_x >> 16;
+ src_y = state->src_y >> 16;
+ src_w = state->src_w >> 16;
+ src_h = state->src_h >> 16;
+
+ DRM_DEBUG_DRIVER(
+ "plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
+ plane->base.id, fb->base.id,
+ src_w, src_h, src_x, src_y,
+ state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y);
+
+ bpcr = reg_read(ldev->regs, LTDC_BPCR);
+ ahbp = (bpcr & BPCR_AHBP) >> 16;
+ avbp = bpcr & BPCR_AVBP;
+
+ /* Configures the horizontal start and stop position */
+ val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
+ reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
+ LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
+
+ /* Configures the vertical start and stop position */
+ val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
+ reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
+ LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
+
+ /* Specifies the pixel format */
+ pf = to_ltdc_pixelformat(fb->format->format);
+ for (val = 0; val < NB_PF; val++)
+ if (ldev->caps.pix_fmt_hw[val] == pf)
+ break;
+
+ if (val == NB_PF) {
+ DRM_ERROR("Pixel format %.4s not supported\n",
+ (char *)&fb->format->format);
+ val = 0; /* set by default ARGB 32 bits */
+ }
+ reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
+
+ /* Configures the color frame buffer pitch in bytes & line length */
+ pitch_in_bytes = fb->pitches[0];
+ line_length = drm_format_plane_cpp(fb->format->format, 0) *
+ (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+ val = ((pitch_in_bytes << 16) | line_length);
+ reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
+ LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+ /* Specifies the constant alpha value */
+ val = CONSTA_MAX;
+ reg_update_bits(ldev->regs, LTDC_L1CACR + lofs,
+ LXCACR_CONSTA, val);
+
+ /* Specifies the blending factors */
+ val = BF1_PAXCA | BF2_1PAXCA;
+ reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
+ LXBFCR_BF2 | LXBFCR_BF1, val);
+
+ /* Configures the frame buffer line number */
+ val = y1 - y0 + 1;
+ reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs,
+ LXCFBLNR_CFBLN, val);
+
+ /* Sets the FB address */
+ paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);
+
+ DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
+ reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr);
+
+ /* Enable layer and CLUT if needed */
+ val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
+ val |= LXCR_LEN;
+ reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
+ LXCR_LEN | LXCR_CLUTEN, val);
+
+ mutex_lock(&ldev->err_lock);
+ if (ldev->error_status & ISR_FUIF) {
+ DRM_DEBUG_DRIVER("Fifo underrun\n");
+ ldev->error_status &= ~ISR_FUIF;
+ }
+ if (ldev->error_status & ISR_TERRIF) {
+ DRM_DEBUG_DRIVER("Transfer error\n");
+ ldev->error_status &= ~ISR_TERRIF;
+ }
+ mutex_unlock(&ldev->err_lock);
+}
+
+static void ltdc_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *oldstate)
+{
+ struct ltdc_device *ldev = plane_to_ltdc(plane);
+ u32 lofs = plane->index * LAY_OFS;
+
+ /* disable layer */
+ reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
+
+ DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
+ oldstate->crtc->base.id, plane->base.id);
+}
+
+static struct drm_plane_funcs ltdc_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ .set_property = drm_atomic_helper_plane_set_property,
+ .reset = drm_atomic_helper_plane_reset,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
+ .atomic_check = ltdc_plane_atomic_check,
+ .atomic_update = ltdc_plane_atomic_update,
+ .atomic_disable = ltdc_plane_atomic_disable,
+};
+
+static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
+ enum drm_plane_type type)
+{
+ unsigned long possible_crtcs = CRTC_MASK;
+ struct ltdc_device *ldev = ddev->dev_private;
+ struct device *dev = ddev->dev;
+ struct drm_plane *plane;
+ unsigned int i, nb_fmt = 0;
+ u32 formats[NB_PF];
+ u32 drm_fmt;
+ int ret;
+
+ /* Get supported pixel formats */
+ for (i = 0; i < NB_PF; i++) {
+ drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
+ if (!drm_fmt)
+ continue;
+ formats[nb_fmt++] = drm_fmt;
+ }
+
+ plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return 0;
+
+ ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
+ &ltdc_plane_funcs, formats, nb_fmt,
+ type, NULL);
+ if (ret < 0)
+ return 0;
+
+ drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
+
+ DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
+
+ return plane;
+}
+
+static void ltdc_plane_destroy_all(struct drm_device *ddev)
+{
+ struct drm_plane *plane, *plane_temp;
+
+ list_for_each_entry_safe(plane, plane_temp,
+ &ddev->mode_config.plane_list, head)
+ drm_plane_cleanup(plane);
+}
+
+static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+ struct drm_plane *primary, *overlay;
+ unsigned int i;
+ int res;
+
+ primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
+ if (!primary) {
+ DRM_ERROR("Can not create primary plane\n");
+ return -EINVAL;
+ }
+
+ res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+ &ltdc_crtc_funcs, NULL);
+ if (res) {
+ DRM_ERROR("Can not initialize CRTC\n");
+ goto cleanup;
+ }
+
+ drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
+
+ DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
+
+ /* Add planes. Note : the first layer is used by primary plane */
+ for (i = 1; i < ldev->caps.nb_layers; i++) {
+ overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
+ if (!overlay) {
+ res = -ENOMEM;
+ DRM_ERROR("Can not create overlay plane %d\n", i);
+ goto cleanup;
+ }
+ }
+
+ return 0;
+
+cleanup:
+ ltdc_plane_destroy_all(ddev);
+ return res;
+}
+
+/*
+ * DRM_ENCODER
+ */
+
+static void ltdc_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+ struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_panel_prepare(ldev->panel);
+ drm_panel_enable(ldev->panel);
+}
+
+static void ltdc_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+ struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_panel_disable(ldev->panel);
+ drm_panel_unprepare(ldev->panel);
+}
+
+static const struct drm_encoder_helper_funcs ltdc_rgb_encoder_helper_funcs = {
+ .enable = ltdc_rgb_encoder_enable,
+ .disable = ltdc_rgb_encoder_disable,
+};
+
+static const struct drm_encoder_funcs ltdc_rgb_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static struct drm_encoder *ltdc_rgb_encoder_create(struct drm_device *ddev)
+{
+ struct drm_encoder *encoder;
+
+ encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
+ if (!encoder)
+ return NULL;
+
+ encoder->possible_crtcs = CRTC_MASK;
+ encoder->possible_clones = 0; /* No cloning support */
+
+ drm_encoder_init(ddev, encoder, &ltdc_rgb_encoder_funcs,
+ DRM_MODE_ENCODER_DPI, NULL);
+
+ drm_encoder_helper_add(encoder, &ltdc_rgb_encoder_helper_funcs);
+
+ DRM_DEBUG_DRIVER("RGB encoder:%d created\n", encoder->base.id);
+
+ return encoder;
+}
+
+/*
+ * DRM_CONNECTOR
+ */
+
+static int ltdc_rgb_connector_get_modes(struct drm_connector *connector)
+{
+ struct drm_device *ddev = connector->dev;
+ struct ltdc_device *ldev = ddev->dev_private;
+ int ret = 0;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (ldev->panel)
+ ret = drm_panel_get_modes(ldev->panel);
+
+ return ret < 0 ? 0 : ret;
+}
+
+static struct drm_connector_helper_funcs ltdc_rgb_connector_helper_funcs = {
+ .get_modes = ltdc_rgb_connector_get_modes,
+};
+
+static enum drm_connector_status
+ltdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct ltdc_device *ldev = connector_to_ltdc(connector);
+
+ return ldev->panel ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static void ltdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ltdc_rgb_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = ltdc_rgb_connector_detect,
+ .destroy = ltdc_rgb_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+struct drm_connector *ltdc_rgb_connector_create(struct drm_device *ddev)
+{
+ struct drm_connector *connector;
+ int err;
+
+ connector = devm_kzalloc(ddev->dev, sizeof(*connector), GFP_KERNEL);
+ if (!connector) {
+ DRM_ERROR("Failed to allocate connector\n");
+ return NULL;
+ }
+
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ err = drm_connector_init(ddev, connector, &ltdc_rgb_connector_funcs,
+ DRM_MODE_CONNECTOR_DPI);
+ if (err) {
+ DRM_ERROR("Failed to initialize connector\n");
+ return NULL;
+ }
+
+ drm_connector_helper_add(connector, &ltdc_rgb_connector_helper_funcs);
+
+ DRM_DEBUG_DRIVER("RGB connector:%d created\n", connector->base.id);
+
+ return connector;
+}
+
+static int ltdc_get_caps(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+ u32 bus_width_log2, lcr, gc2r;
+
+ /* at least 1 layer must be managed */
+ lcr = reg_read(ldev->regs, LTDC_LCR);
+
+ ldev->caps.nb_layers = max_t(int, lcr, 1);
+
+ /* set data bus width */
+ gc2r = reg_read(ldev->regs, LTDC_GC2R);
+ bus_width_log2 = (gc2r & GC2R_BW) >> 4;
+ ldev->caps.bus_width = 8 << bus_width_log2;
+ ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR);
+
+ switch (ldev->caps.hw_version) {
+ case HWVER_10200:
+ case HWVER_10300:
+ ldev->caps.reg_ofs = REG_OFS_NONE;
+ ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+ break;
+ case HWVER_20101:
+ ldev->caps.reg_ofs = REG_OFS_4;
+ ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct drm_panel *ltdc_get_panel(struct drm_device *ddev)
+{
+ struct device *dev = ddev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *entity, *port = NULL;
+ struct drm_panel *panel = NULL;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ /*
+ * Parse ltdc node to get remote port and find RGB panel / HDMI slave
+ * If a dsi or a bridge (hdmi, lvds...) is connected to ltdc,
+ * a remote port & RGB panel will not be found.
+ */
+ for_each_endpoint_of_node(np, entity) {
+ if (!of_device_is_available(entity))
+ continue;
+
+ port = of_graph_get_remote_port_parent(entity);
+ if (port) {
+ panel = of_drm_find_panel(port);
+ of_node_put(port);
+ if (panel) {
+ DRM_DEBUG_DRIVER("remote panel %s\n",
+ port->full_name);
+ } else {
+ DRM_DEBUG_DRIVER("panel missing\n");
+ of_node_put(entity);
+ }
+ }
+ }
+
+ return panel;
+}
+
+int ltdc_load(struct drm_device *ddev)
+{
+ struct platform_device *pdev = to_platform_device(ddev->dev);
+ struct ltdc_device *ldev = ddev->dev_private;
+ struct device *dev = ddev->dev;
+ struct device_node *np = dev->of_node;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector = NULL;
+ struct drm_crtc *crtc;
+ struct reset_control *rstc;
+ struct resource res;
+ int irq, ret, i;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ ldev->panel = ltdc_get_panel(ddev);
+ if (!ldev->panel)
+ return -EPROBE_DEFER;
+
+ rstc = of_reset_control_get(np, NULL);
+
+ mutex_init(&ldev->err_lock);
+
+ ldev->pixel_clk = devm_clk_get(dev, "lcd");
+ if (IS_ERR(ldev->pixel_clk)) {
+ DRM_ERROR("Unable to get lcd clock\n");
+ return -ENODEV;
+ }
+
+ if (clk_prepare_enable(ldev->pixel_clk)) {
+ DRM_ERROR("Unable to prepare pixel clock\n");
+ return -ENODEV;
+ }
+
+ if (of_address_to_resource(np, 0, &res)) {
+ DRM_ERROR("Unable to get resource\n");
+ return -ENODEV;
+ }
+
+ ldev->regs = devm_ioremap_resource(dev, &res);
+ if (IS_ERR(ldev->regs)) {
+ DRM_ERROR("Unable to get ltdc registers\n");
+ return PTR_ERR(ldev->regs);
+ }
+
+ for (i = 0; i < MAX_IRQ; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ continue;
+
+ ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
+ ltdc_irq_thread, IRQF_ONESHOT,
+ dev_name(dev), ddev);
+ if (ret) {
+ DRM_ERROR("Failed to register LTDC interrupt\n");
+ return ret;
+ }
+ }
+
+ if (!IS_ERR(rstc))
+ reset_control_deassert(rstc);
+
+ /* Disable interrupts */
+ reg_clear(ldev->regs, LTDC_IER,
+ IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
+
+ ret = ltdc_get_caps(ddev);
+ if (ret) {
+ DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
+ ldev->caps.hw_version);
+ return ret;
+ }
+
+ DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version);
+
+ if (ldev->panel) {
+ encoder = ltdc_rgb_encoder_create(ddev);
+ if (!encoder) {
+ DRM_ERROR("Failed to create RGB encoder\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ connector = ltdc_rgb_connector_create(ddev);
+ if (!connector) {
+ DRM_ERROR("Failed to create RGB connector\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
+ if (ret) {
+ DRM_ERROR("Failed to attach connector to encoder\n");
+ goto err;
+ }
+
+ drm_panel_attach(ldev->panel, connector);
+ }
+
+ crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
+ if (!crtc) {
+ DRM_ERROR("Failed to allocate crtc\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = ltdc_crtc_init(ddev, crtc);
+ if (ret) {
+ DRM_ERROR("Failed to init crtc\n");
+ goto err;
+ }
+
+ ret = drm_vblank_init(ddev, NB_CRTC);
+ if (ret) {
+ DRM_ERROR("Failed calling drm_vblank_init()\n");
+ goto err;
+ }
+
+ /* Allow usage of vblank without having to call drm_irq_install */
+ ddev->irq_enabled = 1;
+
+ return 0;
+err:
+ if (ldev->panel)
+ drm_panel_detach(ldev->panel);
+
+ clk_disable_unprepare(ldev->pixel_clk);
+
+ return ret;
+}
+
+void ltdc_unload(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ drm_vblank_cleanup(ddev);
+
+ if (ldev->panel)
+ drm_panel_detach(ldev->panel);
+
+ clk_disable_unprepare(ldev->pixel_clk);
+}
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
new file mode 100644
index 000000000000..d7a9c736ac1e
--- /dev/null
+++ b/drivers/gpu/drm/stm/ltdc.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ * Yannick Fertre <yannick.fertre@st.com>
+ * Fabien Dessenne <fabien.dessenne@st.com>
+ * Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef _LTDC_H_
+#define _LTDC_H_
+
+struct ltdc_caps {
+ u32 hw_version; /* hardware version */
+ u32 nb_layers; /* number of supported layers */
+ u32 reg_ofs; /* register offset for applicable regs */
+ u32 bus_width; /* bus width (32 or 64 bits) */
+ const u32 *pix_fmt_hw; /* supported pixel formats */
+};
+
+struct ltdc_device {
+ struct drm_fbdev_cma *fbdev;
+ void __iomem *regs;
+ struct clk *pixel_clk; /* lcd pixel clock */
+ struct drm_panel *panel;
+ struct mutex err_lock; /* protecting error_status */
+ struct ltdc_caps caps;
+ u32 clut[256]; /* color look up table */
+ u32 error_status;
+ u32 irq_status;
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
+void ltdc_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
+int ltdc_load(struct drm_device *ddev);
+void ltdc_unload(struct drm_device *ddev);
+
+#endif
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 9a1e34e48f64..51c48a8e00ec 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -892,7 +892,7 @@ static int tegra_drm_context_cleanup(int id, void *p, void *data)
return 0;
}
-static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
@@ -960,7 +960,7 @@ static struct drm_driver tegra_drm_driver = {
.load = tegra_drm_load,
.unload = tegra_drm_unload,
.open = tegra_drm_open,
- .preclose = tegra_drm_preclose,
+ .postclose = tegra_drm_postclose,
.lastclose = tegra_drm_lastclose,
#if defined(CONFIG_DEBUG_FS)
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
index 61f45d122bd0..ab687fba4916 100644
--- a/drivers/gpu/drm/vc4/Makefile
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -9,6 +9,7 @@ vc4-y := \
vc4_drv.o \
vc4_dpi.o \
vc4_dsi.o \
+ vc4_fence.o \
vc4_kms.o \
vc4_gem.o \
vc4_hdmi.o \
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index af29432a6471..80b2f9e55c5c 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -19,6 +19,8 @@
* rendering can return quickly.
*/
+#include <linux/dma-buf.h>
+
#include "vc4_drv.h"
#include "uapi/drm/vc4_drm.h"
@@ -88,6 +90,10 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
vc4->bo_stats.num_allocated--;
vc4->bo_stats.size_allocated -= obj->size;
+
+ if (bo->resv == &bo->_resv)
+ reservation_object_fini(bo->resv);
+
drm_gem_cma_free_object(obj);
}
@@ -244,8 +250,12 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
return ERR_PTR(-ENOMEM);
}
}
+ bo = to_vc4_bo(&cma_obj->base);
- return to_vc4_bo(&cma_obj->base);
+ bo->resv = &bo->_resv;
+ reservation_object_init(bo->resv);
+
+ return bo;
}
int vc4_dumb_create(struct drm_file *file_priv,
@@ -369,6 +379,13 @@ static void vc4_bo_cache_time_timer(unsigned long data)
schedule_work(&vc4->bo_cache.time_work);
}
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj)
+{
+ struct vc4_bo *bo = to_vc4_bo(obj);
+
+ return bo->resv;
+}
+
struct dma_buf *
vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
{
@@ -440,6 +457,24 @@ void *vc4_prime_vmap(struct drm_gem_object *obj)
return drm_gem_cma_prime_vmap(obj);
}
+struct drm_gem_object *
+vc4_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt)
+{
+ struct drm_gem_object *obj;
+ struct vc4_bo *bo;
+
+ obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt);
+ if (IS_ERR(obj))
+ return obj;
+
+ bo = to_vc4_bo(obj);
+ bo->resv = attach->dmabuf->resv;
+
+ return obj;
+}
+
int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index d86c8cce3182..1b4dbe9e1c6d 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -151,10 +151,10 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
}
#endif
-int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
- unsigned int flags, int *vpos, int *hpos,
- ktime_t *stime, ktime_t *etime,
- const struct drm_display_mode *mode)
+bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
@@ -162,7 +162,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
u32 val;
int fifo_lines;
int vblank_lines;
- int ret = 0;
+ bool ret = false;
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
@@ -198,7 +198,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
if (fifo_lines > 0)
- ret |= DRM_SCANOUTPOS_VALID;
+ ret = true;
/* HVS more than fifo_lines into frame for compositing? */
if (*vpos > fifo_lines) {
@@ -216,7 +216,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
*/
*vpos -= fifo_lines + 1;
- ret |= DRM_SCANOUTPOS_ACCURATE;
return ret;
}
@@ -229,10 +228,9 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
* We can't get meaningful readings wrt. scanline position of the PV
* and need to make things up in a approximative but consistent way.
*/
- ret |= DRM_SCANOUTPOS_IN_VBLANK;
vblank_lines = mode->vtotal - mode->vdisplay;
- if (flags & DRM_CALLED_FROM_VBLIRQ) {
+ if (in_vblank_irq) {
/*
* Assume the irq handler got called close to first
* line of vblank, so PV has about a full vblank
@@ -254,9 +252,10 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
* we are at the very beginning of vblank, as the hvs just
* started refilling, and the stime and etime timestamps
* truly correspond to start of vblank.
+ *
+ * Unfortunately there's no way to report this to upper levels
+ * and make it more useful.
*/
- if ((val & SCALER_DISPSTATX_FULL) != SCALER_DISPSTATX_FULL)
- ret |= DRM_SCANOUTPOS_ACCURATE;
} else {
/*
* No clue where we are inside vblank. Return a vpos of zero,
@@ -270,19 +269,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
return ret;
}
-int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
- int *max_error, struct timeval *vblank_time,
- unsigned flags)
-{
- struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
- struct drm_crtc_state *state = crtc->state;
-
- /* Helper routine in DRM core does all the work: */
- return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc_id, max_error,
- vblank_time, flags,
- &state->adjusted_mode);
-}
-
static void vc4_crtc_destroy(struct drm_crtc *crtc)
{
drm_crtc_cleanup(crtc);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 61e674baf3a6..863974942c66 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -154,7 +154,7 @@ static struct drm_driver vc4_drm_driver = {
.irq_uninstall = vc4_irq_uninstall,
.get_scanout_position = vc4_crtc_get_scanoutpos,
- .get_vblank_timestamp = vc4_crtc_get_vblank_timestamp,
+ .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
#if defined(CONFIG_DEBUG_FS)
.debugfs_init = vc4_debugfs_init,
@@ -168,8 +168,9 @@ static struct drm_driver vc4_drm_driver = {
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = drm_gem_prime_import,
.gem_prime_export = vc4_prime_export,
+ .gem_prime_res_obj = vc4_prime_res_obj,
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
- .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+ .gem_prime_import_sg_table = vc4_prime_import_sg_table,
.gem_prime_vmap = vc4_prime_vmap,
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
.gem_prime_mmap = vc4_prime_mmap,
@@ -334,6 +335,7 @@ static int vc4_platform_drm_remove(struct platform_device *pdev)
static const struct of_device_id vc4_of_match[] = {
{ .compatible = "brcm,bcm2835-vc4", },
+ { .compatible = "brcm,cygnus-vc4", },
{},
};
MODULE_DEVICE_TABLE(of, vc4_of_match);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index dffce6293d87..5ba281361fb7 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -8,7 +8,9 @@
#include "drmP.h"
#include "drm_gem_cma_helper.h"
+#include "drm_gem_cma_helper.h"
+#include <linux/reservation.h>
#include <drm/drm_encoder.h>
struct vc4_dev {
@@ -56,6 +58,8 @@ struct vc4_dev {
/* Protects bo_cache and the BO stats. */
struct mutex bo_lock;
+ uint64_t dma_fence_context;
+
/* Sequence number for the last job queued in bin_job_list.
* Starts at 0 (no jobs emitted).
*/
@@ -95,12 +99,23 @@ struct vc4_dev {
*/
struct list_head seqno_cb_list;
- /* The binner overflow memory that's currently set up in
- * BPOA/BPOS registers. When overflow occurs and a new one is
- * allocated, the previous one will be moved to
- * vc4->current_exec's free list.
+ /* The memory used for storing binner tile alloc, tile state,
+ * and overflow memory allocations. This is freed when V3D
+ * powers down.
+ */
+ struct vc4_bo *bin_bo;
+
+ /* Size of blocks allocated within bin_bo. */
+ uint32_t bin_alloc_size;
+
+ /* Bitmask of the bin_alloc_size chunks in bin_bo that are
+ * used.
*/
- struct vc4_bo *overflow_mem;
+ uint32_t bin_alloc_used;
+
+ /* Bitmask of the current bin_alloc used for overflow memory. */
+ uint32_t bin_alloc_overflow;
+
struct work_struct overflow_mem_work;
int power_refcount;
@@ -150,6 +165,10 @@ struct vc4_bo {
* DRM_IOCTL_VC4_CREATE_SHADER_BO.
*/
struct vc4_validated_shader_info *validated_shader;
+
+ /* normally (resv == &_resv) except for imported bo's */
+ struct reservation_object *resv;
+ struct reservation_object _resv;
};
static inline struct vc4_bo *
@@ -158,6 +177,19 @@ to_vc4_bo(struct drm_gem_object *bo)
return (struct vc4_bo *)bo;
}
+struct vc4_fence {
+ struct dma_fence base;
+ struct drm_device *dev;
+ /* vc4 seqno for signaled() test */
+ uint64_t seqno;
+};
+
+static inline struct vc4_fence *
+to_vc4_fence(struct dma_fence *fence)
+{
+ return (struct vc4_fence *)fence;
+}
+
struct vc4_seqno_cb {
struct work_struct work;
uint64_t seqno;
@@ -168,6 +200,7 @@ struct vc4_v3d {
struct vc4_dev *vc4;
struct platform_device *pdev;
void __iomem *regs;
+ struct clk *clk;
};
struct vc4_hvs {
@@ -230,6 +263,8 @@ struct vc4_exec_info {
/* Latest write_seqno of any BO that binning depends on. */
uint64_t bin_dep_seqno;
+ struct dma_fence *fence;
+
/* Last current addresses the hardware was processing when the
* hangcheck timer checked on us.
*/
@@ -293,8 +328,12 @@ struct vc4_exec_info {
bool found_increment_semaphore_packet;
bool found_flush;
uint8_t bin_tiles_x, bin_tiles_y;
- struct drm_gem_cma_object *tile_bo;
+ /* Physical address of the start of the tile alloc array
+ * (where each tile's binned CL will start)
+ */
uint32_t tile_alloc_offset;
+ /* Bitmask of which binner slots are freed when this job completes. */
+ uint32_t bin_slots;
/**
* Computed addresses pointing into exec_bo where we start the
@@ -436,7 +475,11 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj);
int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt);
void *vc4_prime_vmap(struct drm_gem_object *obj);
void vc4_bo_cache_init(struct drm_device *dev);
void vc4_bo_cache_destroy(struct drm_device *dev);
@@ -446,13 +489,10 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *arg);
extern struct platform_driver vc4_crtc_driver;
bool vc4_event_pending(struct drm_crtc *crtc);
int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
-int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
- unsigned int flags, int *vpos, int *hpos,
- ktime_t *stime, ktime_t *etime,
- const struct drm_display_mode *mode);
-int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
- int *max_error, struct timeval *vblank_time,
- unsigned flags);
+bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode);
/* vc4_debugfs.c */
int vc4_debugfs_init(struct drm_minor *minor);
@@ -468,6 +508,9 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused);
extern struct platform_driver vc4_dsi_driver;
int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
+/* vc4_fence.c */
+extern const struct dma_fence_ops vc4_fence_ops;
+
/* vc4_gem.c */
void vc4_gem_init(struct drm_device *dev);
void vc4_gem_destroy(struct drm_device *dev);
@@ -522,6 +565,7 @@ void vc4_plane_async_set_fb(struct drm_plane *plane,
extern struct platform_driver vc4_v3d_driver;
int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4);
/* vc4_validate.c */
int
diff --git a/drivers/gpu/drm/vc4/vc4_fence.c b/drivers/gpu/drm/vc4/vc4_fence.c
new file mode 100644
index 000000000000..dbf5a5a5d5f5
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_fence.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2017 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "vc4_drv.h"
+
+static const char *vc4_fence_get_driver_name(struct dma_fence *fence)
+{
+ return "vc4";
+}
+
+static const char *vc4_fence_get_timeline_name(struct dma_fence *fence)
+{
+ return "vc4-v3d";
+}
+
+static bool vc4_fence_enable_signaling(struct dma_fence *fence)
+{
+ return true;
+}
+
+static bool vc4_fence_signaled(struct dma_fence *fence)
+{
+ struct vc4_fence *f = to_vc4_fence(fence);
+ struct vc4_dev *vc4 = to_vc4_dev(f->dev);
+
+ return vc4->finished_seqno >= f->seqno;
+}
+
+const struct dma_fence_ops vc4_fence_ops = {
+ .get_driver_name = vc4_fence_get_driver_name,
+ .get_timeline_name = vc4_fence_get_timeline_name,
+ .enable_signaling = vc4_fence_enable_signaling,
+ .signaled = vc4_fence_signaled,
+ .wait = dma_fence_default_wait,
+ .release = dma_fence_free,
+};
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index e9c381c42139..735412e3725a 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -463,6 +463,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
for (i = 0; i < exec->bo_count; i++) {
bo = to_vc4_bo(&exec->bo[i]->base);
bo->seqno = seqno;
+
+ reservation_object_add_shared_fence(bo->resv, exec->fence);
}
list_for_each_entry(bo, &exec->unref_list, unref_head) {
@@ -472,7 +474,103 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
for (i = 0; i < exec->rcl_write_bo_count; i++) {
bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
bo->write_seqno = seqno;
+
+ reservation_object_add_excl_fence(bo->resv, exec->fence);
+ }
+}
+
+static void
+vc4_unlock_bo_reservations(struct drm_device *dev,
+ struct vc4_exec_info *exec,
+ struct ww_acquire_ctx *acquire_ctx)
+{
+ int i;
+
+ for (i = 0; i < exec->bo_count; i++) {
+ struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
+
+ ww_mutex_unlock(&bo->resv->lock);
+ }
+
+ ww_acquire_fini(acquire_ctx);
+}
+
+/* Takes the reservation lock on all the BOs being referenced, so that
+ * at queue submit time we can update the reservations.
+ *
+ * We don't lock the RCL the tile alloc/state BOs, or overflow memory
+ * (all of which are on exec->unref_list). They're entirely private
+ * to vc4, so we don't attach dma-buf fences to them.
+ */
+static int
+vc4_lock_bo_reservations(struct drm_device *dev,
+ struct vc4_exec_info *exec,
+ struct ww_acquire_ctx *acquire_ctx)
+{
+ int contended_lock = -1;
+ int i, ret;
+ struct vc4_bo *bo;
+
+ ww_acquire_init(acquire_ctx, &reservation_ww_class);
+
+retry:
+ if (contended_lock != -1) {
+ bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+ ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
+ acquire_ctx);
+ if (ret) {
+ ww_acquire_done(acquire_ctx);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < exec->bo_count; i++) {
+ if (i == contended_lock)
+ continue;
+
+ bo = to_vc4_bo(&exec->bo[i]->base);
+
+ ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx);
+ if (ret) {
+ int j;
+
+ for (j = 0; j < i; j++) {
+ bo = to_vc4_bo(&exec->bo[j]->base);
+ ww_mutex_unlock(&bo->resv->lock);
+ }
+
+ if (contended_lock != -1 && contended_lock >= i) {
+ bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+
+ ww_mutex_unlock(&bo->resv->lock);
+ }
+
+ if (ret == -EDEADLK) {
+ contended_lock = i;
+ goto retry;
+ }
+
+ ww_acquire_done(acquire_ctx);
+ return ret;
+ }
}
+
+ ww_acquire_done(acquire_ctx);
+
+ /* Reserve space for our shared (read-only) fence references,
+ * before we commit the CL to the hardware.
+ */
+ for (i = 0; i < exec->bo_count; i++) {
+ bo = to_vc4_bo(&exec->bo[i]->base);
+
+ ret = reservation_object_reserve_shared(bo->resv);
+ if (ret) {
+ vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+ return ret;
+ }
+ }
+
+ return 0;
}
/* Queues a struct vc4_exec_info for execution. If no job is
@@ -484,19 +582,34 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
* then bump the end address. That's a change for a later date,
* though.
*/
-static void
-vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
+static int
+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
+ struct ww_acquire_ctx *acquire_ctx)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
uint64_t seqno;
unsigned long irqflags;
+ struct vc4_fence *fence;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return -ENOMEM;
+ fence->dev = dev;
spin_lock_irqsave(&vc4->job_lock, irqflags);
seqno = ++vc4->emit_seqno;
exec->seqno = seqno;
+
+ dma_fence_init(&fence->base, &vc4_fence_ops, &vc4->job_lock,
+ vc4->dma_fence_context, exec->seqno);
+ fence->seqno = exec->seqno;
+ exec->fence = &fence->base;
+
vc4_update_bo_seqnos(exec, seqno);
+ vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+
list_add_tail(&exec->head, &vc4->bin_job_list);
/* If no job was executing, kick ours off. Otherwise, it'll
@@ -509,6 +622,8 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
}
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+ return 0;
}
/**
@@ -705,8 +820,15 @@ static void
vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
+ unsigned long irqflags;
unsigned i;
+ /* If we got force-completed because of GPU reset rather than
+ * through our IRQ handler, signal the fence now.
+ */
+ if (exec->fence)
+ dma_fence_signal(exec->fence);
+
if (exec->bo) {
for (i = 0; i < exec->bo_count; i++)
drm_gem_object_unreference_unlocked(&exec->bo[i]->base);
@@ -720,6 +842,11 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
drm_gem_object_unreference_unlocked(&bo->base.base);
}
+ /* Free up the allocation of any bin slots we used. */
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
+ vc4->bin_alloc_used &= ~exec->bin_slots;
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
mutex_lock(&vc4->power_lock);
if (--vc4->power_refcount == 0) {
pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
@@ -874,6 +1001,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_vc4_submit_cl *args = data;
struct vc4_exec_info *exec;
+ struct ww_acquire_ctx acquire_ctx;
int ret = 0;
if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
@@ -888,13 +1016,16 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
}
mutex_lock(&vc4->power_lock);
- if (vc4->power_refcount++ == 0)
+ if (vc4->power_refcount++ == 0) {
ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
- mutex_unlock(&vc4->power_lock);
- if (ret < 0) {
- kfree(exec);
- return ret;
+ if (ret < 0) {
+ mutex_unlock(&vc4->power_lock);
+ vc4->power_refcount--;
+ kfree(exec);
+ return ret;
+ }
}
+ mutex_unlock(&vc4->power_lock);
exec->args = args;
INIT_LIST_HEAD(&exec->unref_list);
@@ -916,12 +1047,18 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
if (ret)
goto fail;
+ ret = vc4_lock_bo_reservations(dev, exec, &acquire_ctx);
+ if (ret)
+ goto fail;
+
/* Clear this out of the struct we'll be putting in the queue,
* since it's part of our stack.
*/
exec->args = NULL;
- vc4_queue_submit(dev, exec);
+ ret = vc4_queue_submit(dev, exec, &acquire_ctx);
+ if (ret)
+ goto fail;
/* Return the seqno for our job. */
args->seqno = vc4->emit_seqno;
@@ -939,6 +1076,8 @@ vc4_gem_init(struct drm_device *dev)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
+ vc4->dma_fence_context = dma_fence_context_alloc(1);
+
INIT_LIST_HEAD(&vc4->bin_job_list);
INIT_LIST_HEAD(&vc4->render_job_list);
INIT_LIST_HEAD(&vc4->job_done_list);
@@ -968,9 +1107,9 @@ vc4_gem_destroy(struct drm_device *dev)
/* V3D should already have disabled its interrupt and cleared
* the overflow allocation registers. Now free the object.
*/
- if (vc4->overflow_mem) {
- drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
- vc4->overflow_mem = NULL;
+ if (vc4->bin_bo) {
+ drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+ vc4->bin_bo = NULL;
}
if (vc4->hang_state)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index e9cbe269710b..3c2723f6345c 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -51,6 +51,7 @@
#include "linux/of_address.h"
#include "linux/of_gpio.h"
#include "linux/of_platform.h"
+#include "linux/pm_runtime.h"
#include "linux/rational.h"
#include "sound/dmaengine_pcm.h"
#include "sound/pcm_drm_eld.h"
@@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
vc4_hdmi_set_spd_infoframe(encoder);
}
-static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *unadjusted_mode,
- struct drm_display_mode *mode)
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *hdmi = vc4->hdmi;
+ int ret;
+
+ HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+
+ HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+ HD_WRITE(VC4_HD_VID_CTL,
+ HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
+ HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+ udelay(1);
+ HD_WRITE(VC4_HD_M_CTL, 0);
+
+ clk_disable_unprepare(hdmi->hsm_clock);
+ clk_disable_unprepare(hdmi->pixel_clock);
+
+ ret = pm_runtime_put(&hdmi->pdev->dev);
+ if (ret < 0)
+ DRM_ERROR("Failed to release power domain: %d\n", ret);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
{
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hdmi *hdmi = vc4->hdmi;
bool debug_dump_regs = false;
bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
@@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
interlaced,
VC4_HDMI_VERTB_VBP));
u32 csc_ctl;
+ int ret;
+
+ ret = pm_runtime_get_sync(&hdmi->pdev->dev);
+ if (ret < 0) {
+ DRM_ERROR("Failed to retain power domain: %d\n", ret);
+ return;
+ }
+
+ /* This is the rate that is set by the firmware. The number
+ * needs to be a bit higher than the pixel clock rate
+ * (generally 148.5Mhz).
+ */
+ ret = clk_set_rate(hdmi->hsm_clock, 163682864);
+ if (ret) {
+ DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+ return;
+ }
+
+ ret = clk_set_rate(hdmi->pixel_clock,
+ mode->clock * 1000 *
+ ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
+ if (ret) {
+ DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
+ return;
+ }
+
+ ret = clk_prepare_enable(hdmi->pixel_clock);
+ if (ret) {
+ DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
+ return;
+ }
+
+ ret = clk_prepare_enable(hdmi->hsm_clock);
+ if (ret) {
+ DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
+ ret);
+ clk_disable_unprepare(hdmi->pixel_clock);
+ return;
+ }
+
+ HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+ udelay(1);
+ HD_WRITE(VC4_HD_M_CTL, 0);
+
+ HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
+
+ HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
+ VC4_HDMI_SW_RESET_HDMI |
+ VC4_HDMI_SW_RESET_FORMAT_DETECT);
+
+ HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
+
+ /* PHY should be in reset, like
+ * vc4_hdmi_encoder_disable() does.
+ */
+ HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+
+ HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
if (debug_dump_regs) {
DRM_INFO("HDMI regs before:\n");
@@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
HD_WRITE(VC4_HD_VID_CTL, 0);
- clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
- ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
-
HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
@@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
DRM_INFO("HDMI regs after:\n");
vc4_hdmi_dump_regs(dev);
}
-}
-
-static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
-{
- struct drm_device *dev = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
-
- HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
-
- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
- HD_WRITE(VC4_HD_VID_CTL,
- HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
-}
-
-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
-{
- struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct vc4_dev *vc4 = to_vc4_dev(dev);
- int ret;
-
- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
HD_WRITE(VC4_HD_VID_CTL,
HD_READ(VC4_HD_VID_CTL) |
@@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
}
static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
- .mode_set = vc4_hdmi_encoder_mode_set,
.disable = vc4_hdmi_encoder_disable,
.enable = vc4_hdmi_encoder_enable,
};
@@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return -EPROBE_DEFER;
}
- /* Enable the clocks at startup. We can't quite recover from
- * turning off the pixel clock during disable/enables yet, so
- * it's always running.
- */
- ret = clk_prepare_enable(hdmi->pixel_clock);
- if (ret) {
- DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
- goto err_put_i2c;
- }
-
- /* This is the rate that is set by the firmware. The number
- * needs to be a bit higher than the pixel clock rate
- * (generally 148.5Mhz).
- */
- ret = clk_set_rate(hdmi->hsm_clock, 163682864);
- if (ret) {
- DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
- goto err_unprepare_pix;
- }
-
- ret = clk_prepare_enable(hdmi->hsm_clock);
- if (ret) {
- DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
- ret);
- goto err_unprepare_pix;
- }
-
/* Only use the GPIO HPD pin if present in the DT, otherwise
* we'll use the HDMI core's register.
*/
@@ -1185,7 +1216,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
&hpd_gpio_flags);
if (hdmi->hpd_gpio < 0) {
ret = hdmi->hpd_gpio;
- goto err_unprepare_hsm;
+ goto err_put_i2c;
}
hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
@@ -1193,25 +1224,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
vc4->hdmi = hdmi;
- /* HDMI core must be enabled. */
- if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
- udelay(1);
- HD_WRITE(VC4_HD_M_CTL, 0);
-
- HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
-
- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
- VC4_HDMI_SW_RESET_HDMI |
- VC4_HDMI_SW_RESET_FORMAT_DETECT);
-
- HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
-
- /* PHY should be in reset, like
- * vc4_hdmi_encoder_disable() does.
- */
- HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
- }
+ pm_runtime_enable(dev);
drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, NULL);
@@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
err_destroy_encoder:
vc4_hdmi_encoder_destroy(hdmi->encoder);
-err_unprepare_hsm:
- clk_disable_unprepare(hdmi->hsm_clock);
-err_unprepare_pix:
- clk_disable_unprepare(hdmi->pixel_clock);
+ pm_runtime_disable(dev);
err_put_i2c:
put_device(&hdmi->ddc->dev);
@@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
vc4_hdmi_connector_destroy(hdmi->connector);
vc4_hdmi_encoder_destroy(hdmi->encoder);
- clk_disable_unprepare(hdmi->pixel_clock);
- clk_disable_unprepare(hdmi->hsm_clock);
+ pm_runtime_disable(dev);
+
put_device(&hdmi->ddc->dev);
vc4->hdmi = NULL;
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index cdc6e6760705..7d7af3a93d94 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -59,50 +59,45 @@ vc4_overflow_mem_work(struct work_struct *work)
{
struct vc4_dev *vc4 =
container_of(work, struct vc4_dev, overflow_mem_work);
- struct drm_device *dev = vc4->dev;
- struct vc4_bo *bo;
+ struct vc4_bo *bo = vc4->bin_bo;
+ int bin_bo_slot;
+ struct vc4_exec_info *exec;
+ unsigned long irqflags;
- bo = vc4_bo_create(dev, 256 * 1024, true);
- if (IS_ERR(bo)) {
+ bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
+ if (bin_bo_slot < 0) {
DRM_ERROR("Couldn't allocate binner overflow mem\n");
return;
}
- /* If there's a job executing currently, then our previous
- * overflow allocation is getting used in that job and we need
- * to queue it to be released when the job is done. But if no
- * job is executing at all, then we can free the old overflow
- * object direcctly.
- *
- * No lock necessary for this pointer since we're the only
- * ones that update the pointer, and our workqueue won't
- * reenter.
- */
- if (vc4->overflow_mem) {
- struct vc4_exec_info *current_exec;
- unsigned long irqflags;
-
- spin_lock_irqsave(&vc4->job_lock, irqflags);
- current_exec = vc4_first_bin_job(vc4);
- if (!current_exec)
- current_exec = vc4_last_render_job(vc4);
- if (current_exec) {
- vc4->overflow_mem->seqno = current_exec->seqno;
- list_add_tail(&vc4->overflow_mem->unref_head,
- &current_exec->unref_list);
- vc4->overflow_mem = NULL;
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
+
+ if (vc4->bin_alloc_overflow) {
+ /* If we had overflow memory allocated previously,
+ * then that chunk will free when the current bin job
+ * is done. If we don't have a bin job running, then
+ * the chunk will be done whenever the list of render
+ * jobs has drained.
+ */
+ exec = vc4_first_bin_job(vc4);
+ if (!exec)
+ exec = vc4_last_render_job(vc4);
+ if (exec) {
+ exec->bin_slots |= vc4->bin_alloc_overflow;
+ } else {
+ /* There's nothing queued in the hardware, so
+ * the old slot is free immediately.
+ */
+ vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
}
- spin_unlock_irqrestore(&vc4->job_lock, irqflags);
}
+ vc4->bin_alloc_overflow = BIT(bin_bo_slot);
- if (vc4->overflow_mem)
- drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
- vc4->overflow_mem = bo;
-
- V3D_WRITE(V3D_BPOA, bo->base.paddr);
+ V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
V3D_WRITE(V3D_BPOS, bo->base.base.size);
V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
}
static void
@@ -142,6 +137,10 @@ vc4_irq_finish_render_job(struct drm_device *dev)
vc4->finished_seqno++;
list_move_tail(&exec->head, &vc4->job_done_list);
+ if (exec->fence) {
+ dma_fence_signal_locked(exec->fence);
+ exec->fence = NULL;
+ }
vc4_submit_next_render_job(dev);
wake_up_all(&vc4->job_wait_queue);
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index ad7925a9e0ea..237a504f11f0 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -230,10 +230,12 @@ int vc4_kms_load(struct drm_device *dev)
drm_mode_config_reset(dev);
- vc4->fbdev = drm_fbdev_cma_init(dev, 32,
- dev->mode_config.num_connector);
- if (IS_ERR(vc4->fbdev))
- vc4->fbdev = NULL;
+ if (dev->mode_config.num_connector) {
+ vc4->fbdev = drm_fbdev_cma_init(dev, 32,
+ dev->mode_config.num_connector);
+ if (IS_ERR(vc4->fbdev))
+ vc4->fbdev = NULL;
+ }
drm_kms_helper_poll_init(dev);
diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c
index 4339471f517f..5dc19429d4ae 100644
--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
@@ -182,8 +182,7 @@ static void emit_tile(struct vc4_exec_info *exec,
if (has_bin) {
rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
- rcl_u32(setup, (exec->tile_bo->paddr +
- exec->tile_alloc_offset +
+ rcl_u32(setup, (exec->tile_alloc_offset +
(y * exec->bin_tiles_x + x) * 32));
}
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index 7cc346ad9b0b..c53afec34586 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -16,6 +16,7 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include "linux/clk.h"
#include "linux/component.h"
#include "linux/pm_runtime.h"
#include "vc4_drv.h"
@@ -156,6 +157,144 @@ static void vc4_v3d_init_hw(struct drm_device *dev)
V3D_WRITE(V3D_VPMBASE, 0);
}
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4)
+{
+ struct drm_device *dev = vc4->dev;
+ unsigned long irqflags;
+ int slot;
+ uint64_t seqno = 0;
+ struct vc4_exec_info *exec;
+
+try_again:
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
+ slot = ffs(~vc4->bin_alloc_used);
+ if (slot != 0) {
+ /* Switch from ffs() bit index to a 0-based index. */
+ slot--;
+ vc4->bin_alloc_used |= BIT(slot);
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+ return slot;
+ }
+
+ /* Couldn't find an open slot. Wait for render to complete
+ * and try again.
+ */
+ exec = vc4_last_render_job(vc4);
+ if (exec)
+ seqno = exec->seqno;
+ spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+ if (seqno) {
+ int ret = vc4_wait_for_seqno(dev, seqno, ~0ull, true);
+
+ if (ret == 0)
+ goto try_again;
+
+ return ret;
+ }
+
+ return -ENOMEM;
+}
+
+/**
+ * vc4_allocate_bin_bo() - allocates the memory that will be used for
+ * tile binning.
+ *
+ * The binner has a limitation that the addresses in the tile state
+ * buffer that point into the tile alloc buffer or binner overflow
+ * memory only have 28 bits (256MB), and the top 4 on the bus for
+ * tile alloc references end up coming from the tile state buffer's
+ * address.
+ *
+ * To work around this, we allocate a single large buffer while V3D is
+ * in use, make sure that it has the top 4 bits constant across its
+ * entire extent, and then put the tile state, tile alloc, and binner
+ * overflow memory inside that buffer.
+ *
+ * This creates a limitation where we may not be able to execute a job
+ * if it doesn't fit within the buffer that we allocated up front.
+ * However, it turns out that 16MB is "enough for anybody", and
+ * real-world applications run into allocation failures from the
+ * overall CMA pool before they make scenes complicated enough to run
+ * out of bin space.
+ */
+int
+vc4_allocate_bin_bo(struct drm_device *drm)
+{
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_v3d *v3d = vc4->v3d;
+ uint32_t size = 16 * 1024 * 1024;
+ int ret = 0;
+ struct list_head list;
+
+ /* We may need to try allocating more than once to get a BO
+ * that doesn't cross 256MB. Track the ones we've allocated
+ * that failed so far, so that we can free them when we've got
+ * one that succeeded (if we freed them right away, our next
+ * allocation would probably be the same chunk of memory).
+ */
+ INIT_LIST_HEAD(&list);
+
+ while (true) {
+ struct vc4_bo *bo = vc4_bo_create(drm, size, true);
+
+ if (IS_ERR(bo)) {
+ ret = PTR_ERR(bo);
+
+ dev_err(&v3d->pdev->dev,
+ "Failed to allocate memory for tile binning: "
+ "%d. You may need to enable CMA or give it "
+ "more memory.",
+ ret);
+ break;
+ }
+
+ /* Check if this BO won't trigger the addressing bug. */
+ if ((bo->base.paddr & 0xf0000000) ==
+ ((bo->base.paddr + bo->base.base.size - 1) & 0xf0000000)) {
+ vc4->bin_bo = bo;
+
+ /* Set up for allocating 512KB chunks of
+ * binner memory. The biggest allocation we
+ * need to do is for the initial tile alloc +
+ * tile state buffer. We can render to a
+ * maximum of ((2048*2048) / (32*32) = 4096
+ * tiles in a frame (until we do floating
+ * point rendering, at which point it would be
+ * 8192). Tile state is 48b/tile (rounded to
+ * a page), and tile alloc is 32b/tile
+ * (rounded to a page), plus a page of extra,
+ * for a total of 320kb for our worst-case.
+ * We choose 512kb so that it divides evenly
+ * into our 16MB, and the rest of the 512kb
+ * will be used as storage for the overflow
+ * from the initial 32b CL per bin.
+ */
+ vc4->bin_alloc_size = 512 * 1024;
+ vc4->bin_alloc_used = 0;
+ vc4->bin_alloc_overflow = 0;
+ WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 !=
+ bo->base.base.size / vc4->bin_alloc_size);
+
+ break;
+ }
+
+ /* Put it on the list to free later, and try again. */
+ list_add(&bo->unref_head, &list);
+ }
+
+ /* Free all the BOs we allocated but didn't choose. */
+ while (!list_empty(&list)) {
+ struct vc4_bo *bo = list_last_entry(&list,
+ struct vc4_bo, unref_head);
+
+ list_del(&bo->unref_head);
+ drm_gem_object_put_unlocked(&bo->base.base);
+ }
+
+ return ret;
+}
+
#ifdef CONFIG_PM
static int vc4_v3d_runtime_suspend(struct device *dev)
{
@@ -164,6 +303,11 @@ static int vc4_v3d_runtime_suspend(struct device *dev)
vc4_irq_uninstall(vc4->dev);
+ drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+ vc4->bin_bo = NULL;
+
+ clk_disable_unprepare(v3d->clk);
+
return 0;
}
@@ -171,6 +315,15 @@ static int vc4_v3d_runtime_resume(struct device *dev)
{
struct vc4_v3d *v3d = dev_get_drvdata(dev);
struct vc4_dev *vc4 = v3d->vc4;
+ int ret;
+
+ ret = vc4_allocate_bin_bo(vc4->dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(v3d->clk);
+ if (ret != 0)
+ return ret;
vc4_v3d_init_hw(vc4->dev);
vc4_irq_postinstall(vc4->dev);
@@ -202,12 +355,38 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
vc4->v3d = v3d;
v3d->vc4 = vc4;
+ v3d->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(v3d->clk)) {
+ int ret = PTR_ERR(v3d->clk);
+
+ if (ret == -ENOENT) {
+ /* bcm2835 didn't have a clock reference in the DT. */
+ ret = 0;
+ v3d->clk = NULL;
+ } else {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get V3D clock: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
return -EINVAL;
}
+ ret = clk_prepare_enable(v3d->clk);
+ if (ret != 0)
+ return ret;
+
+ ret = vc4_allocate_bin_bo(drm);
+ if (ret) {
+ clk_disable_unprepare(v3d->clk);
+ return ret;
+ }
+
/* Reset the binner overflow address/size at setup, to be sure
* we don't reuse an old one.
*/
@@ -271,6 +450,7 @@ static int vc4_v3d_dev_remove(struct platform_device *pdev)
static const struct of_device_id vc4_v3d_dt_match[] = {
{ .compatible = "brcm,bcm2835-v3d" },
+ { .compatible = "brcm,cygnus-v3d" },
{ .compatible = "brcm,vc4-v3d" },
{}
};
diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c
index da6f1e138e8d..3de8f11595c0 100644
--- a/drivers/gpu/drm/vc4/vc4_validate.c
+++ b/drivers/gpu/drm/vc4/vc4_validate.c
@@ -348,10 +348,11 @@ static int
validate_tile_binning_config(VALIDATE_ARGS)
{
struct drm_device *dev = exec->exec_bo->base.dev;
- struct vc4_bo *tile_bo;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
uint8_t flags;
- uint32_t tile_state_size, tile_alloc_size;
- uint32_t tile_count;
+ uint32_t tile_state_size;
+ uint32_t tile_count, bin_addr;
+ int bin_slot;
if (exec->found_tile_binning_mode_config_packet) {
DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
@@ -377,13 +378,28 @@ validate_tile_binning_config(VALIDATE_ARGS)
return -EINVAL;
}
+ bin_slot = vc4_v3d_get_bin_slot(vc4);
+ if (bin_slot < 0) {
+ if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
+ DRM_ERROR("Failed to allocate binner memory: %d\n",
+ bin_slot);
+ }
+ return bin_slot;
+ }
+
+ /* The slot we allocated will only be used by this job, and is
+ * free when the job completes rendering.
+ */
+ exec->bin_slots |= BIT(bin_slot);
+ bin_addr = vc4->bin_bo->base.paddr + bin_slot * vc4->bin_alloc_size;
+
/* The tile state data array is 48 bytes per tile, and we put it at
* the start of a BO containing both it and the tile alloc.
*/
tile_state_size = 48 * tile_count;
/* Since the tile alloc array will follow us, align. */
- exec->tile_alloc_offset = roundup(tile_state_size, 4096);
+ exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
*(uint8_t *)(validated + 14) =
((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
@@ -394,35 +410,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
- /* Initial block size. */
- tile_alloc_size = 32 * tile_count;
-
- /*
- * The initial allocation gets rounded to the next 256 bytes before
- * the hardware starts fulfilling further allocations.
- */
- tile_alloc_size = roundup(tile_alloc_size, 256);
-
- /* Add space for the extra allocations. This is what gets used first,
- * before overflow memory. It must have at least 4096 bytes, but we
- * want to avoid overflow memory usage if possible.
- */
- tile_alloc_size += 1024 * 1024;
-
- tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size,
- true);
- exec->tile_bo = &tile_bo->base;
- if (IS_ERR(exec->tile_bo))
- return PTR_ERR(exec->tile_bo);
- list_add_tail(&tile_bo->unref_head, &exec->unref_list);
-
/* tile alloc address. */
- *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr +
- exec->tile_alloc_offset);
+ *(uint32_t *)(validated + 0) = exec->tile_alloc_offset;
/* tile alloc size. */
- *(uint32_t *)(validated + 4) = tile_alloc_size;
+ *(uint32_t *)(validated + 4) = (bin_addr + vc4->bin_alloc_size -
+ exec->tile_alloc_offset);
/* tile state address. */
- *(uint32_t *)(validated + 8) = exec->tile_bo->paddr;
+ *(uint32_t *)(validated + 8) = bin_addr;
return 0;
}
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
index 9fee38a942c4..4b23ba049632 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.c
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -42,10 +42,20 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
+static struct vgem_device {
+ struct drm_device drm;
+ struct platform_device *platform;
+} *vgem_device;
+
static void vgem_gem_free_object(struct drm_gem_object *obj)
{
struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
+ drm_free_large(vgem_obj->pages);
+
+ if (obj->import_attach)
+ drm_prime_gem_destroy(obj, vgem_obj->table);
+
drm_gem_object_release(obj);
kfree(vgem_obj);
}
@@ -56,26 +66,49 @@ static int vgem_gem_fault(struct vm_fault *vmf)
struct drm_vgem_gem_object *obj = vma->vm_private_data;
/* We don't use vmf->pgoff since that has the fake offset */
unsigned long vaddr = vmf->address;
- struct page *page;
-
- page = shmem_read_mapping_page(file_inode(obj->base.filp)->i_mapping,
- (vaddr - vma->vm_start) >> PAGE_SHIFT);
- if (!IS_ERR(page)) {
- vmf->page = page;
- return 0;
- } else switch (PTR_ERR(page)) {
- case -ENOSPC:
- case -ENOMEM:
- return VM_FAULT_OOM;
- case -EBUSY:
- return VM_FAULT_RETRY;
- case -EFAULT:
- case -EINVAL:
- return VM_FAULT_SIGBUS;
- default:
- WARN_ON_ONCE(PTR_ERR(page));
- return VM_FAULT_SIGBUS;
+ int ret;
+ loff_t num_pages;
+ pgoff_t page_offset;
+ page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
+
+ num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE);
+
+ if (page_offset > num_pages)
+ return VM_FAULT_SIGBUS;
+
+ if (obj->pages) {
+ get_page(obj->pages[page_offset]);
+ vmf->page = obj->pages[page_offset];
+ ret = 0;
+ } else {
+ struct page *page;
+
+ page = shmem_read_mapping_page(
+ file_inode(obj->base.filp)->i_mapping,
+ page_offset);
+ if (!IS_ERR(page)) {
+ vmf->page = page;
+ ret = 0;
+ } else switch (PTR_ERR(page)) {
+ case -ENOSPC:
+ case -ENOMEM:
+ ret = VM_FAULT_OOM;
+ break;
+ case -EBUSY:
+ ret = VM_FAULT_RETRY;
+ break;
+ case -EFAULT:
+ case -EINVAL:
+ ret = VM_FAULT_SIGBUS;
+ break;
+ default:
+ WARN_ON(PTR_ERR(page));
+ ret = VM_FAULT_SIGBUS;
+ break;
+ }
+
}
+ return ret;
}
static const struct vm_operations_struct vgem_gem_vm_ops = {
@@ -112,12 +145,8 @@ static void vgem_postclose(struct drm_device *dev, struct drm_file *file)
kfree(vfile);
}
-/* ioctls */
-
-static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
- struct drm_file *file,
- unsigned int *handle,
- unsigned long size)
+static struct drm_vgem_gem_object *__vgem_gem_create(struct drm_device *dev,
+ unsigned long size)
{
struct drm_vgem_gem_object *obj;
int ret;
@@ -127,8 +156,31 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
return ERR_PTR(-ENOMEM);
ret = drm_gem_object_init(dev, &obj->base, roundup(size, PAGE_SIZE));
- if (ret)
- goto err_free;
+ if (ret) {
+ kfree(obj);
+ return ERR_PTR(ret);
+ }
+
+ return obj;
+}
+
+static void __vgem_gem_destroy(struct drm_vgem_gem_object *obj)
+{
+ drm_gem_object_release(&obj->base);
+ kfree(obj);
+}
+
+static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
+ struct drm_file *file,
+ unsigned int *handle,
+ unsigned long size)
+{
+ struct drm_vgem_gem_object *obj;
+ int ret;
+
+ obj = __vgem_gem_create(dev, size);
+ if (IS_ERR(obj))
+ return ERR_CAST(obj);
ret = drm_gem_handle_create(file, &obj->base, handle);
drm_gem_object_unreference_unlocked(&obj->base);
@@ -137,9 +189,8 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
return &obj->base;
-err_free:
- kfree(obj);
err:
+ __vgem_gem_destroy(obj);
return ERR_PTR(ret);
}
@@ -256,6 +307,37 @@ static struct sg_table *vgem_prime_get_sg_table(struct drm_gem_object *obj)
return st;
}
+static struct drm_gem_object* vgem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
+
+ return drm_gem_prime_import_dev(dev, dma_buf, &vgem->platform->dev);
+}
+
+static struct drm_gem_object *vgem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach, struct sg_table *sg)
+{
+ struct drm_vgem_gem_object *obj;
+ int npages;
+
+ obj = __vgem_gem_create(dev, attach->dmabuf->size);
+ if (IS_ERR(obj))
+ return ERR_CAST(obj);
+
+ npages = PAGE_ALIGN(attach->dmabuf->size) / PAGE_SIZE;
+
+ obj->table = sg;
+ obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
+ if (!obj->pages) {
+ __vgem_gem_destroy(obj);
+ return ERR_PTR(-ENOMEM);
+ }
+ drm_prime_sg_to_page_addr_arrays(obj->table, obj->pages, NULL,
+ npages);
+ return &obj->base;
+}
+
static void *vgem_prime_vmap(struct drm_gem_object *obj)
{
long n_pages = obj->size >> PAGE_SHIFT;
@@ -300,8 +382,19 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
return 0;
}
+static void vgem_release(struct drm_device *dev)
+{
+ struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
+
+ platform_device_unregister(vgem->platform);
+ drm_dev_fini(&vgem->drm);
+
+ kfree(vgem);
+}
+
static struct drm_driver vgem_driver = {
.driver_features = DRIVER_GEM | DRIVER_PRIME,
+ .release = vgem_release,
.open = vgem_open,
.postclose = vgem_postclose,
.gem_free_object_unlocked = vgem_gem_free_object,
@@ -314,8 +407,11 @@ static struct drm_driver vgem_driver = {
.dumb_map_offset = vgem_gem_dumb_map,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_pin = vgem_prime_pin,
+ .gem_prime_import = vgem_prime_import,
.gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import_sg_table = vgem_prime_import_sg_table,
.gem_prime_get_sg_table = vgem_prime_get_sg_table,
.gem_prime_vmap = vgem_prime_vmap,
.gem_prime_vunmap = vgem_prime_vunmap,
@@ -328,34 +424,48 @@ static struct drm_driver vgem_driver = {
.minor = DRIVER_MINOR,
};
-static struct drm_device *vgem_device;
-
static int __init vgem_init(void)
{
int ret;
- vgem_device = drm_dev_alloc(&vgem_driver, NULL);
- if (IS_ERR(vgem_device)) {
- ret = PTR_ERR(vgem_device);
- goto out;
+ vgem_device = kzalloc(sizeof(*vgem_device), GFP_KERNEL);
+ if (!vgem_device)
+ return -ENOMEM;
+
+ ret = drm_dev_init(&vgem_device->drm, &vgem_driver, NULL);
+ if (ret)
+ goto out_free;
+
+ vgem_device->platform =
+ platform_device_register_simple("vgem", -1, NULL, 0);
+ if (!vgem_device->platform) {
+ ret = -ENODEV;
+ goto out_fini;
}
- ret = drm_dev_register(vgem_device, 0);
+ dma_coerce_mask_and_coherent(&vgem_device->platform->dev,
+ DMA_BIT_MASK(64));
+
+ /* Final step: expose the device/driver to userspace */
+ ret = drm_dev_register(&vgem_device->drm, 0);
if (ret)
- goto out_unref;
+ goto out_unregister;
return 0;
-out_unref:
- drm_dev_unref(vgem_device);
-out:
+out_unregister:
+ platform_device_unregister(vgem_device->platform);
+out_fini:
+ drm_dev_fini(&vgem_device->drm);
+out_free:
+ kfree(vgem_device);
return ret;
}
static void __exit vgem_exit(void)
{
- drm_dev_unregister(vgem_device);
- drm_dev_unref(vgem_device);
+ drm_dev_unregister(&vgem_device->drm);
+ drm_dev_unref(&vgem_device->drm);
}
module_init(vgem_init);
diff --git a/drivers/gpu/drm/vgem/vgem_drv.h b/drivers/gpu/drm/vgem/vgem_drv.h
index cb59c7ab98b9..1aae01419112 100644
--- a/drivers/gpu/drm/vgem/vgem_drv.h
+++ b/drivers/gpu/drm/vgem/vgem_drv.h
@@ -43,6 +43,8 @@ struct vgem_file {
#define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base)
struct drm_vgem_gem_object {
struct drm_gem_object base;
+ struct page **pages;
+ struct sg_table *table;
};
int vgem_fence_open(struct vgem_file *file);
diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile
index 01352b56c418..9df7766a7f9d 100644
--- a/drivers/gpu/drm/zte/Makefile
+++ b/drivers/gpu/drm/zte/Makefile
@@ -3,6 +3,7 @@ zxdrm-y := \
zx_hdmi.o \
zx_plane.o \
zx_tvenc.o \
+ zx_vga.o \
zx_vou.o
obj-$(CONFIG_DRM_ZTE) += zxdrm.o
diff --git a/drivers/gpu/drm/zte/zx_common_regs.h b/drivers/gpu/drm/zte/zx_common_regs.h
new file mode 100644
index 000000000000..2afd80664c51
--- /dev/null
+++ b/drivers/gpu/drm/zte/zx_common_regs.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZX_COMMON_REGS_H__
+#define __ZX_COMMON_REGS_H__
+
+/* CSC registers */
+#define CSC_CTRL0 0x30
+#define CSC_COV_MODE_SHIFT 16
+#define CSC_COV_MODE_MASK (0xffff << CSC_COV_MODE_SHIFT)
+#define CSC_BT601_IMAGE_RGB2YCBCR 0
+#define CSC_BT601_IMAGE_YCBCR2RGB 1
+#define CSC_BT601_VIDEO_RGB2YCBCR 2
+#define CSC_BT601_VIDEO_YCBCR2RGB 3
+#define CSC_BT709_IMAGE_RGB2YCBCR 4
+#define CSC_BT709_IMAGE_YCBCR2RGB 5
+#define CSC_BT709_VIDEO_RGB2YCBCR 6
+#define CSC_BT709_VIDEO_YCBCR2RGB 7
+#define CSC_BT2020_IMAGE_RGB2YCBCR 8
+#define CSC_BT2020_IMAGE_YCBCR2RGB 9
+#define CSC_BT2020_VIDEO_RGB2YCBCR 10
+#define CSC_BT2020_VIDEO_YCBCR2RGB 11
+#define CSC_WORK_ENABLE BIT(0)
+
+#endif /* __ZX_COMMON_REGS_H__ */
diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
index 614e854f6be5..490aafc99610 100644
--- a/drivers/gpu/drm/zte/zx_drm_drv.c
+++ b/drivers/gpu/drm/zte/zx_drm_drv.c
@@ -233,6 +233,7 @@ static struct platform_driver *drivers[] = {
&zx_crtc_driver,
&zx_hdmi_driver,
&zx_tvenc_driver,
+ &zx_vga_driver,
&zx_drm_platform_driver,
};
diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h
index 5ca035b079c7..2a8cdc5f8be4 100644
--- a/drivers/gpu/drm/zte/zx_drm_drv.h
+++ b/drivers/gpu/drm/zte/zx_drm_drv.h
@@ -14,6 +14,7 @@
extern struct platform_driver zx_crtc_driver;
extern struct platform_driver zx_hdmi_driver;
extern struct platform_driver zx_tvenc_driver;
+extern struct platform_driver zx_vga_driver;
static inline u32 zx_readl(void __iomem *reg)
{
diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
index d646ac931663..4a6252720c10 100644
--- a/drivers/gpu/drm/zte/zx_plane.c
+++ b/drivers/gpu/drm/zte/zx_plane.c
@@ -16,6 +16,7 @@
#include <drm/drm_plane_helper.h>
#include <drm/drmP.h>
+#include "zx_common_regs.h"
#include "zx_drm_drv.h"
#include "zx_plane.h"
#include "zx_plane_regs.h"
diff --git a/drivers/gpu/drm/zte/zx_plane_regs.h b/drivers/gpu/drm/zte/zx_plane_regs.h
index 65f271aeabed..9c655f59f9f7 100644
--- a/drivers/gpu/drm/zte/zx_plane_regs.h
+++ b/drivers/gpu/drm/zte/zx_plane_regs.h
@@ -77,24 +77,6 @@
#define LUMA_STRIDE(x) (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
#define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
-/* CSC registers */
-#define CSC_CTRL0 0x30
-#define CSC_COV_MODE_SHIFT 16
-#define CSC_COV_MODE_MASK (0xffff << CSC_COV_MODE_SHIFT)
-#define CSC_BT601_IMAGE_RGB2YCBCR 0
-#define CSC_BT601_IMAGE_YCBCR2RGB 1
-#define CSC_BT601_VIDEO_RGB2YCBCR 2
-#define CSC_BT601_VIDEO_YCBCR2RGB 3
-#define CSC_BT709_IMAGE_RGB2YCBCR 4
-#define CSC_BT709_IMAGE_YCBCR2RGB 5
-#define CSC_BT709_VIDEO_RGB2YCBCR 6
-#define CSC_BT709_VIDEO_YCBCR2RGB 7
-#define CSC_BT2020_IMAGE_RGB2YCBCR 8
-#define CSC_BT2020_IMAGE_YCBCR2RGB 9
-#define CSC_BT2020_VIDEO_RGB2YCBCR 10
-#define CSC_BT2020_VIDEO_YCBCR2RGB 11
-#define CSC_WORK_ENABLE BIT(0)
-
/* RSZ registers */
#define RSZ_SRC_CFG 0x00
#define RSZ_DEST_CFG 0x04
diff --git a/drivers/gpu/drm/zte/zx_vga.c b/drivers/gpu/drm/zte/zx_vga.c
new file mode 100644
index 000000000000..1e0811f775cb
--- /dev/null
+++ b/drivers/gpu/drm/zte/zx_vga.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include "zx_drm_drv.h"
+#include "zx_vga_regs.h"
+#include "zx_vou.h"
+
+struct zx_vga_pwrctrl {
+ struct regmap *regmap;
+ u32 reg;
+ u32 mask;
+};
+
+struct zx_vga_i2c {
+ struct i2c_adapter adap;
+ struct mutex lock;
+};
+
+struct zx_vga {
+ struct drm_connector connector;
+ struct drm_encoder encoder;
+ struct zx_vga_i2c *ddc;
+ struct device *dev;
+ void __iomem *mmio;
+ struct clk *i2c_wclk;
+ struct zx_vga_pwrctrl pwrctrl;
+ struct completion complete;
+ bool connected;
+};
+
+#define to_zx_vga(x) container_of(x, struct zx_vga, x)
+
+static void zx_vga_encoder_enable(struct drm_encoder *encoder)
+{
+ struct zx_vga *vga = to_zx_vga(encoder);
+ struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+
+ /* Set bit to power up VGA DACs */
+ regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
+ pwrctrl->mask);
+
+ vou_inf_enable(VOU_VGA, encoder->crtc);
+}
+
+static void zx_vga_encoder_disable(struct drm_encoder *encoder)
+{
+ struct zx_vga *vga = to_zx_vga(encoder);
+ struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+
+ vou_inf_disable(VOU_VGA, encoder->crtc);
+
+ /* Clear bit to power down VGA DACs */
+ regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
+}
+
+static const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = {
+ .enable = zx_vga_encoder_enable,
+ .disable = zx_vga_encoder_disable,
+};
+
+static const struct drm_encoder_funcs zx_vga_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static int zx_vga_connector_get_modes(struct drm_connector *connector)
+{
+ struct zx_vga *vga = to_zx_vga(connector);
+ struct edid *edid;
+ int ret;
+
+ /*
+ * Clear both detection bits to switch I2C bus from device
+ * detecting to EDID reading.
+ */
+ zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
+
+ edid = drm_get_edid(connector, &vga->ddc->adap);
+ if (!edid) {
+ /*
+ * If EDID reading fails, we set the device state into
+ * disconnected. Locking is not required here, since the
+ * VGA_AUTO_DETECT_SEL register write in irq handler cannot
+ * be triggered when both detection bits are cleared as above.
+ */
+ zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
+ VGA_DETECT_SEL_NO_DEVICE);
+ vga->connected = false;
+ return 0;
+ }
+
+ /*
+ * As edid reading succeeds, device must be connected, so we set
+ * up detection bit for unplug interrupt here.
+ */
+ zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+
+ return ret;
+}
+
+static enum drm_mode_status
+zx_vga_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = {
+ .get_modes = zx_vga_connector_get_modes,
+ .mode_valid = zx_vga_connector_mode_valid,
+};
+
+static enum drm_connector_status
+zx_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct zx_vga *vga = to_zx_vga(connector);
+
+ return vga->connected ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static const struct drm_connector_funcs zx_vga_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = zx_vga_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga)
+{
+ struct drm_encoder *encoder = &vga->encoder;
+ struct drm_connector *connector = &vga->connector;
+ struct device *dev = vga->dev;
+ int ret;
+
+ encoder->possible_crtcs = VOU_CRTC_MASK;
+
+ ret = drm_encoder_init(drm, encoder, &zx_vga_encoder_funcs,
+ DRM_MODE_ENCODER_DAC, NULL);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret);
+ return ret;
+ };
+
+ drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs);
+
+ vga->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(drm, connector, &zx_vga_connector_funcs,
+ DRM_MODE_CONNECTOR_VGA);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret);
+ goto clean_encoder;
+ };
+
+ drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs);
+
+ ret = drm_mode_connector_attach_encoder(connector, encoder);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret);
+ goto clean_connector;
+ };
+
+ return 0;
+
+clean_connector:
+ drm_connector_cleanup(connector);
+clean_encoder:
+ drm_encoder_cleanup(encoder);
+ return ret;
+}
+
+static int zx_vga_pwrctrl_init(struct zx_vga *vga)
+{
+ struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+ struct device *dev = vga->dev;
+ struct of_phandle_args out_args;
+ struct regmap *regmap;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(dev->of_node,
+ "zte,vga-power-control", 2, 0, &out_args);
+ if (ret)
+ return ret;
+
+ regmap = syscon_node_to_regmap(out_args.np);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto out;
+ }
+
+ pwrctrl->regmap = regmap;
+ pwrctrl->reg = out_args.args[0];
+ pwrctrl->mask = out_args.args[1];
+
+out:
+ of_node_put(out_args.np);
+ return ret;
+}
+
+static int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg)
+{
+ int len = msg->len;
+ u8 *buf = msg->buf;
+ u32 offset = 0;
+ int i;
+
+ reinit_completion(&vga->complete);
+
+ /* Select combo write */
+ zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO);
+ zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0);
+
+ while (len > 0) {
+ u32 cnt;
+
+ /* Clear RX FIFO */
+ zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR,
+ VGA_RX_FIFO_CLEAR);
+
+ /* Data offset to read from */
+ zx_writel(vga->mmio + VGA_SUB_ADDR, offset);
+
+ /* Kick off the transfer */
+ zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS,
+ VGA_CMD_TRANS);
+
+ if (!wait_for_completion_timeout(&vga->complete,
+ msecs_to_jiffies(1000))) {
+ DRM_DEV_ERROR(vga->dev, "transfer timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ cnt = zx_readl(vga->mmio + VGA_RXF_STATUS);
+ cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT;
+ /* FIFO status may report more data than we need to read */
+ cnt = min_t(u32, len, cnt);
+
+ for (i = 0; i < cnt; i++)
+ *buf++ = zx_readl(vga->mmio + VGA_DATA);
+
+ len -= cnt;
+ offset += cnt;
+ }
+
+ return 0;
+}
+
+static int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg)
+{
+ /*
+ * The DDC I2C adapter is only for reading EDID data, so we assume
+ * that the write to this adapter must be the EDID data offset.
+ */
+ if ((msg->len != 1) || ((msg->addr != DDC_ADDR)))
+ return -EINVAL;
+
+ /* Hardware will take care of the slave address shifting */
+ zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr);
+
+ return 0;
+}
+
+static int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct zx_vga *vga = i2c_get_adapdata(adap);
+ struct zx_vga_i2c *ddc = vga->ddc;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&ddc->lock);
+
+ for (i = 0; i < num; i++) {
+ if (msgs[i].flags & I2C_M_RD)
+ ret = zx_vga_i2c_read(vga, &msgs[i]);
+ else
+ ret = zx_vga_i2c_write(vga, &msgs[i]);
+
+ if (ret < 0)
+ break;
+ }
+
+ if (!ret)
+ ret = num;
+
+ mutex_unlock(&ddc->lock);
+
+ return ret;
+}
+
+static u32 zx_vga_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm zx_vga_algorithm = {
+ .master_xfer = zx_vga_i2c_xfer,
+ .functionality = zx_vga_i2c_func,
+};
+
+static int zx_vga_ddc_register(struct zx_vga *vga)
+{
+ struct device *dev = vga->dev;
+ struct i2c_adapter *adap;
+ struct zx_vga_i2c *ddc;
+ int ret;
+
+ ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
+ if (!ddc)
+ return -ENOMEM;
+
+ vga->ddc = ddc;
+ mutex_init(&ddc->lock);
+
+ adap = &ddc->adap;
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_DDC;
+ adap->dev.parent = dev;
+ adap->algo = &zx_vga_algorithm;
+ snprintf(adap->name, sizeof(adap->name), "zx vga i2c");
+
+ ret = i2c_add_adapter(adap);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_adapdata(adap, vga);
+
+ return 0;
+}
+
+static irqreturn_t zx_vga_irq_thread(int irq, void *dev_id)
+{
+ struct zx_vga *vga = dev_id;
+
+ drm_helper_hpd_irq_event(vga->connector.dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id)
+{
+ struct zx_vga *vga = dev_id;
+ u32 status;
+
+ status = zx_readl(vga->mmio + VGA_I2C_STATUS);
+
+ /* Clear interrupt status */
+ zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ,
+ VGA_CLEAR_IRQ);
+
+ if (status & VGA_DEVICE_CONNECTED) {
+ /*
+ * Since VGA_DETECT_SEL bits need to be reset for switching DDC
+ * bus from device detection to EDID read, rather than setting
+ * up HAS_DEVICE bit here, we need to do that in .get_modes
+ * hook for unplug detecting after EDID read succeeds.
+ */
+ vga->connected = true;
+ return IRQ_WAKE_THREAD;
+ }
+
+ if (status & VGA_DEVICE_DISCONNECTED) {
+ zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
+ VGA_DETECT_SEL_NO_DEVICE);
+ vga->connected = false;
+ return IRQ_WAKE_THREAD;
+ }
+
+ if (status & VGA_TRANS_DONE) {
+ complete(&vga->complete);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void zx_vga_hw_init(struct zx_vga *vga)
+{
+ unsigned long ref = clk_get_rate(vga->i2c_wclk);
+ int div;
+
+ /*
+ * Set up I2C fast speed divider per formula below to get 400kHz.
+ * scl = ref / ((div + 1) * 4)
+ */
+ div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1;
+ zx_writel(vga->mmio + VGA_CLK_DIV_FS, div);
+
+ /* Set up device detection */
+ zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80);
+ zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE);
+
+ /*
+ * We need to poke monitor via DDC bus to get connection irq
+ * start working.
+ */
+ zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR);
+ zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS);
+}
+
+static int zx_vga_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = data;
+ struct resource *res;
+ struct zx_vga *vga;
+ int irq;
+ int ret;
+
+ vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
+ if (!vga)
+ return -ENOMEM;
+
+ vga->dev = dev;
+ dev_set_drvdata(dev, vga);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vga->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(vga->mmio))
+ return PTR_ERR(vga->mmio);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk");
+ if (IS_ERR(vga->i2c_wclk)) {
+ ret = PTR_ERR(vga->i2c_wclk);
+ DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret);
+ return ret;
+ }
+
+ ret = zx_vga_pwrctrl_init(vga);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
+ return ret;
+ }
+
+ ret = zx_vga_ddc_register(vga);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret);
+ return ret;
+ }
+
+ ret = zx_vga_register(drm, vga);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret);
+ return ret;
+ }
+
+ init_completion(&vga->complete);
+
+ ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler,
+ zx_vga_irq_thread, IRQF_SHARED,
+ dev_name(dev), vga);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(vga->i2c_wclk);
+ if (ret)
+ return ret;
+
+ zx_vga_hw_init(vga);
+
+ return 0;
+}
+
+static void zx_vga_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct zx_vga *vga = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(vga->i2c_wclk);
+}
+
+static const struct component_ops zx_vga_component_ops = {
+ .bind = zx_vga_bind,
+ .unbind = zx_vga_unbind,
+};
+
+static int zx_vga_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &zx_vga_component_ops);
+}
+
+static int zx_vga_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &zx_vga_component_ops);
+ return 0;
+}
+
+static const struct of_device_id zx_vga_of_match[] = {
+ { .compatible = "zte,zx296718-vga", },
+ { /* end */ },
+};
+MODULE_DEVICE_TABLE(of, zx_vga_of_match);
+
+struct platform_driver zx_vga_driver = {
+ .probe = zx_vga_probe,
+ .remove = zx_vga_remove,
+ .driver = {
+ .name = "zx-vga",
+ .of_match_table = zx_vga_of_match,
+ },
+};
diff --git a/drivers/gpu/drm/zte/zx_vga_regs.h b/drivers/gpu/drm/zte/zx_vga_regs.h
new file mode 100644
index 000000000000..feaa345fe6a6
--- /dev/null
+++ b/drivers/gpu/drm/zte/zx_vga_regs.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZX_VGA_REGS_H__
+#define __ZX_VGA_REGS_H__
+
+#define VGA_CMD_CFG 0x04
+#define VGA_CMD_TRANS BIT(6)
+#define VGA_CMD_COMBO BIT(5)
+#define VGA_CMD_RW BIT(4)
+#define VGA_SUB_ADDR 0x0c
+#define VGA_DEVICE_ADDR 0x10
+#define VGA_CLK_DIV_FS 0x14
+#define VGA_RXF_CTRL 0x20
+#define VGA_RX_FIFO_CLEAR BIT(7)
+#define VGA_DATA 0x24
+#define VGA_I2C_STATUS 0x28
+#define VGA_DEVICE_DISCONNECTED BIT(7)
+#define VGA_DEVICE_CONNECTED BIT(6)
+#define VGA_CLEAR_IRQ BIT(4)
+#define VGA_TRANS_DONE BIT(0)
+#define VGA_RXF_STATUS 0x30
+#define VGA_RXF_COUNT_SHIFT 2
+#define VGA_RXF_COUNT_MASK GENMASK(7, 2)
+#define VGA_AUTO_DETECT_PARA 0x34
+#define VGA_AUTO_DETECT_SEL 0x38
+#define VGA_DETECT_SEL_HAS_DEVICE BIT(1)
+#define VGA_DETECT_SEL_NO_DEVICE BIT(0)
+
+#endif /* __ZX_VGA_REGS_H__ */
diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
index b500c8dd0d9d..5fbd10b60ee5 100644
--- a/drivers/gpu/drm/zte/zx_vou.c
+++ b/drivers/gpu/drm/zte/zx_vou.c
@@ -23,6 +23,7 @@
#include <drm/drm_plane_helper.h>
#include <drm/drmP.h>
+#include "zx_common_regs.h"
#include "zx_drm_drv.h"
#include "zx_plane.h"
#include "zx_vou.h"
@@ -122,6 +123,8 @@ struct zx_crtc {
struct drm_plane *primary;
struct zx_vou_hw *vou;
void __iomem *chnreg;
+ void __iomem *chncsc;
+ void __iomem *dither;
const struct zx_crtc_regs *regs;
const struct zx_crtc_bits *bits;
enum vou_chn_type chn_type;
@@ -204,6 +207,11 @@ static struct vou_inf vou_infs[] = {
.clocks_en_bits = BIT(15),
.clocks_sel_bits = BIT(11) | BIT(0),
},
+ [VOU_VGA] = {
+ .data_sel = VOU_RGB_888,
+ .clocks_en_bits = BIT(1),
+ .clocks_sel_bits = BIT(10),
+ },
};
static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
@@ -227,9 +235,26 @@ void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
struct zx_crtc *zcrtc = to_zx_crtc(crtc);
struct zx_vou_hw *vou = zcrtc->vou;
struct vou_inf *inf = &vou_infs[id];
+ void __iomem *dither = zcrtc->dither;
+ void __iomem *csc = zcrtc->chncsc;
bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
u32 data_sel_shift = id << 1;
+ if (inf->data_sel != VOU_YUV444) {
+ /* Enable channel CSC for RGB output */
+ zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
+ CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT);
+ zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE,
+ CSC_WORK_ENABLE);
+
+ /* Bypass Dither block for RGB output */
+ zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS,
+ DITHER_BYSPASS);
+ } else {
+ zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0);
+ zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0);
+ }
+
/* Select data format */
zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
inf->data_sel << data_sel_shift);
@@ -525,20 +550,24 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
if (chn_type == VOU_CHN_MAIN) {
zplane->layer = vou->osd + MAIN_GL_OFFSET;
- zplane->csc = vou->osd + MAIN_CSC_OFFSET;
+ zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET;
zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
zplane->bits = &zx_gl_bits[0];
zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
+ zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET;
+ zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET;
zcrtc->regs = &main_crtc_regs;
zcrtc->bits = &main_crtc_bits;
} else {
zplane->layer = vou->osd + AUX_GL_OFFSET;
- zplane->csc = vou->osd + AUX_CSC_OFFSET;
+ zplane->csc = vou->osd + AUX_GL_CSC_OFFSET;
zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
zplane->bits = &zx_gl_bits[1];
zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
+ zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET;
+ zcrtc->dither = vou->osd + AUX_DITHER_OFFSET;
zcrtc->regs = &aux_crtc_regs;
zcrtc->bits = &aux_crtc_bits;
}
@@ -705,9 +734,6 @@ static void vou_hw_init(struct zx_vou_hw *vou)
/* Release reset for all VOU modules */
zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
- /* Enable clock auto-gating for all VOU modules */
- zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
-
/* Enable all VOU module clocks */
zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
index c066ef123434..5a218351b497 100644
--- a/drivers/gpu/drm/zte/zx_vou_regs.h
+++ b/drivers/gpu/drm/zte/zx_vou_regs.h
@@ -13,13 +13,17 @@
/* Sub-module offset */
#define MAIN_GL_OFFSET 0x130
-#define MAIN_CSC_OFFSET 0x580
+#define MAIN_GL_CSC_OFFSET 0x580
+#define MAIN_CHN_CSC_OFFSET 0x6c0
#define MAIN_HBSC_OFFSET 0x820
+#define MAIN_DITHER_OFFSET 0x960
#define MAIN_RSZ_OFFSET 0x600 /* OTFPPU sub-module */
#define AUX_GL_OFFSET 0x200
-#define AUX_CSC_OFFSET 0x5d0
+#define AUX_GL_CSC_OFFSET 0x5d0
+#define AUX_CHN_CSC_OFFSET 0x710
#define AUX_HBSC_OFFSET 0x860
+#define AUX_DITHER_OFFSET 0x970
#define AUX_RSZ_OFFSET 0x800
#define OSD_VL0_OFFSET 0x040
@@ -78,6 +82,10 @@
#define CHN_INTERLACE_BUF_CTRL 0x24
#define CHN_INTERLACE_EN BIT(2)
+/* Dither registers */
+#define OSD_DITHER_CTRL0 0x00
+#define DITHER_BYSPASS BIT(31)
+
/* TIMING_CTRL registers */
#define TIMING_TC_ENABLE 0x04
#define AUX_TC_EN BIT(1)