@@ -40,6 +40,27 @@
static void evergreen_gpu_init(struct radeon_device *rdev);
void evergreen_fini(struct radeon_device *rdev);
+void evergreen_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+ radeon_ring_write(rdev,
+ PACKET0(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, upper_32_bits(crtc_base));
+ radeon_ring_write(rdev,
+ PACKET0(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+ radeon_ring_write(rdev,
+ PACKET0(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, upper_32_bits(crtc_base));
+ radeon_ring_write(rdev,
+ PACKET0(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+}
+
/* get temperature in millidegrees */
u32 evergreen_get_temp(struct radeon_device *rdev)
{
@@ -2300,6 +2321,7 @@ int evergreen_irq_process(struct radeon_device *rdev)
u32 ring_index;
unsigned long flags;
bool queue_hotplug = false;
+ bool queue_unpin = false;
DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
if (!rdev->ih.enabled)
@@ -2456,36 +2478,48 @@ restart_ih:
break;
case 8: /* D1 pflip */
if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 0);
+ queue_unpin = true;
rdev->irq.stat_regs.evergreen.d1grph_int &= ~GRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D1 pflip\n");
}
break;
case 10: /* D2 pflip */
if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 1);
+ queue_unpin = true;
rdev->irq.stat_regs.evergreen.d2grph_int &= ~GRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D2 pflip\n");
}
break;
case 12: /* D3 pflip */
if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 2);
+ queue_unpin = true;
rdev->irq.stat_regs.evergreen.d3grph_int &= ~GRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D3 pflip\n");
}
break;
case 14: /* D4 pflip */
if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 3);
+ queue_unpin = true;
rdev->irq.stat_regs.evergreen.d4grph_int &= ~GRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D4 pflip\n");
}
break;
case 16: /* D5 pflip */
if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 4);
+ queue_unpin = true;
rdev->irq.stat_regs.evergreen.d5grph_int &= ~GRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D5 pflip\n");
}
break;
case 18: /* D6 pflip */
if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 5);
+ queue_unpin = true;
rdev->irq.stat_regs.evergreen.d6grph_int &= ~GRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D6 pflip\n");
}
@@ -2569,6 +2603,8 @@ restart_ih:
goto restart_ih;
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
+ if (queue_unpin)
+ queue_work(rdev->wq, &rdev->unpin_work);
rdev->ih.rptr = rptr;
WREG32(IH_RB_RPTR, rdev->ih.rptr);
spin_unlock_irqrestore(&rdev->ih.lock, flags);
@@ -105,6 +105,9 @@
#define EVERGREEN_GRPH_Y_START 0x6830
#define EVERGREEN_GRPH_X_END 0x6834
#define EVERGREEN_GRPH_Y_END 0x6838
+#define EVERGREEN_GRPH_UPDATE 0x6844
+# define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING (1 << 2)
+# define EVERGREEN_GRPH_UPDATE_LOCK (1 << 16)
/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */
#define EVERGREEN_CUR_CONTROL 0x6998
@@ -628,6 +628,20 @@
#define PACKET3_MEM_SEMAPHORE 0x39
#define PACKET3_MPEG_INDEX 0x3A
#define PACKET3_WAIT_REG_MEM 0x3C
+# define PACKET3_WAIT_REG_MEM_ENGINE(x) ((x) << 8)
+# define ENGINE_ME 0
+# define ENGINE_PFP 1
+# define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
+# define MEM_SPACE_REG 0
+# define MEM_SPACE_MEM 1
+# define PACKET3_WAIT_REG_MEM_FUNCTION(x) ((x) << 0)
+# define FUNCTION_ALWAYS 0
+# define FUNCTION_LT 1
+# define FUNCTION_LTE 2
+# define FUNCTION_E 3
+# define FUNCTION_NE 4
+# define FUNCTION_GTE 5
+# define FUNCTION_GT 6
#define PACKET3_MEM_WRITE 0x3D
#define PACKET3_INDIRECT_BUFFER 0x32
#define PACKET3_SURFACE_SYNC 0x43
@@ -68,6 +68,77 @@ MODULE_FIRMWARE(FIRMWARE_R520);
* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
*/
+void r100_pre_page_flip(struct radeon_device *rdev, int crtc)
+{
+ /* enable the pflip int */
+ radeon_irq_kms_pflip_irq_get(rdev, crtc);
+}
+
+void r100_post_page_flip(struct radeon_device *rdev, int crtc)
+{
+ /* disable the pflip int */
+ radeon_irq_kms_pflip_irq_put(rdev, crtc);
+}
+
+void r100_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(crtc->fb);
+ struct drm_framebuffer *target_fb = crtc->fb;
+ struct drm_gem_object *obj = radeon_fb->obj;
+ struct radeon_bo *rbo = obj->driver_private;
+ u32 tiling_flags, pitch_pixels;
+ int r;
+
+ /* crtc offset is from display base addr not FB location */
+ crtc_base -= radeon_crtc->legacy_display_base_addr;
+
+ r = radeon_bo_reserve(rbo, false);
+ if (likely(r == 0)) {
+ radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
+ radeon_bo_unreserve(rbo);
+ }
+
+ pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
+
+ if (tiling_flags & RADEON_TILING_MACRO) {
+ if (ASIC_IS_R300(rdev)) {
+ crtc_base &= ~0x7ff;
+ } else {
+ int byteshift = target_fb->bits_per_pixel >> 4;
+ int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11;
+ crtc_base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8);
+ }
+ } else {
+ int offset = crtc->y * pitch_pixels + crtc->x;
+ switch (target_fb->bits_per_pixel) {
+ case 8:
+ default:
+ offset *= 1;
+ break;
+ case 15:
+ case 16:
+ offset *= 2;
+ break;
+ case 24:
+ offset *= 3;
+ break;
+ case 32:
+ offset *= 4;
+ break;
+ }
+ crtc_base += offset;
+ }
+ crtc_base &= ~7;
+
+ radeon_ring_write(rdev,
+ PACKET0(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
void r100_pm_get_dynpm_state(struct radeon_device *rdev)
{
int i;
@@ -526,10 +597,12 @@ int r100_irq_set(struct radeon_device *rdev)
if (rdev->irq.gui_idle) {
tmp |= RADEON_GUI_IDLE_MASK;
}
- if (rdev->irq.crtc_vblank_int[0]) {
+ if (rdev->irq.crtc_vblank_int[0] ||
+ rdev->irq.pflip[0]) {
tmp |= RADEON_CRTC_VBLANK_MASK;
}
- if (rdev->irq.crtc_vblank_int[1]) {
+ if (rdev->irq.crtc_vblank_int[1] ||
+ rdev->irq.pflip[1]) {
tmp |= RADEON_CRTC2_VBLANK_MASK;
}
if (rdev->irq.hpd[0]) {
@@ -576,6 +649,7 @@ int r100_irq_process(struct radeon_device *rdev)
{
uint32_t status, msi_rearm;
bool queue_hotplug = false;
+ bool queue_unpin = false;
/* reset gui idle ack. the status bit is broken */
rdev->irq.gui_idle_acked = false;
@@ -600,14 +674,26 @@ int r100_irq_process(struct radeon_device *rdev)
}
/* Vertical blank interrupts */
if (status & RADEON_CRTC_VBLANK_STAT) {
- drm_handle_vblank(rdev->ddev, 0);
- rdev->pm.vblank_sync = true;
- wake_up(&rdev->irq.vblank_queue);
+ if (rdev->irq.crtc_vblank_int[0]) {
+ drm_handle_vblank(rdev->ddev, 0);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[0]) {
+ radeon_crtc_handle_flip(rdev, 0);
+ queue_unpin = true;
+ }
}
if (status & RADEON_CRTC2_VBLANK_STAT) {
- drm_handle_vblank(rdev->ddev, 1);
- rdev->pm.vblank_sync = true;
- wake_up(&rdev->irq.vblank_queue);
+ if (rdev->irq.crtc_vblank_int[1]) {
+ drm_handle_vblank(rdev->ddev, 1);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[1]) {
+ radeon_crtc_handle_flip(rdev, 1);
+ queue_unpin = true;
+ }
}
if (status & RADEON_FP_DETECT_STAT) {
queue_hotplug = true;
@@ -623,6 +709,8 @@ int r100_irq_process(struct radeon_device *rdev)
rdev->irq.gui_idle_acked = false;
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
+ if (queue_unpin)
+ queue_work(rdev->wq, &rdev->unpin_work);
if (rdev->msi_enabled) {
switch (rdev->family) {
case CHIP_RS400:
@@ -409,6 +409,7 @@
#define AVIVO_D1GRPH_X_END 0x6134
#define AVIVO_D1GRPH_Y_END 0x6138
#define AVIVO_D1GRPH_UPDATE 0x6144
+# define AVIVO_D1GRPH_SURFACE_UPDATE_PENDING (1 << 2)
# define AVIVO_D1GRPH_UPDATE_LOCK (1 << 16)
#define AVIVO_D1GRPH_FLIP_CONTROL 0x6148
@@ -92,6 +92,22 @@ void r600_gpu_init(struct radeon_device *rdev);
void r600_fini(struct radeon_device *rdev);
void r600_irq_disable(struct radeon_device *rdev);
+void r600_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+ radeon_ring_write(rdev,
+ PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+ radeon_ring_write(rdev,
+ PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
/* get temperature in millidegrees */
u32 rv6xx_get_temp(struct radeon_device *rdev)
{
@@ -3274,6 +3290,7 @@ int r600_irq_process(struct radeon_device *rdev)
u32 ring_index;
unsigned long flags;
bool queue_hotplug = false;
+ bool queue_unpin = false;
DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
if (!rdev->ih.enabled)
@@ -3348,12 +3365,16 @@ restart_ih:
break;
case 9: /* D1 pflip */
if (rdev->irq.stat_regs.r600.d1grph_int & DxGRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 0);
+ queue_unpin = true;
rdev->irq.stat_regs.r600.d1grph_int &= ~DxGRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D1 pflip\n");
}
break;
case 11: /* D2 pflip */
if (rdev->irq.stat_regs.r600.d2grph_int & DxGRPH_PFLIP_INT_OCCURRED) {
+ radeon_crtc_handle_flip(rdev, 1);
+ queue_unpin = true;
rdev->irq.stat_regs.r600.d2grph_int &= ~DxGRPH_PFLIP_INT_OCCURRED;
DRM_DEBUG("IH: D2 pflip\n");
}
@@ -3441,6 +3462,8 @@ restart_ih:
goto restart_ih;
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
+ if (queue_unpin)
+ queue_work(rdev->wq, &rdev->unpin_work);
rdev->ih.rptr = rptr;
WREG32(IH_RB_RPTR, rdev->ih.rptr);
spin_unlock_irqrestore(&rdev->ih.lock, flags);
@@ -731,6 +731,12 @@
# define DxGRPH_PFLIP_INT_MASK (1 << 0)
# define DxGRPH_PFLIP_INT_TYPE (1 << 8)
+#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110
+#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118
+#define D1GRPH_UPDATE 0x6144
+# define D1GRPH_SURFACE_UPDATE_PENDING (1 << 2)
+# define D1GRPH_UPDATE_LOCK (1 << 16)
+
/*
* PM4
*/
@@ -771,6 +777,20 @@
#define PACKET3_MEM_SEMAPHORE 0x39
#define PACKET3_MPEG_INDEX 0x3A
#define PACKET3_WAIT_REG_MEM 0x3C
+# define PACKET3_WAIT_REG_MEM_ENGINE(x) ((x) << 8)
+# define ENGINE_ME 0
+# define ENGINE_PFP 1
+# define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
+# define MEM_SPACE_REG 0
+# define MEM_SPACE_MEM 1
+# define PACKET3_WAIT_REG_MEM_FUNCTION(x) ((x) << 0)
+# define FUNCTION_ALWAYS 0
+# define FUNCTION_LT 1
+# define FUNCTION_LTE 2
+# define FUNCTION_E 3
+# define FUNCTION_NE 4
+# define FUNCTION_GTE 5
+# define FUNCTION_GT 6
#define PACKET3_MEM_WRITE 0x3D
#define PACKET3_INDIRECT_BUFFER 0x32
#define PACKET3_SURFACE_SYNC 0x43
@@ -427,12 +427,16 @@ struct radeon_irq {
spinlock_t sw_lock;
int sw_refcount;
union radeon_irq_stat_regs stat_regs;
+ spinlock_t pflip_lock[6];
+ int pflip_refcount[6];
};
int radeon_irq_kms_init(struct radeon_device *rdev);
void radeon_irq_kms_fini(struct radeon_device *rdev);
void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev);
void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev);
+void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc);
+void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc);
/*
* CP & ring.
@@ -916,6 +920,10 @@ struct radeon_asic {
void (*pm_finish)(struct radeon_device *rdev);
void (*pm_init_profile)(struct radeon_device *rdev);
void (*pm_get_dynpm_state)(struct radeon_device *rdev);
+ /* pageflipping */
+ void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
+ void (*page_flip)(struct drm_crtc *crtc, u64 crtc_base);
+ void (*post_page_flip)(struct radeon_device *rdev, int crtc);
};
/*
@@ -1132,6 +1140,7 @@ struct radeon_device {
struct r600_ih ih; /* r6/700 interrupt ring */
struct workqueue_struct *wq;
struct work_struct hotplug_work;
+ struct work_struct unpin_work;
int num_crtc; /* number of crtcs */
struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
struct mutex vram_mutex;
@@ -1375,6 +1384,9 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
#define radeon_pm_finish(rdev) (rdev)->asic->pm_finish((rdev))
#define radeon_pm_init_profile(rdev) (rdev)->asic->pm_init_profile((rdev))
#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm_get_dynpm_state((rdev))
+#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pre_page_flip((rdev), (crtc))
+#define radeon_page_flip(crtc, base) rdev->asic->page_flip((crtc), (base))
+#define radeon_post_page_flip(rdev, crtc) rdev->asic->post_page_flip((rdev), (crtc))
/* Common functions */
/* AGP */
@@ -171,6 +171,9 @@ static struct radeon_asic r100_asic = {
.pm_finish = &r100_pm_finish,
.pm_init_profile = &r100_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic r200_asic = {
@@ -215,6 +218,9 @@ static struct radeon_asic r200_asic = {
.pm_finish = &r100_pm_finish,
.pm_init_profile = &r100_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic r300_asic = {
@@ -260,6 +266,9 @@ static struct radeon_asic r300_asic = {
.pm_finish = &r100_pm_finish,
.pm_init_profile = &r100_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic r300_asic_pcie = {
@@ -304,6 +313,9 @@ static struct radeon_asic r300_asic_pcie = {
.pm_finish = &r100_pm_finish,
.pm_init_profile = &r100_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic r420_asic = {
@@ -349,6 +361,9 @@ static struct radeon_asic r420_asic = {
.pm_finish = &r100_pm_finish,
.pm_init_profile = &r420_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic rs400_asic = {
@@ -394,6 +409,9 @@ static struct radeon_asic rs400_asic = {
.pm_finish = &r100_pm_finish,
.pm_init_profile = &r100_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic rs600_asic = {
@@ -439,6 +457,9 @@ static struct radeon_asic rs600_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &r420_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic rs690_asic = {
@@ -484,6 +505,9 @@ static struct radeon_asic rs690_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &r420_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic rv515_asic = {
@@ -529,6 +553,9 @@ static struct radeon_asic rv515_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &r420_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic r520_asic = {
@@ -574,6 +601,9 @@ static struct radeon_asic r520_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &r420_pm_init_profile,
.pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic r600_asic = {
@@ -618,6 +648,9 @@ static struct radeon_asic r600_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &r600_pm_init_profile,
.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r600_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic rs780_asic = {
@@ -662,6 +695,9 @@ static struct radeon_asic rs780_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &rs780_pm_init_profile,
.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r600_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic rv770_asic = {
@@ -706,6 +742,9 @@ static struct radeon_asic rv770_asic = {
.pm_finish = &rs600_pm_finish,
.pm_init_profile = &r600_pm_init_profile,
.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &rv770_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
static struct radeon_asic evergreen_asic = {
@@ -749,6 +788,9 @@ static struct radeon_asic evergreen_asic = {
.pm_finish = &evergreen_pm_finish,
.pm_init_profile = &r600_pm_init_profile,
.pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &r100_post_page_flip,
};
int radeon_asic_init(struct radeon_device *rdev)
@@ -130,6 +130,9 @@ extern void r100_pm_prepare(struct radeon_device *rdev);
extern void r100_pm_finish(struct radeon_device *rdev);
extern void r100_pm_init_profile(struct radeon_device *rdev);
extern void r100_pm_get_dynpm_state(struct radeon_device *rdev);
+extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc);
+extern void r100_page_flip(struct drm_crtc *crtc, u64 crtc_base);
+extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);
/*
* r200,rv250,rs300,rv280
@@ -205,6 +208,7 @@ void rs600_hpd_set_polarity(struct radeon_device *rdev,
extern void rs600_pm_misc(struct radeon_device *rdev);
extern void rs600_pm_prepare(struct radeon_device *rdev);
extern void rs600_pm_finish(struct radeon_device *rdev);
+extern void rs600_page_flip(struct drm_crtc *crtc, u64 crtc_base);
/*
* rs690,rs740
@@ -278,6 +282,7 @@ extern void r600_pm_misc(struct radeon_device *rdev);
extern void r600_pm_init_profile(struct radeon_device *rdev);
extern void rs780_pm_init_profile(struct radeon_device *rdev);
extern void r600_pm_get_dynpm_state(struct radeon_device *rdev);
+extern void r600_page_flip(struct drm_crtc *crtc, u64 crtc_base);
/*
* rv770,rv730,rv710,rv740
@@ -287,6 +292,7 @@ void rv770_fini(struct radeon_device *rdev);
int rv770_suspend(struct radeon_device *rdev);
int rv770_resume(struct radeon_device *rdev);
extern void rv770_pm_misc(struct radeon_device *rdev);
+extern void rv770_page_flip(struct drm_crtc *crtc, u64 crtc_base);
/*
* evergreen
@@ -314,5 +320,6 @@ extern int evergreen_cs_parse(struct radeon_cs_parser *p);
extern void evergreen_pm_misc(struct radeon_device *rdev);
extern void evergreen_pm_prepare(struct radeon_device *rdev);
extern void evergreen_pm_finish(struct radeon_device *rdev);
+extern void evergreen_page_flip(struct drm_crtc *crtc, u64 crtc_base);
#endif
@@ -183,12 +183,113 @@ static void radeon_crtc_destroy(struct drm_crtc *crtc)
kfree(radeon_crtc);
}
+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
+ struct drm_pending_vblank_event *e = radeon_crtc->event;
+ struct timeval now;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rdev->ddev->event_lock, flags);
+ if (radeon_crtc->flip_old_bo &&
+ radeon_fence_signaled(radeon_crtc->pflip_fence)) {
+
+ drm_vblank_put(rdev->ddev, crtc_id);
+
+ /* wakeup userspace */
+ e = radeon_crtc->event;
+ radeon_crtc->event = NULL;
+ if (e) {
+ do_gettimeofday(&now);
+ e->event.sequence = drm_vblank_count(rdev->ddev, crtc_id);
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+ list_add_tail(&e->base.link, &e->base.file_priv->event_list);
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+ }
+ }
+ spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
+}
+
+static int radeon_crtc_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_framebuffer *old_radeon_fb;
+ struct radeon_framebuffer *new_radeon_fb;
+ struct drm_gem_object *obj;
+ struct radeon_bo *rbo;
+ struct radeon_fence *fence;
+ unsigned long flags;
+ u64 base;
+ int r;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (radeon_crtc->flip_old_bo) {
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ return -EBUSY;
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ old_radeon_fb = to_radeon_framebuffer(crtc->fb);
+ new_radeon_fb = to_radeon_framebuffer(fb);
+
+ /* pin the new buffer */
+ obj = new_radeon_fb->obj;
+ rbo = obj->driver_private;
+ r = radeon_bo_reserve(rbo, false);
+ if (unlikely(r != 0))
+ return r;
+ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+ if (unlikely(r != 0)) {
+ radeon_bo_unreserve(rbo);
+ return -EINVAL;
+ }
+ radeon_bo_unreserve(rbo);
+
+ /* update crtc fb */
+ crtc->fb = fb;
+
+ r = drm_vblank_get(dev, radeon_crtc->crtc_id);
+ if (r)
+ return r;
+
+ /* 64 ought to cover us */
+ r = radeon_ring_lock(rdev, 64);
+ if (r)
+ return r;
+
+ /* set the proper interrupt */
+ radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
+ /* put the flip on the ring */
+ radeon_page_flip(crtc, base);
+ /* emit a fence */
+ radeon_fence_create(rdev, &fence);
+ radeon_fence_emit(rdev, fence);
+ radeon_ring_unlock_commit(rdev);
+
+ /* schedule unpin of the old buffer */
+ obj = old_radeon_fb->obj;
+ rbo = obj->driver_private;
+ spin_lock_irqsave(&dev->event_lock, flags);
+ radeon_crtc->flip_old_bo = rbo;
+ radeon_crtc->event = event;
+ radeon_crtc->pflip_fence = fence;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ return 0;
+}
+
static const struct drm_crtc_funcs radeon_crtc_funcs = {
.cursor_set = radeon_crtc_cursor_set,
.cursor_move = radeon_crtc_cursor_move,
.gamma_set = radeon_crtc_gamma_set,
.set_config = drm_crtc_helper_set_config,
.destroy = radeon_crtc_destroy,
+ .page_flip = radeon_crtc_page_flip,
};
static void radeon_crtc_init(struct drm_device *dev, int index)
@@ -47,7 +47,7 @@
* - 2.4.0 - add crtc id query
* - 2.5.0 - add get accel 2 to work around ddx breakage for evergreen
* - 2.6.0 - add tiling config query (r6xx+), add initial HiZ support (r300->r500)
- * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs
+ * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs, pageflip support
*/
#define KMS_DRIVER_MAJOR 2
#define KMS_DRIVER_MINOR 7
@@ -59,12 +59,43 @@ static void radeon_hotplug_work_func(struct work_struct *work)
drm_helper_hpd_irq_event(dev);
}
+/*
+ * Handle unpin events outside the interrupt handler proper.
+ */
+static void radeon_unpin_work_func(struct work_struct *work)
+{
+ struct radeon_device *rdev = container_of(work, struct radeon_device,
+ unpin_work);
+ struct radeon_crtc *radeon_crtc;
+ int i, r;
+
+ for (i = 0; i < rdev->num_crtc; i++) {
+ radeon_crtc = rdev->mode_info.crtcs[i];
+ if (radeon_crtc->flip_old_bo &&
+ radeon_fence_signaled(radeon_crtc->pflip_fence)) {
+ radeon_post_page_flip(rdev, radeon_crtc->crtc_id);
+ /* unpin of the old buffer */
+ r = radeon_bo_reserve(radeon_crtc->flip_old_bo, false);
+ if (likely(r == 0)) {
+ r = radeon_bo_unpin(radeon_crtc->flip_old_bo);
+ if (unlikely(r != 0)) {
+ DRM_ERROR("failed to unpin buffer after flip\n");
+ }
+ radeon_bo_unreserve(radeon_crtc->flip_old_bo);
+ radeon_crtc->flip_old_bo = NULL;
+ radeon_fence_unref(&radeon_crtc->pflip_fence);
+ }
+ }
+ }
+}
+
void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
{
struct radeon_device *rdev = dev->dev_private;
unsigned i;
INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
+ INIT_WORK(&rdev->unpin_work, radeon_unpin_work_func);
/* Disable *all* interrupts */
rdev->irq.sw_int = false;
@@ -179,3 +210,34 @@ void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev)
spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
}
+void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc)
+{
+ unsigned long irqflags;
+
+ if (crtc < 0 || crtc >= rdev->num_crtc)
+ return;
+
+ spin_lock_irqsave(&rdev->irq.pflip_lock[crtc], irqflags);
+ if (rdev->ddev->irq_enabled && (++rdev->irq.pflip_refcount[crtc] == 1)) {
+ rdev->irq.pflip[crtc] = true;
+ radeon_irq_set(rdev);
+ }
+ spin_unlock_irqrestore(&rdev->irq.pflip_lock[crtc], irqflags);
+}
+
+void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc)
+{
+ unsigned long irqflags;
+
+ if (crtc < 0 || crtc >= rdev->num_crtc)
+ return;
+
+ spin_lock_irqsave(&rdev->irq.pflip_lock[crtc], irqflags);
+ BUG_ON(rdev->ddev->irq_enabled && rdev->irq.pflip_refcount[crtc] <= 0);
+ if (rdev->ddev->irq_enabled && (--rdev->irq.pflip_refcount[crtc] == 0)) {
+ rdev->irq.pflip[crtc] = false;
+ radeon_irq_set(rdev);
+ }
+ spin_unlock_irqrestore(&rdev->irq.pflip_lock[crtc], irqflags);
+}
+
@@ -278,6 +278,10 @@ struct radeon_crtc {
fixed20_12 hsc;
struct drm_display_mode native_mode;
int pll_id;
+ /* page flipping */
+ struct radeon_bo *flip_old_bo;
+ struct drm_pending_vblank_event *event;
+ struct radeon_fence *pflip_fence;
};
struct radeon_encoder_primary_dac {
@@ -653,4 +657,7 @@ int radeon_fbdev_total_size(struct radeon_device *rdev);
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
void radeon_fb_output_poll_changed(struct radeon_device *rdev);
+
+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id);
+
#endif
@@ -46,6 +46,22 @@
void rs600_gpu_init(struct radeon_device *rdev);
int rs600_mc_wait_for_idle(struct radeon_device *rdev);
+void rs600_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+ radeon_ring_write(rdev,
+ PACKET0(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+ radeon_ring_write(rdev,
+ PACKET0(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
void rs600_pm_misc(struct radeon_device *rdev)
{
int requested_index = rdev->pm.requested_power_state_index;
@@ -515,10 +531,12 @@ int rs600_irq_set(struct radeon_device *rdev)
if (rdev->irq.gui_idle) {
tmp |= S_000040_GUI_IDLE(1);
}
- if (rdev->irq.crtc_vblank_int[0]) {
+ if (rdev->irq.crtc_vblank_int[0] ||
+ rdev->irq.pflip[0]) {
mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1);
}
- if (rdev->irq.crtc_vblank_int[1]) {
+ if (rdev->irq.crtc_vblank_int[1] ||
+ rdev->irq.pflip[1]) {
mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1);
}
if (rdev->irq.hpd[0]) {
@@ -589,6 +607,7 @@ int rs600_irq_process(struct radeon_device *rdev)
{
u32 status, msi_rearm;
bool queue_hotplug = false;
+ bool queue_unpin = false;
/* reset gui idle ack. the status bit is broken */
rdev->irq.gui_idle_acked = false;
@@ -609,14 +628,26 @@ int rs600_irq_process(struct radeon_device *rdev)
}
/* Vertical blank interrupts */
if (G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
- drm_handle_vblank(rdev->ddev, 0);
- rdev->pm.vblank_sync = true;
- wake_up(&rdev->irq.vblank_queue);
+ if (rdev->irq.crtc_vblank_int[0]) {
+ drm_handle_vblank(rdev->ddev, 0);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[0]) {
+ radeon_crtc_handle_flip(rdev, 0);
+ queue_unpin = true;
+ }
}
if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
- drm_handle_vblank(rdev->ddev, 1);
- rdev->pm.vblank_sync = true;
- wake_up(&rdev->irq.vblank_queue);
+ if (rdev->irq.crtc_vblank_int[1]) {
+ drm_handle_vblank(rdev->ddev, 1);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[1]) {
+ radeon_crtc_handle_flip(rdev, 1);
+ queue_unpin = true;
+ }
}
if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
queue_hotplug = true;
@@ -632,6 +663,8 @@ int rs600_irq_process(struct radeon_device *rdev)
rdev->irq.gui_idle_acked = false;
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
+ if (queue_unpin)
+ queue_work(rdev->wq, &rdev->unpin_work);
if (rdev->msi_enabled) {
switch (rdev->family) {
case CHIP_RS600:
@@ -28,6 +28,19 @@
#ifndef __RS600D_H__
#define __RS600D_H__
+/* PM4 */
+#define PACKET_TYPE0 0
+#define PACKET_TYPE1 1
+#define PACKET_TYPE2 2
+#define PACKET_TYPE3 3
+
+#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \
+ (((reg) >> 2) & 0xFFFF) | \
+ ((n) & 0x3FFF) << 16)
+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \
+ (((op) & 0xFF) << 8) | \
+ ((n) & 0x3FFF) << 16)
+
/* Registers */
#define R_000040_GEN_INT_CNTL 0x000040
#define S_000040_SCRATCH_INT_MASK(x) (((x) & 0x1) << 18)
@@ -42,6 +42,40 @@
static void rv770_gpu_init(struct radeon_device *rdev);
void rv770_fini(struct radeon_device *rdev);
+void rv770_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+ if (radeon_crtc->crtc_id) {
+ radeon_ring_write(rdev,
+ PACKET0(D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0));
+ radeon_ring_write(rdev, upper_32_bits(crtc_base));
+ } else {
+ radeon_ring_write(rdev,
+ PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0));
+ radeon_ring_write(rdev, upper_32_bits(crtc_base));
+ }
+ radeon_ring_write(rdev,
+ PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+ if (radeon_crtc->crtc_id) {
+ radeon_ring_write(rdev,
+ PACKET0(D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0));
+ radeon_ring_write(rdev, upper_32_bits(crtc_base));
+ } else {
+ radeon_ring_write(rdev,
+ PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0));
+ radeon_ring_write(rdev, upper_32_bits(crtc_base));
+ }
+ radeon_ring_write(rdev,
+ PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0));
+ radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
/* get temperature in millidegrees */
u32 rv770_get_temp(struct radeon_device *rdev)
{
@@ -351,4 +351,43 @@
#define SRBM_STATUS 0x0E50
+#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110
+#define D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914
+#define D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114
+#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118
+#define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c
+#define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c
+#define D1GRPH_UPDATE 0x6144
+# define D1GRPH_SURFACE_UPDATE_PENDING (1 << 2)
+# define D1GRPH_UPDATE_LOCK (1 << 16)
+
+/* PM4 */
+#define PACKET_TYPE0 0
+#define PACKET_TYPE1 1
+#define PACKET_TYPE2 2
+#define PACKET_TYPE3 3
+
+#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \
+ (((reg) >> 2) & 0xFFFF) | \
+ ((n) & 0x3FFF) << 16)
+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \
+ (((op) & 0xFF) << 8) | \
+ ((n) & 0x3FFF) << 16)
+
+#define PACKET3_WAIT_REG_MEM 0x3C
+# define PACKET3_WAIT_REG_MEM_ENGINE(x) ((x) << 8)
+# define ENGINE_ME 0
+# define ENGINE_PFP 1
+# define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
+# define MEM_SPACE_REG 0
+# define MEM_SPACE_MEM 1
+# define PACKET3_WAIT_REG_MEM_FUNCTION(x) ((x) << 0)
+# define FUNCTION_ALWAYS 0
+# define FUNCTION_LT 1
+# define FUNCTION_LTE 2
+# define FUNCTION_E 3
+# define FUNCTION_NE 4
+# define FUNCTION_GTE 5
+# define FUNCTION_GT 6
+
#endif