diff mbox

drm/radeon: cleanup and fix crtc while programming mc

Message ID 1343345088-950-1-git-send-email-j.glisse@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jerome Glisse July 26, 2012, 11:24 p.m. UTC
From: Jerome Glisse <jglisse@redhat.com>

When we change start address of vram for the GPU memory controller
we need to make sure that nothing in the GPU still use the old vram
address. This patch cleanup and fix crtc address.

However there is still someissue somewhere if we reenable the crtc
after updating the address at which they sancout. So to avoid any
issue disable crtc. Once we want to do flicker less transition
btw uefi and radeon we will probably want to revisit how we program
the GPU memory controller.

This probably fix :
https://bugs.freedesktop.org/show_bug.cgi?id=52467
https://bugs.freedesktop.org/show_bug.cgi?id=42373

Cc: <stable@vger.kernel.org>
Signed-off-by: Jerome Glisse <jglisse@redhat.com>
---
 drivers/gpu/drm/radeon/evergreen.c   |  178 ++++++++++++++++------------------
 drivers/gpu/drm/radeon/radeon_asic.h |   18 +++-
 2 files changed, 99 insertions(+), 97 deletions(-)

Comments

Alex Deucher July 27, 2012, 12:35 a.m. UTC | #1
On Thu, Jul 26, 2012 at 7:24 PM,  <j.glisse@gmail.com> wrote:
> From: Jerome Glisse <jglisse@redhat.com>
>
> When we change start address of vram for the GPU memory controller
> we need to make sure that nothing in the GPU still use the old vram
> address. This patch cleanup and fix crtc address.
>
> However there is still someissue somewhere if we reenable the crtc
> after updating the address at which they sancout. So to avoid any
> issue disable crtc. Once we want to do flicker less transition
> btw uefi and radeon we will probably want to revisit how we program
> the GPU memory controller.
>
> This probably fix :
> https://bugs.freedesktop.org/show_bug.cgi?id=52467
> https://bugs.freedesktop.org/show_bug.cgi?id=42373
>
> Cc: <stable@vger.kernel.org>

this should be stable@vger.kernel.org

> Signed-off-by: Jerome Glisse <jglisse@redhat.com>

Also, I think we probably need to poll on bit 16
(CRTC_CURRENT_MASTER_EN_STATE) of CRTC_CONTROL to make sure the crtc
is actually disabled.  Something like the following in mc_stop():

WREG32(CRTC_CONTROL, 0);
while (RREG32(CRTC_CONTROL & CRTC_CURRENT_MASTER_EN_STATE))
    udelay(1);

Any chance you want to fix the rv515_mc_stop() and rv515_mc_resume()
functions as well?

Alex

> ---
>  drivers/gpu/drm/radeon/evergreen.c   |  178 ++++++++++++++++------------------
>  drivers/gpu/drm/radeon/radeon_asic.h |   18 +++-
>  2 files changed, 99 insertions(+), 97 deletions(-)
>
> diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
> index e585a3b..c6ede66 100644
> --- a/drivers/gpu/drm/radeon/evergreen.c
> +++ b/drivers/gpu/drm/radeon/evergreen.c
> @@ -1227,70 +1227,94 @@ void evergreen_agp_enable(struct radeon_device *rdev)
>         WREG32(VM_CONTEXT1_CNTL, 0);
>  }
>
> +static void evergreen_crtc_save(struct radeon_device *rdev, struct evergreen_mc_save *save, unsigned idx)
> +{
> +       save->crtc[idx].paddress = 0;
> +       save->crtc[idx].saddress = 0;
> +       save->crtc[idx].crtc_control = 0;
> +
> +       if (idx >= rdev->num_crtc) {
> +               /* it seems accessing non existant crtc trigger high latency */
> +               return;
> +       }
> +
> +       save->crtc[idx].paddress = RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + save->crtc[idx].offset);
> +       save->crtc[idx].paddress |= ((uint64_t)RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset)) << 32ULL;
> +       save->crtc[idx].saddress = RREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + save->crtc[idx].offset);
> +       save->crtc[idx].saddress |= ((uint64_t)RREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset)) << 32ULL;
> +       save->crtc[idx].crtc_control = RREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset);
> +       /* We need to disable all crtc as for some reason crtc scanout
> +        * base address does not happen properly when changing the
> +        * mc vram start address. Don't remove this or you will break
> +        * things on uefi system.
> +        */
> +       save->crtc[idx].crtc_control = 0;
> +       save->crtc[idx].vga_control = RREG32(save->crtc[idx].vga_control_offset);
> +
> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
> +       WREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset, 0);
> +       WREG32(save->crtc[idx].vga_control_offset, 0);
> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
> +}
> +
>  void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
>  {
> -       save->vga_control[0] = RREG32(D1VGA_CONTROL);
> -       save->vga_control[1] = RREG32(D2VGA_CONTROL);
> +       save->crtc[0].offset = EVERGREEN_CRTC0_REGISTER_OFFSET;
> +       save->crtc[1].offset = EVERGREEN_CRTC1_REGISTER_OFFSET;
> +       save->crtc[2].offset = EVERGREEN_CRTC2_REGISTER_OFFSET;
> +       save->crtc[3].offset = EVERGREEN_CRTC3_REGISTER_OFFSET;
> +       save->crtc[4].offset = EVERGREEN_CRTC4_REGISTER_OFFSET;
> +       save->crtc[5].offset = EVERGREEN_CRTC5_REGISTER_OFFSET;
> +       save->crtc[0].vga_control_offset = D1VGA_CONTROL;
> +       save->crtc[1].vga_control_offset = D2VGA_CONTROL;
> +       save->crtc[2].vga_control_offset = EVERGREEN_D3VGA_CONTROL;
> +       save->crtc[3].vga_control_offset = EVERGREEN_D4VGA_CONTROL;
> +       save->crtc[4].vga_control_offset = EVERGREEN_D5VGA_CONTROL;
> +       save->crtc[5].vga_control_offset = EVERGREEN_D6VGA_CONTROL;
> +
> +       save->fb_address = (uint64_t)(RREG32(MC_VM_FB_LOCATION) & 0xffff) << 24ULL;
>         save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
>         save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
> -       save->crtc_control[0] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET);
> -       save->crtc_control[1] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET);
> -       if (rdev->num_crtc >= 4) {
> -               save->vga_control[2] = RREG32(EVERGREEN_D3VGA_CONTROL);
> -               save->vga_control[3] = RREG32(EVERGREEN_D4VGA_CONTROL);
> -               save->crtc_control[2] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET);
> -               save->crtc_control[3] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               save->vga_control[4] = RREG32(EVERGREEN_D5VGA_CONTROL);
> -               save->vga_control[5] = RREG32(EVERGREEN_D6VGA_CONTROL);
> -               save->crtc_control[4] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET);
> -               save->crtc_control[5] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
> -       }
>
>         /* Stop all video */
>         WREG32(VGA_RENDER_CONTROL, 0);
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
> -       }
> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
> -       }
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
> +       evergreen_crtc_save(rdev, save, 0);
> +       evergreen_crtc_save(rdev, save, 1);
> +       evergreen_crtc_save(rdev, save, 2);
> +       evergreen_crtc_save(rdev, save, 3);
> +       evergreen_crtc_save(rdev, save, 4);
> +       evergreen_crtc_save(rdev, save, 5);
> +}
> +
> +static void evergreen_crtc_restore(struct radeon_device *rdev, struct evergreen_mc_save *save, unsigned idx)
> +{
> +       if (idx >= rdev->num_crtc) {
> +               /* it seems accessing non existant crtc trigger high latency */
> +               return;
>         }
>
> -       WREG32(D1VGA_CONTROL, 0);
> -       WREG32(D2VGA_CONTROL, 0);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_D3VGA_CONTROL, 0);
> -               WREG32(EVERGREEN_D4VGA_CONTROL, 0);
> +       /* update new primary and secondary address */
> +       if (save->crtc[idx].paddress) {
> +               save->crtc[idx].paddress -= save->fb_address;
>         }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_D5VGA_CONTROL, 0);
> -               WREG32(EVERGREEN_D6VGA_CONTROL, 0);
> +       save->crtc[idx].paddress += rdev->mc.vram_start;
> +       if (save->crtc[idx].saddress) {
> +               save->crtc[idx].saddress -= save->fb_address;
>         }
> +       save->crtc[idx].saddress += rdev->mc.vram_start;
> +
> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
> +       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + save->crtc[idx].offset, lower_32_bits(save->crtc[idx].paddress));
> +       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset, upper_32_bits(save->crtc[idx].paddress) & 0xff);
> +       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + save->crtc[idx].offset, lower_32_bits(save->crtc[idx].saddress));
> +       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset, upper_32_bits(save->crtc[idx].saddress) & 0xff);
> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
> +       WREG32(EVERGREEN_MASTER_UPDATE_MODE + save->crtc[idx].offset, 0);
> +
> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
> +       WREG32(save->crtc[idx].vga_control_offset, save->crtc[idx].vga_control);
> +       WREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset, save->crtc[idx].crtc_control | EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE);
> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
>  }
>
>  void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save)
> @@ -1357,47 +1381,15 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s
>         /* Unlock host access */
>         WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
>         mdelay(1);
> +
>         /* Restore video state */
> -       WREG32(D1VGA_CONTROL, save->vga_control[0]);
> -       WREG32(D2VGA_CONTROL, save->vga_control[1]);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_D3VGA_CONTROL, save->vga_control[2]);
> -               WREG32(EVERGREEN_D4VGA_CONTROL, save->vga_control[3]);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_D5VGA_CONTROL, save->vga_control[4]);
> -               WREG32(EVERGREEN_D6VGA_CONTROL, save->vga_control[5]);
> -       }
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
> -       }
> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, save->crtc_control[0]);
> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, save->crtc_control[1]);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, save->crtc_control[2]);
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, save->crtc_control[3]);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, save->crtc_control[4]);
> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, save->crtc_control[5]);
> -       }
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
> -       if (rdev->num_crtc >= 4) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
> -       }
> -       if (rdev->num_crtc >= 6) {
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
> -       }
> +       evergreen_crtc_restore(rdev, save, 0);
> +       evergreen_crtc_restore(rdev, save, 1);
> +       evergreen_crtc_restore(rdev, save, 2);
> +       evergreen_crtc_restore(rdev, save, 3);
> +       evergreen_crtc_restore(rdev, save, 4);
> +       evergreen_crtc_restore(rdev, save, 5);
> +
>         WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
>  }
>
> diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
> index f4af243..d450218 100644
> --- a/drivers/gpu/drm/radeon/radeon_asic.h
> +++ b/drivers/gpu/drm/radeon/radeon_asic.h
> @@ -388,12 +388,22 @@ void r700_cp_fini(struct radeon_device *rdev);
>  /*
>   * evergreen
>   */
> +struct evergreen_crtc_save {
> +       uint64_t                        paddress;
> +       uint64_t                        saddress;
> +       uint32_t                        crtc_control;
> +       uint32_t                        vga_control;
> +       uint32_t                        offset;
> +       uint32_t                        vga_control_offset;
> +};
> +
>  struct evergreen_mc_save {
> -       u32 vga_control[6];
> -       u32 vga_render_control;
> -       u32 vga_hdp_control;
> -       u32 crtc_control[6];
> +       struct evergreen_crtc_save      crtc[RADEON_MAX_CRTCS];
> +       uint64_t                        fb_address;
> +       uint32_t                        vga_render_control;
> +       uint32_t                        vga_hdp_control;
>  };
> +
>  void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev);
>  int evergreen_init(struct radeon_device *rdev);
>  void evergreen_fini(struct radeon_device *rdev);
> --
> 1.7.10.4
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Jerome Glisse July 27, 2012, 2:04 a.m. UTC | #2
On Thu, Jul 26, 2012 at 8:35 PM, Alex Deucher <alexdeucher@gmail.com> wrote:
> On Thu, Jul 26, 2012 at 7:24 PM,  <j.glisse@gmail.com> wrote:
>> From: Jerome Glisse <jglisse@redhat.com>
>>
>> When we change start address of vram for the GPU memory controller
>> we need to make sure that nothing in the GPU still use the old vram
>> address. This patch cleanup and fix crtc address.
>>
>> However there is still someissue somewhere if we reenable the crtc
>> after updating the address at which they sancout. So to avoid any
>> issue disable crtc. Once we want to do flicker less transition
>> btw uefi and radeon we will probably want to revisit how we program
>> the GPU memory controller.
>>
>> This probably fix :
>> https://bugs.freedesktop.org/show_bug.cgi?id=52467
>> https://bugs.freedesktop.org/show_bug.cgi?id=42373
>>
>> Cc: <stable@vger.kernel.org>
>
> this should be stable@vger.kernel.org
>
>> Signed-off-by: Jerome Glisse <jglisse@redhat.com>
>
> Also, I think we probably need to poll on bit 16
> (CRTC_CURRENT_MASTER_EN_STATE) of CRTC_CONTROL to make sure the crtc
> is actually disabled.  Something like the following in mc_stop():
>
> WREG32(CRTC_CONTROL, 0);
> while (RREG32(CRTC_CONTROL & CRTC_CURRENT_MASTER_EN_STATE))
>     udelay(1);
>
> Any chance you want to fix the rv515_mc_stop() and rv515_mc_resume()
> functions as well?
>
> Alex

I will respin with splitting out what fix and what just
improve/cleanup the code. I might change rv515 but i don't think it
will hit such issue, on rv515 crtc is enabled through VGA, here the
issue seems to only show up with uefi, i am trying to grab some non
uefi laptop with same gpu to check.

Cheers,
Jerome

>
>> ---
>>  drivers/gpu/drm/radeon/evergreen.c   |  178 ++++++++++++++++------------------
>>  drivers/gpu/drm/radeon/radeon_asic.h |   18 +++-
>>  2 files changed, 99 insertions(+), 97 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
>> index e585a3b..c6ede66 100644
>> --- a/drivers/gpu/drm/radeon/evergreen.c
>> +++ b/drivers/gpu/drm/radeon/evergreen.c
>> @@ -1227,70 +1227,94 @@ void evergreen_agp_enable(struct radeon_device *rdev)
>>         WREG32(VM_CONTEXT1_CNTL, 0);
>>  }
>>
>> +static void evergreen_crtc_save(struct radeon_device *rdev, struct evergreen_mc_save *save, unsigned idx)
>> +{
>> +       save->crtc[idx].paddress = 0;
>> +       save->crtc[idx].saddress = 0;
>> +       save->crtc[idx].crtc_control = 0;
>> +
>> +       if (idx >= rdev->num_crtc) {
>> +               /* it seems accessing non existant crtc trigger high latency */
>> +               return;
>> +       }
>> +
>> +       save->crtc[idx].paddress = RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + save->crtc[idx].offset);
>> +       save->crtc[idx].paddress |= ((uint64_t)RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset)) << 32ULL;
>> +       save->crtc[idx].saddress = RREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + save->crtc[idx].offset);
>> +       save->crtc[idx].saddress |= ((uint64_t)RREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset)) << 32ULL;
>> +       save->crtc[idx].crtc_control = RREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset);
>> +       /* We need to disable all crtc as for some reason crtc scanout
>> +        * base address does not happen properly when changing the
>> +        * mc vram start address. Don't remove this or you will break
>> +        * things on uefi system.
>> +        */
>> +       save->crtc[idx].crtc_control = 0;
>> +       save->crtc[idx].vga_control = RREG32(save->crtc[idx].vga_control_offset);
>> +
>> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
>> +       WREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset, 0);
>> +       WREG32(save->crtc[idx].vga_control_offset, 0);
>> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
>> +}
>> +
>>  void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
>>  {
>> -       save->vga_control[0] = RREG32(D1VGA_CONTROL);
>> -       save->vga_control[1] = RREG32(D2VGA_CONTROL);
>> +       save->crtc[0].offset = EVERGREEN_CRTC0_REGISTER_OFFSET;
>> +       save->crtc[1].offset = EVERGREEN_CRTC1_REGISTER_OFFSET;
>> +       save->crtc[2].offset = EVERGREEN_CRTC2_REGISTER_OFFSET;
>> +       save->crtc[3].offset = EVERGREEN_CRTC3_REGISTER_OFFSET;
>> +       save->crtc[4].offset = EVERGREEN_CRTC4_REGISTER_OFFSET;
>> +       save->crtc[5].offset = EVERGREEN_CRTC5_REGISTER_OFFSET;
>> +       save->crtc[0].vga_control_offset = D1VGA_CONTROL;
>> +       save->crtc[1].vga_control_offset = D2VGA_CONTROL;
>> +       save->crtc[2].vga_control_offset = EVERGREEN_D3VGA_CONTROL;
>> +       save->crtc[3].vga_control_offset = EVERGREEN_D4VGA_CONTROL;
>> +       save->crtc[4].vga_control_offset = EVERGREEN_D5VGA_CONTROL;
>> +       save->crtc[5].vga_control_offset = EVERGREEN_D6VGA_CONTROL;
>> +
>> +       save->fb_address = (uint64_t)(RREG32(MC_VM_FB_LOCATION) & 0xffff) << 24ULL;
>>         save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
>>         save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
>> -       save->crtc_control[0] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET);
>> -       save->crtc_control[1] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET);
>> -       if (rdev->num_crtc >= 4) {
>> -               save->vga_control[2] = RREG32(EVERGREEN_D3VGA_CONTROL);
>> -               save->vga_control[3] = RREG32(EVERGREEN_D4VGA_CONTROL);
>> -               save->crtc_control[2] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET);
>> -               save->crtc_control[3] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               save->vga_control[4] = RREG32(EVERGREEN_D5VGA_CONTROL);
>> -               save->vga_control[5] = RREG32(EVERGREEN_D6VGA_CONTROL);
>> -               save->crtc_control[4] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET);
>> -               save->crtc_control[5] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
>> -       }
>>
>>         /* Stop all video */
>>         WREG32(VGA_RENDER_CONTROL, 0);
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
>> -       }
>> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
>> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
>> -       }
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
>> +       evergreen_crtc_save(rdev, save, 0);
>> +       evergreen_crtc_save(rdev, save, 1);
>> +       evergreen_crtc_save(rdev, save, 2);
>> +       evergreen_crtc_save(rdev, save, 3);
>> +       evergreen_crtc_save(rdev, save, 4);
>> +       evergreen_crtc_save(rdev, save, 5);
>> +}
>> +
>> +static void evergreen_crtc_restore(struct radeon_device *rdev, struct evergreen_mc_save *save, unsigned idx)
>> +{
>> +       if (idx >= rdev->num_crtc) {
>> +               /* it seems accessing non existant crtc trigger high latency */
>> +               return;
>>         }
>>
>> -       WREG32(D1VGA_CONTROL, 0);
>> -       WREG32(D2VGA_CONTROL, 0);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_D3VGA_CONTROL, 0);
>> -               WREG32(EVERGREEN_D4VGA_CONTROL, 0);
>> +       /* update new primary and secondary address */
>> +       if (save->crtc[idx].paddress) {
>> +               save->crtc[idx].paddress -= save->fb_address;
>>         }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_D5VGA_CONTROL, 0);
>> -               WREG32(EVERGREEN_D6VGA_CONTROL, 0);
>> +       save->crtc[idx].paddress += rdev->mc.vram_start;
>> +       if (save->crtc[idx].saddress) {
>> +               save->crtc[idx].saddress -= save->fb_address;
>>         }
>> +       save->crtc[idx].saddress += rdev->mc.vram_start;
>> +
>> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
>> +       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + save->crtc[idx].offset, lower_32_bits(save->crtc[idx].paddress));
>> +       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset, upper_32_bits(save->crtc[idx].paddress) & 0xff);
>> +       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + save->crtc[idx].offset, lower_32_bits(save->crtc[idx].saddress));
>> +       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset, upper_32_bits(save->crtc[idx].saddress) & 0xff);
>> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
>> +       WREG32(EVERGREEN_MASTER_UPDATE_MODE + save->crtc[idx].offset, 0);
>> +
>> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
>> +       WREG32(save->crtc[idx].vga_control_offset, save->crtc[idx].vga_control);
>> +       WREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset, save->crtc[idx].crtc_control | EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE);
>> +       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
>>  }
>>
>>  void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save)
>> @@ -1357,47 +1381,15 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s
>>         /* Unlock host access */
>>         WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
>>         mdelay(1);
>> +
>>         /* Restore video state */
>> -       WREG32(D1VGA_CONTROL, save->vga_control[0]);
>> -       WREG32(D2VGA_CONTROL, save->vga_control[1]);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_D3VGA_CONTROL, save->vga_control[2]);
>> -               WREG32(EVERGREEN_D4VGA_CONTROL, save->vga_control[3]);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_D5VGA_CONTROL, save->vga_control[4]);
>> -               WREG32(EVERGREEN_D6VGA_CONTROL, save->vga_control[5]);
>> -       }
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
>> -       }
>> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, save->crtc_control[0]);
>> -       WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, save->crtc_control[1]);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, save->crtc_control[2]);
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, save->crtc_control[3]);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, save->crtc_control[4]);
>> -               WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, save->crtc_control[5]);
>> -       }
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
>> -       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
>> -       if (rdev->num_crtc >= 4) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
>> -       }
>> -       if (rdev->num_crtc >= 6) {
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
>> -               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
>> -       }
>> +       evergreen_crtc_restore(rdev, save, 0);
>> +       evergreen_crtc_restore(rdev, save, 1);
>> +       evergreen_crtc_restore(rdev, save, 2);
>> +       evergreen_crtc_restore(rdev, save, 3);
>> +       evergreen_crtc_restore(rdev, save, 4);
>> +       evergreen_crtc_restore(rdev, save, 5);
>> +
>>         WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
>>  }
>>
>> diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
>> index f4af243..d450218 100644
>> --- a/drivers/gpu/drm/radeon/radeon_asic.h
>> +++ b/drivers/gpu/drm/radeon/radeon_asic.h
>> @@ -388,12 +388,22 @@ void r700_cp_fini(struct radeon_device *rdev);
>>  /*
>>   * evergreen
>>   */
>> +struct evergreen_crtc_save {
>> +       uint64_t                        paddress;
>> +       uint64_t                        saddress;
>> +       uint32_t                        crtc_control;
>> +       uint32_t                        vga_control;
>> +       uint32_t                        offset;
>> +       uint32_t                        vga_control_offset;
>> +};
>> +
>>  struct evergreen_mc_save {
>> -       u32 vga_control[6];
>> -       u32 vga_render_control;
>> -       u32 vga_hdp_control;
>> -       u32 crtc_control[6];
>> +       struct evergreen_crtc_save      crtc[RADEON_MAX_CRTCS];
>> +       uint64_t                        fb_address;
>> +       uint32_t                        vga_render_control;
>> +       uint32_t                        vga_hdp_control;
>>  };
>> +
>>  void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev);
>>  int evergreen_init(struct radeon_device *rdev);
>>  void evergreen_fini(struct radeon_device *rdev);
>> --
>> 1.7.10.4
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
diff mbox

Patch

diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index e585a3b..c6ede66 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1227,70 +1227,94 @@  void evergreen_agp_enable(struct radeon_device *rdev)
 	WREG32(VM_CONTEXT1_CNTL, 0);
 }
 
+static void evergreen_crtc_save(struct radeon_device *rdev, struct evergreen_mc_save *save, unsigned idx)
+{
+	save->crtc[idx].paddress = 0;
+	save->crtc[idx].saddress = 0;
+	save->crtc[idx].crtc_control = 0;
+
+	if (idx >= rdev->num_crtc) {
+		/* it seems accessing non existant crtc trigger high latency */
+		return;
+	}
+
+	save->crtc[idx].paddress = RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + save->crtc[idx].offset);
+	save->crtc[idx].paddress |= ((uint64_t)RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset)) << 32ULL;
+	save->crtc[idx].saddress = RREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + save->crtc[idx].offset);
+	save->crtc[idx].saddress |= ((uint64_t)RREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset)) << 32ULL;
+	save->crtc[idx].crtc_control = RREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset);
+	/* We need to disable all crtc as for some reason crtc scanout
+	 * base address does not happen properly when changing the
+	 * mc vram start address. Don't remove this or you will break
+	 * things on uefi system.
+	 */
+	save->crtc[idx].crtc_control = 0;
+	save->crtc[idx].vga_control = RREG32(save->crtc[idx].vga_control_offset);
+
+	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
+	WREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset, 0);
+	WREG32(save->crtc[idx].vga_control_offset, 0);
+	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
+}
+
 void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
 {
-	save->vga_control[0] = RREG32(D1VGA_CONTROL);
-	save->vga_control[1] = RREG32(D2VGA_CONTROL);
+	save->crtc[0].offset = EVERGREEN_CRTC0_REGISTER_OFFSET;
+	save->crtc[1].offset = EVERGREEN_CRTC1_REGISTER_OFFSET;
+	save->crtc[2].offset = EVERGREEN_CRTC2_REGISTER_OFFSET;
+	save->crtc[3].offset = EVERGREEN_CRTC3_REGISTER_OFFSET;
+	save->crtc[4].offset = EVERGREEN_CRTC4_REGISTER_OFFSET;
+	save->crtc[5].offset = EVERGREEN_CRTC5_REGISTER_OFFSET;
+	save->crtc[0].vga_control_offset = D1VGA_CONTROL;
+	save->crtc[1].vga_control_offset = D2VGA_CONTROL;
+	save->crtc[2].vga_control_offset = EVERGREEN_D3VGA_CONTROL;
+	save->crtc[3].vga_control_offset = EVERGREEN_D4VGA_CONTROL;
+	save->crtc[4].vga_control_offset = EVERGREEN_D5VGA_CONTROL;
+	save->crtc[5].vga_control_offset = EVERGREEN_D6VGA_CONTROL;
+
+	save->fb_address = (uint64_t)(RREG32(MC_VM_FB_LOCATION) & 0xffff) << 24ULL;
 	save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
 	save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
-	save->crtc_control[0] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET);
-	save->crtc_control[1] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET);
-	if (rdev->num_crtc >= 4) {
-		save->vga_control[2] = RREG32(EVERGREEN_D3VGA_CONTROL);
-		save->vga_control[3] = RREG32(EVERGREEN_D4VGA_CONTROL);
-		save->crtc_control[2] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET);
-		save->crtc_control[3] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET);
-	}
-	if (rdev->num_crtc >= 6) {
-		save->vga_control[4] = RREG32(EVERGREEN_D5VGA_CONTROL);
-		save->vga_control[5] = RREG32(EVERGREEN_D6VGA_CONTROL);
-		save->crtc_control[4] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET);
-		save->crtc_control[5] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
-	}
 
 	/* Stop all video */
 	WREG32(VGA_RENDER_CONTROL, 0);
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
-	}
-	WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
-	WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
-	}
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+	evergreen_crtc_save(rdev, save, 0);
+	evergreen_crtc_save(rdev, save, 1);
+	evergreen_crtc_save(rdev, save, 2);
+	evergreen_crtc_save(rdev, save, 3);
+	evergreen_crtc_save(rdev, save, 4);
+	evergreen_crtc_save(rdev, save, 5);
+}
+
+static void evergreen_crtc_restore(struct radeon_device *rdev, struct evergreen_mc_save *save, unsigned idx)
+{
+	if (idx >= rdev->num_crtc) {
+		/* it seems accessing non existant crtc trigger high latency */
+		return;
 	}
 
-	WREG32(D1VGA_CONTROL, 0);
-	WREG32(D2VGA_CONTROL, 0);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_D3VGA_CONTROL, 0);
-		WREG32(EVERGREEN_D4VGA_CONTROL, 0);
+	/* update new primary and secondary address */
+	if (save->crtc[idx].paddress) {
+		save->crtc[idx].paddress -= save->fb_address;
 	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_D5VGA_CONTROL, 0);
-		WREG32(EVERGREEN_D6VGA_CONTROL, 0);
+	save->crtc[idx].paddress += rdev->mc.vram_start;
+	if (save->crtc[idx].saddress) {
+		save->crtc[idx].saddress -= save->fb_address;
 	}
+	save->crtc[idx].saddress += rdev->mc.vram_start;
+
+	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
+	WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + save->crtc[idx].offset, lower_32_bits(save->crtc[idx].paddress));
+	WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset, upper_32_bits(save->crtc[idx].paddress) & 0xff);
+	WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + save->crtc[idx].offset, lower_32_bits(save->crtc[idx].saddress));
+	WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + save->crtc[idx].offset, upper_32_bits(save->crtc[idx].saddress) & 0xff);
+	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
+	WREG32(EVERGREEN_MASTER_UPDATE_MODE + save->crtc[idx].offset, 0);
+
+	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 1);
+	WREG32(save->crtc[idx].vga_control_offset, save->crtc[idx].vga_control);
+	WREG32(EVERGREEN_CRTC_CONTROL + save->crtc[idx].offset, save->crtc[idx].crtc_control | EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE);
+	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + save->crtc[idx].offset, 0);
 }
 
 void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save)
@@ -1357,47 +1381,15 @@  void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s
 	/* Unlock host access */
 	WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
 	mdelay(1);
+
 	/* Restore video state */
-	WREG32(D1VGA_CONTROL, save->vga_control[0]);
-	WREG32(D2VGA_CONTROL, save->vga_control[1]);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_D3VGA_CONTROL, save->vga_control[2]);
-		WREG32(EVERGREEN_D4VGA_CONTROL, save->vga_control[3]);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_D5VGA_CONTROL, save->vga_control[4]);
-		WREG32(EVERGREEN_D6VGA_CONTROL, save->vga_control[5]);
-	}
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
-	}
-	WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, save->crtc_control[0]);
-	WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, save->crtc_control[1]);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, save->crtc_control[2]);
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, save->crtc_control[3]);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, save->crtc_control[4]);
-		WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, save->crtc_control[5]);
-	}
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
-	WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
-	if (rdev->num_crtc >= 4) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
-	}
-	if (rdev->num_crtc >= 6) {
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
-		WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
-	}
+	evergreen_crtc_restore(rdev, save, 0);
+	evergreen_crtc_restore(rdev, save, 1);
+	evergreen_crtc_restore(rdev, save, 2);
+	evergreen_crtc_restore(rdev, save, 3);
+	evergreen_crtc_restore(rdev, save, 4);
+	evergreen_crtc_restore(rdev, save, 5);
+
 	WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
 }
 
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index f4af243..d450218 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -388,12 +388,22 @@  void r700_cp_fini(struct radeon_device *rdev);
 /*
  * evergreen
  */
+struct evergreen_crtc_save {
+	uint64_t			paddress;
+	uint64_t			saddress;
+	uint32_t			crtc_control;
+	uint32_t			vga_control;
+	uint32_t			offset;
+	uint32_t			vga_control_offset;
+};
+
 struct evergreen_mc_save {
-	u32 vga_control[6];
-	u32 vga_render_control;
-	u32 vga_hdp_control;
-	u32 crtc_control[6];
+	struct evergreen_crtc_save	crtc[RADEON_MAX_CRTCS];
+	uint64_t			fb_address;
+	uint32_t			vga_render_control;
+	uint32_t			vga_hdp_control;
 };
+
 void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev);
 int evergreen_init(struct radeon_device *rdev);
 void evergreen_fini(struct radeon_device *rdev);