#include <linux/bitfield.h>
 #include <linux/devfreq.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/soc/qcom/llcc-qcom.h>
 
 #define GPU_PAS_ID 13
        a6xx_gmu_remove(a6xx_gpu);
 
        adreno_gpu_cleanup(adreno_gpu);
+
+       if (a6xx_gpu->opp_table)
+               dev_pm_opp_put_supported_hw(a6xx_gpu->opp_table);
+
        kfree(a6xx_gpu);
 }
 
        return ring->memptrs->rptr = gpu_read(gpu, REG_A6XX_CP_RB_RPTR);
 }
 
+static u32 a618_get_speed_bin(u32 fuse)
+{
+       if (fuse == 0)
+               return 0;
+       else if (fuse == 169)
+               return 1;
+       else if (fuse == 174)
+               return 2;
+
+       return UINT_MAX;
+}
+
+static u32 fuse_to_supp_hw(struct device *dev, u32 revn, u32 fuse)
+{
+       u32 val = UINT_MAX;
+
+       if (revn == 618)
+               val = a618_get_speed_bin(fuse);
+
+       if (val == UINT_MAX) {
+               DRM_DEV_ERROR(dev,
+                       "missing support for speed-bin: %u. Some OPPs may not be supported by hardware",
+                       fuse);
+               return UINT_MAX;
+       }
+
+       return (1 << val);
+}
+
+static int a6xx_set_supported_hw(struct device *dev, struct a6xx_gpu *a6xx_gpu,
+               u32 revn)
+{
+       struct opp_table *opp_table;
+       struct nvmem_cell *cell;
+       u32 supp_hw = UINT_MAX;
+       void *buf;
+
+       cell = nvmem_cell_get(dev, "speed_bin");
+       /*
+        * -ENOENT means that the platform doesn't support speedbin which is
+        * fine
+        */
+       if (PTR_ERR(cell) == -ENOENT)
+               return 0;
+       else if (IS_ERR(cell)) {
+               DRM_DEV_ERROR(dev,
+                               "failed to read speed-bin. Some OPPs may not be supported by hardware");
+               goto done;
+       }
+
+       buf = nvmem_cell_read(cell, NULL);
+       if (IS_ERR(buf)) {
+               nvmem_cell_put(cell);
+               DRM_DEV_ERROR(dev,
+                               "failed to read speed-bin. Some OPPs may not be supported by hardware");
+               goto done;
+       }
+
+       supp_hw = fuse_to_supp_hw(dev, revn, *((u32 *) buf));
+
+       kfree(buf);
+       nvmem_cell_put(cell);
+
+done:
+       opp_table = dev_pm_opp_set_supported_hw(dev, &supp_hw, 1);
+       if (IS_ERR(opp_table))
+               return PTR_ERR(opp_table);
+
+       a6xx_gpu->opp_table = opp_table;
+       return 0;
+}
+
 static const struct adreno_gpu_funcs funcs = {
        .base = {
                .get_param = adreno_get_param,
 
        a6xx_llc_slices_init(pdev, a6xx_gpu);
 
+       ret = a6xx_set_supported_hw(&pdev->dev, a6xx_gpu, info->revn);
+       if (ret) {
+               a6xx_destroy(&(a6xx_gpu->base.base));
+               return ERR_PTR(ret);
+       }
+
        ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
        if (ret) {
                a6xx_destroy(&(a6xx_gpu->base.base));