diff --git a/drivers/gpu/drm/apple/dcp-internal.h b/drivers/gpu/drm/apple/dcp-internal.h index a9d977033c6c9..bfd095637f879 100644 --- a/drivers/gpu/drm/apple/dcp-internal.h +++ b/drivers/gpu/drm/apple/dcp-internal.h @@ -290,6 +290,8 @@ struct dcp_desktop_state { struct dcpext_scanout; struct dcp_hdmi; +struct dcp_call_history; + struct apple_dcp { /* Generation-specific wire/transaction state, sharing the common KMS owner. */ struct iomfb_m4 *v26_6; @@ -345,6 +347,8 @@ struct apple_dcp { /* firmware version and compatible firmware version */ enum dcp_firmware_version fw_compat; + /* Firmware 27.x: the method IDs of this build (iomfb_v27_methods()). */ + const struct dcp_method_entry *v27_methods; bool external; /* * T8122 dcpext: the panel's DRM device never waits on this processor. @@ -403,6 +407,9 @@ struct apple_dcp { /* DCP has crashed */ bool crashed; + /* T6050: the last AP-link messages, printed at a crash (dcp_call_history_start()) */ + struct dcp_call_history *call_history; + DECLARE_BITMAP(iomfb_surfaces, DCP_MAX_PLANES); /************* IOMFB ************************************************** @@ -894,6 +901,21 @@ static inline bool dcp_ext_dptx_607(const struct apple_dcp *dcp) dcp->hw.fw_14_7_endpoints; } +/* + * Whether this DCP reads a surface's element geometry as the surface record + * defines it: bytes per element (pix_size, a linear plane's tile_size), the + * element's width and height in pixels, and compression information only for + * a compressed plane. Only firmware 27 (DCP_FIRMWARE_V_27_0, which only + * T6050's DCP hw data selects) is tested with it. Earlier firmware keeps the + * fixed 1/1/1 and has_compr_info 1 it has run on: before widening this, + * check a linear XRGB8888 and an NV12 swap, and the default framebuffer, on + * 12.x-26.x firmware. + */ +static inline bool dcp_surface_element_geometry(const struct apple_dcp *dcp) +{ + return dcp->fw_compat == DCP_FIRMWARE_V_27_0; +} + #define DCP_AUDIO_MAX_CHANS 15 #endif /* __APPLE_DCP_INTERNAL_H__ */ diff --git a/drivers/gpu/drm/apple/dcp.c b/drivers/gpu/drm/apple/dcp.c index d80e2a1914135..bda1f55b1adb5 100644 --- a/drivers/gpu/drm/apple/dcp.c +++ b/drivers/gpu/drm/apple/dcp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -998,6 +999,261 @@ static void dcp_recv_msg(void *cookie, u8 endpoint, u64 message) afk_receive_message(ep, message); } +/* + * T6050: the AP-link traffic that led to a DCP crash. A crashed DCP needs a + * reboot to show anything again, so the record has to reach the kernel log, + * beside the firmware's own crash log. Each T6050 DCP keeps its last + * DCP_CALL_HISTORY_LEN IOMFB calls, callbacks, swaps and mailbox messages in + * a small ring of its own, fed by this driver's tracepoints (a probe per DCP + * that takes only that DCP's events), and prints it once when it crashes. + */ +#define DCP_CALL_HISTORY_LEN 256U + +static bool call_history = true; +module_param(call_history, bool, 0444); +MODULE_PARM_DESC(call_history, + "T6050: print each DCP's last AP-link messages when it crashes (default: on)"); + +enum dcp_call_kind { + DCP_CALL_SEND, + DCP_CALL_RECV, + DCP_CALL_PUSH, + DCP_CALL_CALLBACK, + DCP_CALL_SWAP_SUBMIT, + DCP_CALL_SWAP_COMPLETE, +}; + +struct dcp_call_record { + u64 time; /* local_clock(), as the kernel log's stamps */ + u64 value; /* the message, or the swap ID */ + const char *name; /* method or callback name, static */ + u16 number; /* callback number, or method context */ + u8 kind; + u8 endpoint; + char tag[4]; /* method tag */ +}; + +struct dcp_call_history { + raw_spinlock_t lock; + bool frozen; + unsigned int count; + struct dcp_call_record rec[DCP_CALL_HISTORY_LEN]; +}; + +static bool dcp_is_t6050(struct device *dev) +{ + return of_device_is_compatible(dev->of_node, "apple,t6050-dcpext") || + of_device_is_compatible(dev->of_node, "apple,t6050-dcp"); +} + +static void dcp_call_record(struct apple_dcp *dcp, const struct dcp_call_record *in) +{ + struct dcp_call_history *h = dcp->call_history; + unsigned long flags; + + raw_spin_lock_irqsave(&h->lock, flags); + if (!h->frozen) { + struct dcp_call_record *r = &h->rec[h->count++ % DCP_CALL_HISTORY_LEN]; + + *r = *in; + r->time = local_clock(); + } + raw_spin_unlock_irqrestore(&h->lock, flags); +} + +/* Each probe is registered with its DCP as data and drops the others' events. */ +static void dcp_call_history_send(void *data, struct apple_dcp *dcp, + u8 endpoint, u64 message) +{ + if (dcp == data) + dcp_call_record(dcp, &(struct dcp_call_record){ + .kind = DCP_CALL_SEND, .endpoint = endpoint, + .value = message }); +} + +static void dcp_call_history_recv(void *data, struct apple_dcp *dcp, + u8 endpoint, u64 message) +{ + if (dcp == data) + dcp_call_record(dcp, &(struct dcp_call_record){ + .kind = DCP_CALL_RECV, .endpoint = endpoint, + .value = message }); +} + +static void dcp_call_history_push(void *data, struct apple_dcp *dcp, + const struct dcp_method_entry *method, + int context, int offset, int depth) +{ + struct dcp_call_record r = { + .kind = DCP_CALL_PUSH, .name = method->name, .number = context, + }; + + if (dcp != data) + return; + memcpy(r.tag, method->tag, sizeof(r.tag)); + dcp_call_record(dcp, &r); +} + +static void dcp_call_history_callback(void *data, struct apple_dcp *dcp, + int tag, const char *name) +{ + if (dcp == data) + dcp_call_record(dcp, &(struct dcp_call_record){ + .kind = DCP_CALL_CALLBACK, .number = tag, .name = name }); +} + +static void dcp_call_history_submit(void *data, struct apple_dcp *dcp, + u32 swap_id) +{ + if (dcp == data) + dcp_call_record(dcp, &(struct dcp_call_record){ + .kind = DCP_CALL_SWAP_SUBMIT, .value = swap_id }); +} + +static void dcp_call_history_complete(void *data, struct apple_dcp *dcp, + u32 swap_id) +{ + if (dcp == data) + dcp_call_record(dcp, &(struct dcp_call_record){ + .kind = DCP_CALL_SWAP_COMPLETE, .value = swap_id }); +} + +static void dcp_call_history_release(void *data) +{ + struct apple_dcp *dcp = data; + + unregister_trace_iomfb_swap_complete(dcp_call_history_complete, dcp); + unregister_trace_iomfb_swap_submit(dcp_call_history_submit, dcp); + unregister_trace_iomfb_callback(dcp_call_history_callback, dcp); + unregister_trace_iomfb_push(dcp_call_history_push, dcp); + unregister_trace_dcp_recv_msg(dcp_call_history_recv, dcp); + unregister_trace_dcp_send_msg(dcp_call_history_send, dcp); + tracepoint_synchronize_unregister(); + dcp->call_history = NULL; +} + +static int dcp_call_history_register(struct apple_dcp *dcp) +{ + int ret; + + ret = register_trace_dcp_send_msg(dcp_call_history_send, dcp); + if (ret) + return ret; + ret = register_trace_dcp_recv_msg(dcp_call_history_recv, dcp); + if (ret) + goto err_send; + ret = register_trace_iomfb_push(dcp_call_history_push, dcp); + if (ret) + goto err_recv; + ret = register_trace_iomfb_callback(dcp_call_history_callback, dcp); + if (ret) + goto err_push; + ret = register_trace_iomfb_swap_submit(dcp_call_history_submit, dcp); + if (ret) + goto err_callback; + ret = register_trace_iomfb_swap_complete(dcp_call_history_complete, dcp); + if (ret) + goto err_submit; + return 0; + +err_submit: + unregister_trace_iomfb_swap_submit(dcp_call_history_submit, dcp); +err_callback: + unregister_trace_iomfb_callback(dcp_call_history_callback, dcp); +err_push: + unregister_trace_iomfb_push(dcp_call_history_push, dcp); +err_recv: + unregister_trace_dcp_recv_msg(dcp_call_history_recv, dcp); +err_send: + unregister_trace_dcp_send_msg(dcp_call_history_send, dcp); + tracepoint_synchronize_unregister(); + return ret; +} + +static void dcp_call_history_start(struct device *dev) +{ + struct apple_dcp *dcp = dev_get_drvdata(dev); + struct dcp_call_history *h; + int ret; + + /* The tracepoints are built with CONFIG_TRACING (trace.o). */ + if (!IS_ENABLED(CONFIG_TRACING) || !call_history || !dcp || + !dcp_is_t6050(dev)) + return; + h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); + if (!h) + return; + raw_spin_lock_init(&h->lock); + dcp->call_history = h; + ret = dcp_call_history_register(dcp); + if (ret) { + dcp->call_history = NULL; + dev_warn(dev, "call history: tracepoint probes not registered: %d\n", + ret); + return; + } + if (devm_add_action_or_reset(dev, dcp_call_history_release, dcp)) + return; + dev_info(dev, "call history: keeping the last %u AP-link messages\n", + DCP_CALL_HISTORY_LEN); +} + +static void dcp_call_record_print(struct apple_dcp *dcp, + const struct dcp_call_record *r) +{ + u64 sec = r->time; + u32 usec = do_div(sec, NSEC_PER_SEC) / NSEC_PER_USEC; + + switch (r->kind) { + case DCP_CALL_SEND: + case DCP_CALL_RECV: + dev_info(dcp->dev, "call history: [%5llu.%06u] %s ep 0x%02x 0x%016llx\n", + sec, usec, r->kind == DCP_CALL_SEND ? "send" : "recv", + r->endpoint, r->value); + break; + case DCP_CALL_PUSH: + dev_info(dcp->dev, "call history: [%5llu.%06u] call %.4s %s (context %u)\n", + sec, usec, r->tag, r->name, r->number); + break; + case DCP_CALL_CALLBACK: + dev_info(dcp->dev, "call history: [%5llu.%06u] callback D%03u %s\n", + sec, usec, r->number, r->name); + break; + case DCP_CALL_SWAP_SUBMIT: + case DCP_CALL_SWAP_COMPLETE: + dev_info(dcp->dev, "call history: [%5llu.%06u] swap %llu %s\n", + sec, usec, r->value, + r->kind == DCP_CALL_SWAP_SUBMIT ? "submitted" : "completed"); + break; + } +} + +/* Once, at the crash, before the crash handling adds traffic of its own. */ +static void dcp_call_history_freeze(struct apple_dcp *dcp) +{ + struct dcp_call_history *h = dcp->call_history; + unsigned int count, n, i; + unsigned long flags; + + if (!h) + return; + raw_spin_lock_irqsave(&h->lock, flags); + if (h->frozen) { + raw_spin_unlock_irqrestore(&h->lock, flags); + return; + } + h->frozen = true; + count = h->count; + raw_spin_unlock_irqrestore(&h->lock, flags); + + /* Frozen: nothing writes the ring any more. */ + n = min(count, DCP_CALL_HISTORY_LEN); + dev_err(dcp->dev, "call history: the last %u of %u AP-link messages before the crash:\n", + n, count); + for (i = count - n; i != count; i++) + dcp_call_record_print(dcp, &h->rec[i % DCP_CALL_HISTORY_LEN]); +} + static void dcp_rtk_crashed(void *cookie, const void *crashlog, size_t crashlog_size) { struct apple_dcp *dcp = cookie; @@ -1005,6 +1261,9 @@ static void dcp_rtk_crashed(void *cookie, const void *crashlog, size_t crashlog_ if (READ_ONCE(dcp->quiescing)) return; + /* Before the crash handling adds traffic of its own. */ + dcp_call_history_freeze(dcp); + /* * A failed T8122 dcpext: its start never ran (start_done is not set * up) and it has no connector. Its firmware may still report a crash. @@ -4341,8 +4600,11 @@ static enum dcp_firmware_version dcp_check_firmware_version(struct device *dev) if (hw->firmware_compat == DCP_FIRMWARE_V_27_0) { ret = dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); - if (ret < 0 || strcmp(compat_str, "27.0.0")) + if (ret < 0 || !iomfb_v27_methods(compat_str)) { + dev_err(dev, "DCP firmware-compat %s has no 27.x method table\n", + ret < 0 ? "missing" : compat_str); return DCP_FIRMWARE_UNKNOWN; + } return DCP_FIRMWARE_V_27_0; } @@ -5371,6 +5633,13 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev) iomfb_queue_init(dcp); dcp->fw_compat = fw_compat; + if (fw_compat == DCP_FIRMWARE_V_27_0) { + char compat_str[DCP_FW_VERSION_STR_LEN]; + + /* Checked by dcp_check_firmware_version(). */ + dcp_read_fw_version(dev, "apple,firmware-compat", compat_str); + dcp->v27_methods = iomfb_v27_methods(compat_str); + } if (fw_compat == DCP_FIRMWARE_V_26_6_DESKTOP) { dcp->desktop = devm_kzalloc(dev, sizeof(*dcp->desktop), GFP_KERNEL); if (!dcp->desktop) @@ -5720,6 +5989,8 @@ static int dcp_platform_probe_pipe(struct platform_device *pdev) iomfb_v14_7_external_refuse(dcp, "its Type-C display routes did not register"); } + /* Before the bind, which can start the firmware session. */ + dcp_call_history_start(&pdev->dev); ret = component_add(&pdev->dev, &dcp_comp_ops); if (ret && dcp->external_prepared) { dcp_disable_typec_work(dcp, true); diff --git a/drivers/gpu/drm/apple/dptx-service.h b/drivers/gpu/drm/apple/dptx-service.h index 3d990a9ca1ea1..2cb8f68210eb8 100644 --- a/drivers/gpu/drm/apple/dptx-service.h +++ b/drivers/gpu/drm/apple/dptx-service.h @@ -5,14 +5,18 @@ #include "afk.h" #include "dptxep.h" +/* + * T8140 external firmware names its one port by the DCP index; the others, + * firmware 27 among them, by port unit 0 or 1. + */ static inline int dptx_service_port(const char *name, const char *class, - s64 unit, bool wire_v2, u32 index, + s64 unit, bool unit_is_index, u32 index, u32 num_ports) { if (!name || !class || strcmp(name, "dcpdptx-port-epic") || strcmp(class, "AppleDCPDPTXRemotePort")) return -EINVAL; - if (wire_v2) + if (unit_is_index) return num_ports == 1 && unit == index ? 0 : -EINVAL; return unit >= 0 && unit < 2 ? unit : -EINVAL; } diff --git a/drivers/gpu/drm/apple/dptxep.c b/drivers/gpu/drm/apple/dptxep.c index 3796aef4ac128..553b2df5dae2c 100644 --- a/drivers/gpu/drm/apple/dptxep.c +++ b/drivers/gpu/drm/apple/dptxep.c @@ -272,7 +272,7 @@ dptxport_call_get_max_drive_settings(struct apple_epic_service *service, return -EINVAL; reply->retcode = cpu_to_le32(0); - if (service->ep->wire_v2) { + if (dcp_is_external_h17p(service->ep->dcp)) { memset(reply, 0, reply_size); put_unaligned_le32(3, reply_ + 20); put_unaligned_le32(3, reply_ + 24); @@ -425,7 +425,7 @@ static int dptxport_call_set_active_lane_count(struct apple_epic_service *servic u64 lane_count = le64_to_cpu(request->lane_count); - if (service->ep->wire_v2) { + if (dcp_is_external_h17p(dcp)) { ret = dptx_j700_validate_lanes(dptx, lane_count); if (ret) return ret; @@ -439,7 +439,8 @@ static int dptxport_call_set_active_lane_count(struct apple_epic_service *servic dptx->phy_ops.dp.lanes = lane_count; /* USB4 DP IN has no ATC PHY; still accept the lane count. */ dptx->phy_ops.dp.set_lanes = - service->ep->wire_v2 || dcp_is_usb4_output(dcp) || (dcp->dptx_phy > 3); + dcp_is_external_h17p(dcp) || dcp_is_usb4_output(dcp) || + (dcp->dptx_phy > 3); break; default: dev_err(dcp->dev, "set_active_lane_count: invalid lane count:%llu\n", lane_count); @@ -983,7 +984,7 @@ static int dptxport_prepare(struct apple_epic_service *service, const char *name (!class || !class[0] || !strcmp(class, "AppleDCPDPTXRemotePort"))) port = unit >= 0 && unit < 2 ? unit : -EINVAL; else - port = dptx_service_port(name, class, unit, service->ep->wire_v2, + port = dptx_service_port(name, class, unit, dcp_is_external_h17p(dcp), dcp->index, dcp->hw.num_dptx_ports); if (port < 0) @@ -1080,8 +1081,12 @@ static int dptxport_group_call(struct apple_epic_service *service, u16 group, { struct dptx_port *dptx = service->cookie; - /* Keep native AFK-v2 training and its refusal policy distinct. */ - if (service->ep->wire_v2) + /* + * Keep the T8140 external (J700) training and its refusal policy + * distinct. It keys on that profile, not on the AFK v2 codec, which + * firmware 27 speaks with the common DPTX calls. + */ + if (dcp_is_external_h17p(service->ep->dcp)) return dptxport_v2_group_call(service, group, command, data, size, reply, room); diff --git a/drivers/gpu/drm/apple/iomfb-dfb.h b/drivers/gpu/drm/apple/iomfb-dfb.h index 30e84ab8366c3..8175cb2d3c922 100644 --- a/drivers/gpu/drm/apple/iomfb-dfb.h +++ b/drivers/gpu/drm/apple/iomfb-dfb.h @@ -54,39 +54,56 @@ static inline u64 apple_dcp_dfb_plane_size(u32 width, u32 height, u32 bpe, return data + ALIGN(2 * roundup_pow_of_two(cols) * roundup_pow_of_two(rows), 128); } -static inline int apple_dcp_dfb_allocation(u32 width, u32 height, u32 format, - u32 compression, u64 region_size, - u32 *surface_format, u32 *allocation) +/* + * The DFB's surface format and bytes per element (the surface record's pix_size) + * for the firmware's format and compression; *alpha_plane for a separate alpha plane. + */ +static inline int apple_dcp_dfb_format(u32 format, u32 compression, + u32 *surface_format, u32 *bpe, + bool *alpha_plane) { - u32 bpe; - u64 size; - bool alpha_plane = false; - - if (!width || !height || width > 16384 || height > 16384 || !region_size) - return -EINVAL; - if (compression != 0 && compression != 2 && compression != 3) - return -EOPNOTSUPP; + *alpha_plane = false; switch (format) { case 0x62336138: if (compression == 2) { format = 0x77343061; - bpe = 8; + *bpe = 8; } else { - bpe = 4; - alpha_plane = true; + *bpe = 4; + *alpha_plane = true; } break; case 0x77343061: - bpe = 8; + *bpe = 8; break; case 0x42475241: case 0x77333072: case 0x6c313072: - bpe = 4; + *bpe = 4; break; default: return -EOPNOTSUPP; } + *surface_format = format; + return 0; +} + +static inline int apple_dcp_dfb_allocation(u32 width, u32 height, u32 format, + u32 compression, u64 region_size, + u32 *surface_format, u32 *allocation) +{ + u32 bpe; + u64 size; + bool alpha_plane; + int ret; + + if (!width || !height || width > 16384 || height > 16384 || !region_size) + return -EINVAL; + if (compression != 0 && compression != 2 && compression != 3) + return -EOPNOTSUPP; + ret = apple_dcp_dfb_format(format, compression, &format, &bpe, &alpha_plane); + if (ret) + return ret; size = apple_dcp_dfb_plane_size(width, height, bpe, compression); if (alpha_plane) size += apple_dcp_dfb_plane_size(width, height, 1, compression); diff --git a/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc b/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc index c2a681328c102..0c0089e1ce979 100644 --- a/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc +++ b/drivers/gpu/drm/apple/iomfb-peer-runtime-v27.inc @@ -83,12 +83,12 @@ static void dcp_peer_started_on_owner(struct apple_dcp *owner, void *data, void dcp_peer_swap_armed_v27(owner, id); trace_iomfb_swap_submit(owner, id); trace_iomfb_swap_submit(peer, id); - dcp_push(owner, false, &dcp_methods[dcpep_swap_submit], sizeof(owner->present_v27), + dcp_push(owner, false, &owner->v27_methods[dcpep_swap_submit], sizeof(owner->present_v27), sizeof(struct dcp_swap_submit_resp_v27), &owner->present_v27, dcp_peer_submitted, owner); if (READ_ONCE(owner->crashed)) return; - dcp_push(peer, false, &dcp_methods[dcpep_swap_submit], sizeof(owner->peer_present_v27), + dcp_push(peer, false, &peer->v27_methods[dcpep_swap_submit], sizeof(owner->peer_present_v27), sizeof(struct dcp_swap_submit_resp_v27), &owner->peer_present_v27, dcp_peer_submitted, owner); if (READ_ONCE(peer->crashed)) diff --git a/drivers/gpu/drm/apple/iomfb.h b/drivers/gpu/drm/apple/iomfb.h index 568a6a7b8c529..c8455e4f3578c 100644 --- a/drivers/gpu/drm/apple/iomfb.h +++ b/drivers/gpu/drm/apple/iomfb.h @@ -4,6 +4,8 @@ #ifndef __APPLE_DCPEP_H__ #define __APPLE_DCPEP_H__ +#include +#include #include #include "version_utils.h" @@ -210,6 +212,9 @@ struct dcp_method_entry { char tag_h17g[4]; }; +/* The IOMFB method table of a firmware 27.x build, by its compat string; NULL if unknown. */ +const struct dcp_method_entry *iomfb_v27_methods(const char *compat); + #define IOMFB_MAX_CB (1000) bool iomfb_validate_callback_h17p(int tag, u32 in_len, u32 out_len); @@ -470,6 +475,44 @@ struct dcp_hotplug_resp_v26_6 { u8 opaque[0x4c]; } __packed; +/* + * A name and a serialized dictionary: the firmware's analytics event + * (D113 on 27.x, D114 on desktop 26.6). + */ +struct dcp_dict_passthrough_req { + u8 key[0x40]; + u8 value[0x1000]; + u8 value_null; + u8 padding[3]; +} __packed; + +struct dcp_dict_passthrough_resp { + u8 value[0x1000]; + u8 ret; + u8 padding[3]; +} __packed; + +static_assert(sizeof(struct dcp_dict_passthrough_req) == 0x1044); +static_assert(sizeof(struct dcp_dict_passthrough_resp) == 0x1004); + +/* + * The analytics event passes its dictionary by pointer, so the firmware + * deserializes the reply's copy when the call returns, and stops if that + * fails. Return the dictionary as it came, and report the event not sent. + * A zeroed reply is not a dictionary. + */ +static inline void +dcp_dict_passthrough_reply(const struct dcp_dict_passthrough_req *req, + struct dcp_dict_passthrough_resp *resp) +{ + if (req->value_null) + memset(resp->value, 0, sizeof(resp->value)); + else + memcpy(resp->value, req->value, sizeof(resp->value)); + resp->ret = false; + memset(resp->padding, 0, sizeof(resp->padding)); +} + /* A boolean argument, padded to four bytes. */ struct dcp_bool_arg { u8 value; diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index d1fb3226547f8..af1f9dd61e39b 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -636,7 +636,8 @@ static bool iomfbep_cb_create_dfb_surface(struct apple_dcp *dcp, int tag, { struct dcp_dfb_dimensions_h17p dim; struct dcp_surface *surface = (void *)req->surface; - u32 format, allocation; + u32 format, allocation, bpe; + bool alpha_plane; int ret = -EINVAL; memcpy(&dim, in, sizeof(dim)); @@ -653,9 +654,11 @@ static bool iomfbep_cb_create_dfb_surface(struct apple_dcp *dcp, int tag, return true; } + apple_dcp_dfb_format(dcp->dfb_format, dcp->dfb_compression, + &format, &bpe, &alpha_plane); surface->is_premultiplied = 1; surface->format = format; - surface->pix_size = 1; + surface->pix_size = dcp_surface_element_geometry(dcp) ? bpe : 1; surface->pel_w = 1; surface->pel_h = 1; surface->buf_size = allocation; diff --git a/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c b/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c index 05417d2c2b2cb..a773bf68a61cc 100644 --- a/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c +++ b/drivers/gpu/drm/apple/iomfb_v26_6_desktop.c @@ -52,41 +52,15 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { #include "iomfb_template.c" -struct dcp_dict_passthrough_req_v26_6 { - u8 key[0x40]; - u8 value[0x1000]; - u8 value_null; - u8 padding[3]; -} __packed; - -struct dcp_dict_passthrough_resp_v26_6 { - u8 value[0x1000]; - u8 ret; - u8 padding[3]; -} __packed; - -static_assert(sizeof(struct dcp_dict_passthrough_req_v26_6) == 0x1044); -static_assert(sizeof(struct dcp_dict_passthrough_resp_v26_6) == 0x1004); - /* - * D114 periodically publishes dictionary-valued statistics. The value is an - * in/out pointer, which the firmware deserializes again: the firmware's own - * client returns the serialized buffer unchanged and reports false. An - * all-zero reply is not a harmless false. + * D114 periodically publishes dictionary-valued statistics: the analytics + * event, answered by dcp_dict_passthrough_reply(). */ static bool trampoline_passthrough_dict_v26_6(struct apple_dcp *dcp, int tag, void *out, void *in) { - const struct dcp_dict_passthrough_req_v26_6 *req = in; - struct dcp_dict_passthrough_resp_v26_6 *resp = out; - trace_iomfb_callback(dcp, tag, "dcpep_cb_passthrough_dict"); - - if (!req->value_null) - memcpy(resp->value, req->value, sizeof(resp->value)); - - resp->ret = false; - memset(resp->padding, 0, sizeof(resp->padding)); + dcp_dict_passthrough_reply(in, out); return true; } diff --git a/drivers/gpu/drm/apple/iomfb_v27.c b/drivers/gpu/drm/apple/iomfb_v27.c index da0ed8b8f9c24..b52f51666411c 100644 --- a/drivers/gpu/drm/apple/iomfb_v27.c +++ b/drivers/gpu/drm/apple/iomfb_v27.c @@ -51,7 +51,8 @@ static_assert(offsetof(struct dcp_swap_v27, brightness_update) == 0x354); static_assert(offsetof(struct dcp_swap_v27, brightness_nits) == 0x35e); static_assert(sizeof(struct dcp_swap_v27) == 0x6e0); -static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { +/* Firmware 27.0, the build Aurora's t6050-gpu table was written for. */ +static const struct dcp_method_entry dcp_methods_27_0[dcpep_num_methods] = { IOMFB_METHOD("A000", dcpep_late_init_signal), IOMFB_METHOD("A027", dcpep_setup_video_limits), IOMFB_METHOD("A136", iomfbep_a131_pmu_service_matched), @@ -95,6 +96,72 @@ static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = { IOMFB_METHOD("A483", dcpep_register_dfb_surface), }; +/* + * Firmware 27.2 renumbered the AP methods: UnifiedPipeline2 grew into + * A400/A401, so IOMobileFramebufferAP moved up by 50 (start_signal A451, + * get_color_remap_mode A477), and set_create_DFB and vi_set_temperature_hint + * up by one (dfb_ready_query is moved with them, inferred, not yet observed). + * The UPPipe A0xx and A1xx IDs are unchanged. These are firmware 27.2's + * method tags: each IOMobileFramebufferAP / UnifiedPipeline2 method has its + * own constant tag. The firmware rejects an unknown tag with a panic + * ("link_rpc_lookup callback not found"). + */ +static const struct dcp_method_entry dcp_methods_27_2[dcpep_num_methods] = { + IOMFB_METHOD("A000", dcpep_late_init_signal), + IOMFB_METHOD("A027", dcpep_setup_video_limits), + IOMFB_METHOD("A136", iomfbep_a131_pmu_service_matched), + IOMFB_METHOD("A137", iomfbep_a132_backlight_service_matched), + IOMFB_METHOD("A390", dcpep_set_create_dfb), + IOMFB_METHOD("A391", iomfbep_a358_vi_set_temperature_hint), + IOMFB_METHOD("A451", dcpep_start_signal), + IOMFB_METHOD("A456", dcpep_swap_start), + IOMFB_METHOD("A458", dcpep_swap_submit), + IOMFB_METHOD("A461", dcpep_set_display_device), + IOMFB_METHOD("A462", dcpep_is_main_display), + IOMFB_METHOD("A463", dcpep_set_digital_out_mode), + IOMFB_METHOD("A473", iomfbep_set_matrix), + IOMFB_METHOD("A477", iomfbep_get_color_remap_mode), + IOMFB_METHOD("A500", dcpep_set_parameter_dcp), + IOMFB_METHOD("A504", dcpep_create_default_fb), + IOMFB_METHOD("A037", dcpep_get_dfb_compression_info), + IOMFB_METHOD("A036", dcpep_get_dfb_info), + IOMFB_METHOD("A103", dcpep_get_dfb_layout), + IOMFB_METHOD("A503", dcpep_get_dfb_state), + IOMFB_METHOD("A104", dcpep_set_dfb_dimensions), + IOMFB_METHOD("A105", dcpep_commit_dfb_info), + IOMFB_METHOD("A035", dcpep_dfb_query), + IOMFB_METHOD("A385", dcpep_dfb_ready_query), + IOMFB_METHOD("A353", dcpep_pipe_query_353), + IOMFB_METHOD("A352", dcpep_pipe_set_352), + IOMFB_METHOD("A465", dcpep_pipe_cfg_415), + IOMFB_METHOD("A033", dcpep_pipe_cfg_031), + IOMFB_METHOD("A464", dcpep_pipe_cfg_414), + IOMFB_METHOD("A539", dcpep_pipe_query_478), + IOMFB_METHOD("A535", dcpep_pipe_query_474), + IOMFB_METHOD("A537", dcpep_pipe_set_476), + IOMFB_METHOD("A478", dcpep_pipe_set_428), + IOMFB_METHOD("A508", dcpep_enable_disable_video_power_savings), + IOMFB_METHOD("A516", dcpep_first_client_open), + IOMFB_METHOD("A517", iomfbep_last_client_close), + IOMFB_METHOD("A525", dcpep_set_display_refresh_properties), + IOMFB_METHOD("A528", dcpep_flush_supports_power), + IOMFB_METHOD("A529", iomfbep_abort_swaps_dcp), + IOMFB_METHOD("A534", dcpep_set_power_state), + IOMFB_METHOD("A533", dcpep_register_dfb_surface), +}; + +const struct dcp_method_entry *iomfb_v27_methods(const char *compat) +{ + if (!strcmp(compat, "27.0.0")) + return dcp_methods_27_0; + if (!strcmp(compat, "27.2.0")) + return dcp_methods_27_2; + return NULL; +} + +/* The template's calls resolve through the table probe chose for this build. */ +#define dcp_methods (dcp->v27_methods) + #define iomfb_modeset_h17p iomfb_modeset_v27 #define iomfb_flush_h17p iomfb_flush_v27 #define iomfb_poweron_h17p iomfb_poweron_v27 @@ -329,6 +396,18 @@ static bool trampoline_tiling_state_v27(struct apple_dcp *dcp, int tag, return true; } +/* + * D113, about an hour after the firmware starts and periodically after: the + * analytics event. A zeroed reply crashes the firmware. + */ +static bool trampoline_analytics_event_v27(struct apple_dcp *dcp, int tag, + void *out, void *in) +{ + trace_iomfb_callback(dcp, tag, "dcpep_cb_analytics_event"); + dcp_dict_passthrough_reply(in, out); + return true; +} + /* Native D115: same16-byte input as D114, boolean/padded4 output. */ static bool trampoline_set_tiling_state_v27(struct apple_dcp *dcp, int tag, void *out, void *in) @@ -362,7 +441,7 @@ static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = { [110] = trampoline_true, [111] = trampoline_create_backlight_service, [112] = trampoline_true, - [113] = trampoline_zero, + [113] = trampoline_analytics_event_v27, [116] = trampoline_nop, [117] = trampoline_zero, [118] = trampoline_zero, @@ -449,7 +528,8 @@ static const struct { [110] = { 0x0, 0x4 }, [111] = { 0x0, 0x4 }, [112] = { 0x0, 0x4 }, - [113] = { 0x1044, 0x1004 }, + [113] = { sizeof(struct dcp_dict_passthrough_req), + sizeof(struct dcp_dict_passthrough_resp) }, [116] = { 0xc, 0x0 }, [117] = { 0x14, 0x4 }, [118] = { 0x3e88, 0x4 }, diff --git a/drivers/gpu/drm/apple/plane.c b/drivers/gpu/drm/apple/plane.c index a0343aa5ec5ec..ec408460ef03a 100644 --- a/drivers/gpu/drm/apple/plane.c +++ b/drivers/gpu/drm/apple/plane.c @@ -144,8 +144,16 @@ static int apple_get_ix_layout(u32 width, u32 height, return 0; } +static bool apple_plane_element_geometry(struct drm_crtc *crtc) +{ + struct apple_dcp *dcp = platform_get_drvdata(to_apple_crtc(crtc)->dcp); + + return dcp_surface_element_geometry(dcp); +} + /* Use exactly the same geometry for admission and surface construction. */ static int apple_plane_get_fb_layout(struct drm_framebuffer *fb, + bool element_geometry, struct apple_plane_layout *planes, u32 *buf_size) { @@ -194,7 +202,9 @@ static int apple_plane_get_fb_layout(struct drm_framebuffer *fb, return -EINVAL; p->image.tile_w = drm_format_info_block_width(fmt, i); p->image.tile_h = drm_format_info_block_height(fmt, i); - p->tile_size = p->image.tile_w * p->image.tile_h; + /* tile_size is bytes per element, not pixels. */ + p->tile_size = element_geometry ? fmt->char_per_block[i] : + p->image.tile_w * p->image.tile_h; p->stride = fb->pitches[i]; break; default: @@ -250,7 +260,9 @@ static int apple_plane_atomic_check(struct drm_plane *plane, if (ret < 0) return ret; - ret = apple_plane_get_fb_layout(new_plane_state->fb, layouts, &buf_size); + ret = apple_plane_get_fb_layout(new_plane_state->fb, + apple_plane_element_geometry(new_plane_state->crtc), + layouts, &buf_size); if (ret) return ret; @@ -416,8 +428,9 @@ static void apple_plane_atomic_update(struct drm_plane *plane, struct drm_framebuffer *fb = base->fb; const struct drm_format_info *fmt = fb->format; + bool element_geometry = apple_plane_element_geometry(base->crtc); - if (WARN_ON_ONCE(apple_plane_get_fb_layout(fb, layouts, &buf_size))) { + if (WARN_ON_ONCE(apple_plane_get_fb_layout(fb, element_geometry, layouts, &buf_size))) { memset(&new_state->surf, 0, sizeof(new_state->surf)); return; } @@ -448,7 +461,7 @@ static void apple_plane_atomic_update(struct drm_plane *plane, .surface_id = plane->base.id, .buf_size = buf_size, - /* Only used for compressed or multiplanar surfaces */ + /* Only used for compressed or multiplanar surfaces before firmware 27 */ .pix_size = 1, .pel_w = 1, .pel_h = 1, @@ -457,6 +470,16 @@ static void apple_plane_atomic_update(struct drm_plane *plane, .has_compr_info = 1, }; + if (element_geometry) { + /* Bytes per element, and the element's width and height in pixels */ + new_state->surf.pix_size = fmt->char_per_block[0]; + new_state->surf.pel_w = drm_format_info_block_width(fmt, 0); + new_state->surf.pel_h = drm_format_info_block_height(fmt, 0); + /* Present only when compression_info describes a compressed plane */ + new_state->surf.has_compr_info = + fb->modifier == DRM_FORMAT_MOD_APPLE_INTERCHANGE; + } + /* Populate plane information for planar formats */ struct dcp_surface *surf = &new_state->surf; for (int i = 0; fb->format->num_planes && i < fb->format->num_planes; i++) {