summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/a6xx_hfi.c')
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_hfi.c403
1 files changed, 378 insertions, 25 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
index 2cc83e049613..53cfdf4e6c34 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
@@ -5,6 +5,9 @@
#include <linux/circ_buf.h>
#include <linux/list.h>
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/tcs.h>
+
#include "a6xx_gmu.h"
#include "a6xx_gmu.xml.h"
#include "a6xx_gpu.h"
@@ -18,7 +21,9 @@ static const char * const a6xx_hfi_msg_id[] = {
HFI_MSG_ID(HFI_H2F_MSG_PERF_TABLE),
HFI_MSG_ID(HFI_H2F_MSG_TEST),
HFI_MSG_ID(HFI_H2F_MSG_START),
+ HFI_MSG_ID(HFI_H2F_FEATURE_CTRL),
HFI_MSG_ID(HFI_H2F_MSG_CORE_FW_START),
+ HFI_MSG_ID(HFI_H2F_MSG_TABLE),
HFI_MSG_ID(HFI_H2F_MSG_GX_BW_PERF_VOTE),
HFI_MSG_ID(HFI_H2F_MSG_PREPARE_SLUMBER),
};
@@ -97,16 +102,29 @@ static int a6xx_hfi_queue_write(struct a6xx_gmu *gmu,
return 0;
}
-static int a6xx_hfi_wait_for_ack(struct a6xx_gmu *gmu, u32 id, u32 seqnum,
- u32 *payload, u32 payload_size)
+static int a6xx_hfi_wait_for_msg_interrupt(struct a6xx_gmu *gmu, u32 id, u32 seqnum)
{
- struct a6xx_hfi_queue *queue = &gmu->queues[HFI_RESPONSE_QUEUE];
- u32 val;
int ret;
+ u32 val;
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+
+ do {
+ /* Wait for a response */
+ ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
+ val & A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ, 100, 1000000);
+
+ if (!ret)
+ break;
- /* Wait for a response */
- ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
- val & A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ, 100, 5000);
+ if (completion_done(&a6xx_gpu->base.fault_coredump_done))
+ break;
+
+ /* We may timeout because the GMU is temporarily wedged from
+ * pending faults from the GPU and we are taking a devcoredump.
+ * Wait until the MMU is resumed and try again.
+ */
+ wait_for_completion(&a6xx_gpu->base.fault_coredump_done);
+ } while (true);
if (ret) {
DRM_DEV_ERROR(gmu->dev,
@@ -119,6 +137,19 @@ static int a6xx_hfi_wait_for_ack(struct a6xx_gmu *gmu, u32 id, u32 seqnum,
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR,
A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ);
+ return 0;
+}
+
+static int a6xx_hfi_wait_for_ack(struct a6xx_gmu *gmu, u32 id, u32 seqnum,
+ u32 *payload, u32 payload_size)
+{
+ struct a6xx_hfi_queue *queue = &gmu->queues[HFI_RESPONSE_QUEUE];
+ int ret;
+
+ ret = a6xx_hfi_wait_for_msg_interrupt(gmu, id, seqnum);
+ if (ret)
+ return ret;
+
for (;;) {
struct a6xx_hfi_msg_response resp;
@@ -126,12 +157,18 @@ static int a6xx_hfi_wait_for_ack(struct a6xx_gmu *gmu, u32 id, u32 seqnum,
ret = a6xx_hfi_queue_read(gmu, queue, (u32 *) &resp,
sizeof(resp) >> 2);
- /* If the queue is empty our response never made it */
+ /* If the queue is empty, there may have been previous missed
+ * responses that preceded the response to our packet. Wait
+ * further before we give up.
+ */
if (!ret) {
- DRM_DEV_ERROR(gmu->dev,
- "The HFI response queue is unexpectedly empty\n");
-
- return -ENOENT;
+ ret = a6xx_hfi_wait_for_msg_interrupt(gmu, id, seqnum);
+ if (ret) {
+ DRM_DEV_ERROR(gmu->dev,
+ "The HFI response queue is unexpectedly empty\n");
+ return ret;
+ }
+ continue;
}
if (HFI_HEADER_ID(resp.header) == HFI_F2H_MSG_ERROR) {
@@ -234,11 +271,63 @@ static int a6xx_hfi_send_perf_table_v1(struct a6xx_gmu *gmu)
NULL, 0);
}
+static int a8xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
+{
+ unsigned int num_gx_votes = 3, num_cx_votes = 2;
+ struct a6xx_hfi_table_entry *entry;
+ struct a6xx_hfi_table *tbl;
+ int ret, i;
+ u32 size;
+
+ size = sizeof(*tbl) + (2 * sizeof(tbl->entry[0])) +
+ (gmu->nr_gpu_freqs * num_gx_votes * sizeof(gmu->gx_arc_votes[0])) +
+ (gmu->nr_gmu_freqs * num_cx_votes * sizeof(gmu->cx_arc_votes[0]));
+ tbl = kzalloc(size, GFP_KERNEL);
+ tbl->type = HFI_TABLE_GPU_PERF;
+
+ /* First fill GX votes */
+ entry = &tbl->entry[0];
+ entry->count = gmu->nr_gpu_freqs;
+ entry->stride = num_gx_votes;
+
+ for (i = 0; i < gmu->nr_gpu_freqs; i++) {
+ unsigned int base = i * entry->stride;
+
+ entry->data[base+0] = gmu->gx_arc_votes[i];
+ entry->data[base+1] = gmu->dep_arc_votes[i];
+ entry->data[base+2] = gmu->gpu_freqs[i] / 1000;
+ }
+
+ /* Then fill CX votes */
+ entry = (struct a6xx_hfi_table_entry *)
+ &tbl->entry[0].data[gmu->nr_gpu_freqs * num_gx_votes];
+
+ entry->count = gmu->nr_gmu_freqs;
+ entry->stride = num_cx_votes;
+
+ for (i = 0; i < gmu->nr_gmu_freqs; i++) {
+ unsigned int base = i * entry->stride;
+
+ entry->data[base] = gmu->cx_arc_votes[i];
+ entry->data[base+1] = gmu->gmu_freqs[i] / 1000;
+ }
+
+ ret = a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_TABLE, tbl, size, NULL, 0);
+
+ kfree(tbl);
+ return ret;
+}
+
static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct a6xx_hfi_msg_perf_table msg = { 0 };
int i;
+ if (adreno_is_a8xx(adreno_gpu))
+ return a8xx_hfi_send_perf_table(gmu);
+
msg.num_gpu_levels = gmu->nr_gpu_freqs;
msg.num_gmu_levels = gmu->nr_gmu_freqs;
@@ -257,6 +346,48 @@ static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
NULL, 0);
}
+static void a6xx_generate_bw_table(const struct a6xx_info *info, struct a6xx_gmu *gmu,
+ struct a6xx_hfi_msg_bw_table *msg)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < GMU_MAX_BCMS; i++) {
+ if (!info->bcms[i].name)
+ break;
+ msg->ddr_cmds_addrs[i] = cmd_db_read_addr(info->bcms[i].name);
+ }
+ msg->ddr_cmds_num = i;
+
+ for (i = 0; i < gmu->nr_gpu_bws; ++i)
+ for (j = 0; j < msg->ddr_cmds_num; j++)
+ msg->ddr_cmds_data[i][j] = gmu->gpu_ib_votes[i][j];
+ msg->bw_level_num = gmu->nr_gpu_bws;
+
+ /* Compute the wait bitmask with each BCM having the commit bit */
+ msg->ddr_wait_bitmask = 0;
+ for (j = 0; j < msg->ddr_cmds_num; j++)
+ if (msg->ddr_cmds_data[0][j] & BCM_TCS_CMD_COMMIT_MASK)
+ msg->ddr_wait_bitmask |= BIT(j);
+
+ /*
+ * These are the CX (CNOC) votes - these are used by the GMU
+ * The 'CN0' BCM is used on all targets, and votes are basically
+ * 'off' and 'on' states with first bit to enable the path.
+ */
+
+ msg->cnoc_cmds_addrs[0] = cmd_db_read_addr("CN0");
+ msg->cnoc_cmds_num = 1;
+
+ msg->cnoc_cmds_data[0][0] = BCM_TCS_CMD(true, false, 0, 0);
+ msg->cnoc_cmds_data[1][0] = BCM_TCS_CMD(true, true, 0, BIT(0));
+
+ /* Compute the wait bitmask with each BCM having the commit bit */
+ msg->cnoc_wait_bitmask = 0;
+ for (j = 0; j < msg->cnoc_cmds_num; j++)
+ if (msg->cnoc_cmds_data[0][j] & BCM_TCS_CMD_COMMIT_MASK)
+ msg->cnoc_wait_bitmask |= BIT(j);
+}
+
static void a618_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
{
/* Send a single "off" entry since the 618 GMU doesn't do bus scaling */
@@ -414,6 +545,37 @@ static void a650_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
msg->cnoc_cmds_data[1][0] = 0x60000001;
}
+static void a690_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
+{
+ /*
+ * Send a single "off" entry just to get things running
+ * TODO: bus scaling
+ */
+ msg->bw_level_num = 1;
+
+ msg->ddr_cmds_num = 3;
+ msg->ddr_wait_bitmask = 0x01;
+
+ msg->ddr_cmds_addrs[0] = 0x50004;
+ msg->ddr_cmds_addrs[1] = 0x50000;
+ msg->ddr_cmds_addrs[2] = 0x500ac;
+
+ msg->ddr_cmds_data[0][0] = 0x40000000;
+ msg->ddr_cmds_data[0][1] = 0x40000000;
+ msg->ddr_cmds_data[0][2] = 0x40000000;
+
+ /*
+ * These are the CX (CNOC) votes - these are used by the GMU but the
+ * votes are known and fixed for the target
+ */
+ msg->cnoc_cmds_num = 1;
+ msg->cnoc_wait_bitmask = 0x01;
+
+ msg->cnoc_cmds_addrs[0] = 0x5003c;
+ msg->cnoc_cmds_data[0][0] = 0x40000000;
+ msg->cnoc_cmds_data[1][0] = 0x60000001;
+}
+
static void a660_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
{
/*
@@ -445,6 +607,37 @@ static void a660_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
msg->cnoc_cmds_data[1][0] = 0x60000001;
}
+static void a663_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
+{
+ /*
+ * Send a single "off" entry just to get things running
+ * TODO: bus scaling
+ */
+ msg->bw_level_num = 1;
+
+ msg->ddr_cmds_num = 3;
+ msg->ddr_wait_bitmask = 0x07;
+
+ msg->ddr_cmds_addrs[0] = 0x50004;
+ msg->ddr_cmds_addrs[1] = 0x50000;
+ msg->ddr_cmds_addrs[2] = 0x500b4;
+
+ msg->ddr_cmds_data[0][0] = 0x40000000;
+ msg->ddr_cmds_data[0][1] = 0x40000000;
+ msg->ddr_cmds_data[0][2] = 0x40000000;
+
+ /*
+ * These are the CX (CNOC) votes - these are used by the GMU but the
+ * votes are known and fixed for the target
+ */
+ msg->cnoc_cmds_num = 1;
+ msg->cnoc_wait_bitmask = 0x01;
+
+ msg->cnoc_cmds_addrs[0] = 0x50058;
+ msg->cnoc_cmds_data[0][0] = 0x40000000;
+ msg->cnoc_cmds_data[1][0] = 0x60000001;
+}
+
static void adreno_7c3_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
{
/*
@@ -475,6 +668,88 @@ static void adreno_7c3_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
msg->cnoc_cmds_data[0][0] = 0x40000000;
msg->cnoc_cmds_data[1][0] = 0x60000001;
}
+
+static void a730_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
+{
+ msg->bw_level_num = 12;
+
+ msg->ddr_cmds_num = 3;
+ msg->ddr_wait_bitmask = 0x7;
+
+ msg->ddr_cmds_addrs[0] = cmd_db_read_addr("SH0");
+ msg->ddr_cmds_addrs[1] = cmd_db_read_addr("MC0");
+ msg->ddr_cmds_addrs[2] = cmd_db_read_addr("ACV");
+
+ msg->ddr_cmds_data[0][0] = 0x40000000;
+ msg->ddr_cmds_data[0][1] = 0x40000000;
+ msg->ddr_cmds_data[0][2] = 0x40000000;
+ msg->ddr_cmds_data[1][0] = 0x600002e8;
+ msg->ddr_cmds_data[1][1] = 0x600003d0;
+ msg->ddr_cmds_data[1][2] = 0x60000008;
+ msg->ddr_cmds_data[2][0] = 0x6000068d;
+ msg->ddr_cmds_data[2][1] = 0x6000089a;
+ msg->ddr_cmds_data[2][2] = 0x60000008;
+ msg->ddr_cmds_data[3][0] = 0x600007f2;
+ msg->ddr_cmds_data[3][1] = 0x60000a6e;
+ msg->ddr_cmds_data[3][2] = 0x60000008;
+ msg->ddr_cmds_data[4][0] = 0x600009e5;
+ msg->ddr_cmds_data[4][1] = 0x60000cfd;
+ msg->ddr_cmds_data[4][2] = 0x60000008;
+ msg->ddr_cmds_data[5][0] = 0x60000b29;
+ msg->ddr_cmds_data[5][1] = 0x60000ea6;
+ msg->ddr_cmds_data[5][2] = 0x60000008;
+ msg->ddr_cmds_data[6][0] = 0x60001698;
+ msg->ddr_cmds_data[6][1] = 0x60001da8;
+ msg->ddr_cmds_data[6][2] = 0x60000008;
+ msg->ddr_cmds_data[7][0] = 0x600018d2;
+ msg->ddr_cmds_data[7][1] = 0x60002093;
+ msg->ddr_cmds_data[7][2] = 0x60000008;
+ msg->ddr_cmds_data[8][0] = 0x60001e66;
+ msg->ddr_cmds_data[8][1] = 0x600027e6;
+ msg->ddr_cmds_data[8][2] = 0x60000008;
+ msg->ddr_cmds_data[9][0] = 0x600027c2;
+ msg->ddr_cmds_data[9][1] = 0x6000342f;
+ msg->ddr_cmds_data[9][2] = 0x60000008;
+ msg->ddr_cmds_data[10][0] = 0x60002e71;
+ msg->ddr_cmds_data[10][1] = 0x60003cf5;
+ msg->ddr_cmds_data[10][2] = 0x60000008;
+ msg->ddr_cmds_data[11][0] = 0x600030ae;
+ msg->ddr_cmds_data[11][1] = 0x60003fe5;
+ msg->ddr_cmds_data[11][2] = 0x60000008;
+
+ msg->cnoc_cmds_num = 1;
+ msg->cnoc_wait_bitmask = 0x1;
+
+ msg->cnoc_cmds_addrs[0] = cmd_db_read_addr("CN0");
+ msg->cnoc_cmds_data[0][0] = 0x40000000;
+ msg->cnoc_cmds_data[1][0] = 0x60000001;
+}
+
+static void a740_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
+{
+ msg->bw_level_num = 1;
+
+ msg->ddr_cmds_num = 3;
+ msg->ddr_wait_bitmask = 0x7;
+
+ msg->ddr_cmds_addrs[0] = cmd_db_read_addr("SH0");
+ msg->ddr_cmds_addrs[1] = cmd_db_read_addr("MC0");
+ msg->ddr_cmds_addrs[2] = cmd_db_read_addr("ACV");
+
+ msg->ddr_cmds_data[0][0] = 0x40000000;
+ msg->ddr_cmds_data[0][1] = 0x40000000;
+ msg->ddr_cmds_data[0][2] = 0x40000000;
+
+ /* TODO: add a proper dvfs table */
+
+ msg->cnoc_cmds_num = 1;
+ msg->cnoc_wait_bitmask = 0x1;
+
+ msg->cnoc_cmds_addrs[0] = cmd_db_read_addr("CN0");
+ msg->cnoc_cmds_data[0][0] = 0x40000000;
+ msg->cnoc_cmds_data[1][0] = 0x60000001;
+}
+
static void a6xx_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
{
/* Send a single "off" entry since the 630 GMU doesn't do bus scaling */
@@ -515,29 +790,99 @@ static void a6xx_build_bw_table(struct a6xx_hfi_msg_bw_table *msg)
static int a6xx_hfi_send_bw_table(struct a6xx_gmu *gmu)
{
- struct a6xx_hfi_msg_bw_table msg = { 0 };
+ struct a6xx_hfi_msg_bw_table *msg;
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ const struct a6xx_info *info = adreno_gpu->info->a6xx;
+
+ if (gmu->bw_table)
+ goto send;
+
+ msg = devm_kzalloc(gmu->dev, sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
- if (adreno_is_a618(adreno_gpu))
- a618_build_bw_table(&msg);
+ if (info->bcms && gmu->nr_gpu_bws > 1)
+ a6xx_generate_bw_table(info, gmu, msg);
+ else if (adreno_is_a618(adreno_gpu))
+ a618_build_bw_table(msg);
else if (adreno_is_a619(adreno_gpu))
- a619_build_bw_table(&msg);
+ a619_build_bw_table(msg);
else if (adreno_is_a640_family(adreno_gpu))
- a640_build_bw_table(&msg);
+ a640_build_bw_table(msg);
else if (adreno_is_a650(adreno_gpu))
- a650_build_bw_table(&msg);
+ a650_build_bw_table(msg);
else if (adreno_is_7c3(adreno_gpu))
- adreno_7c3_build_bw_table(&msg);
+ adreno_7c3_build_bw_table(msg);
else if (adreno_is_a660(adreno_gpu))
- a660_build_bw_table(&msg);
+ a660_build_bw_table(msg);
+ else if (adreno_is_a663(adreno_gpu))
+ a663_build_bw_table(msg);
+ else if (adreno_is_a690(adreno_gpu))
+ a690_build_bw_table(msg);
+ else if (adreno_is_a730(adreno_gpu))
+ a730_build_bw_table(msg);
+ else if (adreno_is_a740_family(adreno_gpu))
+ a740_build_bw_table(msg);
else
- a6xx_build_bw_table(&msg);
+ a6xx_build_bw_table(msg);
- return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_BW_TABLE, &msg, sizeof(msg),
+ gmu->bw_table = msg;
+
+send:
+ return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_BW_TABLE, gmu->bw_table, sizeof(*(gmu->bw_table)),
NULL, 0);
}
+static int a6xx_hfi_feature_ctrl_msg(struct a6xx_gmu *gmu, u32 feature, u32 enable, u32 data)
+{
+ struct a6xx_hfi_msg_feature_ctrl msg = {
+ .feature = feature,
+ .enable = enable,
+ .data = data,
+ };
+
+ return a6xx_hfi_send_msg(gmu, HFI_H2F_FEATURE_CTRL, &msg, sizeof(msg), NULL, 0);
+}
+
+#define HFI_FEATURE_IFPC 9
+#define IFPC_LONG_HYST 0x1680
+
+static int a6xx_hfi_enable_ifpc(struct a6xx_gmu *gmu)
+{
+ if (gmu->idle_level != GMU_IDLE_STATE_IFPC)
+ return 0;
+
+ return a6xx_hfi_feature_ctrl_msg(gmu, HFI_FEATURE_IFPC, 1, IFPC_LONG_HYST);
+}
+
+#define HFI_FEATURE_ACD 12
+
+static int a6xx_hfi_enable_acd(struct a6xx_gmu *gmu)
+{
+ struct a6xx_hfi_acd_table *acd_table = &gmu->acd_table;
+ int ret;
+
+ if (!acd_table->enable_by_level)
+ return 0;
+
+ /* Enable ACD feature at GMU */
+ ret = a6xx_hfi_feature_ctrl_msg(gmu, HFI_FEATURE_ACD, 1, 0);
+ if (ret) {
+ DRM_DEV_ERROR(gmu->dev, "Unable to enable ACD (%d)\n", ret);
+ return ret;
+ }
+
+ /* Send ACD table to GMU */
+ ret = a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_ACD, acd_table, sizeof(*acd_table), NULL, 0);
+ if (ret) {
+ DRM_DEV_ERROR(gmu->dev, "Unable to ACD table (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int a6xx_hfi_send_test(struct a6xx_gmu *gmu)
{
struct a6xx_hfi_msg_test msg = { 0 };
@@ -562,13 +907,13 @@ static int a6xx_hfi_send_core_fw_start(struct a6xx_gmu *gmu)
sizeof(msg), NULL, 0);
}
-int a6xx_hfi_set_freq(struct a6xx_gmu *gmu, int index)
+int a6xx_hfi_set_freq(struct a6xx_gmu *gmu, u32 freq_index, u32 bw_index)
{
struct a6xx_hfi_gx_bw_perf_vote_cmd msg = { 0 };
msg.ack_type = 1; /* blocking */
- msg.freq = index;
- msg.bw = 0; /* TODO: bus scaling */
+ msg.freq = freq_index;
+ msg.bw = bw_index;
return a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_GX_BW_PERF_VOTE, &msg,
sizeof(msg), NULL, 0);
@@ -635,6 +980,14 @@ int a6xx_hfi_start(struct a6xx_gmu *gmu, int boot_state)
if (ret)
return ret;
+ ret = a6xx_hfi_enable_acd(gmu);
+ if (ret)
+ return ret;
+
+ ret = a6xx_hfi_enable_ifpc(gmu);
+ if (ret)
+ return ret;
+
ret = a6xx_hfi_send_core_fw_start(gmu);
if (ret)
return ret;