diff mbox series

[v16,08/40] gpu: host1x: Add initial runtime PM and OPP support

Message ID 20211130232347.950-9-digetx@gmail.com (mailing list archive)
State New, archived
Headers show
Series NVIDIA Tegra power management patches for 5.17 | expand

Commit Message

Dmitry Osipenko Nov. 30, 2021, 11:23 p.m. UTC
Add runtime PM and OPP support to the Host1x driver. For the starter we
will keep host1x always-on because dynamic power management require a major
refactoring of the driver code since lot's of code paths are missing the
RPM handling and we're going to remove some of these paths in the future.

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Paul Fertser <fercerpav@gmail.com> # PAZ00 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/gpu/host1x/debug.c         |  15 +++
 drivers/gpu/host1x/dev.c           | 150 +++++++++++++++++++++++------
 drivers/gpu/host1x/dev.h           |   3 +-
 drivers/gpu/host1x/hw/channel_hw.c |  44 ++++-----
 drivers/gpu/host1x/intr.c          |   3 -
 drivers/gpu/host1x/syncpt.c        |   5 +-
 6 files changed, 164 insertions(+), 56 deletions(-)

Comments

Jon Hunter Dec. 21, 2021, 6:55 p.m. UTC | #1
Hi Dmitry, Thierry,

On 30/11/2021 23:23, Dmitry Osipenko wrote:
> Add runtime PM and OPP support to the Host1x driver. For the starter we
> will keep host1x always-on because dynamic power management require a major
> refactoring of the driver code since lot's of code paths are missing the
> RPM handling and we're going to remove some of these paths in the future.


Unfortunately, this change is breaking boot on Tegra186. Bisect points 
to this and reverting on top of -next gets the board booting again. 
Sadly, there is no panic or error reported, it is just a hard hang. I 
will not have time to look at this this week and so we may need to 
revert for the moment.

Jon
Dmitry Osipenko Dec. 21, 2021, 8:58 p.m. UTC | #2
Hi,

Thank you for testing it all.

21.12.2021 21:55, Jon Hunter пишет:
> Hi Dmitry, Thierry,
> 
> On 30/11/2021 23:23, Dmitry Osipenko wrote:
>> Add runtime PM and OPP support to the Host1x driver. For the starter we
>> will keep host1x always-on because dynamic power management require a
>> major
>> refactoring of the driver code since lot's of code paths are missing the
>> RPM handling and we're going to remove some of these paths in the future.
> 
> 
> Unfortunately, this change is breaking boot on Tegra186. Bisect points
> to this and reverting on top of -next gets the board booting again.
> Sadly, there is no panic or error reported, it is just a hard hang. I
> will not have time to look at this this week and so we may need to
> revert for the moment.

Only T186 broken? What about T194?

Which board model fails to boot? Is it running in hypervisor mode?

Do you use any additional patches?

Could you please test the below diff? I suspect that
host1x_syncpt_save/restore may be entirely broken for T186 since we
never used these funcs before.

--- >8 ---

diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index f5b4dcded088..fd5dfb875422 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -580,7 +580,6 @@ static int __maybe_unused
host1x_runtime_suspend(struct device *dev)
 	int err;

 	host1x_intr_stop(host);
-	host1x_syncpt_save(host);

 	err = reset_control_bulk_assert(host->nresets, host->resets);
 	if (err) {
@@ -596,9 +595,8 @@ static int __maybe_unused
host1x_runtime_suspend(struct device *dev)
 	return 0;

 resume_host1x:
-	host1x_setup_sid_table(host);
-	host1x_syncpt_restore(host);
 	host1x_intr_start(host);
+	host1x_setup_sid_table(host);

 	return err;
 }
@@ -626,9 +624,8 @@ static int __maybe_unused
host1x_runtime_resume(struct device *dev)
 		goto disable_clk;
 	}

-	host1x_setup_sid_table(host);
-	host1x_syncpt_restore(host);
 	host1x_intr_start(host);
+	host1x_setup_sid_table(host);

 	return 0;
Jon Hunter Dec. 22, 2021, 9:47 a.m. UTC | #3
On 21/12/2021 20:58, Dmitry Osipenko wrote:
> Hi,
> 
> Thank you for testing it all.
> 
> 21.12.2021 21:55, Jon Hunter пишет:
>> Hi Dmitry, Thierry,
>>
>> On 30/11/2021 23:23, Dmitry Osipenko wrote:
>>> Add runtime PM and OPP support to the Host1x driver. For the starter we
>>> will keep host1x always-on because dynamic power management require a
>>> major
>>> refactoring of the driver code since lot's of code paths are missing the
>>> RPM handling and we're going to remove some of these paths in the future.
>>
>>
>> Unfortunately, this change is breaking boot on Tegra186. Bisect points
>> to this and reverting on top of -next gets the board booting again.
>> Sadly, there is no panic or error reported, it is just a hard hang. I
>> will not have time to look at this this week and so we may need to
>> revert for the moment.
> 
> Only T186 broken? What about T194?

Yes interestingly only Tegra186 and no other board.

> Which board model fails to boot? Is it running in hypervisor mode?

This is Jetson TX2. No hypervisor.

> Do you use any additional patches?

No just plain -next. The tests run every day on top of tree.

> Could you please test the below diff? I suspect that
> host1x_syncpt_save/restore may be entirely broken for T186 since we
> never used these funcs before.
> 
> --- >8 ---
> 
> diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
> index f5b4dcded088..fd5dfb875422 100644
> --- a/drivers/gpu/host1x/dev.c
> +++ b/drivers/gpu/host1x/dev.c
> @@ -580,7 +580,6 @@ static int __maybe_unused
> host1x_runtime_suspend(struct device *dev)
>   	int err;
> 
>   	host1x_intr_stop(host);
> -	host1x_syncpt_save(host);
> 
>   	err = reset_control_bulk_assert(host->nresets, host->resets);
>   	if (err) {
> @@ -596,9 +595,8 @@ static int __maybe_unused
> host1x_runtime_suspend(struct device *dev)
>   	return 0;
> 
>   resume_host1x:
> -	host1x_setup_sid_table(host);
> -	host1x_syncpt_restore(host);
>   	host1x_intr_start(host);
> +	host1x_setup_sid_table(host);
> 
>   	return err;
>   }
> @@ -626,9 +624,8 @@ static int __maybe_unused
> host1x_runtime_resume(struct device *dev)
>   		goto disable_clk;
>   	}
> 
> -	host1x_setup_sid_table(host);
> -	host1x_syncpt_restore(host);
>   	host1x_intr_start(host);
> +	host1x_setup_sid_table(host);


Thanks! Will try this later, once the next bisect is finished :-)

Jon
Jon Hunter Dec. 22, 2021, 6:41 p.m. UTC | #4
On 22/12/2021 09:47, Jon Hunter wrote:
> 
> On 21/12/2021 20:58, Dmitry Osipenko wrote:
>> Hi,
>>
>> Thank you for testing it all.
>>
>> 21.12.2021 21:55, Jon Hunter пишет:
>>> Hi Dmitry, Thierry,
>>>
>>> On 30/11/2021 23:23, Dmitry Osipenko wrote:
>>>> Add runtime PM and OPP support to the Host1x driver. For the starter we
>>>> will keep host1x always-on because dynamic power management require a
>>>> major
>>>> refactoring of the driver code since lot's of code paths are missing 
>>>> the
>>>> RPM handling and we're going to remove some of these paths in the 
>>>> future.
>>>
>>>
>>> Unfortunately, this change is breaking boot on Tegra186. Bisect points
>>> to this and reverting on top of -next gets the board booting again.
>>> Sadly, there is no panic or error reported, it is just a hard hang. I
>>> will not have time to look at this this week and so we may need to
>>> revert for the moment.
>>
>> Only T186 broken? What about T194?
> 
> Yes interestingly only Tegra186 and no other board.
> 
>> Which board model fails to boot? Is it running in hypervisor mode?
> 
> This is Jetson TX2. No hypervisor.
> 
>> Do you use any additional patches?
> 
> No just plain -next. The tests run every day on top of tree.
> 
>> Could you please test the below diff? I suspect that
>> host1x_syncpt_save/restore may be entirely broken for T186 since we
>> never used these funcs before.
>>
>> --- >8 ---
>>
>> diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
>> index f5b4dcded088..fd5dfb875422 100644
>> --- a/drivers/gpu/host1x/dev.c
>> +++ b/drivers/gpu/host1x/dev.c
>> @@ -580,7 +580,6 @@ static int __maybe_unused
>> host1x_runtime_suspend(struct device *dev)
>>       int err;
>>
>>       host1x_intr_stop(host);
>> -    host1x_syncpt_save(host);
>>
>>       err = reset_control_bulk_assert(host->nresets, host->resets);
>>       if (err) {
>> @@ -596,9 +595,8 @@ static int __maybe_unused
>> host1x_runtime_suspend(struct device *dev)
>>       return 0;
>>
>>   resume_host1x:
>> -    host1x_setup_sid_table(host);
>> -    host1x_syncpt_restore(host);
>>       host1x_intr_start(host);
>> +    host1x_setup_sid_table(host);
>>
>>       return err;
>>   }
>> @@ -626,9 +624,8 @@ static int __maybe_unused
>> host1x_runtime_resume(struct device *dev)
>>           goto disable_clk;
>>       }
>>
>> -    host1x_setup_sid_table(host);
>> -    host1x_syncpt_restore(host);
>>       host1x_intr_start(host);
>> +    host1x_setup_sid_table(host);
> 
> 
> Thanks! Will try this later, once the next bisect is finished :-)

I tested the above, but this did not fix it. It still hangs on boot.

Jon
Dmitry Osipenko Dec. 22, 2021, 7:01 p.m. UTC | #5
22.12.2021 21:41, Jon Hunter пишет:
> 
> On 22/12/2021 09:47, Jon Hunter wrote:
>>
>> On 21/12/2021 20:58, Dmitry Osipenko wrote:
>>> Hi,
>>>
>>> Thank you for testing it all.
>>>
>>> 21.12.2021 21:55, Jon Hunter пишет:
>>>> Hi Dmitry, Thierry,
>>>>
>>>> On 30/11/2021 23:23, Dmitry Osipenko wrote:
>>>>> Add runtime PM and OPP support to the Host1x driver. For the
>>>>> starter we
>>>>> will keep host1x always-on because dynamic power management require a
>>>>> major
>>>>> refactoring of the driver code since lot's of code paths are
>>>>> missing the
>>>>> RPM handling and we're going to remove some of these paths in the
>>>>> future.
>>>>
>>>>
>>>> Unfortunately, this change is breaking boot on Tegra186. Bisect points
>>>> to this and reverting on top of -next gets the board booting again.
>>>> Sadly, there is no panic or error reported, it is just a hard hang. I
>>>> will not have time to look at this this week and so we may need to
>>>> revert for the moment.
>>>
>>> Only T186 broken? What about T194?
>>
>> Yes interestingly only Tegra186 and no other board.
>>
>>> Which board model fails to boot? Is it running in hypervisor mode?
>>
>> This is Jetson TX2. No hypervisor.
>>
>>> Do you use any additional patches?
>>
>> No just plain -next. The tests run every day on top of tree.
>>
>>> Could you please test the below diff? I suspect that
>>> host1x_syncpt_save/restore may be entirely broken for T186 since we
>>> never used these funcs before.
>>>
>>> --- >8 ---
>>>
>>> diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
>>> index f5b4dcded088..fd5dfb875422 100644
>>> --- a/drivers/gpu/host1x/dev.c
>>> +++ b/drivers/gpu/host1x/dev.c
>>> @@ -580,7 +580,6 @@ static int __maybe_unused
>>> host1x_runtime_suspend(struct device *dev)
>>>       int err;
>>>
>>>       host1x_intr_stop(host);
>>> -    host1x_syncpt_save(host);
>>>
>>>       err = reset_control_bulk_assert(host->nresets, host->resets);
>>>       if (err) {
>>> @@ -596,9 +595,8 @@ static int __maybe_unused
>>> host1x_runtime_suspend(struct device *dev)
>>>       return 0;
>>>
>>>   resume_host1x:
>>> -    host1x_setup_sid_table(host);
>>> -    host1x_syncpt_restore(host);
>>>       host1x_intr_start(host);
>>> +    host1x_setup_sid_table(host);
>>>
>>>       return err;
>>>   }
>>> @@ -626,9 +624,8 @@ static int __maybe_unused
>>> host1x_runtime_resume(struct device *dev)
>>>           goto disable_clk;
>>>       }
>>>
>>> -    host1x_setup_sid_table(host);
>>> -    host1x_syncpt_restore(host);
>>>       host1x_intr_start(host);
>>> +    host1x_setup_sid_table(host);
>>
>>
>> Thanks! Will try this later, once the next bisect is finished :-)
> 
> I tested the above, but this did not fix it. It still hangs on boot.

Thank you, now I see where the problem should be. Apparently host1x is
disabled at a boot time on T186 and we touch h/w before RPM is resumed.

Could you please revert the above change and try this instead:

diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index e08e331e46ae..8194826c9ce3 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -137,6 +137,15 @@ void host1x_syncpt_restore(struct host1x *host)
 	struct host1x_syncpt *sp_base = host->syncpt;
 	unsigned int i;

+	for (i = 0; i < host->info->nb_pts; i++) {
+		/*
+		 * Unassign syncpt from channels for purposes of Tegra186
+		 * syncpoint protection. This prevents any channel from
+		 * accessing it until it is reassigned.
+		 */
+		host1x_hw_syncpt_assign_to_channel(host, sp_base + i, NULL);
+	}
+
 	for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
 		host1x_hw_syncpt_restore(host, sp_base + i);

@@ -352,13 +361,6 @@ int host1x_syncpt_init(struct host1x *host)
 	for (i = 0; i < host->info->nb_pts; i++) {
 		syncpt[i].id = i;
 		syncpt[i].host = host;
-
-		/*
-		 * Unassign syncpt from channels for purposes of Tegra186
-		 * syncpoint protection. This prevents any channel from
-		 * accessing it until it is reassigned.
-		 */
-		host1x_hw_syncpt_assign_to_channel(host, &syncpt[i], NULL);
 	}

 	for (i = 0; i < host->info->nb_bases; i++)
Jon Hunter Dec. 22, 2021, 7:30 p.m. UTC | #6
On 22/12/2021 19:01, Dmitry Osipenko wrote:

...

> diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
> index e08e331e46ae..8194826c9ce3 100644
> --- a/drivers/gpu/host1x/syncpt.c
> +++ b/drivers/gpu/host1x/syncpt.c
> @@ -137,6 +137,15 @@ void host1x_syncpt_restore(struct host1x *host)
>   	struct host1x_syncpt *sp_base = host->syncpt;
>   	unsigned int i;
> 
> +	for (i = 0; i < host->info->nb_pts; i++) {
> +		/*
> +		 * Unassign syncpt from channels for purposes of Tegra186
> +		 * syncpoint protection. This prevents any channel from
> +		 * accessing it until it is reassigned.
> +		 */
> +		host1x_hw_syncpt_assign_to_channel(host, sp_base + i, NULL);
> +	}
> +
>   	for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
>   		host1x_hw_syncpt_restore(host, sp_base + i);
> 
> @@ -352,13 +361,6 @@ int host1x_syncpt_init(struct host1x *host)
>   	for (i = 0; i < host->info->nb_pts; i++) {
>   		syncpt[i].id = i;
>   		syncpt[i].host = host;
> -
> -		/*
> -		 * Unassign syncpt from channels for purposes of Tegra186
> -		 * syncpoint protection. This prevents any channel from
> -		 * accessing it until it is reassigned.
> -		 */
> -		host1x_hw_syncpt_assign_to_channel(host, &syncpt[i], NULL);
>   	}
> 
>   	for (i = 0; i < host->info->nb_bases; i++)
> 


Thanks! This fixed it!

Jon
Dmitry Osipenko Dec. 22, 2021, 7:31 p.m. UTC | #7
22.12.2021 22:30, Jon Hunter пишет:
> 
> On 22/12/2021 19:01, Dmitry Osipenko wrote:
> 
> ...
> 
>> diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
>> index e08e331e46ae..8194826c9ce3 100644
>> --- a/drivers/gpu/host1x/syncpt.c
>> +++ b/drivers/gpu/host1x/syncpt.c
>> @@ -137,6 +137,15 @@ void host1x_syncpt_restore(struct host1x *host)
>>       struct host1x_syncpt *sp_base = host->syncpt;
>>       unsigned int i;
>>
>> +    for (i = 0; i < host->info->nb_pts; i++) {
>> +        /*
>> +         * Unassign syncpt from channels for purposes of Tegra186
>> +         * syncpoint protection. This prevents any channel from
>> +         * accessing it until it is reassigned.
>> +         */
>> +        host1x_hw_syncpt_assign_to_channel(host, sp_base + i, NULL);
>> +    }
>> +
>>       for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
>>           host1x_hw_syncpt_restore(host, sp_base + i);
>>
>> @@ -352,13 +361,6 @@ int host1x_syncpt_init(struct host1x *host)
>>       for (i = 0; i < host->info->nb_pts; i++) {
>>           syncpt[i].id = i;
>>           syncpt[i].host = host;
>> -
>> -        /*
>> -         * Unassign syncpt from channels for purposes of Tegra186
>> -         * syncpoint protection. This prevents any channel from
>> -         * accessing it until it is reassigned.
>> -         */
>> -        host1x_hw_syncpt_assign_to_channel(host, &syncpt[i], NULL);
>>       }
>>
>>       for (i = 0; i < host->info->nb_bases; i++)
>>
> 
> 
> Thanks! This fixed it!

I'll prepare proper patch with yours t-b, thank you.
Marc Zyngier Jan. 31, 2022, 8:39 p.m. UTC | #8
Hi all,

On 2021-12-22 19:31, Dmitry Osipenko wrote:
> 22.12.2021 22:30, Jon Hunter пишет:
>> 
>> On 22/12/2021 19:01, Dmitry Osipenko wrote:
>> 
>> ...
>> 
>>> diff --git a/drivers/gpu/host1x/syncpt.c 
>>> b/drivers/gpu/host1x/syncpt.c
>>> index e08e331e46ae..8194826c9ce3 100644
>>> --- a/drivers/gpu/host1x/syncpt.c
>>> +++ b/drivers/gpu/host1x/syncpt.c
>>> @@ -137,6 +137,15 @@ void host1x_syncpt_restore(struct host1x *host)
>>>       struct host1x_syncpt *sp_base = host->syncpt;
>>>       unsigned int i;
>>> 
>>> +    for (i = 0; i < host->info->nb_pts; i++) {
>>> +        /*
>>> +         * Unassign syncpt from channels for purposes of Tegra186
>>> +         * syncpoint protection. This prevents any channel from
>>> +         * accessing it until it is reassigned.
>>> +         */
>>> +        host1x_hw_syncpt_assign_to_channel(host, sp_base + i, NULL);
>>> +    }
>>> +
>>>       for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
>>>           host1x_hw_syncpt_restore(host, sp_base + i);
>>> 
>>> @@ -352,13 +361,6 @@ int host1x_syncpt_init(struct host1x *host)
>>>       for (i = 0; i < host->info->nb_pts; i++) {
>>>           syncpt[i].id = i;
>>>           syncpt[i].host = host;
>>> -
>>> -        /*
>>> -         * Unassign syncpt from channels for purposes of Tegra186
>>> -         * syncpoint protection. This prevents any channel from
>>> -         * accessing it until it is reassigned.
>>> -         */
>>> -        host1x_hw_syncpt_assign_to_channel(host, &syncpt[i], NULL);
>>>       }
>>> 
>>>       for (i = 0; i < host->info->nb_bases; i++)
>>> 
>> 
>> 
>> Thanks! This fixed it!
> 
> I'll prepare proper patch with yours t-b, thank you.

The fix has been in -next for some time now, but it still hasn't
made it into Linus' tree (at least not in -rc2).

Any hope for this to land -rc3?

Thanks,

      M.
diff mbox series

Patch

diff --git a/drivers/gpu/host1x/debug.c b/drivers/gpu/host1x/debug.c
index 8a14880c61bb..18d9c8d206e3 100644
--- a/drivers/gpu/host1x/debug.c
+++ b/drivers/gpu/host1x/debug.c
@@ -7,6 +7,7 @@ 
  */
 
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
 
@@ -52,6 +53,11 @@  static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
 {
 	struct host1x *m = dev_get_drvdata(ch->dev->parent);
 	struct output *o = data;
+	int err;
+
+	err = pm_runtime_resume_and_get(m->dev);
+	if (err < 0)
+		return err;
 
 	mutex_lock(&ch->cdma.lock);
 	mutex_lock(&debug_lock);
@@ -64,6 +70,8 @@  static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
 	mutex_unlock(&debug_lock);
 	mutex_unlock(&ch->cdma.lock);
 
+	pm_runtime_put(m->dev);
+
 	return 0;
 }
 
@@ -71,9 +79,14 @@  static void show_syncpts(struct host1x *m, struct output *o)
 {
 	struct list_head *pos;
 	unsigned int i;
+	int err;
 
 	host1x_debug_output(o, "---- syncpts ----\n");
 
+	err = pm_runtime_resume_and_get(m->dev);
+	if (err < 0)
+		return;
+
 	for (i = 0; i < host1x_syncpt_nb_pts(m); i++) {
 		u32 max = host1x_syncpt_read_max(m->syncpt + i);
 		u32 min = host1x_syncpt_load(m->syncpt + i);
@@ -101,6 +114,8 @@  static void show_syncpts(struct host1x *m, struct output *o)
 					    base_val);
 	}
 
+	pm_runtime_put(m->dev);
+
 	host1x_debug_output(o, "\n");
 }
 
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 3d4cabdbc78d..c42ab78327e7 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -6,14 +6,18 @@ 
  */
 
 #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
@@ -208,6 +212,9 @@  static void host1x_setup_sid_table(struct host1x *host)
 	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];
 
@@ -365,6 +372,27 @@  static void host1x_iommu_exit(struct host1x *host)
 	}
 }
 
+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;
@@ -442,12 +470,9 @@  static int host1x_probe(struct platform_device *pdev)
 		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) {
@@ -462,22 +487,10 @@  static int host1x_probe(struct platform_device *pdev)
 		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);
@@ -486,10 +499,18 @@  static int host1x_probe(struct platform_device *pdev)
 		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)
@@ -505,13 +526,14 @@  static int host1x_probe(struct platform_device *pdev)
 	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:
@@ -526,20 +548,94 @@  static int host1x_remove(struct platform_device *pdev)
 
 	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 = {
+	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,
 	},
 	.probe = host1x_probe,
 	.remove = host1x_remove,
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index 5b7fdea5d169..ca4b082f0cd4 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -118,7 +118,8 @@  struct host1x {
 	struct host1x_syncpt_base *bases;
 	struct device *dev;
 	struct clk *clk;
-	struct reset_control *rst;
+	struct reset_control_bulk_data resets[2];
+	unsigned int nresets;
 
 	struct iommu_group *group;
 	struct iommu_domain *domain;
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index 1999780a7203..6b40e9af1e88 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -159,6 +159,27 @@  static void host1x_channel_set_streamid(struct host1x_channel *channel)
 #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;
@@ -190,6 +211,7 @@  static int channel_submit(struct host1x_job *job)
 	}
 
 	host1x_channel_set_streamid(ch);
+	host1x_enable_gather_filter(ch);
 
 	/* begin a CDMA submit */
 	err = host1x_cdma_begin(&ch->cdma, job);
@@ -249,27 +271,6 @@  static int channel_submit(struct host1x_job *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)
 {
@@ -278,7 +279,6 @@  static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
 #else
 	ch->regs = dev->regs + index * 0x100;
 #endif
-	enable_gather_filter(dev, ch);
 	return 0;
 }
 
diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c
index 45b6be927ec4..965ba21818b1 100644
--- a/drivers/gpu/host1x/intr.c
+++ b/drivers/gpu/host1x/intr.c
@@ -297,14 +297,11 @@  int host1x_intr_init(struct host1x *host, unsigned int irq_sync)
 			 "host1x_sp_%02u", id);
 	}
 
-	host1x_intr_start(host);
-
 	return 0;
 }
 
 void host1x_intr_deinit(struct host1x *host)
 {
-	host1x_intr_stop(host);
 }
 
 void host1x_intr_start(struct host1x *host)
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index d198a10848c6..e08e331e46ae 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -143,6 +143,8 @@  void host1x_syncpt_restore(struct host1x *host)
 	for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
 		host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
 
+	host1x_hw_syncpt_enable_protection(host);
+
 	wmb();
 }
 
@@ -366,9 +368,6 @@  int host1x_syncpt_init(struct host1x *host)
 	host->syncpt = syncpt;
 	host->bases = bases;
 
-	host1x_syncpt_restore(host);
-	host1x_hw_syncpt_enable_protection(host);
-
 	/* Allocate sync point to use for clearing waits for expired fences */
 	host->nop_sp = host1x_syncpt_alloc(host, 0, "reserved-nop");
 	if (!host->nop_sp)