#define PACKAGE_V2_MAX_FW_INFO_ENTRIES 32
#define DMC_V1_MAX_MMIO_COUNT 8
#define DMC_V3_MAX_MMIO_COUNT 20
+#define DMC_V1_MMIO_START_RANGE 0x80000
struct intel_css_header {
/* 0x09 for DMC */
void intel_dmc_load_program(struct drm_i915_private *dev_priv)
{
struct intel_dmc *dmc = &dev_priv->dmc;
- struct dmc_fw_info *dmc_info = &dmc->dmc_info[DMC_FW_MAIN];
- u32 i, fw_size;
+ u32 id, i;
if (!HAS_DMC(dev_priv)) {
drm_err(&dev_priv->drm,
return;
}
- fw_size = dmc_info->dmc_fw_size;
assert_rpm_wakelock_held(&dev_priv->runtime_pm);
preempt_disable();
- for (i = 0; i < fw_size; i++)
- intel_uncore_write_fw(&dev_priv->uncore, DMC_PROGRAM(i),
- dmc_info->payload[i]);
+ for (id = 0; id < DMC_FW_MAX; id++) {
+ for (i = 0; i < dmc->dmc_info[id].dmc_fw_size; i++) {
+ intel_uncore_write_fw(&dev_priv->uncore,
+ DMC_PROGRAM(dmc->dmc_info[id].start_mmioaddr, i),
+ dmc->dmc_info[id].payload[i]);
+ }
+ }
preempt_enable();
- for (i = 0; i < dmc_info->mmio_count; i++) {
- intel_de_write(dev_priv, dmc_info->mmioaddr[i],
- dmc_info->mmiodata[i]);
+ for (id = 0; id < DMC_FW_MAX; id++) {
+ for (i = 0; i < dmc->dmc_info[id].mmio_count; i++) {
+ intel_de_write(dev_priv, dmc->dmc_info[id].mmioaddr[i],
+ dmc->dmc_info[id].mmiodata[i]);
+ }
}
dev_priv->dmc.dc_state = 0;
gen9_set_dc_state_debugmask(dev_priv);
}
+static bool fw_info_matches_stepping(const struct intel_fw_info *fw_info,
+ const struct stepping_info *si)
+{
+ if ((fw_info->substepping == '*' && si->stepping == fw_info->stepping) ||
+ (si->stepping == fw_info->stepping && si->substepping == fw_info->substepping) ||
+ /*
+ * If we don't find a more specific one from above two checks, we
+ * then check for the generic one to be sure to work even with
+ * "broken firmware"
+ */
+ (si->stepping == '*' && si->substepping == fw_info->substepping) ||
+ (fw_info->stepping == '*' && fw_info->substepping == '*'))
+ return true;
+
+ return false;
+}
+
/*
* Search fw_info table for dmc_offset to find firmware binary: num_entries is
* already sanitized.
*/
-static u32 find_dmc_fw_offset(const struct intel_fw_info *fw_info,
+static void dmc_set_fw_offset(struct intel_dmc *dmc,
+ const struct intel_fw_info *fw_info,
unsigned int num_entries,
const struct stepping_info *si,
u8 package_ver)
{
- u32 dmc_offset = DMC_DEFAULT_FW_OFFSET;
- unsigned int i;
+ unsigned int i, id;
+
+ struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc);
for (i = 0; i < num_entries; i++) {
- if (package_ver > 1 && fw_info[i].dmc_id != 0)
- continue;
+ id = package_ver <= 1 ? DMC_FW_MAIN : fw_info[i].dmc_id;
- if (fw_info[i].substepping == '*' &&
- si->stepping == fw_info[i].stepping) {
- dmc_offset = fw_info[i].offset;
- break;
+ if (id >= DMC_FW_MAX) {
+ drm_dbg(&i915->drm, "Unsupported firmware id: %u\n", id);
+ continue;
}
- if (si->stepping == fw_info[i].stepping &&
- si->substepping == fw_info[i].substepping) {
- dmc_offset = fw_info[i].offset;
- break;
- }
+ /* More specific versions come first, so we don't even have to
+ * check for the stepping since we already found a previous FW
+ * for this id.
+ */
+ if (dmc->dmc_info[id].present)
+ continue;
- if (fw_info[i].stepping == '*' &&
- fw_info[i].substepping == '*') {
- /*
- * In theory we should stop the search as generic
- * entries should always come after the more specific
- * ones, but let's continue to make sure to work even
- * with "broken" firmwares. If we don't find a more
- * specific one, then we use this entry
- */
- dmc_offset = fw_info[i].offset;
+ if (fw_info_matches_stepping(&fw_info[i], si)) {
+ dmc->dmc_info[id].present = true;
+ dmc->dmc_info[id].dmc_offset = fw_info[i].offset;
}
}
-
- return dmc_offset;
}
static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
const struct intel_dmc_header_base *dmc_header,
- size_t rem_size)
+ size_t rem_size, u8 dmc_id)
{
struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc);
- struct dmc_fw_info *dmc_info = &dmc->dmc_info[DMC_FW_MAIN];
+ struct dmc_fw_info *dmc_info = &dmc->dmc_info[dmc_id];
unsigned int header_len_bytes, dmc_header_size, payload_size, i;
const u32 *mmioaddr, *mmiodata;
- u32 mmio_count, mmio_count_max;
+ u32 mmio_count, mmio_count_max, start_mmioaddr;
u8 *payload;
BUILD_BUG_ON(ARRAY_SIZE(dmc_info->mmioaddr) < DMC_V3_MAX_MMIO_COUNT ||
mmio_count_max = DMC_V3_MAX_MMIO_COUNT;
/* header_len is in dwords */
header_len_bytes = dmc_header->header_len * 4;
+ start_mmioaddr = v3->start_mmioaddr;
dmc_header_size = sizeof(*v3);
} else if (dmc_header->header_ver == 1) {
const struct intel_dmc_header_v1 *v1 =
mmio_count = v1->mmio_count;
mmio_count_max = DMC_V1_MAX_MMIO_COUNT;
header_len_bytes = dmc_header->header_len;
+ start_mmioaddr = DMC_V1_MMIO_START_RANGE;
dmc_header_size = sizeof(*v1);
} else {
drm_err(&i915->drm, "Unknown DMC fw header version: %u\n",
}
for (i = 0; i < mmio_count; i++) {
- if (mmioaddr[i] < DMC_MMIO_START_RANGE ||
- mmioaddr[i] > DMC_MMIO_END_RANGE) {
- drm_err(&i915->drm, "DMC firmware has wrong mmio address 0x%x\n",
- mmioaddr[i]);
- return 0;
- }
dmc_info->mmioaddr[i] = _MMIO(mmioaddr[i]);
dmc_info->mmiodata[i] = mmiodata[i];
}
dmc_info->mmio_count = mmio_count;
+ dmc_info->start_mmioaddr = start_mmioaddr;
rem_size -= header_len_bytes;
{
struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc);
u32 package_size = sizeof(struct intel_package_header);
- u32 num_entries, max_entries, dmc_offset;
+ u32 num_entries, max_entries;
const struct intel_fw_info *fw_info;
if (rem_size < package_size)
fw_info = (const struct intel_fw_info *)
((u8 *)package_header + sizeof(*package_header));
- dmc_offset = find_dmc_fw_offset(fw_info, num_entries, si,
- package_header->header_ver);
- if (dmc_offset == DMC_DEFAULT_FW_OFFSET) {
- drm_err(&i915->drm, "DMC firmware not supported for %c stepping\n",
- si->stepping);
- return 0;
- }
+ dmc_set_fw_offset(dmc, fw_info, num_entries, si,
+ package_header->header_ver);
/* dmc_offset is in dwords */
- return package_size + dmc_offset * 4;
+ return package_size;
error_truncated:
drm_err(&i915->drm, "Truncated DMC firmware, refusing.\n");
struct intel_dmc *dmc = &dev_priv->dmc;
const struct stepping_info *si = intel_get_stepping_info(dev_priv);
u32 readcount = 0;
- u32 r;
+ u32 r, offset;
+ int id;
if (!fw)
return;
readcount += r;
- /* Extract dmc_header information */
- dmc_header = (struct intel_dmc_header_base *)&fw->data[readcount];
- parse_dmc_fw_header(dmc, dmc_header, fw->size - readcount);
+ for (id = 0; id < DMC_FW_MAX; id++) {
+ if (!dev_priv->dmc.dmc_info[id].present)
+ continue;
+
+ offset = readcount + dmc->dmc_info[id].dmc_offset * 4;
+ if (fw->size - offset < 0) {
+ drm_err(&dev_priv->drm, "Reading beyond the fw_size\n");
+ continue;
+ }
+
+ dmc_header = (struct intel_dmc_header_base *)&fw->data[offset];
+ parse_dmc_fw_header(dmc, dmc_header, fw->size - offset, id);
+ }
}
static void intel_dmc_runtime_pm_get(struct drm_i915_private *dev_priv)