#include "intel_mocs.h"
#include "intel_lrc.h"
#include "intel_ringbuffer.h"
struct drm_i915_mocs_entry {
u32 control_value;
u16 l3cc_value;
};
struct drm_i915_mocs_table {
u32 size;
const struct drm_i915_mocs_entry *table;
};
#define LE_CACHEABILITY(value) ((value) << 0)
#define LE_TGT_CACHE(value) ((value) << 2)
#define LE_LRUM(value) ((value) << 4)
#define LE_AOM(value) ((value) << 6)
#define LE_RSC(value) ((value) << 7)
#define LE_SCC(value) ((value) << 8)
#define LE_PFM(value) ((value) << 11)
#define LE_SCF(value) ((value) << 14)
#define L3_ESC(value) ((value) << 0)
#define L3_SCC(value) ((value) << 1)
#define L3_CACHEABILITY(value) ((value) << 4)
#define GEN9_NUM_MOCS_ENTRIES 62
#define LE_PAGETABLE 0
#define LE_UC 1
#define LE_WT 2
#define LE_WB 3
#define L3_DIRECT 0
#define L3_UC 1
#define L3_RESERVED 2
#define L3_WB 3
#define LE_TC_PAGETABLE 0
#define LE_TC_LLC 1
#define LE_TC_LLC_ELLC 2
#define LE_TC_LLC_ELLC_ALT 3
static const struct drm_i915_mocs_entry skylake_mocs_table[] = {
[I915_MOCS_UNCACHED] = {
.control_value = LE_CACHEABILITY(LE_UC) |
LE_TGT_CACHE(LE_TC_LLC_ELLC) |
LE_LRUM(0) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
LE_PFM(0) | LE_SCF(0),
.l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_UC),
},
[I915_MOCS_PTE] = {
.control_value = LE_CACHEABILITY(LE_PAGETABLE) |
LE_TGT_CACHE(LE_TC_LLC_ELLC) |
LE_LRUM(3) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
LE_PFM(0) | LE_SCF(0),
.l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB),
},
[I915_MOCS_CACHED] = {
.control_value = LE_CACHEABILITY(LE_WB) |
LE_TGT_CACHE(LE_TC_LLC_ELLC) |
LE_LRUM(3) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
LE_PFM(0) | LE_SCF(0),
.l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB),
},
};
static const struct drm_i915_mocs_entry broxton_mocs_table[] = {
[I915_MOCS_UNCACHED] = {
.control_value = LE_CACHEABILITY(LE_UC) |
LE_TGT_CACHE(LE_TC_LLC_ELLC) |
LE_LRUM(0) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
LE_PFM(0) | LE_SCF(0),
.l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_UC),
},
[I915_MOCS_PTE] = {
.control_value = LE_CACHEABILITY(LE_PAGETABLE) |
LE_TGT_CACHE(LE_TC_LLC_ELLC) |
LE_LRUM(3) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
LE_PFM(0) | LE_SCF(0),
.l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB),
},
[I915_MOCS_CACHED] = {
.control_value = LE_CACHEABILITY(LE_UC) |
LE_TGT_CACHE(LE_TC_LLC_ELLC) |
LE_LRUM(3) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
LE_PFM(0) | LE_SCF(0),
.l3cc_value = L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB),
},
};
static bool get_mocs_settings(struct drm_i915_private *dev_priv,
struct drm_i915_mocs_table *table)
{
bool result = false;
if (IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv) ||
IS_ICELAKE(dev_priv)) {
table->size = ARRAY_SIZE(skylake_mocs_table);
table->table = skylake_mocs_table;
result = true;
} else if (IS_GEN9_LP(dev_priv)) {
table->size = ARRAY_SIZE(broxton_mocs_table);
table->table = broxton_mocs_table;
result = true;
} else {
WARN_ONCE(INTEL_GEN(dev_priv) >= 9,
"Platform that should have a MOCS table does not.\n");
}
if (IS_GEN9(dev_priv)) {
int i;
for (i = 0; i < table->size; i++)
if (WARN_ON(table->table[i].l3cc_value &
(L3_ESC(1) | L3_SCC(0x7))))
return false;
}
return result;
}
static i915_reg_t mocs_register(enum intel_engine_id engine_id, int index)
{
switch (engine_id) {
case RCS:
return GEN9_GFX_MOCS(index);
case VCS:
return GEN9_MFX0_MOCS(index);
case BCS:
return GEN9_BLT_MOCS(index);
case VECS:
return GEN9_VEBOX_MOCS(index);
case VCS2:
return GEN9_MFX1_MOCS(index);
case VCS3:
return GEN11_MFX2_MOCS(index);
default:
MISSING_CASE(engine_id);
return INVALID_MMIO_REG;
}
}
void intel_mocs_init_engine(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
struct drm_i915_mocs_table table;
unsigned int index;
if (!get_mocs_settings(dev_priv, &table))
return;
GEM_BUG_ON(table.size > GEN9_NUM_MOCS_ENTRIES);
for (index = 0; index < table.size; index++)
I915_WRITE(mocs_register(engine->id, index),
table.table[index].control_value);
for (; index < GEN9_NUM_MOCS_ENTRIES; index++)
I915_WRITE(mocs_register(engine->id, index),
table.table[0].control_value);
}
static int emit_mocs_control_table(struct i915_request *rq,
const struct drm_i915_mocs_table *table)
{
enum intel_engine_id engine = rq->engine->id;
unsigned int index;
u32 *cs;
if (WARN_ON(table->size > GEN9_NUM_MOCS_ENTRIES))
return -ENODEV;
cs = intel_ring_begin(rq, 2 + 2 * GEN9_NUM_MOCS_ENTRIES);
if (IS_ERR(cs))
return PTR_ERR(cs);
*cs++ = MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES);
for (index = 0; index < table->size; index++) {
*cs++ = i915_mmio_reg_offset(mocs_register(engine, index));
*cs++ = table->table[index].control_value;
}
for (; index < GEN9_NUM_MOCS_ENTRIES; index++) {
*cs++ = i915_mmio_reg_offset(mocs_register(engine, index));
*cs++ = table->table[0].control_value;
}
*cs++ = MI_NOOP;
intel_ring_advance(rq, cs);
return 0;
}
static inline u32 l3cc_combine(const struct drm_i915_mocs_table *table,
u16 low,
u16 high)
{
return table->table[low].l3cc_value |
table->table[high].l3cc_value << 16;
}
static int emit_mocs_l3cc_table(struct i915_request *rq,
const struct drm_i915_mocs_table *table)
{
unsigned int i;
u32 *cs;
if (WARN_ON(table->size > GEN9_NUM_MOCS_ENTRIES))
return -ENODEV;
cs = intel_ring_begin(rq, 2 + GEN9_NUM_MOCS_ENTRIES);
if (IS_ERR(cs))
return PTR_ERR(cs);
*cs++ = MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES / 2);
for (i = 0; i < table->size/2; i++) {
*cs++ = i915_mmio_reg_offset(GEN9_LNCFCMOCS(i));
*cs++ = l3cc_combine(table, 2 * i, 2 * i + 1);
}
if (table->size & 0x01) {
*cs++ = i915_mmio_reg_offset(GEN9_LNCFCMOCS(i));
*cs++ = l3cc_combine(table, 2 * i, 0);
i++;
}
for (; i < GEN9_NUM_MOCS_ENTRIES / 2; i++) {
*cs++ = i915_mmio_reg_offset(GEN9_LNCFCMOCS(i));
*cs++ = l3cc_combine(table, 0, 0);
}
*cs++ = MI_NOOP;
intel_ring_advance(rq, cs);
return 0;
}
void intel_mocs_init_l3cc_table(struct drm_i915_private *dev_priv)
{
struct drm_i915_mocs_table table;
unsigned int i;
if (!get_mocs_settings(dev_priv, &table))
return;
for (i = 0; i < table.size/2; i++)
I915_WRITE(GEN9_LNCFCMOCS(i), l3cc_combine(&table, 2*i, 2*i+1));
if (table.size & 0x01) {
I915_WRITE(GEN9_LNCFCMOCS(i), l3cc_combine(&table, 2*i, 0));
i++;
}
for (; i < (GEN9_NUM_MOCS_ENTRIES / 2); i++)
I915_WRITE(GEN9_LNCFCMOCS(i), l3cc_combine(&table, 0, 0));
}
int intel_rcs_context_init_mocs(struct i915_request *rq)
{
struct drm_i915_mocs_table t;
int ret;
if (get_mocs_settings(rq->i915, &t)) {
ret = emit_mocs_control_table(rq, &t);
if (ret)
return ret;
ret = emit_mocs_l3cc_table(rq, &t);
if (ret)
return ret;
}
return 0;
}