drm/i915/guc: Prepare to handle messages from CT RECV buffer

GuC can respond to our commands not only by updating SEND buffer
descriptor, but can also send a response message over RECV buffer.
Guc can also send unsolicited request messages over RECV buffer.
Let's start reading those messages and make placeholders
for actual response/request handlers.

v2: misc improvements (Michal)
v3: change response detection (Michal)
    invalid status is protocol error (Michal)
v4: rebase
v5: fix checkpatch (Michel)
    don't use fields before check (Jani)
    add some documentation (Michal)

Signed-off-by: Michal Wajdeczko <michal.wajdeczko@intel.com>
Cc: Oscar Mateo <oscar.mateo@intel.com>
Cc: Michel Thierry <michel.thierry@intel.com>
Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Reviewed-by: Michel Thierry <michel.thierry@intel.com> # 4.5
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Link: https://patchwork.freedesktop.org/patch/msgid/20180326194829.58836-7-michal.wajdeczko@intel.com
This commit is contained in:
Michal Wajdeczko 2018-03-26 19:48:23 +00:00 committed by Chris Wilson
parent 769bfbf943
commit 1d40709600

View File

@ -273,6 +273,24 @@ static u32 ctch_get_next_fence(struct intel_guc_ct_channel *ctch)
return ++ctch->next_fence;
}
/**
* DOC: CTB Host to GuC request
*
* Format of the CTB Host to GuC request message is as follows::
*
* +------------+---------+---------+---------+---------+
* | msg[0] | [1] | [2] | ... | [n-1] |
* +------------+---------+---------+---------+---------+
* | MESSAGE | MESSAGE PAYLOAD |
* + HEADER +---------+---------+---------+---------+
* | | 0 | 1 | ... | n |
* +============+=========+=========+=========+=========+
* | len >= 1 | FENCE | request specific data |
* +------+-----+---------+---------+---------+---------+
*
* ^-----------------len-------------------^
*/
static int ctb_write(struct intel_guc_ct_buffer *ctb,
const u32 *action,
u32 len /* in dwords */,
@ -305,7 +323,8 @@ static int ctb_write(struct intel_guc_ct_buffer *ctb,
if (unlikely(used + len + 1 >= size))
return -ENOSPC;
/* Write the message. The format is the following:
/*
* Write the message. The format is the following:
* DW0: header (including action code)
* DW1: fence
* DW2+: action data
@ -427,6 +446,167 @@ static int intel_guc_send_ct(struct intel_guc *guc, const u32 *action, u32 len,
return ret;
}
static inline unsigned int ct_header_get_len(u32 header)
{
return (header >> GUC_CT_MSG_LEN_SHIFT) & GUC_CT_MSG_LEN_MASK;
}
static inline unsigned int ct_header_get_action(u32 header)
{
return (header >> GUC_CT_MSG_ACTION_SHIFT) & GUC_CT_MSG_ACTION_MASK;
}
static inline bool ct_header_is_response(u32 header)
{
return ct_header_get_action(header) == INTEL_GUC_ACTION_DEFAULT;
}
static int ctb_read(struct intel_guc_ct_buffer *ctb, u32 *data)
{
struct guc_ct_buffer_desc *desc = ctb->desc;
u32 head = desc->head / 4; /* in dwords */
u32 tail = desc->tail / 4; /* in dwords */
u32 size = desc->size / 4; /* in dwords */
u32 *cmds = ctb->cmds;
s32 available; /* in dwords */
unsigned int len;
unsigned int i;
GEM_BUG_ON(desc->size % 4);
GEM_BUG_ON(desc->head % 4);
GEM_BUG_ON(desc->tail % 4);
GEM_BUG_ON(tail >= size);
GEM_BUG_ON(head >= size);
/* tail == head condition indicates empty */
available = tail - head;
if (unlikely(available == 0))
return -ENODATA;
/* beware of buffer wrap case */
if (unlikely(available < 0))
available += size;
GEM_BUG_ON(available < 0);
data[0] = cmds[head];
head = (head + 1) % size;
/* message len with header */
len = ct_header_get_len(data[0]) + 1;
if (unlikely(len > (u32)available)) {
DRM_ERROR("CT: incomplete message %*phn %*phn %*phn\n",
4, data,
4 * (head + available - 1 > size ?
size - head : available - 1), &cmds[head],
4 * (head + available - 1 > size ?
available - 1 - size + head : 0), &cmds[0]);
return -EPROTO;
}
for (i = 1; i < len; i++) {
data[i] = cmds[head];
head = (head + 1) % size;
}
desc->head = head * 4;
return 0;
}
/**
* DOC: CTB GuC to Host response
*
* Format of the CTB GuC to Host response message is as follows::
*
* +------------+---------+---------+---------+---------+---------+
* | msg[0] | [1] | [2] | [3] | ... | [n-1] |
* +------------+---------+---------+---------+---------+---------+
* | MESSAGE | MESSAGE PAYLOAD |
* + HEADER +---------+---------+---------+---------+---------+
* | | 0 | 1 | 2 | ... | n |
* +============+=========+=========+=========+=========+=========+
* | len >= 2 | FENCE | STATUS | response specific data |
* +------+-----+---------+---------+---------+---------+---------+
*
* ^-----------------------len-----------------------^
*/
static int ct_handle_response(struct intel_guc_ct *ct, const u32 *msg)
{
u32 header = msg[0];
u32 len = ct_header_get_len(header);
u32 msglen = len + 1; /* total message length including header */
u32 fence;
u32 status;
GEM_BUG_ON(!ct_header_is_response(header));
/* Response payload shall at least include fence and status */
if (unlikely(len < 2)) {
DRM_ERROR("CT: corrupted response %*phn\n", 4 * msglen, msg);
return -EPROTO;
}
fence = msg[1];
status = msg[2];
/* Format of the status follows RESPONSE message */
if (unlikely(!INTEL_GUC_MSG_IS_RESPONSE(status))) {
DRM_ERROR("CT: corrupted response %*phn\n", 4 * msglen, msg);
return -EPROTO;
}
/* XXX */
return 0;
}
static int ct_handle_request(struct intel_guc_ct *ct, const u32 *msg)
{
u32 header = msg[0];
GEM_BUG_ON(ct_header_is_response(header));
/* XXX */
return 0;
}
static void ct_process_host_channel(struct intel_guc_ct *ct)
{
struct intel_guc_ct_channel *ctch = &ct->host_channel;
struct intel_guc_ct_buffer *ctb = &ctch->ctbs[CTB_RECV];
u32 msg[GUC_CT_MSG_LEN_MASK + 1]; /* one extra dw for the header */
int err = 0;
if (!ctch_is_open(ctch))
return;
do {
err = ctb_read(ctb, msg);
if (err)
break;
if (ct_header_is_response(msg[0]))
err = ct_handle_response(ct, msg);
else
err = ct_handle_request(ct, msg);
} while (!err);
if (GEM_WARN_ON(err == -EPROTO)) {
DRM_ERROR("CT: corrupted message detected!\n");
ctb->desc->is_in_error = 1;
}
}
/*
* When we're communicating with the GuC over CT, GuC uses events
* to notify us about new messages being posted on the RECV buffer.
*/
static void intel_guc_to_host_event_handler_ct(struct intel_guc *guc)
{
struct intel_guc_ct *ct = &guc->ct;
ct_process_host_channel(ct);
}
/**
* intel_guc_ct_enable - Enable buffer based command transport.
* @ct: pointer to CT struct
@ -450,6 +630,7 @@ int intel_guc_ct_enable(struct intel_guc_ct *ct)
/* Switch into cmd transport buffer based send() */
guc->send = intel_guc_send_ct;
guc->handler = intel_guc_to_host_event_handler_ct;
DRM_INFO("CT: %s\n", enableddisabled(true));
return 0;
}
@ -475,5 +656,6 @@ void intel_guc_ct_disable(struct intel_guc_ct *ct)
/* Disable send */
guc->send = intel_guc_send_nop;
guc->handler = intel_guc_to_host_event_handler_nop;
DRM_INFO("CT: %s\n", enableddisabled(false));
}