diff --git a/drivers/gpu/drm/apple/afk.c b/drivers/gpu/drm/apple/afk.c index 688b2d2f5c622..08dc5cf8a242b 100644 --- a/drivers/gpu/drm/apple/afk.c +++ b/drivers/gpu/drm/apple/afk.c @@ -1380,13 +1380,23 @@ static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel, } ret = parse_epic_service_init(&ctx, &epic_name, &epic_class, &epic_unit, ep->dcp->fw_compat == DCP_FIRMWARE_H17P); - if (ret) { + if (ret == -ENOENT && + ep->dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G) { + /* + * M5 (H17G) announces powerlog-service with only + * ep-has-desc-mgr and interface-id; match it by the + * announced name, as for props-less announces. + */ + epic_name = NULL; + epic_class = NULL; + } else if (ret) { dev_err(ep->dcp->dev, "AFK[ep:%02x]: failed to extract init props: %d\n", ep->endpoint, ret); return; + } else { + service_name = epic_class; } - service_name = epic_class; } else { service_name = name; } diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index d71c5c0370f2e..e7d8b28ca5b48 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -136,9 +136,12 @@ static int apple_drm_fbdev_probe(struct drm_fb_helper *helper, struct apple_dcp *dcp; dcp = platform_get_drvdata(to_apple_crtc(crtc)->dcp); - if (dcp->fw_compat == DCP_FIRMWARE_H17P && - dcp->hw.iomfb_method_profile != DCP_IOMFB_METHODS_H17G) { - /* H17P applies the stored fourth byte as straight alpha. */ + if (dcp->fw_compat == DCP_FIRMWARE_H17P) { + /* + * H17P applies the stored fourth byte as straight alpha, + * the H17G (M5) profile too: an XRGB console with X = 0 + * stays black. ARGB makes fbcon write it opaque. + */ sizes->surface_bpp = 32; sizes->surface_depth = 32; break; @@ -751,6 +754,8 @@ static int apple_probe_per_dcp(struct device *dev, planes[zpos] = apple_plane_init(drm, 1U << num, surf, supports_l10r, supports_xrgb2101010, + dcp_data->hw.iomfb_method_profile == + DCP_IOMFB_METHODS_H17G, plane_type, interchange, supports_premultiplied); } diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index 2c3404593e69a..2f2660982f599 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -3866,6 +3866,18 @@ static void __maybe_unused dcp_sleep(struct apple_dcp *dcp) #define DCP_BL_FALLBACK_DELAY msecs_to_jiffies(2000) +/* + * H17G (M5) internal panel: a pipe stop never comes back (see + * iomfb_blank_h17p()), so DPMS blanks the panel and keeps the pipe running. + */ +static bool dcp_h17g_panel(struct apple_dcp *dcp) +{ + return dcp->fw_compat == DCP_FIRMWARE_H17P && + dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G && + dcp->connector_type == DRM_MODE_CONNECTOR_eDP && + dcp_has_panel(dcp); +} + static bool dcp_uses_soft_dpms(struct apple_dcp *dcp) { return (dcp_backlight_active(dcp) && dcp_has_panel(dcp)) || @@ -3954,6 +3966,12 @@ void dcp_poweron(struct platform_device *pdev) return; } + if (dcp_h17g_panel(dcp)) { + /* The pipe never stopped; the next swap restores the level. */ + dcp->brightness.update = true; + return; + } + if (dcp_is_typec_output(dcp)) { /* This CRTC now owns the current Type-C display session. */ scoped_guard(mutex, &dcp->hpd_mutex) @@ -4081,6 +4099,11 @@ void dcp_poweroff(struct platform_device *pdev) return; } + if (dcp_h17g_panel(dcp)) { + iomfb_blank_h17p(dcp); + return; + } + if (dcp->avep) av_service_disconnect(dcp); diff --git a/drivers/gpu/drm/apple/iomfb_h17p.c b/drivers/gpu/drm/apple/iomfb_h17p.c index 4f13cee71c872..3b217b0fcb53f 100644 --- a/drivers/gpu/drm/apple/iomfb_h17p.c +++ b/drivers/gpu/drm/apple/iomfb_h17p.c @@ -168,30 +168,37 @@ static u64 iomfb_nits_binary64_h17p(u32 nits) return value; } -void iomfb_encode_backlight_h17p(struct dcp_present_h17p *wire, u32 nits, - u32 maximum, bool update) +/* @swap is the swap record of a present; H17G sends it unserialized. */ +void iomfb_encode_backlight_swap_h17p(u8 *swap, u32 nits, u32 maximum, + bool update) { u64 ceiling = iomfb_nits_binary64_h17p(maximum); u64 unity = iomfb_nits_binary64_h17p(1); /* This profile does not use the older DAC/power words. */ - memset(wire->swap + 0x32f, 0, 0xd); - memset(wire->swap + 0x354, 0, 0x9a); + memset(swap + 0x32f, 0, 0xd); + memset(swap + 0x354, 0, 0x9a); /* The ceiling pair must remain valid even on a brightness-idle frame. */ - put_unaligned_le64(ceiling, wire->swap + 0x36e); - put_unaligned_le64(ceiling, wire->swap + 0x376); + put_unaligned_le64(ceiling, swap + 0x36e); + put_unaligned_le64(ceiling, swap + 0x376); if (!update) return; /* Linux brightness changes and soft DPMS use the same activation bytes. */ - put_unaligned_le32(1, wire->swap + 0x354); - wire->swap[0x358] = 1; - wire->swap[0x359] = 1; - wire->swap[0x35a] = 1; - put_unaligned_le16(1, wire->swap + 0x35c); - put_unaligned_le64(iomfb_nits_binary64_h17p(nits), wire->swap + 0x35e); - put_unaligned_le64(unity, wire->swap + 0x37e); - put_unaligned_le64(unity, wire->swap + 0x3e6); + put_unaligned_le32(1, swap + 0x354); + swap[0x358] = 1; + swap[0x359] = 1; + swap[0x35a] = 1; + put_unaligned_le16(1, swap + 0x35c); + put_unaligned_le64(iomfb_nits_binary64_h17p(nits), swap + 0x35e); + put_unaligned_le64(unity, swap + 0x37e); + put_unaligned_le64(unity, swap + 0x3e6); +} + +void iomfb_encode_backlight_h17p(struct dcp_present_h17p *wire, u32 nits, + u32 maximum, bool update) +{ + iomfb_encode_backlight_swap_h17p(wire->swap, nits, maximum, update); } static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { @@ -308,9 +315,12 @@ static bool trampoline_get_frequency_h17p(struct apple_dcp *dcp, int tag, static bool trampoline_analytics_h17p(struct apple_dcp *dcp, int tag, void *out, void *in) { - if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G) - return trampoline_zero(dcp, tag, out, in); - + /* + * H17G (M5) needs the same empty dictionary. A zeroed reply fails the + * firmware's OSDictionary deserialization ("OSDictionary + * deserialization failed" in the DCP crash log) and the DCP panics + * with "app NMI" one hour after boot. + */ trace_iomfb_callback(dcp, tag, __func__); /* The dispatcher has validated the D114 reply size. */ memset(out, 0, 0x1004); diff --git a/drivers/gpu/drm/apple/iomfb_h17p.h b/drivers/gpu/drm/apple/iomfb_h17p.h index 9880e82078178..112a4e002138d 100644 --- a/drivers/gpu/drm/apple/iomfb_h17p.h +++ b/drivers/gpu/drm/apple/iomfb_h17p.h @@ -158,6 +158,9 @@ static inline struct dcp_apply_property_h17p dcp_opaque_x_property_h17p(void) }; } +void iomfb_blank_h17p(struct apple_dcp *dcp); +void iomfb_encode_backlight_swap_h17p(u8 *swap, u32 nits, u32 maximum, + bool update); void iomfb_encode_backlight_h17p(struct dcp_present_h17p *wire, u32 nits, u32 maximum, bool update); diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index b649590127626..7a45dba123777 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -2355,6 +2355,44 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) dev_info(dcp->dev, "dcp_poweroff() done\n"); } +#if DCP_FW_VERSION(26, 0, 0) <= DCP_FW_VER && DCP_FW_VER < DCP_FW_VERSION(27, 0, 0) +/* + * H17G (M5) internal panel DPMS off. A pipe stop does not come back: + * setPowerState(0) times out and the next setPowerState(1) is never + * answered. Blank instead and keep the pipe running: a black, + * surface-free swap that also takes the backlight to 0 nits. The next CRTC + * enable presents the scanout and the brightness again. + */ +void DCP_FW_NAME(iomfb_blank)(struct apple_dcp *dcp) +{ + struct DCP_FW_NAME(dcp_swap_start_req) swap_req = { 0 }; + typeof(DCP_FW_UNION(dcp->swap)) *swap = &DCP_FW_UNION(dcp->swap); + struct dcp_swap_cookie *cookie; + unsigned long ret; + + cookie = kzalloc(sizeof(*cookie), GFP_KERNEL); + if (!cookie) + return; + cookie->status = ~0U; + init_completion(&cookie->done); + kref_init(&cookie->refcount); + /* increase refcount to ensure the receiver has a reference */ + kref_get(&cookie->refcount); + + dcp_prepare_clear_swap(dcp, swap); + if (dcp_has_panel(dcp)) + iomfb_encode_backlight_swap_h17p((u8 *)&swap->swap, 0, + dcp->brightness.maximum, true); + + dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(500)); + if (!ret || READ_ONCE(cookie->status)) + dev_warn(dcp->dev, "%s: blank swap %s\n", __func__, + ret ? "failed" : "timed out"); + kref_put(&cookie->refcount, release_swap_cookie); +} +#endif + static void last_client_closed_sleep(struct apple_dcp *dcp, void *out, void *cookie) { #if DCP_FW_DESKTOP_26(DCP_FW_VER) @@ -3633,6 +3671,21 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G; #endif if (update_brightness) { +#if DCP_FW_H17(DCP_FW_VER) + /* + * H17G firmware ignores the DAC words and takes nits in the + * same swap bytes as H17P. Without a requested or reported + * level, send nothing rather than 0 nits. + */ + int nits = dcp->brightness.bl_dev ? + dcp->brightness.bl_dev->props.brightness : + dcp->brightness.nits; + + if (nits > 0) + iomfb_encode_backlight_swap_h17p((u8 *)&req->swap, nits, + dcp->brightness.maximum, + true); +#else #if DCP_FW_LAYOUT_14_7(DCP_FW_VER) req->swap.bl_update = 1; #else @@ -3640,6 +3693,7 @@ void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, stru #endif req->swap.bl_value = dcp->brightness.dac; req->swap.bl_power = 0x40; +#endif dcp->brightness.update = false; } #endif diff --git a/drivers/gpu/drm/apple/plane.c b/drivers/gpu/drm/apple/plane.c index 87fecce18ee20..c019cdbc9844e 100644 --- a/drivers/gpu/drm/apple/plane.c +++ b/drivers/gpu/drm/apple/plane.c @@ -743,6 +743,7 @@ struct drm_plane *apple_plane_init(struct drm_device *dev, u32 iomfb_surf, bool supports_l10r, bool supports_xrgb2101010, + bool xrgb_is_alpha, enum drm_plane_type type, bool interchange, bool premultiplied) @@ -755,7 +756,16 @@ struct drm_plane *apple_plane_init(struct drm_device *dev, switch (type) { case DRM_PLANE_TYPE_PRIMARY: - if (supports_l10r && supports_xrgb2101010) { + if (xrgb_is_alpha) { + /* + * The firmware blends the X byte as alpha and ignores the + * opaque-X property: offer only formats with alpha, so that + * compositors write it instead of leaving whatever the + * renderer stored there. + */ + fmts = dcp_overlay_formats; + num_fmts = ARRAY_SIZE(dcp_overlay_formats); + } else if (supports_l10r && supports_xrgb2101010) { fmts = dcp_primary_formats; num_fmts = ARRAY_SIZE(dcp_primary_formats); } else if (supports_l10r) { diff --git a/drivers/gpu/drm/apple/plane.h b/drivers/gpu/drm/apple/plane.h index 69229ad6a5775..32d7da10a6a4f 100644 --- a/drivers/gpu/drm/apple/plane.h +++ b/drivers/gpu/drm/apple/plane.h @@ -37,6 +37,7 @@ struct drm_plane *apple_plane_init(struct drm_device *dev, u32 iomfb_surf, bool supports_l10r, bool supports_xrgb2101010, + bool xrgb_is_alpha, enum drm_plane_type type, bool interchange, bool premultiplied); diff --git a/drivers/gpu/drm/apple/systemep.c b/drivers/gpu/drm/apple/systemep.c index 44e239c6016b0..b729ef50189d6 100644 --- a/drivers/gpu/drm/apple/systemep.c +++ b/drivers/gpu/drm/apple/systemep.c @@ -112,6 +112,10 @@ static int powerlog_report(struct apple_epic_service *service, enum epic_subtype } dcp->brightness.nits = mnits.millinits / 1000; + /* H17G publishes no NITS property; its first report registers the device. */ + if (dcp->hw.iomfb_method_profile == DCP_IOMFB_METHODS_H17G && + dcp_has_panel(dcp) && !dcp->brightness.bl_dev && dcp->brightness.maximum > 0) + schedule_work(&dcp->bl_register_wq); return 0; }