diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dmc.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_dmc.c | 175 | 
1 files changed, 162 insertions, 13 deletions
| diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 7616a3906b9e..a171d42a5c5b 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -28,6 +28,7 @@  #include "i915_reg.h"  #include "intel_de.h"  #include "intel_dmc.h" +#include "intel_dmc_regs.h"  /**   * DOC: DMC Firmware Support @@ -37,6 +38,10 @@   * low-power state and comes back to normal.   */ +#define DMC_VERSION(major, minor)	((major) << 16 | (minor)) +#define DMC_VERSION_MAJOR(version)	((version) >> 16) +#define DMC_VERSION_MINOR(version)	((version) & 0xffff) +  #define DMC_PATH(platform, major, minor) \  	"i915/"				 \  	__stringify(platform) "_dmc_ver" \ @@ -47,8 +52,8 @@  #define DISPLAY_VER12_DMC_MAX_FW_SIZE	ICL_DMC_MAX_FW_SIZE -#define ADLP_DMC_PATH			DMC_PATH(adlp, 2, 14) -#define ADLP_DMC_VERSION_REQUIRED	DMC_VERSION(2, 14) +#define ADLP_DMC_PATH			DMC_PATH(adlp, 2, 16) +#define ADLP_DMC_VERSION_REQUIRED	DMC_VERSION(2, 16)  MODULE_FIRMWARE(ADLP_DMC_PATH);  #define ADLS_DMC_PATH			DMC_PATH(adls, 2, 01) @@ -276,17 +281,8 @@ void intel_dmc_load_program(struct drm_i915_private *dev_priv)  	struct intel_dmc *dmc = &dev_priv->dmc;  	u32 id, i; -	if (!HAS_DMC(dev_priv)) { -		drm_err(&dev_priv->drm, -			"No DMC support available for this platform\n"); -		return; -	} - -	if (!dev_priv->dmc.dmc_info[DMC_FW_MAIN].payload) { -		drm_err(&dev_priv->drm, -			"Tried to program CSR with empty payload\n"); +	if (!intel_dmc_has_payload(dev_priv))  		return; -	}  	assert_rpm_wakelock_held(&dev_priv->runtime_pm); @@ -314,6 +310,17 @@ void intel_dmc_load_program(struct drm_i915_private *dev_priv)  	gen9_set_dc_state_debugmask(dev_priv);  } +void assert_dmc_loaded(struct drm_i915_private *i915) +{ +	drm_WARN_ONCE(&i915->drm, +		      !intel_de_read(i915, DMC_PROGRAM(i915->dmc.dmc_info[DMC_FW_MAIN].start_mmioaddr, 0)), +		      "DMC program storage start is NULL\n"); +	drm_WARN_ONCE(&i915->drm, !intel_de_read(i915, DMC_SSP_BASE), +		      "DMC SSP Base Not fine\n"); +	drm_WARN_ONCE(&i915->drm, !intel_de_read(i915, DMC_HTP_SKL), +		      "DMC HTP Not fine\n"); +} +  static bool fw_info_matches_stepping(const struct intel_fw_info *fw_info,  				     const struct stepping_info *si)  { @@ -367,6 +374,44 @@ static void dmc_set_fw_offset(struct intel_dmc *dmc,  	}  } +static bool dmc_mmio_addr_sanity_check(struct intel_dmc *dmc, +				       const u32 *mmioaddr, u32 mmio_count, +				       int header_ver, u8 dmc_id) +{ +	struct drm_i915_private *i915 = container_of(dmc, typeof(*i915), dmc); +	u32 start_range, end_range; +	int i; + +	if (dmc_id >= DMC_FW_MAX) { +		drm_warn(&i915->drm, "Unsupported firmware id %u\n", dmc_id); +		return false; +	} + +	if (header_ver == 1) { +		start_range = DMC_MMIO_START_RANGE; +		end_range = DMC_MMIO_END_RANGE; +	} else if (dmc_id == DMC_FW_MAIN) { +		start_range = TGL_MAIN_MMIO_START; +		end_range = TGL_MAIN_MMIO_END; +	} else if (DISPLAY_VER(i915) >= 13) { +		start_range = ADLP_PIPE_MMIO_START; +		end_range = ADLP_PIPE_MMIO_END; +	} else if (DISPLAY_VER(i915) >= 12) { +		start_range = TGL_PIPE_MMIO_START(dmc_id); +		end_range = TGL_PIPE_MMIO_END(dmc_id); +	} else { +		drm_warn(&i915->drm, "Unknown mmio range for sanity check"); +		return false; +	} + +	for (i = 0; i < mmio_count; i++) { +		if (mmioaddr[i] < start_range || mmioaddr[i] > end_range) +			return false; +	} + +	return true; +} +  static u32 parse_dmc_fw_header(struct intel_dmc *dmc,  			       const struct intel_dmc_header_base *dmc_header,  			       size_t rem_size, u8 dmc_id) @@ -436,6 +481,12 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc,  		return 0;  	} +	if (!dmc_mmio_addr_sanity_check(dmc, mmioaddr, mmio_count, +					dmc_header->header_ver, dmc_id)) { +		drm_err(&i915->drm, "DMC firmware has Wrong MMIO Addresses\n"); +		return 0; +	} +  	for (i = 0; i < mmio_count; i++) {  		dmc_info->mmioaddr[i] = _MMIO(mmioaddr[i]);  		dmc_info->mmiodata[i] = mmiodata[i]; @@ -697,7 +748,7 @@ void intel_dmc_ucode_init(struct drm_i915_private *dev_priv)  		dmc->fw_path = RKL_DMC_PATH;  		dmc->required_version = RKL_DMC_VERSION_REQUIRED;  		dmc->max_fw_size = DISPLAY_VER12_DMC_MAX_FW_SIZE; -	} else if (DISPLAY_VER(dev_priv) >= 12) { +	} else if (IS_TIGERLAKE(dev_priv)) {  		dmc->fw_path = TGL_DMC_PATH;  		dmc->required_version = TGL_DMC_VERSION_REQUIRED;  		dmc->max_fw_size = DISPLAY_VER12_DMC_MAX_FW_SIZE; @@ -808,3 +859,101 @@ void intel_dmc_ucode_fini(struct drm_i915_private *dev_priv)  	for (id = 0; id < DMC_FW_MAX; id++)  		kfree(dev_priv->dmc.dmc_info[id].payload);  } + +void intel_dmc_print_error_state(struct drm_i915_error_state_buf *m, +				 struct drm_i915_private *i915) +{ +	struct intel_dmc *dmc = &i915->dmc; + +	if (!HAS_DMC(i915)) +		return; + +	i915_error_printf(m, "DMC loaded: %s\n", +			  str_yes_no(intel_dmc_has_payload(i915))); +	i915_error_printf(m, "DMC fw version: %d.%d\n", +			  DMC_VERSION_MAJOR(dmc->version), +			  DMC_VERSION_MINOR(dmc->version)); +} + +static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused) +{ +	struct drm_i915_private *i915 = m->private; +	intel_wakeref_t wakeref; +	struct intel_dmc *dmc; +	i915_reg_t dc5_reg, dc6_reg = INVALID_MMIO_REG; + +	if (!HAS_DMC(i915)) +		return -ENODEV; + +	dmc = &i915->dmc; + +	wakeref = intel_runtime_pm_get(&i915->runtime_pm); + +	seq_printf(m, "fw loaded: %s\n", +		   str_yes_no(intel_dmc_has_payload(i915))); +	seq_printf(m, "path: %s\n", dmc->fw_path); +	seq_printf(m, "Pipe A fw support: %s\n", +		   str_yes_no(GRAPHICS_VER(i915) >= 12)); +	seq_printf(m, "Pipe A fw loaded: %s\n", +		   str_yes_no(dmc->dmc_info[DMC_FW_PIPEA].payload)); +	seq_printf(m, "Pipe B fw support: %s\n", +		   str_yes_no(IS_ALDERLAKE_P(i915))); +	seq_printf(m, "Pipe B fw loaded: %s\n", +		   str_yes_no(dmc->dmc_info[DMC_FW_PIPEB].payload)); + +	if (!intel_dmc_has_payload(i915)) +		goto out; + +	seq_printf(m, "version: %d.%d\n", DMC_VERSION_MAJOR(dmc->version), +		   DMC_VERSION_MINOR(dmc->version)); + +	if (DISPLAY_VER(i915) >= 12) { +		if (IS_DGFX(i915)) { +			dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; +		} else { +			dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; +			dc6_reg = TGL_DMC_DEBUG_DC6_COUNT; +		} + +		/* +		 * NOTE: DMC_DEBUG3 is a general purpose reg. +		 * According to B.Specs:49196 DMC f/w reuses DC5/6 counter +		 * reg for DC3CO debugging and validation, +		 * but TGL DMC f/w is using DMC_DEBUG3 reg for DC3CO counter. +		 */ +		seq_printf(m, "DC3CO count: %d\n", +			   intel_de_read(i915, IS_DGFX(i915) ? +					 DG1_DMC_DEBUG3 : TGL_DMC_DEBUG3)); +	} else { +		dc5_reg = IS_BROXTON(i915) ? BXT_DMC_DC3_DC5_COUNT : +			SKL_DMC_DC3_DC5_COUNT; +		if (!IS_GEMINILAKE(i915) && !IS_BROXTON(i915)) +			dc6_reg = SKL_DMC_DC5_DC6_COUNT; +	} + +	seq_printf(m, "DC3 -> DC5 count: %d\n", intel_de_read(i915, dc5_reg)); +	if (i915_mmio_reg_valid(dc6_reg)) +		seq_printf(m, "DC5 -> DC6 count: %d\n", +			   intel_de_read(i915, dc6_reg)); + +out: +	seq_printf(m, "program base: 0x%08x\n", +		   intel_de_read(i915, DMC_PROGRAM(dmc->dmc_info[DMC_FW_MAIN].start_mmioaddr, 0))); +	seq_printf(m, "ssp base: 0x%08x\n", +		   intel_de_read(i915, DMC_SSP_BASE)); +	seq_printf(m, "htp: 0x%08x\n", intel_de_read(i915, DMC_HTP_SKL)); + +	intel_runtime_pm_put(&i915->runtime_pm, wakeref); + +	return 0; +} + +DEFINE_SHOW_ATTRIBUTE(intel_dmc_debugfs_status); + +void intel_dmc_debugfs_register(struct drm_i915_private *i915) +{ +	struct drm_minor *minor = i915->drm.primary; + +	debugfs_create_file("i915_dmc_info", 0444, minor->debugfs_root, +			    i915, &intel_dmc_debugfs_status_fops); +} | 
