162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2013, NVIDIA Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/iommu.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1362306a36Sopenharmony_ci#include <linux/reset.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <soc/tegra/common.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "drm.h"
1862306a36Sopenharmony_ci#include "gem.h"
1962306a36Sopenharmony_ci#include "gr2d.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cienum {
2262306a36Sopenharmony_ci	RST_MC,
2362306a36Sopenharmony_ci	RST_GR2D,
2462306a36Sopenharmony_ci	RST_GR2D_MAX,
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct gr2d_soc {
2862306a36Sopenharmony_ci	unsigned int version;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct gr2d {
3262306a36Sopenharmony_ci	struct tegra_drm_client client;
3362306a36Sopenharmony_ci	struct host1x_channel *channel;
3462306a36Sopenharmony_ci	struct clk *clk;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	struct reset_control_bulk_data resets[RST_GR2D_MAX];
3762306a36Sopenharmony_ci	unsigned int nresets;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	const struct gr2d_soc *soc;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return container_of(client, struct gr2d, client);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int gr2d_init(struct host1x_client *client)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct tegra_drm_client *drm = host1x_to_drm_client(client);
5262306a36Sopenharmony_ci	struct drm_device *dev = dev_get_drvdata(client->host);
5362306a36Sopenharmony_ci	unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
5462306a36Sopenharmony_ci	struct gr2d *gr2d = to_gr2d(drm);
5562306a36Sopenharmony_ci	int err;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	gr2d->channel = host1x_channel_request(client);
5862306a36Sopenharmony_ci	if (!gr2d->channel)
5962306a36Sopenharmony_ci		return -ENOMEM;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	client->syncpts[0] = host1x_syncpt_request(client, flags);
6262306a36Sopenharmony_ci	if (!client->syncpts[0]) {
6362306a36Sopenharmony_ci		err = -ENOMEM;
6462306a36Sopenharmony_ci		dev_err(client->dev, "failed to request syncpoint: %d\n", err);
6562306a36Sopenharmony_ci		goto put;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	err = host1x_client_iommu_attach(client);
6962306a36Sopenharmony_ci	if (err < 0) {
7062306a36Sopenharmony_ci		dev_err(client->dev, "failed to attach to domain: %d\n", err);
7162306a36Sopenharmony_ci		goto free;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	err = tegra_drm_register_client(dev->dev_private, drm);
7562306a36Sopenharmony_ci	if (err < 0) {
7662306a36Sopenharmony_ci		dev_err(client->dev, "failed to register client: %d\n", err);
7762306a36Sopenharmony_ci		goto detach_iommu;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cidetach_iommu:
8362306a36Sopenharmony_ci	host1x_client_iommu_detach(client);
8462306a36Sopenharmony_cifree:
8562306a36Sopenharmony_ci	host1x_syncpt_put(client->syncpts[0]);
8662306a36Sopenharmony_ciput:
8762306a36Sopenharmony_ci	host1x_channel_put(gr2d->channel);
8862306a36Sopenharmony_ci	return err;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int gr2d_exit(struct host1x_client *client)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct tegra_drm_client *drm = host1x_to_drm_client(client);
9462306a36Sopenharmony_ci	struct drm_device *dev = dev_get_drvdata(client->host);
9562306a36Sopenharmony_ci	struct tegra_drm *tegra = dev->dev_private;
9662306a36Sopenharmony_ci	struct gr2d *gr2d = to_gr2d(drm);
9762306a36Sopenharmony_ci	int err;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	err = tegra_drm_unregister_client(tegra, drm);
10062306a36Sopenharmony_ci	if (err < 0)
10162306a36Sopenharmony_ci		return err;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(client->dev);
10462306a36Sopenharmony_ci	pm_runtime_force_suspend(client->dev);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	host1x_client_iommu_detach(client);
10762306a36Sopenharmony_ci	host1x_syncpt_put(client->syncpts[0]);
10862306a36Sopenharmony_ci	host1x_channel_put(gr2d->channel);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	gr2d->channel = NULL;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return 0;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic const struct host1x_client_ops gr2d_client_ops = {
11662306a36Sopenharmony_ci	.init = gr2d_init,
11762306a36Sopenharmony_ci	.exit = gr2d_exit,
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int gr2d_open_channel(struct tegra_drm_client *client,
12162306a36Sopenharmony_ci			     struct tegra_drm_context *context)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct gr2d *gr2d = to_gr2d(client);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	context->channel = host1x_channel_get(gr2d->channel);
12662306a36Sopenharmony_ci	if (!context->channel)
12762306a36Sopenharmony_ci		return -ENOMEM;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void gr2d_close_channel(struct tegra_drm_context *context)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	host1x_channel_put(context->channel);
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct gr2d *gr2d = dev_get_drvdata(dev);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	switch (class) {
14262306a36Sopenharmony_ci	case HOST1X_CLASS_HOST1X:
14362306a36Sopenharmony_ci		if (offset == 0x2b)
14462306a36Sopenharmony_ci			return 1;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	case HOST1X_CLASS_GR2D:
14962306a36Sopenharmony_ci	case HOST1X_CLASS_GR2D_SB:
15062306a36Sopenharmony_ci		if (offset >= GR2D_NUM_REGS)
15162306a36Sopenharmony_ci			break;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		if (test_bit(offset, gr2d->addr_regs))
15462306a36Sopenharmony_ci			return 1;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		break;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int gr2d_is_valid_class(u32 class)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	return (class == HOST1X_CLASS_GR2D ||
16562306a36Sopenharmony_ci		class == HOST1X_CLASS_GR2D_SB);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct tegra_drm_client_ops gr2d_ops = {
16962306a36Sopenharmony_ci	.open_channel = gr2d_open_channel,
17062306a36Sopenharmony_ci	.close_channel = gr2d_close_channel,
17162306a36Sopenharmony_ci	.is_addr_reg = gr2d_is_addr_reg,
17262306a36Sopenharmony_ci	.is_valid_class = gr2d_is_valid_class,
17362306a36Sopenharmony_ci	.submit = tegra_drm_submit,
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic const struct gr2d_soc tegra20_gr2d_soc = {
17762306a36Sopenharmony_ci	.version = 0x20,
17862306a36Sopenharmony_ci};
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic const struct gr2d_soc tegra30_gr2d_soc = {
18162306a36Sopenharmony_ci	.version = 0x30,
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic const struct gr2d_soc tegra114_gr2d_soc = {
18562306a36Sopenharmony_ci	.version = 0x35,
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic const struct of_device_id gr2d_match[] = {
18962306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra114-gr2d", .data = &tegra114_gr2d_soc },
19062306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra30-gr2d", .data = &tegra30_gr2d_soc },
19162306a36Sopenharmony_ci	{ .compatible = "nvidia,tegra20-gr2d", .data = &tegra20_gr2d_soc },
19262306a36Sopenharmony_ci	{ },
19362306a36Sopenharmony_ci};
19462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gr2d_match);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic const u32 gr2d_addr_regs[] = {
19762306a36Sopenharmony_ci	GR2D_UA_BASE_ADDR,
19862306a36Sopenharmony_ci	GR2D_VA_BASE_ADDR,
19962306a36Sopenharmony_ci	GR2D_PAT_BASE_ADDR,
20062306a36Sopenharmony_ci	GR2D_DSTA_BASE_ADDR,
20162306a36Sopenharmony_ci	GR2D_DSTB_BASE_ADDR,
20262306a36Sopenharmony_ci	GR2D_DSTC_BASE_ADDR,
20362306a36Sopenharmony_ci	GR2D_SRCA_BASE_ADDR,
20462306a36Sopenharmony_ci	GR2D_SRCB_BASE_ADDR,
20562306a36Sopenharmony_ci	GR2D_PATBASE_ADDR,
20662306a36Sopenharmony_ci	GR2D_SRC_BASE_ADDR_SB,
20762306a36Sopenharmony_ci	GR2D_DSTA_BASE_ADDR_SB,
20862306a36Sopenharmony_ci	GR2D_DSTB_BASE_ADDR_SB,
20962306a36Sopenharmony_ci	GR2D_UA_BASE_ADDR_SB,
21062306a36Sopenharmony_ci	GR2D_VA_BASE_ADDR_SB,
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int gr2d_get_resets(struct device *dev, struct gr2d *gr2d)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	int err;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	gr2d->resets[RST_MC].id = "mc";
21862306a36Sopenharmony_ci	gr2d->resets[RST_GR2D].id = "2d";
21962306a36Sopenharmony_ci	gr2d->nresets = RST_GR2D_MAX;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	err = devm_reset_control_bulk_get_optional_exclusive_released(
22262306a36Sopenharmony_ci				dev, gr2d->nresets, gr2d->resets);
22362306a36Sopenharmony_ci	if (err) {
22462306a36Sopenharmony_ci		dev_err(dev, "failed to get reset: %d\n", err);
22562306a36Sopenharmony_ci		return err;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (WARN_ON(!gr2d->resets[RST_GR2D].rstc))
22962306a36Sopenharmony_ci		return -ENOENT;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int gr2d_probe(struct platform_device *pdev)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
23762306a36Sopenharmony_ci	struct host1x_syncpt **syncpts;
23862306a36Sopenharmony_ci	struct gr2d *gr2d;
23962306a36Sopenharmony_ci	unsigned int i;
24062306a36Sopenharmony_ci	int err;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
24362306a36Sopenharmony_ci	if (!gr2d)
24462306a36Sopenharmony_ci		return -ENOMEM;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	platform_set_drvdata(pdev, gr2d);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	gr2d->soc = of_device_get_match_data(dev);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
25162306a36Sopenharmony_ci	if (!syncpts)
25262306a36Sopenharmony_ci		return -ENOMEM;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	gr2d->clk = devm_clk_get(dev, NULL);
25562306a36Sopenharmony_ci	if (IS_ERR(gr2d->clk)) {
25662306a36Sopenharmony_ci		dev_err(dev, "cannot get clock\n");
25762306a36Sopenharmony_ci		return PTR_ERR(gr2d->clk);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	err = gr2d_get_resets(dev, gr2d);
26162306a36Sopenharmony_ci	if (err)
26262306a36Sopenharmony_ci		return err;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	INIT_LIST_HEAD(&gr2d->client.base.list);
26562306a36Sopenharmony_ci	gr2d->client.base.ops = &gr2d_client_ops;
26662306a36Sopenharmony_ci	gr2d->client.base.dev = dev;
26762306a36Sopenharmony_ci	gr2d->client.base.class = HOST1X_CLASS_GR2D;
26862306a36Sopenharmony_ci	gr2d->client.base.syncpts = syncpts;
26962306a36Sopenharmony_ci	gr2d->client.base.num_syncpts = 1;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	INIT_LIST_HEAD(&gr2d->client.list);
27262306a36Sopenharmony_ci	gr2d->client.version = gr2d->soc->version;
27362306a36Sopenharmony_ci	gr2d->client.ops = &gr2d_ops;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	err = devm_tegra_core_dev_init_opp_table_common(dev);
27662306a36Sopenharmony_ci	if (err)
27762306a36Sopenharmony_ci		return err;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	err = host1x_client_register(&gr2d->client.base);
28062306a36Sopenharmony_ci	if (err < 0) {
28162306a36Sopenharmony_ci		dev_err(dev, "failed to register host1x client: %d\n", err);
28262306a36Sopenharmony_ci		return err;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* initialize address register map */
28662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
28762306a36Sopenharmony_ci		set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void gr2d_remove(struct platform_device *pdev)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct gr2d *gr2d = platform_get_drvdata(pdev);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
29762306a36Sopenharmony_ci	host1x_client_unregister(&gr2d->client.base);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int __maybe_unused gr2d_runtime_suspend(struct device *dev)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct gr2d *gr2d = dev_get_drvdata(dev);
30362306a36Sopenharmony_ci	int err;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	host1x_channel_stop(gr2d->channel);
30662306a36Sopenharmony_ci	reset_control_bulk_release(gr2d->nresets, gr2d->resets);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/*
30962306a36Sopenharmony_ci	 * GR2D module shouldn't be reset while hardware is idling, otherwise
31062306a36Sopenharmony_ci	 * host1x's cmdproc will stuck on trying to access any G2 register
31162306a36Sopenharmony_ci	 * after reset. GR2D module could be either hot-reset or reset after
31262306a36Sopenharmony_ci	 * power-gating of the HEG partition. Hence we will put in reset only
31362306a36Sopenharmony_ci	 * the memory client part of the module, the HEG GENPD will take care
31462306a36Sopenharmony_ci	 * of resetting GR2D module across power-gating.
31562306a36Sopenharmony_ci	 *
31662306a36Sopenharmony_ci	 * On Tegra20 there is no HEG partition, but it's okay to have
31762306a36Sopenharmony_ci	 * undetermined h/w state since userspace is expected to reprogram
31862306a36Sopenharmony_ci	 * the state on each job submission anyways.
31962306a36Sopenharmony_ci	 */
32062306a36Sopenharmony_ci	err = reset_control_acquire(gr2d->resets[RST_MC].rstc);
32162306a36Sopenharmony_ci	if (err) {
32262306a36Sopenharmony_ci		dev_err(dev, "failed to acquire MC reset: %d\n", err);
32362306a36Sopenharmony_ci		goto acquire_reset;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	err = reset_control_assert(gr2d->resets[RST_MC].rstc);
32762306a36Sopenharmony_ci	reset_control_release(gr2d->resets[RST_MC].rstc);
32862306a36Sopenharmony_ci	if (err) {
32962306a36Sopenharmony_ci		dev_err(dev, "failed to assert MC reset: %d\n", err);
33062306a36Sopenharmony_ci		goto acquire_reset;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	clk_disable_unprepare(gr2d->clk);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ciacquire_reset:
33862306a36Sopenharmony_ci	reset_control_bulk_acquire(gr2d->nresets, gr2d->resets);
33962306a36Sopenharmony_ci	reset_control_bulk_deassert(gr2d->nresets, gr2d->resets);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return err;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int __maybe_unused gr2d_runtime_resume(struct device *dev)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct gr2d *gr2d = dev_get_drvdata(dev);
34762306a36Sopenharmony_ci	int err;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	err = reset_control_bulk_acquire(gr2d->nresets, gr2d->resets);
35062306a36Sopenharmony_ci	if (err) {
35162306a36Sopenharmony_ci		dev_err(dev, "failed to acquire reset: %d\n", err);
35262306a36Sopenharmony_ci		return err;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	err = clk_prepare_enable(gr2d->clk);
35662306a36Sopenharmony_ci	if (err) {
35762306a36Sopenharmony_ci		dev_err(dev, "failed to enable clock: %d\n", err);
35862306a36Sopenharmony_ci		goto release_reset;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	usleep_range(2000, 4000);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* this is a reset array which deasserts both 2D MC and 2D itself */
36462306a36Sopenharmony_ci	err = reset_control_bulk_deassert(gr2d->nresets, gr2d->resets);
36562306a36Sopenharmony_ci	if (err) {
36662306a36Sopenharmony_ci		dev_err(dev, "failed to deassert reset: %d\n", err);
36762306a36Sopenharmony_ci		goto disable_clk;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	pm_runtime_enable(dev);
37162306a36Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
37262306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, 500);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cidisable_clk:
37762306a36Sopenharmony_ci	clk_disable_unprepare(gr2d->clk);
37862306a36Sopenharmony_cirelease_reset:
37962306a36Sopenharmony_ci	reset_control_bulk_release(gr2d->nresets, gr2d->resets);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return err;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic const struct dev_pm_ops tegra_gr2d_pm = {
38562306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(gr2d_runtime_suspend, gr2d_runtime_resume, NULL)
38662306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
38762306a36Sopenharmony_ci				pm_runtime_force_resume)
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistruct platform_driver tegra_gr2d_driver = {
39162306a36Sopenharmony_ci	.driver = {
39262306a36Sopenharmony_ci		.name = "tegra-gr2d",
39362306a36Sopenharmony_ci		.of_match_table = gr2d_match,
39462306a36Sopenharmony_ci		.pm = &tegra_gr2d_pm,
39562306a36Sopenharmony_ci	},
39662306a36Sopenharmony_ci	.probe = gr2d_probe,
39762306a36Sopenharmony_ci	.remove_new = gr2d_remove,
39862306a36Sopenharmony_ci};
399