diff options
Diffstat (limited to 'drivers/gpu/drm/msm/adreno/a6xx_hfi.c')
| -rw-r--r-- | drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 403 |
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; |
