Skip to content
Merged
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
38 changes: 33 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 All @@ -18,6 +19,8 @@ struct dcp_m4_callback_ops {
int (*event)(void *cookie, const char *name, const void *dictionary, size_t size);
int (*property)(void *cookie, unsigned int method, const void *input, void *output);
int (*memory)(void *cookie, unsigned int method, const void *input, void *output);
/* Idle caching: D117 state, D578/D579 fence, D580 cache-surface release. */
int (*idle)(void *cookie, unsigned int method, const void *input, void *output);
};

struct dcp_m4_callback_shape {
Expand All @@ -33,16 +36,18 @@ 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}, {578, 4, 4}, {579, 0, 0}, {580, 0, 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 +141,29 @@ 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, swap_complete_intent,
* enable_backlight_message_ap_gated: notifications.
*/
case 2: case 8: case 592: case 594:
return 0;
/*
* set_idle_caching_state_ap, idle_fence_create, idle_fence_complete,
* idle_surface_release.
*/
case 117: case 578: case 579: case 580:
if (!ops->idle)
return -EOPNOTSUPP;
return dcp_m4_callback_result(ops->idle(cookie, method, input, output));
/*
* 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
16 changes: 16 additions & 0 deletions drivers/gpu/drm/apple/dcp-m4-frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,22 @@ static inline int dcp_m4_swap_encode_full(void *output, size_t length, u32 swap_
return 0;
}

/* Swap intent that registers surface 0 as the idle-cache surface. */
#define DCP_M4_SWAP_INTENT_IDLE_CACHE 8

/*
* Retarget an encoded present: the DCP registers rather than presents its
* sole surface, which must be a distinct object (surface_id other than
* the primary's 1) from every surface on screen.
*/
static inline void dcp_m4_swap_set_intent(void *output, u32 intent, u32 surface_id)
{
u8 *data = output;

put_unaligned_le32(intent, data + dcp_m4_pf->swap_intent);
put_unaligned_le32(surface_id, data + 0x9c);
}

static inline int dcp_m4_swap_encode_checked(void *output, size_t length, u32 swap_id,
const struct dcp_m4_frame *frame,
bool brightness_change, u32 nits,
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/apple/dcp-m4-profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct dcp_m4_profile {
u32 swap_nullable; /* ten nullable surface bytes */
u32 swap_tail; /* final two bytes */
u32 swap_nits; /* binary64 brightness */
u32 swap_intent; /* swap intent, 0 for a present */
u32 disabled_color; /* background with no surface */
u16 done_method; /* swap completion callback */
u32 done_size;
Expand All @@ -28,6 +29,7 @@ static const struct dcp_m4_profile dcp_m4_profile_j713 = {
.panel_width = 2560, .panel_height = 1664, .boot_stride = 10240,
.swap_size = 0x1bd8, .swap_surface = 0x588, .swap_dva = 0xe38,
.swap_nullable = 0x1bcb, .swap_tail = 0x1bd6, .swap_nits = 0x35e,
.swap_intent = 0x17c,
.disabled_color = 0xff000000,
.done_method = 590, .done_size = 0x730, .open_method = 457,
.brightness_max = 500, .brightness_default = 120,
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
34 changes: 29 additions & 5 deletions drivers/gpu/drm/apple/dcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -6386,9 +6386,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;
iomfb_v14_7_backlight_suspend(dcp);
Expand Down Expand Up @@ -6469,7 +6477,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 All @@ -6492,10 +6500,26 @@ static int dcp_platform_resume(struct device *dev)
return 0;
}

/*
* Hibernation: the T8132 power-down keeps the retained session, which a
* hibernation image cannot carry across the boot that restores it.
*/
static int dcp_platform_freeze(struct device *dev)
{
if (iomfb_is_m4(dev_get_drvdata(dev)))
return -EBUSY;
return dcp_platform_suspend(dev);
}

static const struct dev_pm_ops dcp_platform_pm_ops = {
.prepare = pm_sleep_ptr(dcp_platform_prepare),
.complete = pm_sleep_ptr(dcp_platform_complete),
SYSTEM_SLEEP_PM_OPS(dcp_platform_suspend, dcp_platform_resume)
.suspend = pm_sleep_ptr(dcp_platform_suspend),
.resume = pm_sleep_ptr(dcp_platform_resume),
.freeze = pm_sleep_ptr(dcp_platform_freeze),
.thaw = pm_sleep_ptr(dcp_platform_resume),
.poweroff = pm_sleep_ptr(dcp_platform_freeze),
.restore = pm_sleep_ptr(dcp_platform_resume),
};

static const struct apple_dcp_hw_data apple_dcp_hw_t6020 = {
Expand Down
Loading