MGS-6197 [#imx-2532] GPU crash when changing gpu_govern mode
authorNicușor Cîțu <nicusor.citu@nxp.com>
Mon, 5 Apr 2021 06:56:06 +0000 (09:56 +0300)
committerNicușor Cîțu <nicusor.citu@nxp.com>
Wed, 12 May 2021 11:27:17 +0000 (14:27 +0300)
Add gckGALDEVICE_Suspend() and gckGALDEVICE_Resume() to be used by both
gpu_suspend/gpu_resume as well as by gpu_govern_store.
The suspendSemaphore will take care that those are kept running on race.

Signed-off-by: Nicușor Cîțu <nicusor.citu@nxp.com>
drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.c
drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_device.h
drivers/mxc/gpu-viv/hal/os/linux/kernel/gc_hal_kernel_driver.c
drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c

index d3d4da1..ebbc31d 100644 (file)
@@ -2465,6 +2465,9 @@ gckGALDEVICE_Construct(
     gcmkONERROR(_SetupExternalSRAMVidMem(device));
 #endif
 
+    /* Create the suspend semaphore. */
+    gcmkONERROR(gckOS_CreateSemaphore(device->os, &device->suspendSemaphore));
+
     /* Initialize the kernel thread semaphores. */
     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
     {
@@ -2740,6 +2743,11 @@ gckGALDEVICE_Destroy(
             }
         }
 
+        /* Destroy the suspend semaphore. */
+        if (Device->suspendSemaphore)
+        {
+            gcmkVERIFY_OK(gckOS_DestroySemaphore(Device->os, Device->suspendSemaphore));
+        }
 
         if (Device->taos)
         {
@@ -2924,3 +2932,252 @@ OnError:
     return status;
 }
 
+/*******************************************************************************
+**
+**  gckGALDEVICE_Suspend
+**
+**  Suspend the gal device to specific state.
+**
+**  INPUT:
+**
+**      gckGALDEVICE Device
+**          Pointer to an gckGALDEVICE object.
+**
+**      gceCHIPPOWERSTATE State
+**          State to suspend.
+**
+**  OUTPUT:
+**
+**      Nothing.
+**
+**  RETURNS:
+**
+**      gcvSTATUS_OK
+**          Suspend successfully.
+*/
+gceSTATUS
+gckGALDEVICE_Suspend(
+    IN gckGALDEVICE Device,
+    IN gceCHIPPOWERSTATE State
+    )
+{
+    gctUINT i;
+    gceSTATUS status;
+    gckHARDWARE hardware;
+    gceCHIPPOWERSTATE currentState = gcvPOWER_INVALID;
+
+    gcmkHEADER_ARG("Device=%p", Device);
+
+    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+    {
+        if (Device->kernels[i] == gcvNULL)
+        {
+            continue;
+        }
+        Device->statesStored[i] = gcvPOWER_INVALID;
+    }
+
+    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+    {
+        if (Device->kernels[i] == gcvNULL)
+        {
+            continue;
+        }
+
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            hardware = Device->kernels[i]->vg->hardware;
+        }
+        else
+#endif
+        {
+            hardware = Device->kernels[i]->hardware;
+        }
+
+        /* Query state. */
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            gcmkONERROR(gckVGHARDWARE_QueryPowerManagementState(hardware,
+                    &currentState));
+        }
+        else
+#endif
+        {
+            gcmkONERROR(gckHARDWARE_QueryPowerState(hardware, &currentState));
+        }
+
+        /* Store state. */
+        Device->statesStored[i] = currentState;
+
+        /* Pull up power to flush GPU command buffer before suspending. */
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            gcmkONERROR(gckVGHARDWARE_SetPowerState(hardware, gcvPOWER_ON));
+        }
+        else
+#endif
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerState(hardware, gcvPOWER_ON));
+        }
+
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            gcmkONERROR(gckVGHARDWARE_SetPowerState(hardware, State));
+        }
+        else
+#endif
+        {
+            gcmkONERROR(gckHARDWARE_SetPowerState(hardware, State));
+        }
+    }
+
+    gcmkFOOTER_NO();
+    return gcvSTATUS_OK;
+
+OnError:
+    /* Roll back the state for touched cores. */
+    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+    {
+        if (Device->kernels[i] == gcvNULL)
+        {
+            continue;
+        }
+
+        if (Device->statesStored[i] == gcvPOWER_INVALID)
+        {
+            continue;
+        }
+
+        /* Reset stored state. */
+        Device->statesStored[i] = gcvPOWER_INVALID;
+    }
+
+    gcmkFOOTER();
+    return status;
+}
+
+/*******************************************************************************
+**
+**  gckGALDEVICE_Resume
+**
+**  Resume the gal device.
+**
+**  INPUT:
+**
+**      gckGALDEVICE Device
+**          Pointer to an gckGALDEVICE object.
+**
+**  OUTPUT:
+**
+**      Nothing.
+**
+**  RETURNS:
+**
+**      gcvSTATUS_OK
+**          Resume successfully.
+*/
+gceSTATUS
+gckGALDEVICE_Resume(
+    IN gckGALDEVICE Device
+    )
+{
+    gctUINT i;
+    gceSTATUS status;
+    gckHARDWARE hardware;
+    gceCHIPPOWERSTATE state;
+
+    gcmkHEADER_ARG("Device=%p", Device);
+
+    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
+    {
+        if (Device->kernels[i] == gcvNULL)
+        {
+            continue;
+        }
+
+        if (Device->statesStored[i] == gcvPOWER_INVALID)
+        {
+            continue;
+        }
+
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            hardware = Device->kernels[i]->vg->hardware;
+        }
+        else
+#endif
+        {
+            hardware = Device->kernels[i]->hardware;
+        }
+
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            gcmkERR_RETURN(gckVGHARDWARE_SetPowerState(hardware, gcvPOWER_ON));
+        }
+        else
+#endif
+        {
+            gcmkERR_RETURN(gckHARDWARE_SetPowerState(hardware, gcvPOWER_ON));
+        }
+
+        /* Convert global state to corresponding internal state. */
+        switch (Device->statesStored[i])
+        {
+        case gcvPOWER_ON:
+            state = gcvPOWER_ON_AUTO;
+            break;
+        case gcvPOWER_IDLE:
+            state = gcvPOWER_IDLE_BROADCAST;
+            break;
+        case gcvPOWER_SUSPEND:
+            state = gcvPOWER_SUSPEND_BROADCAST;
+            break;
+        case gcvPOWER_OFF:
+            state = gcvPOWER_OFF_BROADCAST;
+            break;
+        default:
+            state = Device->statesStored[i];
+            break;
+        }
+
+        /* Restore state. */
+#if gcdENABLE_VG
+        if (i == gcvCORE_VG)
+        {
+            gcmkERR_RETURN(gckVGHARDWARE_SetPowerState(hardware, state));
+        }
+        else
+#endif
+        {
+            gctINT j = 0;
+
+            for (; j < 100; j++)
+            {
+                status = gckHARDWARE_SetPowerState(hardware, state);
+
+                if ((state != gcvPOWER_OFF_BROADCAST &&
+                        state != gcvPOWER_SUSPEND_BROADCAST) ||
+                    status != gcvSTATUS_CHIP_NOT_READY)
+                {
+                    break;
+                }
+
+                gcmkVERIFY_OK(gckOS_Delay(hardware->os, 10));
+            }
+
+            gcmkERR_RETURN(status);
+        }
+
+        /* Reset stored state. */
+        Device->statesStored[i] = gcvPOWER_INVALID;
+    }
+
+    gcmkFOOTER();
+    return status;
+}
index e523325..45e70fa 100644 (file)
@@ -131,6 +131,7 @@ typedef struct _gckGALDEVICE
 
     /* States before suspend. */
     gceCHIPPOWERSTATE   statesStored[gcdMAX_GPU_COUNT];
+    gctPOINTER          suspendSemaphore;
 
     gcsDEBUGFS_DIR      debugfsDir;
 
@@ -160,6 +161,17 @@ typedef struct _gcsHAL_PRIVATE_DATA
 }
 gcsHAL_PRIVATE_DATA, * gcsHAL_PRIVATE_DATA_PTR;
 
+gceSTATUS
+gckGALDEVICE_Suspend(
+    IN gckGALDEVICE Device,
+    IN gceCHIPPOWERSTATE State
+    );
+
+gceSTATUS
+gckGALDEVICE_Resume(
+    IN gckGALDEVICE Device
+    );
+
 gceSTATUS
 gckGALDEVICE_Start(
     IN gckGALDEVICE Device
index 7f6f2ac..7e941f5 100644 (file)
@@ -1402,162 +1402,54 @@ static int gpu_remove(struct platform_device *pdev)
 static int gpu_suspend(struct platform_device *dev, pm_message_t state)
 {
     gceSTATUS status;
-    gckGALDEVICE device;
-    gctINT i;
-
-    device = platform_get_drvdata(dev);
+    gctBOOL sem_acquired = gcvFALSE;
+    gckGALDEVICE device = platform_get_drvdata(dev);
 
     if (!device)
     {
         return -1;
     }
 
-    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
-    {
-        if (device->kernels[i] != gcvNULL)
-        {
-            /* Store states. */
-#if gcdENABLE_VG
-            if (i == gcvCORE_VG)
-            {
-                status = gckVGHARDWARE_QueryPowerManagementState(device->kernels[i]->vg->hardware, &device->statesStored[i]);
-            }
-            else
-#endif
-            {
-                status = gckHARDWARE_QueryPowerState(device->kernels[i]->hardware, &device->statesStored[i]);
-            }
-
-            if (gcmIS_ERROR(status))
-            {
-                return -1;
-            }
-
-            /* need pull up power to flush gpu command buffer before suspend */
-#if gcdENABLE_VG
-            if (i == gcvCORE_VG)
-            {
-                status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, gcvPOWER_ON);
-            }
-            else
-#endif
-            {
-                status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, gcvPOWER_ON);
-            }
+    /* Acquire the suspend management semaphore. */
+    gcmkONERROR(gckOS_AcquireSemaphore(device->os,
+            device->suspendSemaphore));
 
-            if (gcmIS_ERROR(status))
-            {
-                return -1;
-            }
+    sem_acquired = gcvTRUE;
 
-#if gcdENABLE_VG
-            if (i == gcvCORE_VG)
-            {
-                status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, gcvPOWER_OFF);
-            }
-            else
-#endif
-            {
-                status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, gcvPOWER_OFF);
-            }
+    /* Suspend the GPU to off state. */
+    gcmkONERROR(gckGALDEVICE_Suspend(device, gcvPOWER_OFF));
 
-            if (gcmIS_ERROR(status))
-            {
-                return -1;
-            }
+    return 0;
 
-        }
+OnError:
+    /* Release the suspend semaphore. */
+    if (sem_acquired)
+    {
+        gcmkVERIFY_OK(gckOS_ReleaseSemaphore(device->os,
+                device->suspendSemaphore));
     }
 
-    return 0;
+    return -1;
 }
 
 static int gpu_resume(struct platform_device *dev)
 {
     gceSTATUS status;
-    gckGALDEVICE device;
-    gctINT i;
-    gceCHIPPOWERSTATE   statesStored;
-
-    device = platform_get_drvdata(dev);
+    gckGALDEVICE device = platform_get_drvdata(dev);
 
     if (!device)
     {
         return -1;
     }
 
-    for (i = 0; i < gcdMAX_GPU_COUNT; i++)
-    {
-        if (device->kernels[i] != gcvNULL)
-        {
-#if gcdENABLE_VG
-            if (i == gcvCORE_VG)
-            {
-                status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, gcvPOWER_ON);
-            }
-            else
-#endif
-            {
-                status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, gcvPOWER_ON);
-            }
-
-            if (gcmIS_ERROR(status))
-            {
-                return -1;
-            }
-
-            /* Convert global state to crossponding internal state. */
-            switch(device->statesStored[i])
-            {
-            case gcvPOWER_ON:
-                statesStored = gcvPOWER_ON_AUTO;
-                break;
-            case gcvPOWER_IDLE:
-                statesStored = gcvPOWER_IDLE_BROADCAST;
-                break;
-            case gcvPOWER_SUSPEND:
-                statesStored = gcvPOWER_SUSPEND_BROADCAST;
-                break;
-            case gcvPOWER_OFF:
-                statesStored = gcvPOWER_OFF_BROADCAST;
-                break;
-            default:
-                statesStored = device->statesStored[i];
-                break;
-            }
-
-            /* Restore states. */
-#if gcdENABLE_VG
-            if (i == gcvCORE_VG)
-            {
-                status = gckVGHARDWARE_SetPowerState(device->kernels[i]->vg->hardware, statesStored);
-            }
-            else
-#endif
-            {
-                gctINT j = 0;
-
-                for (; j < 100; j++)
-                {
-                    status = gckHARDWARE_SetPowerState(device->kernels[i]->hardware, statesStored);
-
-                    if (( statesStored != gcvPOWER_OFF_BROADCAST
-                       && statesStored != gcvPOWER_SUSPEND_BROADCAST)
-                       || status != gcvSTATUS_CHIP_NOT_READY)
-                    {
-                        break;
-                    }
+    /* Resume GPU to previous state. */
+    status = gckGALDEVICE_Resume(device);
 
-                    gcmkVERIFY_OK(gckOS_Delay(device->kernels[i]->os, 10));
-                };
-            }
+    /* Release the suspend semaphore. */
+    gcmkVERIFY_OK(gckOS_ReleaseSemaphore(device->os, device->suspendSemaphore));
 
-            if (gcmIS_ERROR(status))
-            {
-                return -1;
-            }
-        }
-    }
+    if (gcmIS_ERROR(status))
+        return -1;
 
     return 0;
 }
index 97e88a9..924691e 100644 (file)
@@ -554,6 +554,14 @@ static ssize_t gpu_govern_store(struct device_driver *dev, const char *buf, size
     struct imx_priv *priv = &imxPriv;
     int core = gcvCORE_MAJOR;
     int i;
+    gckGALDEVICE device = platform_get_drvdata(pdevice);
+    gctBOOL sem_acquired = gcvFALSE;
+    gceSTATUS status;
+
+    if (!device)
+    {
+        return count;
+    }
 
     for (i = 0; i < GOVERN_COUNT; i++)
     {
@@ -568,6 +576,14 @@ static ssize_t gpu_govern_store(struct device_driver *dev, const char *buf, size
         return count;
     }
 
+    /* Acquire the suspend semaphore. */
+    gcmkONERROR(gckOS_AcquireSemaphore(device->os, device->suspendSemaphore));
+
+    sem_acquired = gcvTRUE;
+
+    /* Suspend the GPU to idle state. */
+    gcmkONERROR(gckGALDEVICE_Suspend(device, gcvPOWER_IDLE));
+
     core_freq   = priv->imx_gpu_govern.core_clk_freq[i];
     shader_freq = priv->imx_gpu_govern.shader_clk_freq[i];
     priv->imx_gpu_govern.current_mode = i;
@@ -591,6 +607,17 @@ static ssize_t gpu_govern_store(struct device_driver *dev, const char *buf, size
         }
     }
 
+    /* Resume GPU to previous state. */
+    gcmVERIFY_OK(gckGALDEVICE_Resume(device));
+
+OnError:
+    /* Release the suspend semaphore. */
+    if (sem_acquired)
+    {
+        gcmkVERIFY_OK(gckOS_ReleaseSemaphore(device->os,
+                device->suspendSemaphore));
+    }
+
     return count;
 }