drm/i915/xelpd: Pipe A DMC plugging
This patch adds Pipe A plumbing to the already existing parsing and loading functions which is taken care of in the prep patches. Adding MAX_DMC_FW to keep track for both Main and Pipe A DMC while loading the respective blobs. Also adding present field in dmc_info. s/find_dmc_fw_offset/csr_set_dmc_fw_offset. While at it add fw_info_matches_stepping() helper. CSR_PROGRAM() should now take the starting address of the particular blob (Main or Pipe) and not hardcode it. v2: Add dmc_offset and start_mmioaddr fields for dmc_info struct. v3: Add a missing corner cases of stepping-substepping combination in fw_info_matches_stepping() helper. v4: Add macro for start_mmioaddr for V1 package. Simplify code in dmc_set_fw_offset (Lucas) Cc: Souza, Jose <jose.souza@intel.com> Cc: Lucas De Marchi <lucas.demarchi@intel.com> Signed-off-by: Anusha Srivatsa <anusha.srivatsa@intel.com> Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20210621191415.29823-3-anusha.srivatsa@intel.com
This commit is contained in:
parent
451e05e202
commit
3d5928a168
5 changed files with 87 additions and 63 deletions
|
@ -544,6 +544,8 @@ static int i915_dmc_info(struct seq_file *m, void *unused)
|
||||||
|
|
||||||
seq_printf(m, "fw loaded: %s\n", yesno(intel_dmc_has_payload(dev_priv)));
|
seq_printf(m, "fw loaded: %s\n", yesno(intel_dmc_has_payload(dev_priv)));
|
||||||
seq_printf(m, "path: %s\n", dmc->fw_path);
|
seq_printf(m, "path: %s\n", dmc->fw_path);
|
||||||
|
seq_printf(m, "Pipe A fw support: %s\n", yesno(INTEL_GEN(dev_priv) >= 12));
|
||||||
|
seq_printf(m, "Pipe A fw loaded: %s\n", yesno(dmc->dmc_info[DMC_FW_PIPEA].payload));
|
||||||
|
|
||||||
if (!intel_dmc_has_payload(dev_priv))
|
if (!intel_dmc_has_payload(dev_priv))
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -582,7 +584,7 @@ static int i915_dmc_info(struct seq_file *m, void *unused)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
seq_printf(m, "program base: 0x%08x\n",
|
seq_printf(m, "program base: 0x%08x\n",
|
||||||
intel_de_read(dev_priv, DMC_PROGRAM(0)));
|
intel_de_read(dev_priv, DMC_PROGRAM(dmc->dmc_info[DMC_FW_MAIN].start_mmioaddr, 0)));
|
||||||
seq_printf(m, "ssp base: 0x%08x\n",
|
seq_printf(m, "ssp base: 0x%08x\n",
|
||||||
intel_de_read(dev_priv, DMC_SSP_BASE));
|
intel_de_read(dev_priv, DMC_SSP_BASE));
|
||||||
seq_printf(m, "htp: 0x%08x\n", intel_de_read(dev_priv, DMC_HTP_SKL));
|
seq_printf(m, "htp: 0x%08x\n", intel_de_read(dev_priv, DMC_HTP_SKL));
|
||||||
|
|
|
@ -961,8 +961,9 @@ static void bxt_disable_dc9(struct drm_i915_private *dev_priv)
|
||||||
static void assert_dmc_loaded(struct drm_i915_private *dev_priv)
|
static void assert_dmc_loaded(struct drm_i915_private *dev_priv)
|
||||||
{
|
{
|
||||||
drm_WARN_ONCE(&dev_priv->drm,
|
drm_WARN_ONCE(&dev_priv->drm,
|
||||||
!intel_de_read(dev_priv, DMC_PROGRAM(0)),
|
!intel_de_read(dev_priv,
|
||||||
"DMC program storage start is NULL\n");
|
DMC_PROGRAM(dev_priv->dmc.dmc_info[DMC_FW_MAIN].start_mmioaddr, 0)),
|
||||||
|
"DMC program storage start is NULL\n");
|
||||||
drm_WARN_ONCE(&dev_priv->drm, !intel_de_read(dev_priv, DMC_SSP_BASE),
|
drm_WARN_ONCE(&dev_priv->drm, !intel_de_read(dev_priv, DMC_SSP_BASE),
|
||||||
"DMC SSP Base Not fine\n");
|
"DMC SSP Base Not fine\n");
|
||||||
drm_WARN_ONCE(&dev_priv->drm, !intel_de_read(dev_priv, DMC_HTP_SKL),
|
drm_WARN_ONCE(&dev_priv->drm, !intel_de_read(dev_priv, DMC_HTP_SKL),
|
||||||
|
|
|
@ -96,6 +96,7 @@ MODULE_FIRMWARE(BXT_DMC_PATH);
|
||||||
#define PACKAGE_V2_MAX_FW_INFO_ENTRIES 32
|
#define PACKAGE_V2_MAX_FW_INFO_ENTRIES 32
|
||||||
#define DMC_V1_MAX_MMIO_COUNT 8
|
#define DMC_V1_MAX_MMIO_COUNT 8
|
||||||
#define DMC_V3_MAX_MMIO_COUNT 20
|
#define DMC_V3_MAX_MMIO_COUNT 20
|
||||||
|
#define DMC_V1_MMIO_START_RANGE 0x80000
|
||||||
|
|
||||||
struct intel_css_header {
|
struct intel_css_header {
|
||||||
/* 0x09 for DMC */
|
/* 0x09 for DMC */
|
||||||
|
@ -317,8 +318,7 @@ static void gen9_set_dc_state_debugmask(struct drm_i915_private *dev_priv)
|
||||||
void intel_dmc_load_program(struct drm_i915_private *dev_priv)
|
void intel_dmc_load_program(struct drm_i915_private *dev_priv)
|
||||||
{
|
{
|
||||||
struct intel_dmc *dmc = &dev_priv->dmc;
|
struct intel_dmc *dmc = &dev_priv->dmc;
|
||||||
struct dmc_fw_info *dmc_info = &dmc->dmc_info[DMC_FW_MAIN];
|
u32 id, i;
|
||||||
u32 i, fw_size;
|
|
||||||
|
|
||||||
if (!HAS_DMC(dev_priv)) {
|
if (!HAS_DMC(dev_priv)) {
|
||||||
drm_err(&dev_priv->drm,
|
drm_err(&dev_priv->drm,
|
||||||
|
@ -332,20 +332,25 @@ void intel_dmc_load_program(struct drm_i915_private *dev_priv)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fw_size = dmc_info->dmc_fw_size;
|
|
||||||
assert_rpm_wakelock_held(&dev_priv->runtime_pm);
|
assert_rpm_wakelock_held(&dev_priv->runtime_pm);
|
||||||
|
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
|
|
||||||
for (i = 0; i < fw_size; i++)
|
for (id = 0; id < DMC_FW_MAX; id++) {
|
||||||
intel_uncore_write_fw(&dev_priv->uncore, DMC_PROGRAM(i),
|
for (i = 0; i < dmc->dmc_info[id].dmc_fw_size; i++) {
|
||||||
dmc_info->payload[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();
|
preempt_enable();
|
||||||
|
|
||||||
for (i = 0; i < dmc_info->mmio_count; i++) {
|
for (id = 0; id < DMC_FW_MAX; id++) {
|
||||||
intel_de_write(dev_priv, dmc_info->mmioaddr[i],
|
for (i = 0; i < dmc->dmc_info[id].mmio_count; i++) {
|
||||||
dmc_info->mmiodata[i]);
|
intel_de_write(dev_priv, dmc->dmc_info[id].mmioaddr[i],
|
||||||
|
dmc->dmc_info[id].mmiodata[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_priv->dmc.dc_state = 0;
|
dev_priv->dmc.dc_state = 0;
|
||||||
|
@ -353,59 +358,68 @@ void intel_dmc_load_program(struct drm_i915_private *dev_priv)
|
||||||
gen9_set_dc_state_debugmask(dev_priv);
|
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
|
* Search fw_info table for dmc_offset to find firmware binary: num_entries is
|
||||||
* already sanitized.
|
* 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,
|
unsigned int num_entries,
|
||||||
const struct stepping_info *si,
|
const struct stepping_info *si,
|
||||||
u8 package_ver)
|
u8 package_ver)
|
||||||
{
|
{
|
||||||
u32 dmc_offset = DMC_DEFAULT_FW_OFFSET;
|
unsigned int i, id;
|
||||||
unsigned int i;
|
|
||||||
|
struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc);
|
||||||
|
|
||||||
for (i = 0; i < num_entries; i++) {
|
for (i = 0; i < num_entries; i++) {
|
||||||
if (package_ver > 1 && fw_info[i].dmc_id != 0)
|
id = package_ver <= 1 ? DMC_FW_MAIN : fw_info[i].dmc_id;
|
||||||
|
|
||||||
|
if (id >= DMC_FW_MAX) {
|
||||||
|
drm_dbg(&i915->drm, "Unsupported firmware id: %u\n", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
continue;
|
||||||
|
|
||||||
if (fw_info[i].substepping == '*' &&
|
if (fw_info_matches_stepping(&fw_info[i], si)) {
|
||||||
si->stepping == fw_info[i].stepping) {
|
dmc->dmc_info[id].present = true;
|
||||||
dmc_offset = fw_info[i].offset;
|
dmc->dmc_info[id].dmc_offset = fw_info[i].offset;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (si->stepping == fw_info[i].stepping &&
|
|
||||||
si->substepping == fw_info[i].substepping) {
|
|
||||||
dmc_offset = fw_info[i].offset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dmc_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
|
static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
|
||||||
const struct intel_dmc_header_base *dmc_header,
|
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 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;
|
unsigned int header_len_bytes, dmc_header_size, payload_size, i;
|
||||||
const u32 *mmioaddr, *mmiodata;
|
const u32 *mmioaddr, *mmiodata;
|
||||||
u32 mmio_count, mmio_count_max;
|
u32 mmio_count, mmio_count_max, start_mmioaddr;
|
||||||
u8 *payload;
|
u8 *payload;
|
||||||
|
|
||||||
BUILD_BUG_ON(ARRAY_SIZE(dmc_info->mmioaddr) < DMC_V3_MAX_MMIO_COUNT ||
|
BUILD_BUG_ON(ARRAY_SIZE(dmc_info->mmioaddr) < DMC_V3_MAX_MMIO_COUNT ||
|
||||||
|
@ -432,6 +446,7 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
|
||||||
mmio_count_max = DMC_V3_MAX_MMIO_COUNT;
|
mmio_count_max = DMC_V3_MAX_MMIO_COUNT;
|
||||||
/* header_len is in dwords */
|
/* header_len is in dwords */
|
||||||
header_len_bytes = dmc_header->header_len * 4;
|
header_len_bytes = dmc_header->header_len * 4;
|
||||||
|
start_mmioaddr = v3->start_mmioaddr;
|
||||||
dmc_header_size = sizeof(*v3);
|
dmc_header_size = sizeof(*v3);
|
||||||
} else if (dmc_header->header_ver == 1) {
|
} else if (dmc_header->header_ver == 1) {
|
||||||
const struct intel_dmc_header_v1 *v1 =
|
const struct intel_dmc_header_v1 *v1 =
|
||||||
|
@ -445,6 +460,7 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
|
||||||
mmio_count = v1->mmio_count;
|
mmio_count = v1->mmio_count;
|
||||||
mmio_count_max = DMC_V1_MAX_MMIO_COUNT;
|
mmio_count_max = DMC_V1_MAX_MMIO_COUNT;
|
||||||
header_len_bytes = dmc_header->header_len;
|
header_len_bytes = dmc_header->header_len;
|
||||||
|
start_mmioaddr = DMC_V1_MMIO_START_RANGE;
|
||||||
dmc_header_size = sizeof(*v1);
|
dmc_header_size = sizeof(*v1);
|
||||||
} else {
|
} else {
|
||||||
drm_err(&i915->drm, "Unknown DMC fw header version: %u\n",
|
drm_err(&i915->drm, "Unknown DMC fw header version: %u\n",
|
||||||
|
@ -465,16 +481,11 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < mmio_count; i++) {
|
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->mmioaddr[i] = _MMIO(mmioaddr[i]);
|
||||||
dmc_info->mmiodata[i] = mmiodata[i];
|
dmc_info->mmiodata[i] = mmiodata[i];
|
||||||
}
|
}
|
||||||
dmc_info->mmio_count = mmio_count;
|
dmc_info->mmio_count = mmio_count;
|
||||||
|
dmc_info->start_mmioaddr = start_mmioaddr;
|
||||||
|
|
||||||
rem_size -= header_len_bytes;
|
rem_size -= header_len_bytes;
|
||||||
|
|
||||||
|
@ -511,7 +522,7 @@ parse_dmc_fw_package(struct intel_dmc *dmc,
|
||||||
{
|
{
|
||||||
struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc);
|
struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc);
|
||||||
u32 package_size = sizeof(struct intel_package_header);
|
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;
|
const struct intel_fw_info *fw_info;
|
||||||
|
|
||||||
if (rem_size < package_size)
|
if (rem_size < package_size)
|
||||||
|
@ -547,16 +558,11 @@ parse_dmc_fw_package(struct intel_dmc *dmc,
|
||||||
|
|
||||||
fw_info = (const struct intel_fw_info *)
|
fw_info = (const struct intel_fw_info *)
|
||||||
((u8 *)package_header + sizeof(*package_header));
|
((u8 *)package_header + sizeof(*package_header));
|
||||||
dmc_offset = find_dmc_fw_offset(fw_info, num_entries, si,
|
dmc_set_fw_offset(dmc, fw_info, num_entries, si,
|
||||||
package_header->header_ver);
|
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_offset is in dwords */
|
/* dmc_offset is in dwords */
|
||||||
return package_size + dmc_offset * 4;
|
return package_size;
|
||||||
|
|
||||||
error_truncated:
|
error_truncated:
|
||||||
drm_err(&i915->drm, "Truncated DMC firmware, refusing.\n");
|
drm_err(&i915->drm, "Truncated DMC firmware, refusing.\n");
|
||||||
|
@ -608,7 +614,8 @@ static void parse_dmc_fw(struct drm_i915_private *dev_priv,
|
||||||
struct intel_dmc *dmc = &dev_priv->dmc;
|
struct intel_dmc *dmc = &dev_priv->dmc;
|
||||||
const struct stepping_info *si = intel_get_stepping_info(dev_priv);
|
const struct stepping_info *si = intel_get_stepping_info(dev_priv);
|
||||||
u32 readcount = 0;
|
u32 readcount = 0;
|
||||||
u32 r;
|
u32 r, offset;
|
||||||
|
int id;
|
||||||
|
|
||||||
if (!fw)
|
if (!fw)
|
||||||
return;
|
return;
|
||||||
|
@ -629,9 +636,19 @@ static void parse_dmc_fw(struct drm_i915_private *dev_priv,
|
||||||
|
|
||||||
readcount += r;
|
readcount += r;
|
||||||
|
|
||||||
/* Extract dmc_header information */
|
for (id = 0; id < DMC_FW_MAX; id++) {
|
||||||
dmc_header = (struct intel_dmc_header_base *)&fw->data[readcount];
|
if (!dev_priv->dmc.dmc_info[id].present)
|
||||||
parse_dmc_fw_header(dmc, dmc_header, fw->size - readcount);
|
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)
|
static void intel_dmc_runtime_pm_get(struct drm_i915_private *dev_priv)
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct drm_i915_private;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DMC_FW_MAIN = 0,
|
DMC_FW_MAIN = 0,
|
||||||
|
DMC_FW_PIPEA,
|
||||||
DMC_FW_MAX
|
DMC_FW_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,8 +32,11 @@ struct intel_dmc {
|
||||||
u32 mmio_count;
|
u32 mmio_count;
|
||||||
i915_reg_t mmioaddr[20];
|
i915_reg_t mmioaddr[20];
|
||||||
u32 mmiodata[20];
|
u32 mmiodata[20];
|
||||||
|
u32 dmc_offset;
|
||||||
|
u32 start_mmioaddr;
|
||||||
u32 dmc_fw_size; /*dwords */
|
u32 dmc_fw_size; /*dwords */
|
||||||
u32 *payload;
|
u32 *payload;
|
||||||
|
bool present;
|
||||||
} dmc_info[DMC_FW_MAX];
|
} dmc_info[DMC_FW_MAX];
|
||||||
|
|
||||||
u32 dc_state;
|
u32 dc_state;
|
||||||
|
|
|
@ -7739,7 +7739,7 @@ enum {
|
||||||
#define GAMMA_MODE_MODE_12BIT_MULTI_SEGMENTED (3 << 0) /* icl + */
|
#define GAMMA_MODE_MODE_12BIT_MULTI_SEGMENTED (3 << 0) /* icl + */
|
||||||
|
|
||||||
/* DMC */
|
/* DMC */
|
||||||
#define DMC_PROGRAM(i) _MMIO(0x80000 + (i) * 4)
|
#define DMC_PROGRAM(addr, i) _MMIO((addr) + (i) * 4)
|
||||||
#define DMC_SSP_BASE_ADDR_GEN9 0x00002FC0
|
#define DMC_SSP_BASE_ADDR_GEN9 0x00002FC0
|
||||||
#define DMC_HTP_ADDR_SKL 0x00500034
|
#define DMC_HTP_ADDR_SKL 0x00500034
|
||||||
#define DMC_SSP_BASE _MMIO(0x8F074)
|
#define DMC_SSP_BASE _MMIO(0x8F074)
|
||||||
|
|
Loading…
Add table
Reference in a new issue