Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/i915/dsb: Implement intel_dsb_gosub()

Add support for the new GOSUB DSB instruction (available on ptl+),
which instructs the DSB to jump to a different buffer, execute
the commands there, and then return execution to the next
instruction in the original buffer.

There are a few alignment related workarounds that need to
be dealt with when emitting GOSUB instruction.

v2: Right shift head and tail pointer passed to gosub command (chaitanya)
v3: Add macro for right shifting head/tail pointers (Animesh)
v4: Fix typo in commit message (Uma)
Add comments explaining why right shifting htp is needed (Animesh)

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Chaitanya Kumar Borah <chaitanya.kumar.borah@intel.com>
Reviewed-by: Uma Shankar <uma.shankar@intel.com>
Reviewed-by: Animesh Manna <animesh.manna@intel.com>
Signed-off-by: Animesh Manna <animesh.manna@intel.com>
Link: https://lore.kernel.org/r/20250523062041.166468-5-chaitanya.kumar.borah@intel.com

authored by

Ville Syrjälä and committed by
Animesh Manna
2c41d62f bb3de17e

+75
+73
drivers/gpu/drm/i915/display/intel_dsb.c
··· 93 93 /* see DSB_REG_VALUE_MASK */ 94 94 #define DSB_OPCODE_POLL 0xA 95 95 /* see DSB_REG_VALUE_MASK */ 96 + #define DSB_OPCODE_GOSUB 0xC /* ptl+ */ 97 + #define DSB_GOSUB_HEAD_SHIFT 26 98 + #define DSB_GOSUB_TAIL_SHIFT 0 99 + #define DSB_GOSUB_CONVERT_ADDR(x) ((x) >> 6) 96 100 97 101 static bool pre_commit_is_vrr_active(struct intel_atomic_state *state, 98 102 struct intel_crtc *crtc) ··· 535 531 aligned_tail - tail); 536 532 537 533 dsb->free_pos = aligned_tail / 4; 534 + } 535 + 536 + static void intel_dsb_gosub_align(struct intel_dsb *dsb) 537 + { 538 + u32 aligned_tail, tail; 539 + 540 + intel_dsb_ins_align(dsb); 541 + 542 + tail = dsb->free_pos * 4; 543 + aligned_tail = ALIGN(tail, CACHELINE_BYTES); 544 + 545 + /* 546 + * "The GOSUB instruction cannot be placed in 547 + * cacheline QW slot 6 or 7 (numbered 0-7)" 548 + */ 549 + if (aligned_tail - tail <= 2 * 8) 550 + intel_dsb_buffer_memset(&dsb->dsb_buf, dsb->free_pos, 0, 551 + aligned_tail - tail); 552 + 553 + dsb->free_pos = aligned_tail / 4; 554 + } 555 + 556 + void intel_dsb_gosub(struct intel_dsb *dsb, 557 + struct intel_dsb *sub_dsb) 558 + { 559 + struct intel_crtc *crtc = dsb->crtc; 560 + struct intel_display *display = to_intel_display(crtc->base.dev); 561 + unsigned int head, tail; 562 + u64 head_tail; 563 + 564 + if (drm_WARN_ON(display->drm, dsb->id != sub_dsb->id)) 565 + return; 566 + 567 + if (!assert_dsb_tail_is_aligned(sub_dsb)) 568 + return; 569 + 570 + intel_dsb_gosub_align(dsb); 571 + 572 + head = intel_dsb_head(sub_dsb); 573 + tail = intel_dsb_tail(sub_dsb); 574 + 575 + /* 576 + * The GOSUB instruction has the following memory layout. 577 + * 578 + * +------------------------------------------------------------+ 579 + * | Opcode | Rsvd | Head Ptr | Tail Ptr | 580 + * | 0x0c | | | | 581 + * +------------------------------------------------------------+ 582 + * |<- 8bits->|<- 4bits ->|<-- 26bits -->|<-- 26bits -->| 583 + * 584 + * We have only 26 bits each to represent the head and tail 585 + * pointers even though the addresses itself are of 32 bit. However, this 586 + * is not a problem because the addresses are 64 bit aligned and therefore 587 + * the last 6 bits are always Zero's. Therefore, we right shift the address 588 + * by 6 before embedding it into the GOSUB instruction. 589 + */ 590 + 591 + head_tail = ((u64)(DSB_GOSUB_CONVERT_ADDR(head)) << DSB_GOSUB_HEAD_SHIFT) | 592 + ((u64)(DSB_GOSUB_CONVERT_ADDR(tail)) << DSB_GOSUB_TAIL_SHIFT); 593 + 594 + intel_dsb_emit(dsb, lower_32_bits(head_tail), 595 + (DSB_OPCODE_GOSUB << DSB_OPCODE_SHIFT) | 596 + upper_32_bits(head_tail)); 597 + 598 + /* 599 + * "NOTE: the instructions within the cacheline 600 + * FOLLOWING the GOSUB instruction must be NOPs." 601 + */ 602 + intel_dsb_align_tail(dsb); 538 603 } 539 604 540 605 void intel_dsb_finish(struct intel_dsb *dsb)
+2
drivers/gpu/drm/i915/display/intel_dsb.h
··· 57 57 void intel_dsb_poll(struct intel_dsb *dsb, 58 58 i915_reg_t reg, u32 mask, u32 val, 59 59 int wait_us, int count); 60 + void intel_dsb_gosub(struct intel_dsb *dsb, 61 + struct intel_dsb *sub_dsb); 60 62 void intel_dsb_chain(struct intel_atomic_state *state, 61 63 struct intel_dsb *dsb, 62 64 struct intel_dsb *chained_dsb,