18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2013, NVIDIA Corporation. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/iommu.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/of_device.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "drm.h" 128c2ecf20Sopenharmony_ci#include "gem.h" 138c2ecf20Sopenharmony_ci#include "gr2d.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct gr2d_soc { 168c2ecf20Sopenharmony_ci unsigned int version; 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct gr2d { 208c2ecf20Sopenharmony_ci struct tegra_drm_client client; 218c2ecf20Sopenharmony_ci struct host1x_channel *channel; 228c2ecf20Sopenharmony_ci struct clk *clk; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci const struct gr2d_soc *soc; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS); 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline struct gr2d *to_gr2d(struct tegra_drm_client *client) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci return container_of(client, struct gr2d, client); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int gr2d_init(struct host1x_client *client) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct tegra_drm_client *drm = host1x_to_drm_client(client); 378c2ecf20Sopenharmony_ci struct drm_device *dev = dev_get_drvdata(client->host); 388c2ecf20Sopenharmony_ci unsigned long flags = HOST1X_SYNCPT_HAS_BASE; 398c2ecf20Sopenharmony_ci struct gr2d *gr2d = to_gr2d(drm); 408c2ecf20Sopenharmony_ci int err; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci gr2d->channel = host1x_channel_request(client); 438c2ecf20Sopenharmony_ci if (!gr2d->channel) 448c2ecf20Sopenharmony_ci return -ENOMEM; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci client->syncpts[0] = host1x_syncpt_request(client, flags); 478c2ecf20Sopenharmony_ci if (!client->syncpts[0]) { 488c2ecf20Sopenharmony_ci err = -ENOMEM; 498c2ecf20Sopenharmony_ci dev_err(client->dev, "failed to request syncpoint: %d\n", err); 508c2ecf20Sopenharmony_ci goto put; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci err = host1x_client_iommu_attach(client); 548c2ecf20Sopenharmony_ci if (err < 0) { 558c2ecf20Sopenharmony_ci dev_err(client->dev, "failed to attach to domain: %d\n", err); 568c2ecf20Sopenharmony_ci goto free; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci err = tegra_drm_register_client(dev->dev_private, drm); 608c2ecf20Sopenharmony_ci if (err < 0) { 618c2ecf20Sopenharmony_ci dev_err(client->dev, "failed to register client: %d\n", err); 628c2ecf20Sopenharmony_ci goto detach; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cidetach: 688c2ecf20Sopenharmony_ci host1x_client_iommu_detach(client); 698c2ecf20Sopenharmony_cifree: 708c2ecf20Sopenharmony_ci host1x_syncpt_free(client->syncpts[0]); 718c2ecf20Sopenharmony_ciput: 728c2ecf20Sopenharmony_ci host1x_channel_put(gr2d->channel); 738c2ecf20Sopenharmony_ci return err; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int gr2d_exit(struct host1x_client *client) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct tegra_drm_client *drm = host1x_to_drm_client(client); 798c2ecf20Sopenharmony_ci struct drm_device *dev = dev_get_drvdata(client->host); 808c2ecf20Sopenharmony_ci struct tegra_drm *tegra = dev->dev_private; 818c2ecf20Sopenharmony_ci struct gr2d *gr2d = to_gr2d(drm); 828c2ecf20Sopenharmony_ci int err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci err = tegra_drm_unregister_client(tegra, drm); 858c2ecf20Sopenharmony_ci if (err < 0) 868c2ecf20Sopenharmony_ci return err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci host1x_client_iommu_detach(client); 898c2ecf20Sopenharmony_ci host1x_syncpt_free(client->syncpts[0]); 908c2ecf20Sopenharmony_ci host1x_channel_put(gr2d->channel); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return 0; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const struct host1x_client_ops gr2d_client_ops = { 968c2ecf20Sopenharmony_ci .init = gr2d_init, 978c2ecf20Sopenharmony_ci .exit = gr2d_exit, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int gr2d_open_channel(struct tegra_drm_client *client, 1018c2ecf20Sopenharmony_ci struct tegra_drm_context *context) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct gr2d *gr2d = to_gr2d(client); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci context->channel = host1x_channel_get(gr2d->channel); 1068c2ecf20Sopenharmony_ci if (!context->channel) 1078c2ecf20Sopenharmony_ci return -ENOMEM; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void gr2d_close_channel(struct tegra_drm_context *context) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci host1x_channel_put(context->channel); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct gr2d *gr2d = dev_get_drvdata(dev); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci switch (class) { 1228c2ecf20Sopenharmony_ci case HOST1X_CLASS_HOST1X: 1238c2ecf20Sopenharmony_ci if (offset == 0x2b) 1248c2ecf20Sopenharmony_ci return 1; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci case HOST1X_CLASS_GR2D: 1298c2ecf20Sopenharmony_ci case HOST1X_CLASS_GR2D_SB: 1308c2ecf20Sopenharmony_ci if (offset >= GR2D_NUM_REGS) 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (test_bit(offset, gr2d->addr_regs)) 1348c2ecf20Sopenharmony_ci return 1; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int gr2d_is_valid_class(u32 class) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci return (class == HOST1X_CLASS_GR2D || 1458c2ecf20Sopenharmony_ci class == HOST1X_CLASS_GR2D_SB); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic const struct tegra_drm_client_ops gr2d_ops = { 1498c2ecf20Sopenharmony_ci .open_channel = gr2d_open_channel, 1508c2ecf20Sopenharmony_ci .close_channel = gr2d_close_channel, 1518c2ecf20Sopenharmony_ci .is_addr_reg = gr2d_is_addr_reg, 1528c2ecf20Sopenharmony_ci .is_valid_class = gr2d_is_valid_class, 1538c2ecf20Sopenharmony_ci .submit = tegra_drm_submit, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const struct gr2d_soc tegra20_gr2d_soc = { 1578c2ecf20Sopenharmony_ci .version = 0x20, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct gr2d_soc tegra30_gr2d_soc = { 1618c2ecf20Sopenharmony_ci .version = 0x30, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct of_device_id gr2d_match[] = { 1658c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-gr2d", .data = &tegra20_gr2d_soc }, 1668c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-gr2d", .data = &tegra30_gr2d_soc }, 1678c2ecf20Sopenharmony_ci { }, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gr2d_match); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic const u32 gr2d_addr_regs[] = { 1728c2ecf20Sopenharmony_ci GR2D_UA_BASE_ADDR, 1738c2ecf20Sopenharmony_ci GR2D_VA_BASE_ADDR, 1748c2ecf20Sopenharmony_ci GR2D_PAT_BASE_ADDR, 1758c2ecf20Sopenharmony_ci GR2D_DSTA_BASE_ADDR, 1768c2ecf20Sopenharmony_ci GR2D_DSTB_BASE_ADDR, 1778c2ecf20Sopenharmony_ci GR2D_DSTC_BASE_ADDR, 1788c2ecf20Sopenharmony_ci GR2D_SRCA_BASE_ADDR, 1798c2ecf20Sopenharmony_ci GR2D_SRCB_BASE_ADDR, 1808c2ecf20Sopenharmony_ci GR2D_PATBASE_ADDR, 1818c2ecf20Sopenharmony_ci GR2D_SRC_BASE_ADDR_SB, 1828c2ecf20Sopenharmony_ci GR2D_DSTA_BASE_ADDR_SB, 1838c2ecf20Sopenharmony_ci GR2D_DSTB_BASE_ADDR_SB, 1848c2ecf20Sopenharmony_ci GR2D_UA_BASE_ADDR_SB, 1858c2ecf20Sopenharmony_ci GR2D_VA_BASE_ADDR_SB, 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int gr2d_probe(struct platform_device *pdev) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1918c2ecf20Sopenharmony_ci struct host1x_syncpt **syncpts; 1928c2ecf20Sopenharmony_ci struct gr2d *gr2d; 1938c2ecf20Sopenharmony_ci unsigned int i; 1948c2ecf20Sopenharmony_ci int err; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL); 1978c2ecf20Sopenharmony_ci if (!gr2d) 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci gr2d->soc = of_device_get_match_data(dev); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL); 2038c2ecf20Sopenharmony_ci if (!syncpts) 2048c2ecf20Sopenharmony_ci return -ENOMEM; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci gr2d->clk = devm_clk_get(dev, NULL); 2078c2ecf20Sopenharmony_ci if (IS_ERR(gr2d->clk)) { 2088c2ecf20Sopenharmony_ci dev_err(dev, "cannot get clock\n"); 2098c2ecf20Sopenharmony_ci return PTR_ERR(gr2d->clk); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci err = clk_prepare_enable(gr2d->clk); 2138c2ecf20Sopenharmony_ci if (err) { 2148c2ecf20Sopenharmony_ci dev_err(dev, "cannot turn on clock\n"); 2158c2ecf20Sopenharmony_ci return err; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&gr2d->client.base.list); 2198c2ecf20Sopenharmony_ci gr2d->client.base.ops = &gr2d_client_ops; 2208c2ecf20Sopenharmony_ci gr2d->client.base.dev = dev; 2218c2ecf20Sopenharmony_ci gr2d->client.base.class = HOST1X_CLASS_GR2D; 2228c2ecf20Sopenharmony_ci gr2d->client.base.syncpts = syncpts; 2238c2ecf20Sopenharmony_ci gr2d->client.base.num_syncpts = 1; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&gr2d->client.list); 2268c2ecf20Sopenharmony_ci gr2d->client.version = gr2d->soc->version; 2278c2ecf20Sopenharmony_ci gr2d->client.ops = &gr2d_ops; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci err = host1x_client_register(&gr2d->client.base); 2308c2ecf20Sopenharmony_ci if (err < 0) { 2318c2ecf20Sopenharmony_ci dev_err(dev, "failed to register host1x client: %d\n", err); 2328c2ecf20Sopenharmony_ci clk_disable_unprepare(gr2d->clk); 2338c2ecf20Sopenharmony_ci return err; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* initialize address register map */ 2378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++) 2388c2ecf20Sopenharmony_ci set_bit(gr2d_addr_regs[i], gr2d->addr_regs); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, gr2d); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int gr2d_remove(struct platform_device *pdev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct gr2d *gr2d = platform_get_drvdata(pdev); 2488c2ecf20Sopenharmony_ci int err; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci err = host1x_client_unregister(&gr2d->client.base); 2518c2ecf20Sopenharmony_ci if (err < 0) { 2528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 2538c2ecf20Sopenharmony_ci err); 2548c2ecf20Sopenharmony_ci return err; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci clk_disable_unprepare(gr2d->clk); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistruct platform_driver tegra_gr2d_driver = { 2638c2ecf20Sopenharmony_ci .driver = { 2648c2ecf20Sopenharmony_ci .name = "tegra-gr2d", 2658c2ecf20Sopenharmony_ci .of_match_table = gr2d_match, 2668c2ecf20Sopenharmony_ci }, 2678c2ecf20Sopenharmony_ci .probe = gr2d_probe, 2688c2ecf20Sopenharmony_ci .remove = gr2d_remove, 2698c2ecf20Sopenharmony_ci}; 270