*/
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
+#include <soc/tegra/common.h>
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/host1x.h>
 #undef CREATE_TRACE_POINTS
        const struct host1x_info *info = host->info;
        unsigned int i;
 
+       if (!info->has_hypervisor)
+               return;
+
        for (i = 0; i < info->num_sid_entries; i++) {
                const struct host1x_sid_entry *entry = &info->sid_table[i];
 
        }
 }
 
+static int host1x_get_resets(struct host1x *host)
+{
+       int err;
+
+       host->resets[0].id = "mc";
+       host->resets[1].id = "host1x";
+       host->nresets = ARRAY_SIZE(host->resets);
+
+       err = devm_reset_control_bulk_get_optional_exclusive_released(
+                               host->dev, host->nresets, host->resets);
+       if (err) {
+               dev_err(host->dev, "failed to get reset: %d\n", err);
+               return err;
+       }
+
+       if (WARN_ON(!host->resets[1].rstc))
+               return -ENOENT;
+
+       return 0;
+}
+
 static int host1x_probe(struct platform_device *pdev)
 {
        struct host1x *host;
                return err;
        }
 
-       host->rst = devm_reset_control_get(&pdev->dev, "host1x");
-       if (IS_ERR(host->rst)) {
-               err = PTR_ERR(host->rst);
-               dev_err(&pdev->dev, "failed to get reset: %d\n", err);
+       err = host1x_get_resets(host);
+       if (err)
                return err;
-       }
 
        err = host1x_iommu_init(host);
        if (err < 0) {
                goto iommu_exit;
        }
 
-       err = clk_prepare_enable(host->clk);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to enable clock\n");
-               goto free_channels;
-       }
-
-       err = reset_control_deassert(host->rst);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to deassert reset: %d\n", err);
-               goto unprepare_disable;
-       }
-
        err = host1x_syncpt_init(host);
        if (err) {
                dev_err(&pdev->dev, "failed to initialize syncpts\n");
-               goto reset_assert;
+               goto free_channels;
        }
 
        err = host1x_intr_init(host, syncpt_irq);
                goto deinit_syncpt;
        }
 
-       host1x_debug_init(host);
+       pm_runtime_enable(&pdev->dev);
+
+       err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
+       if (err)
+               goto pm_disable;
 
-       if (host->info->has_hypervisor)
-               host1x_setup_sid_table(host);
+       /* the driver's code isn't ready yet for the dynamic RPM */
+       err = pm_runtime_resume_and_get(&pdev->dev);
+       if (err)
+               goto pm_disable;
+
+       host1x_debug_init(host);
 
        err = host1x_register(host);
        if (err < 0)
        host1x_unregister(host);
 deinit_debugfs:
        host1x_debug_deinit(host);
+
+       pm_runtime_put_sync_suspend(&pdev->dev);
+pm_disable:
+       pm_runtime_disable(&pdev->dev);
+
        host1x_intr_deinit(host);
 deinit_syncpt:
        host1x_syncpt_deinit(host);
-reset_assert:
-       reset_control_assert(host->rst);
-unprepare_disable:
-       clk_disable_unprepare(host->clk);
 free_channels:
        host1x_channel_list_free(&host->channel_list);
 iommu_exit:
 
        host1x_unregister(host);
        host1x_debug_deinit(host);
+
+       pm_runtime_force_suspend(&pdev->dev);
+
        host1x_intr_deinit(host);
        host1x_syncpt_deinit(host);
-       reset_control_assert(host->rst);
-       clk_disable_unprepare(host->clk);
        host1x_iommu_exit(host);
        host1x_bo_cache_destroy(&host->cache);
 
        return 0;
 }
 
+static int __maybe_unused host1x_runtime_suspend(struct device *dev)
+{
+       struct host1x *host = dev_get_drvdata(dev);
+       int err;
+
+       host1x_intr_stop(host);
+       host1x_syncpt_save(host);
+
+       err = reset_control_bulk_assert(host->nresets, host->resets);
+       if (err) {
+               dev_err(dev, "failed to assert reset: %d\n", err);
+               goto resume_host1x;
+       }
+
+       usleep_range(1000, 2000);
+
+       clk_disable_unprepare(host->clk);
+       reset_control_bulk_release(host->nresets, host->resets);
+
+       return 0;
+
+resume_host1x:
+       host1x_setup_sid_table(host);
+       host1x_syncpt_restore(host);
+       host1x_intr_start(host);
+
+       return err;
+}
+
+static int __maybe_unused host1x_runtime_resume(struct device *dev)
+{
+       struct host1x *host = dev_get_drvdata(dev);
+       int err;
+
+       err = reset_control_bulk_acquire(host->nresets, host->resets);
+       if (err) {
+               dev_err(dev, "failed to acquire reset: %d\n", err);
+               return err;
+       }
+
+       err = clk_prepare_enable(host->clk);
+       if (err) {
+               dev_err(dev, "failed to enable clock: %d\n", err);
+               goto release_reset;
+       }
+
+       err = reset_control_bulk_deassert(host->nresets, host->resets);
+       if (err < 0) {
+               dev_err(dev, "failed to deassert reset: %d\n", err);
+               goto disable_clk;
+       }
+
+       host1x_setup_sid_table(host);
+       host1x_syncpt_restore(host);
+       host1x_intr_start(host);
+
+       return 0;
+
+disable_clk:
+       clk_disable_unprepare(host->clk);
+release_reset:
+       reset_control_bulk_release(host->nresets, host->resets);
+
+       return err;
+}
+
+static const struct dev_pm_ops host1x_pm_ops = {
+       SET_RUNTIME_PM_OPS(host1x_runtime_suspend, host1x_runtime_resume,
+                          NULL)
+       /* TODO: add system suspend-resume once driver will be ready for that */
+};
+
 static struct platform_driver tegra_host1x_driver = {
        .driver = {
                .name = "tegra-host1x",
                .of_match_table = host1x_of_match,
+               .pm = &host1x_pm_ops,
        },
        .probe = host1x_probe,
        .remove = host1x_remove,
 
 #endif
 }
 
+static void host1x_enable_gather_filter(struct host1x_channel *ch)
+{
+#if HOST1X_HW >= 6
+       struct host1x *host = dev_get_drvdata(ch->dev->parent);
+       u32 val;
+
+       if (!host->hv_regs)
+               return;
+
+       val = host1x_hypervisor_readl(
+               host, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
+       val |= BIT(ch->id % 32);
+       host1x_hypervisor_writel(
+               host, val, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
+#elif HOST1X_HW >= 4
+       host1x_ch_writel(ch,
+                        HOST1X_CHANNEL_CHANNELCTRL_KERNEL_FILTER_GBUFFER(1),
+                        HOST1X_CHANNEL_CHANNELCTRL);
+#endif
+}
+
 static int channel_submit(struct host1x_job *job)
 {
        struct host1x_channel *ch = job->channel;
        }
 
        host1x_channel_set_streamid(ch);
+       host1x_enable_gather_filter(ch);
 
        /* begin a CDMA submit */
        err = host1x_cdma_begin(&ch->cdma, job);
        return err;
 }
 
-static void enable_gather_filter(struct host1x *host,
-                                struct host1x_channel *ch)
-{
-#if HOST1X_HW >= 6
-       u32 val;
-
-       if (!host->hv_regs)
-               return;
-
-       val = host1x_hypervisor_readl(
-               host, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
-       val |= BIT(ch->id % 32);
-       host1x_hypervisor_writel(
-               host, val, HOST1X_HV_CH_KERNEL_FILTER_GBUFFER(ch->id / 32));
-#elif HOST1X_HW >= 4
-       host1x_ch_writel(ch,
-                        HOST1X_CHANNEL_CHANNELCTRL_KERNEL_FILTER_GBUFFER(1),
-                        HOST1X_CHANNEL_CHANNELCTRL);
-#endif
-}
-
 static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
                               unsigned int index)
 {
 #else
        ch->regs = dev->regs + index * 0x100;
 #endif
-       enable_gather_filter(dev, ch);
        return 0;
 }