Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ properties:
resets:
maxItems: 1

apple,hold-rx-in-sleep:
description:
Messages from this coprocessor need not wake the system. The OS may
leave them in the receive FIFO while the system sleeps and handle
them at resume.
type: boolean

power-domains:
minItems: 1
maxItems: 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ allOf:
then:
properties:
apple,firmware-owned: false
apple,sleep-retained: false
apple,always-on: false
apple,min-state: false
apple,force-disable: false
Expand Down Expand Up @@ -106,6 +107,13 @@ properties:
coprocessors' CPU domains.
type: boolean

apple,sleep-retained:
description:
A coprocessor keeps running in this domain across system sleep with no
system-sleep callbacks of its own. The OS keeps the domain on during
system sleep. Runtime power management is unaffected.
type: boolean

apple,firmware-owned:
description:
The boot loader hands over active hardware whose power domain remains
Expand Down
4 changes: 4 additions & 0 deletions arch/arm64/boot/dts/apple/t8132-j713-gpu.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
/* ASCWrap v6 contains the same mailbox aperture used by ASC v4. */
compatible = "apple,t8132-asc-mailbox", "apple,asc-mailbox-v4";
reg = <0x3 0x02608000 0x0 0x4000>;
/* Idle ring-service kicks need not wake the system. */
apple,hold-rx-in-sleep;
interrupt-parent = <&aic>;
interrupts = <AIC_IRQ 993 IRQ_TYPE_LEVEL_HIGH>,
<AIC_IRQ 994 IRQ_TYPE_LEVEL_HIGH>,
Expand All @@ -95,6 +97,8 @@
&ps_gfx {
status = "okay";
power-domains = <&gfx_tvm>;
/* The G16 firmware stays live across suspend-to-idle. */
apple,sleep-retained;
};

/* The existing report provider owns readiness for shared PMP commands. */
Expand Down
27 changes: 22 additions & 5 deletions drivers/gpu/drm/apple/dcp-m4-callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include "dcp-m4-profile.h"
Expand Down Expand Up @@ -33,16 +34,17 @@ static inline int dcp_m4_callback_result(int ret)

/* Exact qualified J713 shapes. An absent behavior hook is failure, never an ACK. */
static const struct dcp_m4_callback_shape dcp_m4_callbacks[] = {
{0, 0, 4}, {1, 0, 4}, {3, 4, 60}, {6, 84, 80},
{100, 0, 0}, {101, 0, 4}, {102, 68, 0}, {104, 68, 0}, {107, 64, 0},
{0, 0, 4}, {1, 0, 4}, {2, 0, 0}, {3, 4, 60}, {6, 84, 80}, {8, 4, 0},
{100, 0, 0}, {101, 0, 4}, {102, 68, 0}, {104, 68, 0}, {106, 0, 4}, {107, 64, 0},
{108, 0, 4}, {109, 0, 4}, {110, 0, 4}, {111, 0, 4}, {112, 0, 4}, {113, 0, 4},
{114, 4164, 4100}, {121, 0, 4}, {122, 0, 4}, {123, 0, 4},
{114, 4164, 4100}, {117, 12, 0}, {121, 0, 4}, {122, 0, 4}, {123, 0, 4},
{125, 100, 36}, {127, 4, 4}, {128, 4104, 4}, {129, 64, 4},
{201, 12, 16}, {206, 0, 4}, {207, 0, 4}, {300, 16, 0},
{201, 12, 16}, {203, 0, 4}, {206, 0, 4}, {207, 0, 4}, {209, 0, 8}, {300, 16, 0},
{400, 76, 3076}, {401, 80, 12}, {406, 72, 0}, {408, 8, 8},
{413, 4168, 4}, {414, 80, 4}, {415, 76, 4}, {451, 20, 28}, {454, 4, 4},
{552, 4164, 4}, {561, 4164, 4}, {563, 76, 4}, {565, 72, 4}, {567, 128, 4},
{575, 88, 76}, {576, 4, 0}, {582, 8, 4}, {588, 0, 0}, {590, 1840, 0}, {599, 0, 0},
{572, 4, 4}, {575, 88, 76}, {576, 4, 0}, {582, 8, 4}, {588, 0, 0}, {590, 1840, 0},
{592, 20, 0}, {594, 4, 0}, {599, 0, 0}, {700, 4, 4},
};

static inline int dcp_m4_callback_method(u32 tag)
Expand Down Expand Up @@ -136,6 +138,21 @@ static inline int dcp_m4_callback_dispatch(const struct dcp_m4_callback_ops *ops
return 0;
case 300: case 406: case 576: case 588:
return 0;
/*
* will_power_off_signal, set_power_trace_enable, set_idle_caching_state_ap,
* swap_complete_intent, enable_backlight_message_ap_gated: notifications.
*/
case 2: case 8: case 117: case 592: case 594:
return 0;
/*
* acknowledgeSetPowerState, powerUpDART, set_kernel_power_assert: success;
* aot_supported_peek: false.
*/
case 106: case 572: case 700: case 203:
return 0;
case 209: /* get_calendar_time_ms */
put_unaligned_le64(ktime_get_real_ns() / NSEC_PER_MSEC, out);
return 0;
case 122: case 123: case 582:
return 0; /* False, not an unimplemented service returning success. */
case 101:
Expand Down
26 changes: 23 additions & 3 deletions drivers/gpu/drm/apple/dcp-m4-protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,36 @@ static inline int dcp_m4_decode_word_ext(u64 word, struct dcp_m4_packet *packet)
return 0;
}

/*
* While an AP call is outstanding on stream 1 (bit 10, the older contexts'
* out-of-band +4), its reply and callbacks come on stream 1 as well, besides
* any stream-0 traffic.
*/
static inline int dcp_m4_decode_word_oob(u64 word, struct dcp_m4_packet *packet)
{
u8 stream = (word >> 10) & 3;
int ret;

if (!stream)
return dcp_m4_decode_word(word, packet);
if (stream != 1)
return -EPROTO;
ret = dcp_m4_decode_word(word & ~0x400ULL, packet);
if (!ret)
packet->stream = 1;
return ret;
}

/*
* Replies require their pending request record, not a new packet location.
* Only callbacks may name a nonzero stream (external profile); the internal
* decoder never produces one.
* Callbacks may name a nonzero stream (external profile, or stream 1 during
* an out-of-band call); AP calls use stream 0, or stream 1 out of band.
*/
static inline int dcp_m4_packet_offset(const struct dcp_m4_packet *packet, bool callback)
{
unsigned int base;

if (packet->reply || (callback ? packet->stream >= 4 : packet->stream) ||
if (packet->reply || (callback ? packet->stream >= 4 : packet->stream > 1) ||
(packet->callback_flag && (!callback || !packet->remote)) ||
packet->offset % DCP_M4_ALIGNMENT ||
packet->offset >= DCP_M4_STREAM_SIZE || packet->length < DCP_M4_PACKET_HEADER ||
Expand Down
50 changes: 39 additions & 11 deletions drivers/gpu/drm/apple/dcp-m4-transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ struct dcp_m4_transaction {
unsigned int callbacks;
int error;
bool external; /* DCPEXT profile: streams 0..3 and any flagged callback route */
bool oob; /* an AP call is outstanding on stream 1 */
};

static inline int dcp_m4_transaction_decode(const struct dcp_m4_transaction *state, u64 word,
struct dcp_m4_packet *packet)
{
return state->external ? dcp_m4_decode_word_ext(word, packet) :
dcp_m4_decode_word(word, packet);
if (state->external)
return dcp_m4_decode_word_ext(word, packet);
return state->oob ? dcp_m4_decode_word_oob(word, packet) :
dcp_m4_decode_word(word, packet);
}

static inline int dcp_m4_transaction_fail(struct dcp_m4_transaction *state, int error)
Expand All @@ -59,14 +62,15 @@ static inline bool dcp_m4_tag(u32 tag, u8 direction)
}

/* Prepare only; the caller publishes with its DMA barrier and mailbox send. */
static inline int dcp_m4_call_begin(struct dcp_m4_transaction *state, void *rpc,
size_t rpc_size, u32 tag, bool remote,
const void *input, u32 input_size, u32 output_size,
u64 *word)
/* Stream 1 is out of band: top-level calls only, such as setPowerState. */
static inline int dcp_m4_call_begin_stream(struct dcp_m4_transaction *state, void *rpc,
size_t rpc_size, u32 tag, u8 stream, bool remote,
const void *input, u32 input_size, u32 output_size,
u64 *word)
{
struct dcp_m4_packet packet = {
.tag = tag, .input_size = input_size, .output_size = output_size,
.remote = remote,
.remote = remote, .stream = stream,
};
const struct dcp_m4_packet *outer = NULL;
u8 *data;
Expand All @@ -77,6 +81,8 @@ static inline int dcp_m4_call_begin(struct dcp_m4_transaction *state, void *rpc,
if (!rpc || rpc_size != DCP_M4_RPC_SIZE || !word || (input_size && !input) ||
!dcp_m4_tag(tag, 'A') || state->calls >= DCP_M4_CALL_DEPTH)
return dcp_m4_transaction_fail(state, -EINVAL);
if (stream > 1 || (stream && (state->calls || state->callbacks || state->external)))
return dcp_m4_transaction_fail(state, -EINVAL);
if (state->calls) {
struct dcp_m4_call *pending = &state->call[state->calls - 1];

Expand All @@ -88,9 +94,16 @@ static inline int dcp_m4_call_begin(struct dcp_m4_transaction *state, void *rpc,
if (state->callbacks &&
remote != state->callback[state->callbacks - 1].packet.remote)
return dcp_m4_transaction_fail(state, -EPROTO);
/* AP calls use stream 0; a callback on another stream cannot recurse. */
if (state->callbacks && state->callback[state->callbacks - 1].packet.stream)
return dcp_m4_transaction_fail(state, -EOPNOTSUPP);
/*
* AP calls use stream 0; a callback on another stream cannot recurse,
* except that calls nested in an out-of-band callback stay out of band.
*/
if (state->callbacks && state->callback[state->callbacks - 1].packet.stream) {
if (!state->oob || state->callback[state->callbacks - 1].packet.stream != 1)
return dcp_m4_transaction_fail(state, -EOPNOTSUPP);
stream = 1;
packet.stream = stream;
}
ret = dcp_m4_packet_size(input_size, output_size, &packet.length);
if (ret)
return dcp_m4_transaction_fail(state, ret);
Expand Down Expand Up @@ -119,11 +132,22 @@ static inline int dcp_m4_call_begin(struct dcp_m4_transaction *state, void *rpc,
state->call[state->calls++] = (struct dcp_m4_call) {
.packet = packet, .callbacks = state->callbacks,
};
*word = 2 | (remote ? 0x100 : 0) | ((u64)packet.offset << 16) |
*word = 2 | (remote ? 0x100 : 0) | ((u64)stream << 10) | ((u64)packet.offset << 16) |
((u64)packet.length << 32);
if (stream)
state->oob = true;
return 0;
}

static inline int dcp_m4_call_begin(struct dcp_m4_transaction *state, void *rpc,
size_t rpc_size, u32 tag, bool remote,
const void *input, u32 input_size, u32 output_size,
u64 *word)
{
return dcp_m4_call_begin_stream(state, rpc, rpc_size, tag, 0, remote, input,
input_size, output_size, word);
}

/* Firmware may write output, but must not change the owned packet header. */
static inline int dcp_m4_owned_packet(const void *rpc, const struct dcp_m4_packet *owned,
bool callback)
Expand Down Expand Up @@ -160,6 +184,7 @@ static inline int dcp_m4_call_reply(struct dcp_m4_transaction *state, const void
return dcp_m4_transaction_fail(state, ret);
call = &state->call[state->calls - 1];
if (!reply.reply || reply.remote != call->packet.remote ||
reply.stream != call->packet.stream ||
state->callbacks != call->callbacks || output_size != call->packet.output_size ||
(output_size && !output))
return dcp_m4_transaction_fail(state, -EPROTO);
Expand All @@ -169,6 +194,9 @@ static inline int dcp_m4_call_reply(struct dcp_m4_transaction *state, const void
if (output_size)
memcpy(output, (const u8 *)rpc + offset + DCP_M4_PACKET_HEADER +
call->packet.input_size, output_size);
/* The out-of-band call itself, not a call nested in its callbacks. */
if (call->packet.stream && state->calls == 1)
state->oob = false;
state->calls--;
return 0;
}
Expand Down
16 changes: 12 additions & 4 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5998,9 +5998,17 @@ static int dcp_platform_suspend(struct device *dev)
if (dcp_takeover_was_aborted(dcp))
return -EBUSY;

/* The retained T8132 session has no qualified system-sleep handshake. */
if (iomfb_is_m4(dcp))
return -EBUSY;
/*
* T8132: setPowerState powers the display down; the coprocessor keeps
* running and its power domains stay on, as below.
*/
if (iomfb_is_m4(dcp)) {
int ret = iomfb_m4_suspend(dcp);

if (!ret)
device_set_wakeup_path(dev);
return ret;
}
if (READ_ONCE(dcp->ext_dead))
return 0;

Expand Down Expand Up @@ -6080,7 +6088,7 @@ static int dcp_platform_resume(struct device *dev)
return 0;

if (iomfb_is_m4(dcp))
return iomfb_m4_health(dcp);
return iomfb_m4_resume(dcp);
if (READ_ONCE(dcp->ext_dead))
return 0;

Expand Down
Loading