diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dsb.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_dsb.c | 246 | 
1 files changed, 185 insertions, 61 deletions
| diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index 481488d1fe67..53d8ae3a70e9 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -7,11 +7,10 @@  #include <drm/drm_print.h>  #include <drm/drm_vblank.h> -#include "i915_irq.h" -#include "i915_reg.h"  #include "i915_utils.h"  #include "intel_crtc.h"  #include "intel_de.h" +#include "intel_display_regs.h"  #include "intel_display_rpm.h"  #include "intel_display_types.h"  #include "intel_dsb.h" @@ -94,6 +93,10 @@ struct intel_dsb {  /* see DSB_REG_VALUE_MASK */  #define DSB_OPCODE_POLL			0xA  /* see DSB_REG_VALUE_MASK */ +#define DSB_OPCODE_GOSUB		0xC /* ptl+ */ +#define   DSB_GOSUB_HEAD_SHIFT		26 +#define   DSB_GOSUB_TAIL_SHIFT		0 +#define   DSB_GOSUB_CONVERT_ADDR(x)	((x) >> 6)  static bool pre_commit_is_vrr_active(struct intel_atomic_state *state,  				     struct intel_crtc *crtc) @@ -205,6 +208,15 @@ static bool assert_dsb_has_room(struct intel_dsb *dsb)  			 crtc->base.base.id, crtc->base.name, dsb->id);  } +static bool assert_dsb_tail_is_aligned(struct intel_dsb *dsb) +{ +	struct intel_crtc *crtc = dsb->crtc; +	struct intel_display *display = to_intel_display(crtc->base.dev); + +	return !drm_WARN_ON(display->drm, +			    !IS_ALIGNED(dsb->free_pos * 4, CACHELINE_BYTES)); +} +  static void intel_dsb_dump(struct intel_dsb *dsb)  {  	struct intel_crtc *crtc = dsb->crtc; @@ -229,13 +241,40 @@ static bool is_dsb_busy(struct intel_display *display, enum pipe pipe,  	return intel_de_read_fw(display, DSB_CTRL(pipe, dsb_id)) & DSB_STATUS_BUSY;  } +unsigned int intel_dsb_size(struct intel_dsb *dsb) +{ +	return dsb->free_pos * 4; +} + +unsigned int intel_dsb_head(struct intel_dsb *dsb) +{ +	return intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf); +} + +static unsigned int intel_dsb_tail(struct intel_dsb *dsb) +{ +	return intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf) + intel_dsb_size(dsb); +} + +static void intel_dsb_ins_align(struct intel_dsb *dsb) +{ +	/* +	 * Every instruction should be 8 byte aligned. +	 * +	 * The only way to get unaligned free_pos is via +	 * intel_dsb_reg_write_indexed() which already +	 * makes sure the next dword is zeroed, so no need +	 * to clear it here. +	 */ +	dsb->free_pos = ALIGN(dsb->free_pos, 2); +} +  static void intel_dsb_emit(struct intel_dsb *dsb, u32 ldw, u32 udw)  {  	if (!assert_dsb_has_room(dsb))  		return; -	/* Every instruction should be 8 byte aligned. */ -	dsb->free_pos = ALIGN(dsb->free_pos, 2); +	intel_dsb_ins_align(dsb);  	dsb->ins_start_offset = dsb->free_pos;  	dsb->ins[0] = ldw; @@ -493,6 +532,8 @@ static void intel_dsb_align_tail(struct intel_dsb *dsb)  {  	u32 aligned_tail, tail; +	intel_dsb_ins_align(dsb); +  	tail = dsb->free_pos * 4;  	aligned_tail = ALIGN(tail, CACHELINE_BYTES); @@ -503,20 +544,90 @@ static void intel_dsb_align_tail(struct intel_dsb *dsb)  	dsb->free_pos = aligned_tail / 4;  } -void intel_dsb_finish(struct intel_dsb *dsb) +static void intel_dsb_gosub_align(struct intel_dsb *dsb) +{ +	u32 aligned_tail, tail; + +	intel_dsb_ins_align(dsb); + +	tail = dsb->free_pos * 4; +	aligned_tail = ALIGN(tail, CACHELINE_BYTES); + +	/* +	 * Wa_16024917128 +	 * "Ensure GOSUB is not placed in cacheline QW slot 6 or 7 (numbered 0-7)" +	 */ +	if (aligned_tail - tail <= 2 * 8) +		intel_dsb_buffer_memset(&dsb->dsb_buf, dsb->free_pos, 0, +					aligned_tail - tail); + +	dsb->free_pos = aligned_tail / 4; +} + +void intel_dsb_gosub(struct intel_dsb *dsb, +		     struct intel_dsb *sub_dsb)  {  	struct intel_crtc *crtc = dsb->crtc; +	struct intel_display *display = to_intel_display(crtc->base.dev); +	unsigned int head, tail; +	u64 head_tail; + +	if (drm_WARN_ON(display->drm, dsb->id != sub_dsb->id)) +		return; + +	if (!assert_dsb_tail_is_aligned(sub_dsb)) +		return; + +	intel_dsb_gosub_align(dsb); + +	head = intel_dsb_head(sub_dsb); +	tail = intel_dsb_tail(sub_dsb);  	/* -	 * DSB_FORCE_DEWAKE remains active even after DSB is -	 * disabled, so make sure to clear it (if set during -	 * intel_dsb_commit()). And clear DSB_ENABLE_DEWAKE as -	 * well for good measure. +	 * The GOSUB instruction has the following memory layout. +	 * +	 * +------------------------------------------------------------+ +	 * |  Opcode  |   Rsvd    |      Head Ptr     |     Tail Ptr    | +	 * |   0x0c   |           |                   |                 | +	 * +------------------------------------------------------------+ +	 * |<- 8bits->|<- 4bits ->|<--   26bits    -->|<--  26bits   -->| +	 * +	 * We have only 26 bits each to represent the head and  tail +	 * pointers even though the addresses itself are of 32 bit. However, this +	 * is not a problem because the addresses are 64 bit aligned and therefore +	 * the last 6 bits are always Zero's. Therefore, we right shift the address +	 * by 6 before embedding it into the GOSUB instruction.  	 */ -	intel_dsb_reg_write(dsb, DSB_PMCTRL(crtc->pipe, dsb->id), 0); -	intel_dsb_reg_write_masked(dsb, DSB_PMCTRL_2(crtc->pipe, dsb->id), -				   DSB_FORCE_DEWAKE, 0); +	head_tail = ((u64)(DSB_GOSUB_CONVERT_ADDR(head)) << DSB_GOSUB_HEAD_SHIFT) | +		((u64)(DSB_GOSUB_CONVERT_ADDR(tail)) << DSB_GOSUB_TAIL_SHIFT); + +	intel_dsb_emit(dsb, lower_32_bits(head_tail), +		       (DSB_OPCODE_GOSUB << DSB_OPCODE_SHIFT) | +		       upper_32_bits(head_tail)); + +	/* +	 * "NOTE: the instructions within the cacheline +	 *  FOLLOWING the GOSUB instruction must be NOPs." +	 */ +	intel_dsb_align_tail(dsb); +} + +void intel_dsb_gosub_finish(struct intel_dsb *dsb) +{ +	intel_dsb_align_tail(dsb); + +	/* +	 * Wa_16024917128 +	 * "Ensure that all subroutines called by GOSUB end with a cacheline of NOPs" +	 */ +	intel_dsb_noop(dsb, 8); + +	intel_dsb_buffer_flush_map(&dsb->dsb_buf); +} + +void intel_dsb_finish(struct intel_dsb *dsb) +{  	intel_dsb_align_tail(dsb);  	intel_dsb_buffer_flush_map(&dsb->dsb_buf); @@ -539,6 +650,9 @@ static u32 dsb_error_int_status(struct intel_display *display)  	if (DISPLAY_VER(display) >= 14)  		errors |= DSB_ATS_FAULT_INT_STATUS; +	if (DISPLAY_VER(display) >= 30) +		errors |= DSB_GOSUB_INT_STATUS; +  	return errors;  } @@ -553,17 +667,46 @@ static u32 dsb_error_int_en(struct intel_display *display)  	if (DISPLAY_VER(display) >= 14)  		errors |= DSB_ATS_FAULT_INT_EN; +	/* +	 * Wa_16024917128 +	 * "Disable nested GOSUB interrupt (DSB_INTERRUPT bit 21)" +	 */ +	if (0 && DISPLAY_VER(display) >= 30) +		errors |= DSB_GOSUB_INT_EN; +  	return errors;  } +/* + * FIXME calibrate these sensibly, ideally compute based on + * the number of regisetrs to be written. But that requires + * measuring the actual DSB execution speed on each platform + * (and the speed also depends on CDCLK and memory clock)... + */ +static int intel_dsb_noarm_exec_time_us(void) +{ +	return 80; +} + +static int intel_dsb_arm_exec_time_us(void) +{ +	return 20; +} + +int intel_dsb_exec_time_us(void) +{ +	return intel_dsb_noarm_exec_time_us() + +		intel_dsb_arm_exec_time_us(); +} +  void intel_dsb_vblank_evade(struct intel_atomic_state *state,  			    struct intel_dsb *dsb)  {  	struct intel_crtc *crtc = dsb->crtc;  	const struct intel_crtc_state *crtc_state =  		intel_pre_commit_crtc_state(state, crtc); -	/* FIXME calibrate sensibly */ -	int latency = intel_usecs_to_scanlines(&crtc_state->hw.adjusted_mode, 20); +	int latency = intel_usecs_to_scanlines(&crtc_state->hw.adjusted_mode, +					       intel_dsb_arm_exec_time_us());  	int start, end;  	/* @@ -605,13 +748,11 @@ static void _intel_dsb_chain(struct intel_atomic_state *state,  	struct intel_display *display = to_intel_display(state->base.dev);  	struct intel_crtc *crtc = dsb->crtc;  	enum pipe pipe = crtc->pipe; -	u32 tail;  	if (drm_WARN_ON(display->drm, dsb->id == chained_dsb->id))  		return; -	tail = chained_dsb->free_pos * 4; -	if (drm_WARN_ON(display->drm, !IS_ALIGNED(tail, CACHELINE_BYTES))) +	if (!assert_dsb_tail_is_aligned(chained_dsb))  		return;  	intel_dsb_reg_write(dsb, DSB_CTRL(pipe, chained_dsb->id), @@ -631,13 +772,15 @@ static void _intel_dsb_chain(struct intel_atomic_state *state,  		intel_dsb_reg_write(dsb, DSB_PMCTRL(pipe, chained_dsb->id),  				    DSB_ENABLE_DEWAKE |  				    DSB_SCANLINE_FOR_DEWAKE(hw_dewake_scanline)); +	} else { +		intel_dsb_reg_write(dsb, DSB_PMCTRL(pipe, chained_dsb->id), 0);  	}  	intel_dsb_reg_write(dsb, DSB_HEAD(pipe, chained_dsb->id), -			    intel_dsb_buffer_ggtt_offset(&chained_dsb->dsb_buf)); +			    intel_dsb_head(chained_dsb));  	intel_dsb_reg_write(dsb, DSB_TAIL(pipe, chained_dsb->id), -			    intel_dsb_buffer_ggtt_offset(&chained_dsb->dsb_buf) + tail); +			    intel_dsb_tail(chained_dsb));  	if (ctrl & DSB_WAIT_FOR_VBLANK) {  		/* @@ -652,6 +795,13 @@ static void _intel_dsb_chain(struct intel_atomic_state *state,  		intel_dsb_wait_scanline_out(state, dsb,  					    dsb_dewake_scanline_start(state, crtc),  					    dsb_dewake_scanline_end(state, crtc)); + +		/* +		 * DSB_FORCE_DEWAKE remains active even after DSB is +		 * disabled, so make sure to clear it. +		 */ +		intel_dsb_reg_write_masked(dsb, DSB_PMCTRL_2(crtc->pipe, dsb->id), +					   DSB_FORCE_DEWAKE, 0);  	}  } @@ -676,16 +826,19 @@ void intel_dsb_wait_vblank_delay(struct intel_atomic_state *state,  	intel_dsb_wait_usec(dsb, usecs);  } -static void _intel_dsb_commit(struct intel_dsb *dsb, u32 ctrl, -			      int hw_dewake_scanline) +/** + * intel_dsb_commit() - Trigger workload execution of DSB. + * @dsb: DSB context + * + * This function is used to do actual write to hardware using DSB. + */ +void intel_dsb_commit(struct intel_dsb *dsb)  {  	struct intel_crtc *crtc = dsb->crtc;  	struct intel_display *display = to_intel_display(crtc->base.dev);  	enum pipe pipe = crtc->pipe; -	u32 tail; -	tail = dsb->free_pos * 4; -	if (drm_WARN_ON(display->drm, !IS_ALIGNED(tail, CACHELINE_BYTES))) +	if (!assert_dsb_tail_is_aligned(dsb))  		return;  	if (is_dsb_busy(display, pipe, dsb->id)) { @@ -695,7 +848,7 @@ static void _intel_dsb_commit(struct intel_dsb *dsb, u32 ctrl,  	}  	intel_de_write_fw(display, DSB_CTRL(pipe, dsb->id), -			  ctrl | DSB_ENABLE); +			  DSB_ENABLE);  	intel_de_write_fw(display, DSB_CHICKEN(pipe, dsb->id),  			  dsb->chicken); @@ -704,45 +857,13 @@ static void _intel_dsb_commit(struct intel_dsb *dsb, u32 ctrl,  			  dsb_error_int_status(display) | DSB_PROG_INT_STATUS |  			  dsb_error_int_en(display) | DSB_PROG_INT_EN); -	intel_de_write_fw(display, DSB_HEAD(pipe, dsb->id), -			  intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf)); - -	if (hw_dewake_scanline >= 0) { -		int diff, position; +	intel_de_write_fw(display, DSB_PMCTRL(pipe, dsb->id), 0); -		intel_de_write_fw(display, DSB_PMCTRL(pipe, dsb->id), -				  DSB_ENABLE_DEWAKE | -				  DSB_SCANLINE_FOR_DEWAKE(hw_dewake_scanline)); - -		/* -		 * Force DEwake immediately if we're already past -		 * or close to racing past the target scanline. -		 */ -		position = intel_de_read_fw(display, PIPEDSL(display, pipe)) & PIPEDSL_LINE_MASK; - -		diff = hw_dewake_scanline - position; -		intel_de_write_fw(display, DSB_PMCTRL_2(pipe, dsb->id), -				  (diff >= 0 && diff < 5 ? DSB_FORCE_DEWAKE : 0) | -				  DSB_BLOCK_DEWAKE_EXTENSION); -	} +	intel_de_write_fw(display, DSB_HEAD(pipe, dsb->id), +			  intel_dsb_head(dsb));  	intel_de_write_fw(display, DSB_TAIL(pipe, dsb->id), -			  intel_dsb_buffer_ggtt_offset(&dsb->dsb_buf) + tail); -} - -/** - * intel_dsb_commit() - Trigger workload execution of DSB. - * @dsb: DSB context - * @wait_for_vblank: wait for vblank before executing - * - * This function is used to do actual write to hardware using DSB. - */ -void intel_dsb_commit(struct intel_dsb *dsb, -		      bool wait_for_vblank) -{ -	_intel_dsb_commit(dsb, -			  wait_for_vblank ? DSB_WAIT_FOR_VBLANK : 0, -			  wait_for_vblank ? dsb->hw_dewake_scanline : -1); +			  intel_dsb_tail(dsb));  }  void intel_dsb_wait(struct intel_dsb *dsb) @@ -895,4 +1016,7 @@ void intel_dsb_irq_handler(struct intel_display *display,  	if (errors & DSB_POLL_ERR_INT_STATUS)  		drm_err(display->drm, "[CRTC:%d:%s] DSB %d poll error\n",  			crtc->base.base.id, crtc->base.name, dsb_id); +	if (errors & DSB_GOSUB_INT_STATUS) +		drm_err(display->drm, "[CRTC:%d:%s] DSB %d GOSUB programming error\n", +			crtc->base.base.id, crtc->base.name, dsb_id);  } | 
