diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 0f36dad6f96351..31b86e909014ad 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -335,6 +335,10 @@ static int apple_probe_per_dcp(struct device *dev, if (ret) return ret; + ret = drm_connector_attach_vrr_capable_property(&connector->base); + if (ret) + return ret; + connector->base.polled = DRM_CONNECTOR_POLL_HPD; connector->connected = false; connector->dcp = dcp; diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index f2eb2483c9a880..0a6859448e19eb 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -186,6 +186,7 @@ struct apple_dcp { bool during_modeset; bool valid_mode; bool use_timestamps; + bool vrr_enabled; struct dcp_set_digital_out_mode_req mode; /* completion for active turning true */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 9dfc3fd002f530..d830ff72ce8388 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -58,6 +58,10 @@ static bool unstable_edid = true; module_param(unstable_edid, bool, 0644); MODULE_PARM_DESC(unstable_edid, "Enable unstable EDID retrival support"); +bool vrr; +module_param(vrr, bool, 0644); +MODULE_PARM_DESC(vrr, "Enable Adaptive Sync/ProMotion on supported displays"); + /* copied and simplified from drm_vblank.c */ static void send_vblank_event(struct drm_device *dev, struct drm_pending_vblank_event *e, @@ -361,6 +365,11 @@ int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) return -EINVAL; } + if (dcp->vrr_enabled != crtc_state->vrr_enabled) { + dcp->vrr_enabled = crtc_state->vrr_enabled; + crtc_state->mode_changed = true; + } + return 0; } diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index 1d9448f0f4dc47..1d90e4a2597303 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -244,6 +244,7 @@ void dcp_hotplug(struct work_struct *work) if (!connector->connected) { drm_edid_free(connector->drm_edid); + drm_connector_set_vrr_capable_property(&connector->base, false); connector->drm_edid = NULL; } diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 5799586106713e..8a871db0b94a70 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -46,6 +46,15 @@ enum dcpep_type { IOMFB_MESSAGE_TYPE_MSG = 2, }; +/* + * IOMFB supports the setting of a number of parameters + * that alter various aspects of the connected sink's + * behaviour at runtime. + */ +enum iomfb_parameter { + IOMFBPARAM_ADAPTIVE_SYNC = 14, +}; + #define IOMFB_MESSAGE_TYPE GENMASK_ULL( 3, 0) /* Message */ diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index 1b7ecbcba925d1..351ceb605f9749 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -34,6 +34,8 @@ /* Register defines used in bandwidth setup structure */ #define REG_DOORBELL_BIT(idx) (2 + (idx)) +extern bool vrr; + struct dcp_wait_cookie { struct kref refcount; struct completion done; @@ -546,8 +548,9 @@ static u8 dcpep_cb_prop_chunk(struct apple_dcp *dcp, static bool dcpep_process_chunks(struct apple_dcp *dcp, struct dcp_set_dcpav_prop_end_req *req) { + // struct apple_connector *connector = dcp->connector; struct dcp_parse_ctx ctx; - int ret; + int ret; //, i; if (!dcp->chunks.data) { dev_warn(dcp->dev, "ignoring spurious end\n"); @@ -589,6 +592,15 @@ static bool dcpep_process_chunks(struct apple_dcp *dcp, dcp_set_dimensions(dcp); } + // if (connector) { + // for (i = 0; i < dcp->nr_modes; i++) { + // if (dcp->modes[i].vrr) { + // drm_connector_set_vrr_capable_property(&connector->base, true); + // break; + // } + // } + // } + return true; } @@ -784,26 +796,11 @@ static void dcp_on_set_power_state(struct apple_dcp *dcp, void *out, void *cooki dcp_set_power_state(dcp, false, &req, dcp_on_final, cookie); } -static void dcp_on_set_parameter(struct apple_dcp *dcp, void *out, void *cookie) -{ - struct dcp_set_parameter_dcp param = { - .param = 14, - .value = { 0 }, -#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) - .count = 3, -#else - .count = 1, -#endif - }; - - dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_power_state, cookie); -} - void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) { struct dcp_wait_cookie *cookie; int ret; - u32 handle; + u32 handle = 0; dev_info(dcp->dev, "dcp_poweron() starting\n"); cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); @@ -815,15 +812,12 @@ void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp) /* increase refcount to ensure the receiver has a reference */ kref_get(&cookie->refcount); - if (dcp->main_display) { - handle = 0; - dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, - cookie); - } else { + if (!dcp->main_display) handle = 2; - dcp_set_display_device(dcp, false, &handle, - dcp_on_set_parameter, cookie); - } + + dcp_set_display_device(dcp, false, &handle, dcp_on_set_power_state, + cookie); + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(10000)); if (ret == 0) { @@ -1190,6 +1184,33 @@ static void complete_set_digital_out_mode(struct apple_dcp *dcp, void *data, } } +/* Changes to Adaptive Sync require a trip through set_digital_out_mode */ +static void dcp_on_set_adaptive_sync(struct apple_dcp *dcp, void *out, void *cookie) +{ + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); +} + +static void dcp_set_adaptive_sync(struct apple_dcp *dcp, u64 rate, void *cookie) +{ + struct dcp_set_parameter_dcp param = { + .param = IOMFBPARAM_ADAPTIVE_SYNC, + .value = { + rate & 0xffffffff, /* minRR */ + 0, /* mediaTargetRate */ + 0, /* Fractional Rate (?) */ + 0, /* unused */ + }, +#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0) + .count = 3, +#else + .count = 1, +#endif + }; + + dcp_set_parameter_dcp(dcp, false, ¶m, dcp_on_set_adaptive_sync, cookie); +} + int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, struct drm_crtc_state *crtc_state) { @@ -1229,8 +1250,8 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, .timing_mode_id = mode->timing_mode_id }; - /* Keep track of suspected vrr modes */ - dcp->use_timestamps = mode->vrr; + /* Use DCP swap timestamps on MacBook Pros with VRR */ + dcp->use_timestamps = mode->vrr && dcp->main_display; cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); if (!cookie) { @@ -1244,8 +1265,11 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, dcp->during_modeset = true; - dcp_set_digital_out_mode(dcp, false, &dcp->mode, - complete_set_digital_out_mode, cookie); + if (mode->vrr && vrr) + dcp_set_adaptive_sync(dcp, mode->min_vrr, cookie); + else + dcp_set_digital_out_mode(dcp, false, &dcp->mode, + complete_set_digital_out_mode, cookie); /* * The DCP firmware has an internal timeout of ~8 seconds for @@ -1277,6 +1301,15 @@ int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp, return 0; } +/* + * DCP timestamps are expressed in system timer ticks. Approximate + * this by converting from ktime nanoseconds to 24 MHz ticks. + */ +static u64 ns_to_mach(u64 ns) +{ + return ns * 3 / 125; +} + void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_plane *plane; @@ -1391,14 +1424,15 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru req->clear = 1; } - if (has_surface && dcp->use_timestamps) { + if (has_surface && (dcp->use_timestamps || crtc_state->vrr_enabled || vrr)) { /* - * Fake timstamps to get 120hz refresh rate. It looks - * like the actual value does not matter, as long as it is non zero. + * TODO: ascertain with certainty what these timestamps + * are. These names are guesses based on what macOS populates + * them with. These values seem to work well with VRR. */ - req->swap.ts1 = 120; - req->swap.ts2 = 120; - req->swap.ts3 = 120; + req->swap.presentation_time = ns_to_mach(ktime_get_ns()); + req->swap.last_pres_time = ns_to_mach(ktime_to_ns(dcp->swap_start)); + req->swap.submit_time = req->swap.presentation_time; } /* These fields should be set together */ diff --git a/drivers/gpu/drm/apple/iomfb_template.h b/drivers/gpu/drm/apple/iomfb_template.h index 8efab49cc53d08..1fea73ff0b95c7 100644 --- a/drivers/gpu/drm/apple/iomfb_template.h +++ b/drivers/gpu/drm/apple/iomfb_template.h @@ -18,14 +18,14 @@ #include "version_utils.h" struct DCP_FW_NAME(dcp_swap) { - u64 ts1; - u64 ts2; + u64 presentation_time; + u64 last_pres_time; u64 unk_10; u64 unk_18; u64 ts64_unk; u64 unk_28; - u64 ts3; + u64 submit_time; u64 unk_38; u64 flags1; diff --git a/drivers/gpu/drm/apple/parser.c b/drivers/gpu/drm/apple/parser.c index 7c7af18a7c7e15..40f318c8dfd86e 100644 --- a/drivers/gpu/drm/apple/parser.c +++ b/drivers/gpu/drm/apple/parser.c @@ -454,6 +454,10 @@ static int parse_mode(struct dcp_parse_ctx *handle, ret = parse_dimension(it.handle, &horiz); else if (!strcmp(key, "VerticalAttributes")) ret = parse_dimension(it.handle, &vert); + else if (!strcmp(key, "MinimumVariableRefreshRate")) + ret = parse_int(it.handle, &out->min_vrr); + else if (!strcmp(key, "MaximumVariableRefreshRate")) + ret = parse_int(it.handle, &out->max_vrr); else if (!strcmp(key, "ColorModes")) ret = parse_color_modes(it.handle, out); else if (!strcmp(key, "ID")) @@ -502,13 +506,18 @@ static int parse_mode(struct dcp_parse_ctx *handle, /* * HACK: * Mark the 120 Hz mode on j314/j316 (identified by resolution) as vrr. - * We still do not know how to drive VRR but at least seetinng timestamps - * in the the swap_surface message to non-zero values drives the display - * at 120 fps. + * Setting timestamps in the the swap_surface message to non-zero + * values drives the display at 120 fps. */ if (vert.precise_sync_rate >> 16 == 120 && ((horiz.active == 3024 && vert.active == 1964) || - (horiz.active == 3456 && vert.active == 2234))) + (horiz.active == 3456 && vert.active == 2234))) { + out->min_vrr = 24 << 16; + out->max_vrr = 120 << 16; + out->vrr = true; + } + + if (out->min_vrr && out->max_vrr) out->vrr = true; vert.active -= notch_height; diff --git a/drivers/gpu/drm/apple/parser.h b/drivers/gpu/drm/apple/parser.h index e03ee06ae98a75..18b85a06e8f9d7 100644 --- a/drivers/gpu/drm/apple/parser.h +++ b/drivers/gpu/drm/apple/parser.h @@ -92,6 +92,8 @@ struct dcp_display_mode { struct dcp_color_mode sdr; struct dcp_color_mode best; bool vrr; + s64 min_vrr; + s64 max_vrr; }; struct dimension {