drm/radeon/kms: fix rc410 suspend/resume.
[safe/jmp/linux-2.6] / drivers / gpu / drm / radeon / radeon_cp.c
index 7a52c46..4f7afc7 100644 (file)
 #include "radeon_drv.h"
 #include "r300_reg.h"
 
-#include "radeon_microcode.h"
-
 #define RADEON_FIFO_DEBUG      0
 
+/* Firmware Names */
+#define FIRMWARE_R100          "radeon/R100_cp.bin"
+#define FIRMWARE_R200          "radeon/R200_cp.bin"
+#define FIRMWARE_R300          "radeon/R300_cp.bin"
+#define FIRMWARE_R420          "radeon/R420_cp.bin"
+#define FIRMWARE_RS690         "radeon/RS690_cp.bin"
+#define FIRMWARE_RS600         "radeon/RS600_cp.bin"
+#define FIRMWARE_R520          "radeon/R520_cp.bin"
+
+MODULE_FIRMWARE(FIRMWARE_R100);
+MODULE_FIRMWARE(FIRMWARE_R200);
+MODULE_FIRMWARE(FIRMWARE_R300);
+MODULE_FIRMWARE(FIRMWARE_R420);
+MODULE_FIRMWARE(FIRMWARE_RS690);
+MODULE_FIRMWARE(FIRMWARE_RS600);
+MODULE_FIRMWARE(FIRMWARE_R520);
+
 static int radeon_do_cleanup_cp(struct drm_device * dev);
 static void radeon_do_cp_start(drm_radeon_private_t * dev_priv);
 
@@ -460,37 +475,34 @@ static void radeon_init_pipes(drm_radeon_private_t *dev_priv)
  */
 
 /* Load the microcode for the CP */
-static void radeon_cp_load_microcode(drm_radeon_private_t * dev_priv)
+static int radeon_cp_init_microcode(drm_radeon_private_t *dev_priv)
 {
-       int i;
+       struct platform_device *pdev;
+       const char *fw_name = NULL;
+       int err;
+
        DRM_DEBUG("\n");
 
-       radeon_do_wait_for_idle(dev_priv);
+       pdev = platform_device_register_simple("radeon_cp", 0, NULL, 0);
+       err = IS_ERR(pdev);
+       if (err) {
+               printk(KERN_ERR "radeon_cp: Failed to register firmware\n");
+               return -EINVAL;
+       }
 
-       RADEON_WRITE(RADEON_CP_ME_RAM_ADDR, 0);
        if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R100) ||
            ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV100) ||
            ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV200) ||
            ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS100) ||
            ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS200)) {
                DRM_INFO("Loading R100 Microcode\n");
-               for (i = 0; i < 256; i++) {
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    R100_cp_microcode[i][1]);
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    R100_cp_microcode[i][0]);
-               }
+               fw_name = FIRMWARE_R100;
        } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R200) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV250) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV280) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS300)) {
                DRM_INFO("Loading R200 Microcode\n");
-               for (i = 0; i < 256; i++) {
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    R200_cp_microcode[i][1]);
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    R200_cp_microcode[i][0]);
-               }
+               fw_name = FIRMWARE_R200;
        } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R300) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R350) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV350) ||
@@ -498,39 +510,19 @@ static void radeon_cp_load_microcode(drm_radeon_private_t * dev_priv)
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS400) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS480)) {
                DRM_INFO("Loading R300 Microcode\n");
-               for (i = 0; i < 256; i++) {
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    R300_cp_microcode[i][1]);
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    R300_cp_microcode[i][0]);
-               }
+               fw_name = FIRMWARE_R300;
        } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R423) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV410)) {
                DRM_INFO("Loading R400 Microcode\n");
-               for (i = 0; i < 256; i++) {
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    R420_cp_microcode[i][1]);
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    R420_cp_microcode[i][0]);
-               }
+               fw_name = FIRMWARE_R420;
        } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS690) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS740)) {
                DRM_INFO("Loading RS690/RS740 Microcode\n");
-               for (i = 0; i < 256; i++) {
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    RS690_cp_microcode[i][1]);
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    RS690_cp_microcode[i][0]);
-               }
+               fw_name = FIRMWARE_RS690;
        } else if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RS600) {
                DRM_INFO("Loading RS600 Microcode\n");
-               for (i = 0; i < 256; i++) {
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    RS600_cp_microcode[i][1]);
-                       RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    RS600_cp_microcode[i][0]);
-               }
+               fw_name = FIRMWARE_RS600;
        } else if (((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV515) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R520) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) ||
@@ -538,11 +530,41 @@ static void radeon_cp_load_microcode(drm_radeon_private_t * dev_priv)
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV560) ||
                   ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV570)) {
                DRM_INFO("Loading R500 Microcode\n");
-               for (i = 0; i < 256; i++) {
+               fw_name = FIRMWARE_R520;
+       }
+
+       err = request_firmware(&dev_priv->me_fw, fw_name, &pdev->dev);
+       platform_device_unregister(pdev);
+       if (err) {
+               printk(KERN_ERR "radeon_cp: Failed to load firmware \"%s\"\n",
+                      fw_name);
+       } else if (dev_priv->me_fw->size % 8) {
+               printk(KERN_ERR
+                      "radeon_cp: Bogus length %zu in firmware \"%s\"\n",
+                      dev_priv->me_fw->size, fw_name);
+               err = -EINVAL;
+               release_firmware(dev_priv->me_fw);
+               dev_priv->me_fw = NULL;
+       }
+       return err;
+}
+
+static void radeon_cp_load_microcode(drm_radeon_private_t *dev_priv)
+{
+       const __be32 *fw_data;
+       int i, size;
+
+       radeon_do_wait_for_idle(dev_priv);
+
+       if (dev_priv->me_fw) {
+               size = dev_priv->me_fw->size / 4;
+               fw_data = (const __be32 *)&dev_priv->me_fw->data[0];
+               RADEON_WRITE(RADEON_CP_ME_RAM_ADDR, 0);
+               for (i = 0; i < size; i += 2) {
                        RADEON_WRITE(RADEON_CP_ME_RAM_DATAH,
-                                    R520_cp_microcode[i][1]);
+                                    be32_to_cpup(&fw_data[i]));
                        RADEON_WRITE(RADEON_CP_ME_RAM_DATAL,
-                                    R520_cp_microcode[i][0]);
+                                    be32_to_cpup(&fw_data[i + 1]));
                }
        }
 }
@@ -594,6 +616,18 @@ static void radeon_do_cp_start(drm_radeon_private_t * dev_priv)
 
        dev_priv->cp_running = 1;
 
+       /* on r420, any DMA from CP to system memory while 2D is active
+        * can cause a hang.  workaround is to queue a CP RESYNC token
+        */
+       if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) {
+               BEGIN_RING(3);
+               OUT_RING(CP_PACKET0(R300_CP_RESYNC_ADDR, 1));
+               OUT_RING(5); /* scratch reg 5 */
+               OUT_RING(0xdeadbeef);
+               ADVANCE_RING();
+               COMMIT_RING();
+       }
+
        BEGIN_RING(8);
        /* isync can only be written through cp on r5xx write it here */
        OUT_RING(CP_PACKET0(RADEON_ISYNC_CNTL, 0));
@@ -631,8 +665,19 @@ static void radeon_do_cp_reset(drm_radeon_private_t * dev_priv)
  */
 static void radeon_do_cp_stop(drm_radeon_private_t * dev_priv)
 {
+       RING_LOCALS;
        DRM_DEBUG("\n");
 
+       /* finish the pending CP_RESYNC token */
+       if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_R420) {
+               BEGIN_RING(2);
+               OUT_RING(CP_PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0));
+               OUT_RING(R300_RB3D_DC_FINISH);
+               ADVANCE_RING();
+               COMMIT_RING();
+               radeon_do_wait_for_idle(dev_priv);
+       }
+
        RADEON_WRITE(RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS);
 
        dev_priv->cp_running = 0;
@@ -1495,6 +1540,14 @@ static int radeon_do_init_cp(struct drm_device *dev, drm_radeon_init_t *init,
                radeon_set_pcigart(dev_priv, 1);
        }
 
+       if (!dev_priv->me_fw) {
+               int err = radeon_cp_init_microcode(dev_priv);
+               if (err) {
+                       DRM_ERROR("Failed to load firmware!\n");
+                       radeon_do_cleanup_cp(dev);
+                       return err;
+               }
+       }
        radeon_cp_load_microcode(dev_priv);
        radeon_cp_init_ring_buffer(dev, dev_priv, file_priv);
 
@@ -1764,6 +1817,14 @@ void radeon_do_release(struct drm_device * dev)
                        r600_do_cleanup_cp(dev);
                else
                        radeon_do_cleanup_cp(dev);
+               if (dev_priv->me_fw) {
+                       release_firmware(dev_priv->me_fw);
+                       dev_priv->me_fw = NULL;
+               }
+               if (dev_priv->pfp_fw) {
+                       release_firmware(dev_priv->pfp_fw);
+                       dev_priv->pfp_fw = NULL;
+               }
        }
 }