summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2024-03-01 07:14:02 +0300
committerDave Airlie <airlied@redhat.com>2024-03-01 08:27:50 +0300
commit7781cc4274121bd416726dd3352898cb976a844d (patch)
tree678273a2539e94f97b176cc96c943f568879a2a2 /drivers/gpu/drm
parent992909549a2388d424ee3d8d01473fb211161431 (diff)
parent8df1ddb5bf11ab820ad991e164dab82c0960add9 (diff)
downloadlinux-7781cc4274121bd416726dd3352898cb976a844d.tar.xz
Merge tag 'drm-misc-next-2024-02-29' of https://anongit.freedesktop.org/git/drm/drm-misc into drm-next
drm-misc-next for v6.9: UAPI Changes: Cross-subsystem Changes: backlight: - corgi: include backlight header fbdev: - Cleanup includes in public header file - fbtft: Include backlight header Core Changes: edid: - Remove built-in EDID data dp: - Avoid AUX transfers on powered-down displays - Add VSC SDP helpers modesetting: - Add sanity checks for polling - Cleanups scheduler: - Cleanups tests: - Add helpers for mode-setting tests Driver Changes: i915: - Use shared VSC SDP helper mgag200: - Work around PCI write bursts mxsfb: - Use managed mode config nouveau: - Include backlight header where necessary qiac: - Cleanups sun4: - HDMI: updates to atomic mode setting tegra: - Fix GEM refounting in error paths tidss: - Fix multi display - Fix initial Z position v3d: - Support display MMU page size Signed-off-by: Dave Airlie <airlied@redhat.com> From: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20240229084806.GA21616@localhost.localdomain
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c132
-rw-r--r--drivers/gpu/drm/drm_crtc.c15
-rw-r--r--drivers/gpu/drm/drm_edid_load.c162
-rw-r--r--drivers/gpu/drm/drm_modeset_helper.c19
-rw-r--r--drivers/gpu/drm/drm_probe_helper.c21
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c70
-rw-r--r--drivers/gpu/drm/mgag200/Kconfig12
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c17
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c8
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.c7
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_drv.c7
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c1
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c3
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-atna33xc20.c2
-rw-r--r--drivers/gpu/drm/scheduler/sched_fence.c4
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c133
-rw-r--r--drivers/gpu/drm/tegra/fb.c1
-rw-r--r--drivers/gpu/drm/tests/drm_connector_test.c170
-rw-r--r--drivers/gpu/drm/tests/drm_kunit_helpers.c150
-rw-r--r--drivers/gpu/drm/tidss/tidss_crtc.c10
-rw-r--r--drivers/gpu/drm/tidss/tidss_plane.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_bo.c12
-rw-r--r--drivers/gpu/drm/v3d/v3d_debugfs.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h2
-rw-r--r--drivers/gpu/drm/v3d/v3d_irq.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_mmu.c2
26 files changed, 645 insertions, 321 deletions
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index d046dfa79504..266826eac4a7 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -533,6 +533,15 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
mutex_lock(&aux->hw_mutex);
/*
+ * If the device attached to the aux bus is powered down then there's
+ * no reason to attempt a transfer. Error out immediately.
+ */
+ if (aux->powered_down) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ /*
* The specification doesn't give any recommendation on how often to
* retry native transactions. We used to retry 7 times like for
* aux i2c transactions but real world devices this wasn't
@@ -600,6 +609,29 @@ int drm_dp_dpcd_probe(struct drm_dp_aux *aux, unsigned int offset)
EXPORT_SYMBOL(drm_dp_dpcd_probe);
/**
+ * drm_dp_dpcd_set_powered() - Set whether the DP device is powered
+ * @aux: DisplayPort AUX channel; for convenience it's OK to pass NULL here
+ * and the function will be a no-op.
+ * @powered: true if powered; false if not
+ *
+ * If the endpoint device on the DP AUX bus is known to be powered down
+ * then this function can be called to make future transfers fail immediately
+ * instead of needing to time out.
+ *
+ * If this function is never called then a device defaults to being powered.
+ */
+void drm_dp_dpcd_set_powered(struct drm_dp_aux *aux, bool powered)
+{
+ if (!aux)
+ return;
+
+ mutex_lock(&aux->hw_mutex);
+ aux->powered_down = !powered;
+ mutex_unlock(&aux->hw_mutex);
+}
+EXPORT_SYMBOL(drm_dp_dpcd_set_powered);
+
+/**
* drm_dp_dpcd_read() - read a series of bytes from the DPCD
* @aux: DisplayPort AUX channel (SST or MST)
* @offset: address of the (first) register to read
@@ -1858,6 +1890,9 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
struct drm_dp_aux_msg msg;
int err = 0;
+ if (aux->powered_down)
+ return -EBUSY;
+
dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES);
memset(&msg, 0, sizeof(msg));
@@ -2914,6 +2949,103 @@ void drm_dp_vsc_sdp_log(struct drm_printer *p, const struct drm_dp_vsc_sdp *vsc)
EXPORT_SYMBOL(drm_dp_vsc_sdp_log);
/**
+ * drm_dp_vsc_sdp_supported() - check if vsc sdp is supported
+ * @aux: DisplayPort AUX channel
+ * @dpcd: DisplayPort configuration data
+ *
+ * Returns true if vsc sdp is supported, else returns false
+ */
+bool drm_dp_vsc_sdp_supported(struct drm_dp_aux *aux, const u8 dpcd[DP_RECEIVER_CAP_SIZE])
+{
+ u8 rx_feature;
+
+ if (dpcd[DP_DPCD_REV] < DP_DPCD_REV_13)
+ return false;
+
+ if (drm_dp_dpcd_readb(aux, DP_DPRX_FEATURE_ENUMERATION_LIST, &rx_feature) != 1) {
+ drm_dbg_dp(aux->drm_dev, "failed to read DP_DPRX_FEATURE_ENUMERATION_LIST\n");
+ return false;
+ }
+
+ return (rx_feature & DP_VSC_SDP_EXT_FOR_COLORIMETRY_SUPPORTED);
+}
+EXPORT_SYMBOL(drm_dp_vsc_sdp_supported);
+
+/**
+ * drm_dp_vsc_sdp_pack() - pack a given vsc sdp into generic dp_sdp
+ * @vsc: vsc sdp initialized according to its purpose as defined in
+ * table 2-118 - table 2-120 in DP 1.4a specification
+ * @sdp: valid handle to the generic dp_sdp which will be packed
+ *
+ * Returns length of sdp on success and error code on failure
+ */
+ssize_t drm_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc,
+ struct dp_sdp *sdp)
+{
+ size_t length = sizeof(struct dp_sdp);
+
+ memset(sdp, 0, sizeof(struct dp_sdp));
+
+ /*
+ * Prepare VSC Header for SU as per DP 1.4a spec, Table 2-119
+ * VSC SDP Header Bytes
+ */
+ sdp->sdp_header.HB0 = 0; /* Secondary-Data Packet ID = 0 */
+ sdp->sdp_header.HB1 = vsc->sdp_type; /* Secondary-data Packet Type */
+ sdp->sdp_header.HB2 = vsc->revision; /* Revision Number */
+ sdp->sdp_header.HB3 = vsc->length; /* Number of Valid Data Bytes */
+
+ if (vsc->revision == 0x6) {
+ sdp->db[0] = 1;
+ sdp->db[3] = 1;
+ }
+
+ /*
+ * Revision 0x5 and revision 0x7 supports Pixel Encoding/Colorimetry
+ * Format as per DP 1.4a spec and DP 2.0 respectively.
+ */
+ if (!(vsc->revision == 0x5 || vsc->revision == 0x7))
+ goto out;
+
+ /* VSC SDP Payload for DB16 through DB18 */
+ /* Pixel Encoding and Colorimetry Formats */
+ sdp->db[16] = (vsc->pixelformat & 0xf) << 4; /* DB16[7:4] */
+ sdp->db[16] |= vsc->colorimetry & 0xf; /* DB16[3:0] */
+
+ switch (vsc->bpc) {
+ case 6:
+ /* 6bpc: 0x0 */
+ break;
+ case 8:
+ sdp->db[17] = 0x1; /* DB17[3:0] */
+ break;
+ case 10:
+ sdp->db[17] = 0x2;
+ break;
+ case 12:
+ sdp->db[17] = 0x3;
+ break;
+ case 16:
+ sdp->db[17] = 0x4;
+ break;
+ default:
+ WARN(1, "Missing case %d\n", vsc->bpc);
+ return -EINVAL;
+ }
+
+ /* Dynamic Range and Component Bit Depth */
+ if (vsc->dynamic_range == DP_DYNAMIC_RANGE_CTA)
+ sdp->db[17] |= 0x80; /* DB17[7] */
+
+ /* Content Type */
+ sdp->db[18] = vsc->content_type & 0x7;
+
+out:
+ return length;
+}
+EXPORT_SYMBOL(drm_dp_vsc_sdp_pack);
+
+/**
* drm_dp_get_pcon_max_frl_bw() - maximum frl supported by PCON
* @dpcd: DisplayPort configuration data
* @port_cap: port capabilities
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 6795624f16e7..82c665d3e74b 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -107,18 +107,6 @@ int drm_crtc_force_disable(struct drm_crtc *crtc)
return drm_mode_set_config_internal(&set);
}
-static unsigned int drm_num_crtcs(struct drm_device *dev)
-{
- unsigned int num = 0;
- struct drm_crtc *tmp;
-
- drm_for_each_crtc(tmp, dev) {
- num++;
- }
-
- return num;
-}
-
int drm_crtc_register_all(struct drm_device *dev)
{
struct drm_crtc *crtc;
@@ -278,8 +266,7 @@ static int __drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *
if (name) {
crtc->name = kvasprintf(GFP_KERNEL, name, ap);
} else {
- crtc->name = kasprintf(GFP_KERNEL, "crtc-%d",
- drm_num_crtcs(dev));
+ crtc->name = kasprintf(GFP_KERNEL, "crtc-%d", config->num_crtc);
}
if (!crtc->name) {
drm_mode_object_unregister(dev, &crtc->base);
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
index 60fcb80bce61..d1c7e8298702 100644
--- a/drivers/gpu/drm/drm_edid_load.c
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -20,162 +20,28 @@
static char edid_firmware[PATH_MAX];
module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
-MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
- "from built-in data or /lib/firmware instead. ");
-
-#define GENERIC_EDIDS 6
-static const char * const generic_edid_name[GENERIC_EDIDS] = {
- "edid/800x600.bin",
- "edid/1024x768.bin",
- "edid/1280x1024.bin",
- "edid/1600x1200.bin",
- "edid/1680x1050.bin",
- "edid/1920x1080.bin",
-};
-
-static const u8 generic_edid[GENERIC_EDIDS][128] = {
- {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x16, 0x01, 0x03, 0x6d, 0x1b, 0x14, 0x78,
- 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
- 0x20, 0x50, 0x54, 0x01, 0x00, 0x00, 0x45, 0x40,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa0, 0x0f,
- 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80,
- 0x14, 0x00, 0x15, 0xd0, 0x10, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
- 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
- 0x3d, 0x24, 0x26, 0x05, 0x00, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
- 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
- 0x56, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xc2,
- },
- {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
- 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
- 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
- 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
- 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
- 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
- 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
- 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
- 0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
- },
- {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
- 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
- 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
- 0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
- 0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
- 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
- 0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
- 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
- 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
- },
- {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x16, 0x01, 0x03, 0x6d, 0x37, 0x29, 0x78,
- 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
- 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xa9, 0x40,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x48, 0x3f,
- 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
- 0x13, 0x00, 0x2b, 0xa0, 0x21, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
- 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
- 0x3d, 0x4a, 0x4c, 0x11, 0x00, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
- 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x55,
- 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0x9d,
- },
- {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
- 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
- 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
- 0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
- 0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
- 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
- 0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
- 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
- 0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
- },
- {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
- 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
- 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
- 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
- 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
- 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
- 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
- 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
- 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
- },
-};
+MODULE_PARM_DESC(edid_firmware,
+ "Do not probe monitor, use specified EDID blob from /lib/firmware instead.");
static const struct drm_edid *edid_load(struct drm_connector *connector, const char *name)
{
const struct firmware *fw = NULL;
- const u8 *fwdata;
const struct drm_edid *drm_edid;
- int fwsize, builtin;
-
- builtin = match_string(generic_edid_name, GENERIC_EDIDS, name);
- if (builtin >= 0) {
- fwdata = generic_edid[builtin];
- fwsize = sizeof(generic_edid[builtin]);
- } else {
- int err;
-
- err = request_firmware(&fw, name, connector->dev->dev);
- if (err) {
- drm_err(connector->dev,
- "[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n",
- connector->base.id, connector->name,
- name, err);
- return ERR_PTR(err);
- }
-
- fwdata = fw->data;
- fwsize = fw->size;
+ int err;
+
+ err = request_firmware(&fw, name, connector->dev->dev);
+ if (err) {
+ drm_err(connector->dev,
+ "[CONNECTOR:%d:%s] Requesting EDID firmware \"%s\" failed (err=%d)\n",
+ connector->base.id, connector->name,
+ name, err);
+ return ERR_PTR(err);
}
- drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded %s firmware EDID \"%s\"\n",
- connector->base.id, connector->name,
- builtin >= 0 ? "built-in" : "external", name);
+ drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] Loaded external firmware EDID \"%s\"\n",
+ connector->base.id, connector->name, name);
- drm_edid = drm_edid_alloc(fwdata, fwsize);
+ drm_edid = drm_edid_alloc(fw->data, fw->size);
if (!drm_edid_valid(drm_edid)) {
drm_err(connector->dev, "Invalid firmware EDID \"%s\"\n", name);
drm_edid_free(drm_edid);
diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c
index f858dfedf2cf..2c582020cb42 100644
--- a/drivers/gpu/drm/drm_modeset_helper.c
+++ b/drivers/gpu/drm/drm_modeset_helper.c
@@ -193,13 +193,22 @@ int drm_mode_config_helper_suspend(struct drm_device *dev)
if (!dev)
return 0;
+ /*
+ * Don't disable polling if it was never initialized
+ */
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_disable(dev);
- drm_kms_helper_poll_disable(dev);
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 1);
state = drm_atomic_helper_suspend(dev);
if (IS_ERR(state)) {
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
- drm_kms_helper_poll_enable(dev);
+ /*
+ * Don't enable polling if it was never initialized
+ */
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_enable(dev);
+
return PTR_ERR(state);
}
@@ -239,7 +248,11 @@ int drm_mode_config_helper_resume(struct drm_device *dev)
dev->mode_config.suspend_state = NULL;
drm_fb_helper_set_suspend_unlocked(dev->fb_helper, 0);
- drm_kms_helper_poll_enable(dev);
+ /*
+ * Don't enable polling if it is not initialized
+ */
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_enable(dev);
return ret;
}
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index d1e1ade66f81..19ecb749704b 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -293,14 +293,17 @@ static void reschedule_output_poll_work(struct drm_device *dev)
* Drivers can call this helper from their device resume implementation. It is
* not an error to call this even when output polling isn't enabled.
*
+ * If device polling was never initialized before, this call will trigger a
+ * warning and return.
+ *
* Note that calls to enable and disable polling must be strictly ordered, which
* is automatically the case when they're only call from suspend/resume
* callbacks.
*/
void drm_kms_helper_poll_enable(struct drm_device *dev)
{
- if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll ||
- dev->mode_config.poll_running)
+ if (drm_WARN_ON_ONCE(dev, !dev->mode_config.poll_enabled) ||
+ !drm_kms_helper_poll || dev->mode_config.poll_running)
return;
if (drm_kms_helper_enable_hpd(dev) ||
@@ -619,8 +622,12 @@ retry:
0);
}
- /* Re-enable polling in case the global poll config changed. */
- drm_kms_helper_poll_enable(dev);
+ /*
+ * Re-enable polling in case the global poll config changed but polling
+ * is still initialized.
+ */
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_enable(dev);
if (connector->status == connector_status_disconnected) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n",
@@ -871,12 +878,18 @@ EXPORT_SYMBOL(drm_kms_helper_is_poll_worker);
* not an error to call this even when output polling isn't enabled or already
* disabled. Polling is re-enabled by calling drm_kms_helper_poll_enable().
*
+ * If however, the polling was never initialized, this call will trigger a
+ * warning and return
+ *
* Note that calls to enable and disable polling must be strictly ordered, which
* is automatically the case when they're only call from suspend/resume
* callbacks.
*/
void drm_kms_helper_poll_disable(struct drm_device *dev)
{
+ if (drm_WARN_ON(dev, !dev->mode_config.poll_enabled))
+ return;
+
if (dev->mode_config.poll_running)
drm_kms_helper_disable_hpd(dev);
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 523a6d68a52c..6ece2c563c7a 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -4127,73 +4127,6 @@ intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state,
return false;
}
-static ssize_t intel_dp_vsc_sdp_pack(const struct drm_dp_vsc_sdp *vsc,
- struct dp_sdp *sdp, size_t size)
-{
- size_t length = sizeof(struct dp_sdp);
-
- if (size < length)
- return -ENOSPC;
-
- memset(sdp, 0, size);
-
- /*
- * Prepare VSC Header for SU as per DP 1.4a spec, Table 2-119
- * VSC SDP Header Bytes
- */
- sdp->sdp_header.HB0 = 0; /* Secondary-Data Packet ID = 0 */
- sdp->sdp_header.HB1 = vsc->sdp_type; /* Secondary-data Packet Type */
- sdp->sdp_header.HB2 = vsc->revision; /* Revision Number */
- sdp->sdp_header.HB3 = vsc->length; /* Number of Valid Data Bytes */
-
- if (vsc->revision == 0x6) {
- sdp->db[0] = 1;
- sdp->db[3] = 1;
- }
-
- /*
- * Revision 0x5 and revision 0x7 supports Pixel Encoding/Colorimetry
- * Format as per DP 1.4a spec and DP 2.0 respectively.
- */
- if (!(vsc->revision == 0x5 || vsc->revision == 0x7))
- goto out;
-
- /* VSC SDP Payload for DB16 through DB18 */
- /* Pixel Encoding and Colorimetry Formats */
- sdp->db[16] = (vsc->pixelformat & 0xf) << 4; /* DB16[7:4] */
- sdp->db[16] |= vsc->colorimetry & 0xf; /* DB16[3:0] */
-
- switch (vsc->bpc) {
- case 6:
- /* 6bpc: 0x0 */
- break;
- case 8:
- sdp->db[17] = 0x1; /* DB17[3:0] */
- break;
- case 10:
- sdp->db[17] = 0x2;
- break;
- case 12:
- sdp->db[17] = 0x3;
- break;
- case 16:
- sdp->db[17] = 0x4;
- break;
- default:
- MISSING_CASE(vsc->bpc);
- break;
- }
- /* Dynamic Range and Component Bit Depth */
- if (vsc->dynamic_range == DP_DYNAMIC_RANGE_CTA)
- sdp->db[17] |= 0x80; /* DB17[7] */
-
- /* Content Type */
- sdp->db[18] = vsc->content_type & 0x7;
-
-out:
- return length;
-}
-
static ssize_t
intel_dp_hdr_metadata_infoframe_sdp_pack(struct drm_i915_private *i915,
const struct hdmi_drm_infoframe *drm_infoframe,
@@ -4286,8 +4219,7 @@ static void intel_write_dp_sdp(struct intel_encoder *encoder,
switch (type) {
case DP_SDP_VSC:
- len = intel_dp_vsc_sdp_pack(&crtc_state->infoframes.vsc, &sdp,
- sizeof(sdp));
+ len = drm_dp_vsc_sdp_pack(&crtc_state->infoframes.vsc, &sdp);
break;
case HDMI_PACKET_TYPE_GAMUT_METADATA:
len = intel_dp_hdr_metadata_infoframe_sdp_pack(dev_priv,
diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig
index b28c5e4828f4..5e4d48df4854 100644
--- a/drivers/gpu/drm/mgag200/Kconfig
+++ b/drivers/gpu/drm/mgag200/Kconfig
@@ -11,3 +11,15 @@ config DRM_MGAG200
MGA G200 desktop chips and the server variants. It requires 0.3.0
of the modesetting userspace driver, and a version of mga driver
that will fail on KMS enabled devices.
+
+config DRM_MGAG200_IOBURST_WORKAROUND
+ bool "Disable buffer caching"
+ depends on DRM_MGAG200 && PREEMPT_RT && X86
+ help
+ Enable a workaround to avoid I/O bursts within the mgag200 driver at
+ the expense of overall display performance.
+ It restores the <v5.10 behavior, by mapping the framebuffer in system
+ RAM as Write-Combining, and flushing the cache after each write.
+ This is only useful on x86_64 if you want to run processes with
+ deterministic latency.
+ If unsure, say N.
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 54fce00e2136..573dbe256aa8 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -84,6 +84,20 @@ resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size)
return offset - 65536;
}
+#if defined(CONFIG_DRM_MGAG200_IOBURST_WORKAROUND)
+static struct drm_gem_object *mgag200_create_object(struct drm_device *dev, size_t size)
+{
+ struct drm_gem_shmem_object *shmem;
+
+ shmem = kzalloc(sizeof(*shmem), GFP_KERNEL);
+ if (!shmem)
+ return NULL;
+
+ shmem->map_wc = true;
+ return &shmem->base;
+}
+#endif
+
/*
* DRM driver
*/
@@ -99,6 +113,9 @@ static const struct drm_driver mgag200_driver = {
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
+#if defined(CONFIG_DRM_MGAG200_IOBURST_WORKAROUND)
+ .gem_create_object = mgag200_create_object,
+#endif
DRM_GEM_SHMEM_DRIVER_OPS,
};
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 0eb769dd76ce..e17cb4c5f774 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -13,6 +13,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_cache.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_format_helper.h>
@@ -436,6 +437,13 @@ static void mgag200_handle_damage(struct mga_device *mdev, const struct iosys_ma
iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
drm_fb_memcpy(&dst, fb->pitches, vmap, fb, clip);
+
+ /* Flushing the cache greatly improves latency on x86_64 */
+#if defined(CONFIG_DRM_MGAG200_IOBURST_WORKAROUND)
+ if (!vmap->is_iomem)
+ drm_clflush_virt_range(vmap->vaddr + clip->y1 * fb->pitches[0],
+ drm_rect_height(clip) * fb->pitches[0]);
+#endif
}
/*
diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c
index 18de2f17e249..ea10bf81582e 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_drv.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c
@@ -167,7 +167,11 @@ static int lcdif_load(struct drm_device *drm)
return ret;
/* Modeset init */
- drm_mode_config_init(drm);
+ ret = drmm_mode_config_init(drm);
+ if (ret) {
+ dev_err(drm->dev, "Failed to initialize mode config\n");
+ return ret;
+ }
ret = lcdif_kms_init(lcdif);
if (ret < 0) {
@@ -227,7 +231,6 @@ static void lcdif_unload(struct drm_device *drm)
drm_crtc_vblank_off(&lcdif->crtc);
drm_kms_helper_poll_fini(drm);
- drm_mode_config_cleanup(drm);
pm_runtime_put_sync(drm->dev);
pm_runtime_disable(drm->dev);
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
index b483ef48216a..cb5ce4e81fc7 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c
@@ -249,7 +249,11 @@ static int mxsfb_load(struct drm_device *drm,
pm_runtime_enable(drm->dev);
/* Modeset init */
- drm_mode_config_init(drm);
+ ret = drmm_mode_config_init(drm);
+ if (ret) {
+ dev_err(drm->dev, "Failed to initialize mode config\n");
+ goto err_vblank;
+ }
ret = mxsfb_kms_init(mxsfb);
if (ret < 0) {
@@ -312,7 +316,6 @@ err_vblank:
static void mxsfb_unload(struct drm_device *drm)
{
drm_kms_helper_poll_fini(drm);
- drm_mode_config_cleanup(drm);
pm_runtime_get_sync(drm->dev);
mxsfb_irq_uninstall(drm);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 8d37a694b772..0c3d88ad0b0e 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -28,6 +28,7 @@
#include "wndw.h"
#include "handles.h"
+#include <linux/backlight.h>
#include <linux/dma-mapping.h>
#include <linux/hdmi.h>
#include <linux/component.h>
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index bd71d239272a..d58f90bc48fb 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -413,6 +413,7 @@ static int panel_edp_suspend(struct device *dev)
{
struct panel_edp *p = dev_get_drvdata(dev);
+ drm_dp_dpcd_set_powered(p->aux, false);
gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply);
p->unprepared_time = ktime_get_boottime();
@@ -469,6 +470,7 @@ static int panel_edp_prepare_once(struct panel_edp *p)
}
gpiod_set_value_cansleep(p->enable_gpio, 1);
+ drm_dp_dpcd_set_powered(p->aux, true);
p->powered_on_time = ktime_get_boottime();
@@ -507,6 +509,7 @@ static int panel_edp_prepare_once(struct panel_edp *p)
return 0;
error:
+ drm_dp_dpcd_set_powered(p->aux, false);
gpiod_set_value_cansleep(p->enable_gpio, 0);
regulator_disable(p->supply);
p->unprepared_time = ktime_get_boottime();
diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
index 5703f4712d96..76c2a8f6718c 100644
--- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
+++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
@@ -72,6 +72,7 @@ static int atana33xc20_suspend(struct device *dev)
if (p->el3_was_on)
atana33xc20_wait(p->el_on3_off_time, 150);
+ drm_dp_dpcd_set_powered(p->aux, false);
ret = regulator_disable(p->supply);
if (ret)
return ret;
@@ -93,6 +94,7 @@ static int atana33xc20_resume(struct device *dev)
ret = regulator_enable(p->supply);
if (ret)
return ret;
+ drm_dp_dpcd_set_powered(p->aux, true);
p->powered_on_time = ktime_get_boottime();
if (p->no_hpd) {
diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c
index 06cedfe4b486..0f35f009b9d3 100644
--- a/drivers/gpu/drm/scheduler/sched_fence.c
+++ b/drivers/gpu/drm/scheduler/sched_fence.c
@@ -33,9 +33,7 @@ static struct kmem_cache *sched_fence_slab;
static int __init drm_sched_fence_slab_init(void)
{
- sched_fence_slab = kmem_cache_create(
- "drm_sched_fence", sizeof(struct drm_sched_fence), 0,
- SLAB_HWCACHE_ALIGN, NULL);
+ sched_fence_slab = KMEM_CACHE(drm_sched_fence, SLAB_HWCACHE_ALIGN);
if (!sched_fence_slab)
return -ENOMEM;
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 152375f3de2e..b7cf369b1906 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -30,19 +30,11 @@
#include "sun4i_drv.h"
#include "sun4i_hdmi.h"
-static inline struct sun4i_hdmi *
-drm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder)
-{
- return container_of(encoder, struct sun4i_hdmi,
- encoder);
-}
+#define drm_encoder_to_sun4i_hdmi(e) \
+ container_of_const(e, struct sun4i_hdmi, encoder)
-static inline struct sun4i_hdmi *
-drm_connector_to_sun4i_hdmi(struct drm_connector *connector)
-{
- return container_of(connector, struct sun4i_hdmi,
- connector);
-}
+#define drm_connector_to_sun4i_hdmi(c) \
+ container_of_const(c, struct sun4i_hdmi, connector)
static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
struct drm_display_mode *mode)
@@ -70,19 +62,8 @@ static int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi,
return 0;
}
-static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_display_mode *mode = &crtc_state->mode;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- return -EINVAL;
-
- return 0;
-}
-
-static void sun4i_hdmi_disable(struct drm_encoder *encoder)
+static void sun4i_hdmi_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
u32 val;
@@ -96,37 +77,17 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder)
clk_disable_unprepare(hdmi->tmds_clk);
}
-static void sun4i_hdmi_enable(struct drm_encoder *encoder)
+static void sun4i_hdmi_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
struct drm_display_info *display = &hdmi->connector.display_info;
+ unsigned int x, y;
u32 val = 0;
DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
- clk_prepare_enable(hdmi->tmds_clk);
-
- sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
- val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
- writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
-
- val = SUN4I_HDMI_VID_CTRL_ENABLE;
- if (display->is_hdmi)
- val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
-
- writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
-}
-
-static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- unsigned int x, y;
- u32 val;
-
clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000);
clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000);
@@ -178,34 +139,76 @@ static void sun4i_hdmi_mode_set(struct drm_encoder *encoder,
val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC;
writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG);
+
+ clk_prepare_enable(hdmi->tmds_clk);
+
+ sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
+ val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
+ val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
+ writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0));
+
+ val = SUN4I_HDMI_VID_CTRL_ENABLE;
+ if (display->is_hdmi)
+ val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE;
+
+ writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
}
-static enum drm_mode_status sun4i_hdmi_mode_valid(struct drm_encoder *encoder,
- const struct drm_display_mode *mode)
+static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
+ .atomic_disable = sun4i_hdmi_disable,
+ .atomic_enable = sun4i_hdmi_enable,
+};
+
+static enum drm_mode_status
+sun4i_hdmi_connector_clock_valid(const struct drm_connector *connector,
+ const struct drm_display_mode *mode,
+ unsigned long long clock)
{
- struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
- unsigned long rate = mode->clock * 1000;
- unsigned long diff = rate / 200; /* +-0.5% allowed by HDMI spec */
+ const struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector);
+ unsigned long diff = clock / 200; /* +-0.5% allowed by HDMI spec */
long rounded_rate;
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
/* 165 MHz is the typical max pixelclock frequency for HDMI <= 1.2 */
- if (rate > 165000000)
+ if (clock > 165000000)
return MODE_CLOCK_HIGH;
- rounded_rate = clk_round_rate(hdmi->tmds_clk, rate);
+
+ rounded_rate = clk_round_rate(hdmi->tmds_clk, clock);
if (rounded_rate > 0 &&
- max_t(unsigned long, rounded_rate, rate) -
- min_t(unsigned long, rounded_rate, rate) < diff)
+ max_t(unsigned long, rounded_rate, clock) -
+ min_t(unsigned long, rounded_rate, clock) < diff)
return MODE_OK;
+
return MODE_NOCLOCK;
}
-static const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = {
- .atomic_check = sun4i_hdmi_atomic_check,
- .disable = sun4i_hdmi_disable,
- .enable = sun4i_hdmi_enable,
- .mode_set = sun4i_hdmi_mode_set,
- .mode_valid = sun4i_hdmi_mode_valid,
-};
+static int sun4i_hdmi_connector_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector_state *conn_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ struct drm_crtc *crtc = conn_state->crtc;
+ struct drm_crtc_state *crtc_state = crtc->state;
+ struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ enum drm_mode_status status;
+
+ status = sun4i_hdmi_connector_clock_valid(connector, mode,
+ mode->clock * 1000);
+ if (status != MODE_OK)
+ return -EINVAL;
+
+ return 0;
+}
+
+static enum drm_mode_status
+sun4i_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return sun4i_hdmi_connector_clock_valid(connector, mode,
+ mode->clock * 1000);
+}
static int sun4i_hdmi_get_modes(struct drm_connector *connector)
{
@@ -251,6 +254,8 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev)
}
static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = {
+ .atomic_check = sun4i_hdmi_connector_atomic_check,
+ .mode_valid = sun4i_hdmi_connector_mode_valid,
.get_modes = sun4i_hdmi_get_modes,
};
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index a719af1dc9a5..46170753699d 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -159,6 +159,7 @@ struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
if (gem->size < size) {
err = -EINVAL;
+ drm_gem_object_put(gem);
goto unreference;
}
diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c
index c66aa2dc8d9d..44f82ed2a958 100644
--- a/drivers/gpu/drm/tests/drm_connector_test.c
+++ b/drivers/gpu/drm/tests/drm_connector_test.c
@@ -3,10 +3,175 @@
* Kunit test for drm_modes functions
*/
+#include <linux/i2c.h>
+
+#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_kunit_helpers.h>
#include <kunit/test.h>
+struct drm_connector_init_priv {
+ struct drm_device drm;
+ struct drm_connector connector;
+ struct i2c_adapter ddc;
+};
+
+static const struct drm_connector_funcs dummy_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+static int dummy_ddc_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ return num;
+}
+
+static u32 dummy_ddc_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm dummy_ddc_algorithm = {
+ .master_xfer = dummy_ddc_xfer,
+ .functionality = dummy_ddc_func,
+};
+
+static void i2c_del_adapter_wrapper(void *ptr)
+{
+ struct i2c_adapter *adap = ptr;
+
+ i2c_del_adapter(adap);
+}
+
+static int drm_test_connector_init(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv;
+ struct device *dev;
+ int ret;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ priv = drm_kunit_helper_alloc_drm_device(test, dev,
+ struct drm_connector_init_priv, drm,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
+
+ strscpy(priv->ddc.name, "dummy-connector-ddc", sizeof(priv->ddc.name));
+ priv->ddc.owner = THIS_MODULE;
+ priv->ddc.algo = &dummy_ddc_algorithm;
+ priv->ddc.dev.parent = dev;
+
+ ret = i2c_add_adapter(&priv->ddc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = kunit_add_action_or_reset(test, i2c_del_adapter_wrapper, &priv->ddc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ test->priv = priv;
+ return 0;
+}
+
+/*
+ * Test that the registration of a bog standard connector works as
+ * expected and doesn't report any error.
+ */
+static void drm_test_drmm_connector_init(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector without a DDC adapter
+ * doesn't report any error.
+ */
+static void drm_test_drmm_connector_init_null_ddc(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ int ret;
+
+ ret = drmm_connector_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ DRM_MODE_CONNECTOR_HDMIA,
+ NULL);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/*
+ * Test that the registration of a connector succeeds for all possible
+ * connector types.
+ */
+static void drm_test_drmm_connector_init_type_valid(struct kunit *test)
+{
+ struct drm_connector_init_priv *priv = test->priv;
+ unsigned int connector_type = *(unsigned int *)test->param_value;
+ int ret;
+
+ ret = drmm_connector_init(&priv->drm, &priv->connector,
+ &dummy_funcs,
+ connector_type,
+ &priv->ddc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+static const unsigned int drm_connector_init_type_valid_tests[] = {
+ DRM_MODE_CONNECTOR_Unknown,
+ DRM_MODE_CONNECTOR_VGA,
+ DRM_MODE_CONNECTOR_DVII,
+ DRM_MODE_CONNECTOR_DVID,
+ DRM_MODE_CONNECTOR_DVIA,
+ DRM_MODE_CONNECTOR_Composite,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ DRM_MODE_CONNECTOR_LVDS,
+ DRM_MODE_CONNECTOR_Component,
+ DRM_MODE_CONNECTOR_9PinDIN,
+ DRM_MODE_CONNECTOR_DisplayPort,
+ DRM_MODE_CONNECTOR_HDMIA,
+ DRM_MODE_CONNECTOR_HDMIB,
+ DRM_MODE_CONNECTOR_TV,
+ DRM_MODE_CONNECTOR_eDP,
+ DRM_MODE_CONNECTOR_VIRTUAL,
+ DRM_MODE_CONNECTOR_DSI,
+ DRM_MODE_CONNECTOR_DPI,
+ DRM_MODE_CONNECTOR_WRITEBACK,
+ DRM_MODE_CONNECTOR_SPI,
+ DRM_MODE_CONNECTOR_USB,
+};
+
+static void drm_connector_init_type_desc(const unsigned int *type, char *desc)
+{
+ sprintf(desc, "%s", drm_get_connector_type_name(*type));
+}
+
+KUNIT_ARRAY_PARAM(drm_connector_init_type_valid,
+ drm_connector_init_type_valid_tests,
+ drm_connector_init_type_desc);
+
+static struct kunit_case drmm_connector_init_tests[] = {
+ KUNIT_CASE(drm_test_drmm_connector_init),
+ KUNIT_CASE(drm_test_drmm_connector_init_null_ddc),
+ KUNIT_CASE_PARAM(drm_test_drmm_connector_init_type_valid,
+ drm_connector_init_type_valid_gen_params),
+ { }
+};
+
+static struct kunit_suite drmm_connector_init_test_suite = {
+ .name = "drmm_connector_init",
+ .init = drm_test_connector_init,
+ .test_cases = drmm_connector_init_tests,
+};
+
struct drm_get_tv_mode_from_name_test {
const char *name;
enum drm_connector_tv_mode expected_mode;
@@ -70,7 +235,10 @@ static struct kunit_suite drm_get_tv_mode_from_name_test_suite = {
.test_cases = drm_get_tv_mode_from_name_tests,
};
-kunit_test_suite(drm_get_tv_mode_from_name_test_suite);
+kunit_test_suites(
+ &drmm_connector_init_test_suite,
+ &drm_get_tv_mode_from_name_test_suite
+);
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c
index ca4f8e4c5d5d..d5317d13d3fc 100644
--- a/drivers/gpu/drm/tests/drm_kunit_helpers.c
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>
@@ -14,6 +16,8 @@
#define KUNIT_DEVICE_NAME "drm-kunit-mock-device"
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
};
/**
@@ -161,5 +165,151 @@ drm_kunit_helper_atomic_state_alloc(struct kunit *test,
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc);
+static const uint32_t default_plane_formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
+static const uint64_t default_plane_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
+static const struct drm_plane_helper_funcs default_plane_helper_funcs = {
+};
+
+static const struct drm_plane_funcs default_plane_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .reset = drm_atomic_helper_plane_reset,
+};
+
+/**
+ * drm_kunit_helper_create_primary_plane - Creates a mock primary plane for a KUnit test
+ * @test: The test context object
+ * @drm: The device to alloc the plane for
+ * @funcs: Callbacks for the new plane. Optional.
+ * @helper_funcs: Helpers callbacks for the new plane. Optional.
+ * @formats: array of supported formats (DRM_FORMAT\_\*). Optional.
+ * @num_formats: number of elements in @formats
+ * @modifiers: array of struct drm_format modifiers terminated by
+ * DRM_FORMAT_MOD_INVALID. Optional.
+ *
+ * This allocates and initializes a mock struct &drm_plane meant to be
+ * part of a mock device for a KUnit test.
+ *
+ * Resources will be cleaned up automatically.
+ *
+ * @funcs will default to the default helpers implementations.
+ * @helper_funcs will default to an empty implementation. @formats will
+ * default to XRGB8888 only. @modifiers will default to a linear
+ * modifier only.
+ *
+ * Returns:
+ * A pointer to the new plane, or an ERR_PTR() otherwise.
+ */
+struct drm_plane *
+drm_kunit_helper_create_primary_plane(struct kunit *test,
+ struct drm_device *drm,
+ const struct drm_plane_funcs *funcs,
+ const struct drm_plane_helper_funcs *helper_funcs,
+ const uint32_t *formats,
+ unsigned int num_formats,
+ const uint64_t *modifiers)
+{
+ struct drm_plane *plane;
+
+ if (!funcs)
+ funcs = &default_plane_funcs;
+
+ if (!helper_funcs)
+ helper_funcs = &default_plane_helper_funcs;
+
+ if (!formats || !num_formats) {
+ formats = default_plane_formats;
+ num_formats = ARRAY_SIZE(default_plane_formats);
+ }
+
+ if (!modifiers)
+ modifiers = default_plane_modifiers;
+
+ plane = __drmm_universal_plane_alloc(drm,
+ sizeof(struct drm_plane), 0,
+ 0,
+ funcs,
+ formats,
+ num_formats,
+ default_plane_modifiers,
+ DRM_PLANE_TYPE_PRIMARY,
+ NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
+
+ drm_plane_helper_add(plane, helper_funcs);
+
+ return plane;
+}
+EXPORT_SYMBOL_GPL(drm_kunit_helper_create_primary_plane);
+
+static const struct drm_crtc_helper_funcs default_crtc_helper_funcs = {
+};
+
+static const struct drm_crtc_funcs default_crtc_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .reset = drm_atomic_helper_crtc_reset,
+};
+
+/**
+ * drm_kunit_helper_create_crtc - Creates a mock CRTC for a KUnit test
+ * @test: The test context object
+ * @drm: The device to alloc the plane for
+ * @primary: Primary plane for CRTC
+ * @cursor: Cursor plane for CRTC. Optional.
+ * @funcs: Callbacks for the new plane. Optional.
+ * @helper_funcs: Helpers callbacks for the new plane. Optional.
+ *
+ * This allocates and initializes a mock struct &drm_crtc meant to be
+ * part of a mock device for a KUnit test.
+ *
+ * Resources will be cleaned up automatically.
+ *
+ * @funcs will default to the default helpers implementations.
+ * @helper_funcs will default to an empty implementation.
+ *
+ * Returns:
+ * A pointer to the new CRTC, or an ERR_PTR() otherwise.
+ */
+struct drm_crtc *
+drm_kunit_helper_create_crtc(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_plane *primary,
+ struct drm_plane *cursor,
+ const struct drm_crtc_funcs *funcs,
+ const struct drm_crtc_helper_funcs *helper_funcs)
+{
+ struct drm_crtc *crtc;
+ int ret;
+
+ if (!funcs)
+ funcs = &default_crtc_funcs;
+
+ if (!helper_funcs)
+ helper_funcs = &default_crtc_helper_funcs;
+
+ crtc = drmm_kzalloc(drm, sizeof(*crtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, crtc);
+
+ ret = drmm_crtc_init_with_planes(drm, crtc,
+ primary,
+ cursor,
+ funcs,
+ NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_crtc_helper_add(crtc, helper_funcs);
+
+ return crtc;
+}
+EXPORT_SYMBOL_GPL(drm_kunit_helper_create_crtc);
+
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
index 5f838980c7a1..94f8e3178df5 100644
--- a/drivers/gpu/drm/tidss/tidss_crtc.c
+++ b/drivers/gpu/drm/tidss/tidss_crtc.c
@@ -265,6 +265,16 @@ static void tidss_crtc_atomic_disable(struct drm_crtc *crtc,
reinit_completion(&tcrtc->framedone_completion);
+ /*
+ * If a layer is left enabled when the videoport is disabled, and the
+ * vid pipeline that was used for the layer is taken into use on
+ * another videoport, the DSS will report sync lost issues. Disable all
+ * the layers here as a work-around.
+ */
+ for (u32 layer = 0; layer < tidss->feat->num_planes; layer++)
+ dispc_ovr_enable_layer(tidss->dispc, tcrtc->hw_videoport, layer,
+ false);
+
dispc_vp_disable(tidss->dispc, tcrtc->hw_videoport);
if (!wait_for_completion_timeout(&tcrtc->framedone_completion,
diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c
index e1c0ef0c3894..68fed531f6a7 100644
--- a/drivers/gpu/drm/tidss/tidss_plane.c
+++ b/drivers/gpu/drm/tidss/tidss_plane.c
@@ -213,7 +213,7 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
drm_plane_helper_add(&tplane->plane, &tidss_plane_helper_funcs);
- drm_plane_create_zpos_property(&tplane->plane, hw_plane_id, 0,
+ drm_plane_create_zpos_property(&tplane->plane, tidss->num_planes, 0,
num_planes - 1);
ret = drm_plane_create_color_properties(&tplane->plane,
diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c
index 1bdfac8beafd..a07ede668cc1 100644
--- a/drivers/gpu/drm/v3d/v3d_bo.c
+++ b/drivers/gpu/drm/v3d/v3d_bo.c
@@ -40,7 +40,7 @@ void v3d_free_object(struct drm_gem_object *obj)
mutex_lock(&v3d->bo_lock);
v3d->bo_stats.num_allocated--;
- v3d->bo_stats.pages_allocated -= obj->size >> PAGE_SHIFT;
+ v3d->bo_stats.pages_allocated -= obj->size >> V3D_MMU_PAGE_SHIFT;
mutex_unlock(&v3d->bo_lock);
spin_lock(&v3d->mm_lock);
@@ -109,8 +109,8 @@ v3d_bo_create_finish(struct drm_gem_object *obj)
* lifetime of the BO.
*/
ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node,
- obj->size >> PAGE_SHIFT,
- GMP_GRANULARITY >> PAGE_SHIFT, 0, 0);
+ obj->size >> V3D_MMU_PAGE_SHIFT,
+ GMP_GRANULARITY >> V3D_MMU_PAGE_SHIFT, 0, 0);
spin_unlock(&v3d->mm_lock);
if (ret)
return ret;
@@ -118,7 +118,7 @@ v3d_bo_create_finish(struct drm_gem_object *obj)
/* Track stats for /debug/dri/n/bo_stats. */
mutex_lock(&v3d->bo_lock);
v3d->bo_stats.num_allocated++;
- v3d->bo_stats.pages_allocated += obj->size >> PAGE_SHIFT;
+ v3d->bo_stats.pages_allocated += obj->size >> V3D_MMU_PAGE_SHIFT;
mutex_unlock(&v3d->bo_lock);
v3d_mmu_insert_ptes(bo);
@@ -201,7 +201,7 @@ int v3d_create_bo_ioctl(struct drm_device *dev, void *data,
if (IS_ERR(bo))
return PTR_ERR(bo);
- args->offset = bo->node.start << PAGE_SHIFT;
+ args->offset = bo->node.start << V3D_MMU_PAGE_SHIFT;
ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
drm_gem_object_put(&bo->base.base);
@@ -246,7 +246,7 @@ int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
}
bo = to_v3d_bo(gem_obj);
- args->offset = bo->node.start << PAGE_SHIFT;
+ args->offset = bo->node.start << V3D_MMU_PAGE_SHIFT;
drm_gem_object_put(gem_obj);
return 0;
diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c
index dc3cf708d02e..19e3ee7ac897 100644
--- a/drivers/gpu/drm/v3d/v3d_debugfs.c
+++ b/drivers/gpu/drm/v3d/v3d_debugfs.c
@@ -219,7 +219,7 @@ static int v3d_debugfs_bo_stats(struct seq_file *m, void *unused)
seq_printf(m, "allocated bos: %d\n",
v3d->bo_stats.num_allocated);
seq_printf(m, "allocated bo size (kb): %ld\n",
- (long)v3d->bo_stats.pages_allocated << (PAGE_SHIFT - 10));
+ (long)v3d->bo_stats.pages_allocated << (V3D_MMU_PAGE_SHIFT - 10));
mutex_unlock(&v3d->bo_lock);
return 0;
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 3c7d58866570..1950c723dde1 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -19,6 +19,8 @@ struct reset_control;
#define GMP_GRANULARITY (128 * 1024)
+#define V3D_MMU_PAGE_SHIFT 12
+
#define V3D_MAX_QUEUES (V3D_CPU + 1)
static inline char *v3d_queue_to_string(enum v3d_queue queue)
diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c
index afc76390a197..2e04f6cb661e 100644
--- a/drivers/gpu/drm/v3d/v3d_irq.c
+++ b/drivers/gpu/drm/v3d/v3d_irq.c
@@ -70,7 +70,7 @@ v3d_overflow_mem_work(struct work_struct *work)
list_add_tail(&bo->unref_head, &v3d->bin_job->render->unref_list);
spin_unlock_irqrestore(&v3d->job_lock, irqflags);
- V3D_CORE_WRITE(0, V3D_PTB_BPOA, bo->node.start << PAGE_SHIFT);
+ V3D_CORE_WRITE(0, V3D_PTB_BPOA, bo->node.start << V3D_MMU_PAGE_SHIFT);
V3D_CORE_WRITE(0, V3D_PTB_BPOS, obj->size);
out:
diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c
index 5a453532901f..14f3af40d6f6 100644
--- a/drivers/gpu/drm/v3d/v3d_mmu.c
+++ b/drivers/gpu/drm/v3d/v3d_mmu.c
@@ -21,8 +21,6 @@
#include "v3d_drv.h"
#include "v3d_regs.h"
-#define V3D_MMU_PAGE_SHIFT 12
-
/* Note: All PTEs for the 1MB superpage must be filled with the
* superpage bit set.
*/