18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Avionic Design GmbH
48c2ecf20Sopenharmony_ci * Copyright (C) 2013 NVIDIA Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/host1x.h>
98c2ecf20Sopenharmony_ci#include <linux/iommu.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/reset.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <soc/tegra/pmc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "drm.h"
188c2ecf20Sopenharmony_ci#include "gem.h"
198c2ecf20Sopenharmony_ci#include "gr3d.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct gr3d_soc {
228c2ecf20Sopenharmony_ci	unsigned int version;
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistruct gr3d {
268c2ecf20Sopenharmony_ci	struct tegra_drm_client client;
278c2ecf20Sopenharmony_ci	struct host1x_channel *channel;
288c2ecf20Sopenharmony_ci	struct clk *clk_secondary;
298c2ecf20Sopenharmony_ci	struct clk *clk;
308c2ecf20Sopenharmony_ci	struct reset_control *rst_secondary;
318c2ecf20Sopenharmony_ci	struct reset_control *rst;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	const struct gr3d_soc *soc;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return container_of(client, struct gr3d, client);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int gr3d_init(struct host1x_client *client)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct tegra_drm_client *drm = host1x_to_drm_client(client);
468c2ecf20Sopenharmony_ci	struct drm_device *dev = dev_get_drvdata(client->host);
478c2ecf20Sopenharmony_ci	unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
488c2ecf20Sopenharmony_ci	struct gr3d *gr3d = to_gr3d(drm);
498c2ecf20Sopenharmony_ci	int err;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	gr3d->channel = host1x_channel_request(client);
528c2ecf20Sopenharmony_ci	if (!gr3d->channel)
538c2ecf20Sopenharmony_ci		return -ENOMEM;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	client->syncpts[0] = host1x_syncpt_request(client, flags);
568c2ecf20Sopenharmony_ci	if (!client->syncpts[0]) {
578c2ecf20Sopenharmony_ci		err = -ENOMEM;
588c2ecf20Sopenharmony_ci		dev_err(client->dev, "failed to request syncpoint: %d\n", err);
598c2ecf20Sopenharmony_ci		goto put;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	err = host1x_client_iommu_attach(client);
638c2ecf20Sopenharmony_ci	if (err < 0) {
648c2ecf20Sopenharmony_ci		dev_err(client->dev, "failed to attach to domain: %d\n", err);
658c2ecf20Sopenharmony_ci		goto free;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	err = tegra_drm_register_client(dev->dev_private, drm);
698c2ecf20Sopenharmony_ci	if (err < 0) {
708c2ecf20Sopenharmony_ci		dev_err(client->dev, "failed to register client: %d\n", err);
718c2ecf20Sopenharmony_ci		goto detach;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cidetach:
778c2ecf20Sopenharmony_ci	host1x_client_iommu_detach(client);
788c2ecf20Sopenharmony_cifree:
798c2ecf20Sopenharmony_ci	host1x_syncpt_free(client->syncpts[0]);
808c2ecf20Sopenharmony_ciput:
818c2ecf20Sopenharmony_ci	host1x_channel_put(gr3d->channel);
828c2ecf20Sopenharmony_ci	return err;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int gr3d_exit(struct host1x_client *client)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct tegra_drm_client *drm = host1x_to_drm_client(client);
888c2ecf20Sopenharmony_ci	struct drm_device *dev = dev_get_drvdata(client->host);
898c2ecf20Sopenharmony_ci	struct gr3d *gr3d = to_gr3d(drm);
908c2ecf20Sopenharmony_ci	int err;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	err = tegra_drm_unregister_client(dev->dev_private, drm);
938c2ecf20Sopenharmony_ci	if (err < 0)
948c2ecf20Sopenharmony_ci		return err;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	host1x_client_iommu_detach(client);
978c2ecf20Sopenharmony_ci	host1x_syncpt_free(client->syncpts[0]);
988c2ecf20Sopenharmony_ci	host1x_channel_put(gr3d->channel);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic const struct host1x_client_ops gr3d_client_ops = {
1048c2ecf20Sopenharmony_ci	.init = gr3d_init,
1058c2ecf20Sopenharmony_ci	.exit = gr3d_exit,
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int gr3d_open_channel(struct tegra_drm_client *client,
1098c2ecf20Sopenharmony_ci			     struct tegra_drm_context *context)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct gr3d *gr3d = to_gr3d(client);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	context->channel = host1x_channel_get(gr3d->channel);
1148c2ecf20Sopenharmony_ci	if (!context->channel)
1158c2ecf20Sopenharmony_ci		return -ENOMEM;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return 0;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void gr3d_close_channel(struct tegra_drm_context *context)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	host1x_channel_put(context->channel);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int gr3d_is_addr_reg(struct device *dev, u32 class, u32 offset)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct gr3d *gr3d = dev_get_drvdata(dev);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	switch (class) {
1308c2ecf20Sopenharmony_ci	case HOST1X_CLASS_HOST1X:
1318c2ecf20Sopenharmony_ci		if (offset == 0x2b)
1328c2ecf20Sopenharmony_ci			return 1;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		break;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	case HOST1X_CLASS_GR3D:
1378c2ecf20Sopenharmony_ci		if (offset >= GR3D_NUM_REGS)
1388c2ecf20Sopenharmony_ci			break;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci		if (test_bit(offset, gr3d->addr_regs))
1418c2ecf20Sopenharmony_ci			return 1;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		break;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return 0;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic const struct tegra_drm_client_ops gr3d_ops = {
1508c2ecf20Sopenharmony_ci	.open_channel = gr3d_open_channel,
1518c2ecf20Sopenharmony_ci	.close_channel = gr3d_close_channel,
1528c2ecf20Sopenharmony_ci	.is_addr_reg = gr3d_is_addr_reg,
1538c2ecf20Sopenharmony_ci	.submit = tegra_drm_submit,
1548c2ecf20Sopenharmony_ci};
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct gr3d_soc tegra20_gr3d_soc = {
1578c2ecf20Sopenharmony_ci	.version = 0x20,
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const struct gr3d_soc tegra30_gr3d_soc = {
1618c2ecf20Sopenharmony_ci	.version = 0x30,
1628c2ecf20Sopenharmony_ci};
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic const struct gr3d_soc tegra114_gr3d_soc = {
1658c2ecf20Sopenharmony_ci	.version = 0x35,
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_gr3d_match[] = {
1698c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra114-gr3d", .data = &tegra114_gr3d_soc },
1708c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra30-gr3d", .data = &tegra30_gr3d_soc },
1718c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra20-gr3d", .data = &tegra20_gr3d_soc },
1728c2ecf20Sopenharmony_ci	{ }
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_gr3d_match);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic const u32 gr3d_addr_regs[] = {
1778c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 0),
1788c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 1),
1798c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 2),
1808c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 3),
1818c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 4),
1828c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 5),
1838c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 6),
1848c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 7),
1858c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 8),
1868c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE( 9),
1878c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE(10),
1888c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE(11),
1898c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE(12),
1908c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE(13),
1918c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE(14),
1928c2ecf20Sopenharmony_ci	GR3D_IDX_ATTRIBUTE(15),
1938c2ecf20Sopenharmony_ci	GR3D_IDX_INDEX_BASE,
1948c2ecf20Sopenharmony_ci	GR3D_QR_ZTAG_ADDR,
1958c2ecf20Sopenharmony_ci	GR3D_QR_CTAG_ADDR,
1968c2ecf20Sopenharmony_ci	GR3D_QR_CZ_ADDR,
1978c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 0),
1988c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 1),
1998c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 2),
2008c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 3),
2018c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 4),
2028c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 5),
2038c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 6),
2048c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 7),
2058c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 8),
2068c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR( 9),
2078c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR(10),
2088c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR(11),
2098c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR(12),
2108c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR(13),
2118c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR(14),
2128c2ecf20Sopenharmony_ci	GR3D_TEX_TEX_ADDR(15),
2138c2ecf20Sopenharmony_ci	GR3D_DW_MEMORY_OUTPUT_ADDRESS,
2148c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 0),
2158c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 1),
2168c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 2),
2178c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 3),
2188c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 4),
2198c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 5),
2208c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 6),
2218c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 7),
2228c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 8),
2238c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR( 9),
2248c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR(10),
2258c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR(11),
2268c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR(12),
2278c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR(13),
2288c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR(14),
2298c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFADDR(15),
2308c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SPILLSURFADDR,
2318c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 0),
2328c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 1),
2338c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 2),
2348c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 3),
2358c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 4),
2368c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 5),
2378c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 6),
2388c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 7),
2398c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 8),
2408c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR( 9),
2418c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR(10),
2428c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR(11),
2438c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR(12),
2448c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR(13),
2458c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR(14),
2468c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SURFOVERADDR(15),
2478c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 0),
2488c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 1),
2498c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 2),
2508c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 3),
2518c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 4),
2528c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 5),
2538c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 6),
2548c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 7),
2558c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 8),
2568c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR( 9),
2578c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR(10),
2588c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR(11),
2598c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR(12),
2608c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR(13),
2618c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR(14),
2628c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP01SURFADDR(15),
2638c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 0),
2648c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 1),
2658c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 2),
2668c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 3),
2678c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 4),
2688c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 5),
2698c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 6),
2708c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 7),
2718c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 8),
2728c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR( 9),
2738c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR(10),
2748c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR(11),
2758c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR(12),
2768c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR(13),
2778c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR(14),
2788c2ecf20Sopenharmony_ci	GR3D_GLOBAL_SAMP23SURFADDR(15),
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic int gr3d_probe(struct platform_device *pdev)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
2848c2ecf20Sopenharmony_ci	struct host1x_syncpt **syncpts;
2858c2ecf20Sopenharmony_ci	struct gr3d *gr3d;
2868c2ecf20Sopenharmony_ci	unsigned int i;
2878c2ecf20Sopenharmony_ci	int err;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	gr3d = devm_kzalloc(&pdev->dev, sizeof(*gr3d), GFP_KERNEL);
2908c2ecf20Sopenharmony_ci	if (!gr3d)
2918c2ecf20Sopenharmony_ci		return -ENOMEM;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	gr3d->soc = of_device_get_match_data(&pdev->dev);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL);
2968c2ecf20Sopenharmony_ci	if (!syncpts)
2978c2ecf20Sopenharmony_ci		return -ENOMEM;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	gr3d->clk = devm_clk_get(&pdev->dev, NULL);
3008c2ecf20Sopenharmony_ci	if (IS_ERR(gr3d->clk)) {
3018c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot get clock\n");
3028c2ecf20Sopenharmony_ci		return PTR_ERR(gr3d->clk);
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	gr3d->rst = devm_reset_control_get(&pdev->dev, "3d");
3068c2ecf20Sopenharmony_ci	if (IS_ERR(gr3d->rst)) {
3078c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "cannot get reset\n");
3088c2ecf20Sopenharmony_ci		return PTR_ERR(gr3d->rst);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) {
3128c2ecf20Sopenharmony_ci		gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2");
3138c2ecf20Sopenharmony_ci		if (IS_ERR(gr3d->clk_secondary)) {
3148c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "cannot get secondary clock\n");
3158c2ecf20Sopenharmony_ci			return PTR_ERR(gr3d->clk_secondary);
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		gr3d->rst_secondary = devm_reset_control_get(&pdev->dev,
3198c2ecf20Sopenharmony_ci								"3d2");
3208c2ecf20Sopenharmony_ci		if (IS_ERR(gr3d->rst_secondary)) {
3218c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "cannot get secondary reset\n");
3228c2ecf20Sopenharmony_ci			return PTR_ERR(gr3d->rst_secondary);
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk,
3278c2ecf20Sopenharmony_ci						gr3d->rst);
3288c2ecf20Sopenharmony_ci	if (err < 0) {
3298c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to power up 3D unit\n");
3308c2ecf20Sopenharmony_ci		return err;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (gr3d->clk_secondary) {
3348c2ecf20Sopenharmony_ci		err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1,
3358c2ecf20Sopenharmony_ci							gr3d->clk_secondary,
3368c2ecf20Sopenharmony_ci							gr3d->rst_secondary);
3378c2ecf20Sopenharmony_ci		if (err < 0) {
3388c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
3398c2ecf20Sopenharmony_ci				"failed to power up secondary 3D unit\n");
3408c2ecf20Sopenharmony_ci			return err;
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gr3d->client.base.list);
3458c2ecf20Sopenharmony_ci	gr3d->client.base.ops = &gr3d_client_ops;
3468c2ecf20Sopenharmony_ci	gr3d->client.base.dev = &pdev->dev;
3478c2ecf20Sopenharmony_ci	gr3d->client.base.class = HOST1X_CLASS_GR3D;
3488c2ecf20Sopenharmony_ci	gr3d->client.base.syncpts = syncpts;
3498c2ecf20Sopenharmony_ci	gr3d->client.base.num_syncpts = 1;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&gr3d->client.list);
3528c2ecf20Sopenharmony_ci	gr3d->client.version = gr3d->soc->version;
3538c2ecf20Sopenharmony_ci	gr3d->client.ops = &gr3d_ops;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	err = host1x_client_register(&gr3d->client.base);
3568c2ecf20Sopenharmony_ci	if (err < 0) {
3578c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
3588c2ecf20Sopenharmony_ci			err);
3598c2ecf20Sopenharmony_ci		return err;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* initialize address register map */
3638c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
3648c2ecf20Sopenharmony_ci		set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, gr3d);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int gr3d_remove(struct platform_device *pdev)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct gr3d *gr3d = platform_get_drvdata(pdev);
3748c2ecf20Sopenharmony_ci	int err;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	err = host1x_client_unregister(&gr3d->client.base);
3778c2ecf20Sopenharmony_ci	if (err < 0) {
3788c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
3798c2ecf20Sopenharmony_ci			err);
3808c2ecf20Sopenharmony_ci		return err;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (gr3d->clk_secondary) {
3848c2ecf20Sopenharmony_ci		reset_control_assert(gr3d->rst_secondary);
3858c2ecf20Sopenharmony_ci		tegra_powergate_power_off(TEGRA_POWERGATE_3D1);
3868c2ecf20Sopenharmony_ci		clk_disable_unprepare(gr3d->clk_secondary);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	reset_control_assert(gr3d->rst);
3908c2ecf20Sopenharmony_ci	tegra_powergate_power_off(TEGRA_POWERGATE_3D);
3918c2ecf20Sopenharmony_ci	clk_disable_unprepare(gr3d->clk);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return 0;
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistruct platform_driver tegra_gr3d_driver = {
3978c2ecf20Sopenharmony_ci	.driver = {
3988c2ecf20Sopenharmony_ci		.name = "tegra-gr3d",
3998c2ecf20Sopenharmony_ci		.of_match_table = tegra_gr3d_match,
4008c2ecf20Sopenharmony_ci	},
4018c2ecf20Sopenharmony_ci	.probe = gr3d_probe,
4028c2ecf20Sopenharmony_ci	.remove = gr3d_remove,
4038c2ecf20Sopenharmony_ci};
404