diff options
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/a6xx_hfi.c')
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 221 |
1 files changed, 205 insertions, 16 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c index cb8844ed46b2..53cfdf4e6c34 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c @@ -6,6 +6,7 @@ #include <linux/list.h> #include <soc/qcom/cmd-db.h> +#include <soc/qcom/tcs.h> #include "a6xx_gmu.h" #include "a6xx_gmu.xml.h" @@ -20,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), }; @@ -99,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); - /* 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); + 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; + + 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, @@ -121,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; @@ -128,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) { @@ -236,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; @@ -259,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 */ @@ -664,6 +793,7 @@ static int a6xx_hfi_send_bw_table(struct a6xx_gmu *gmu) 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; @@ -672,7 +802,9 @@ static int a6xx_hfi_send_bw_table(struct a6xx_gmu *gmu) if (!msg) return -ENOMEM; - if (adreno_is_a618(adreno_gpu)) + 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); @@ -702,6 +834,55 @@ send: 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 }; @@ -726,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); @@ -799,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; |
