blob: 0b42e99da8adedb18d9c8bedaaafe367a49f7ddb [file] [log] [blame]
Terje Bergstromd43f81c2013-03-22 16:34:09 +02001/*
Terje Bergstromd43f81c2013-03-22 16:34:09 +02002 * Copyright (c) 2012-2013, NVIDIA Corporation.
3 *
Thierry Redingd105a6c2014-02-11 15:53:33 +01004 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
Terje Bergstromd43f81c2013-03-22 16:34:09 +02007 */
8
Terje Bergstromd43f81c2013-03-22 16:34:09 +02009#include <linux/clk.h>
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030010#include <linux/iommu.h>
Terje Bergstromd43f81c2013-03-22 16:34:09 +020011
Terje Bergstromd43f81c2013-03-22 16:34:09 +020012#include "drm.h"
13#include "gem.h"
Thierry Reding497c56a2013-10-07 09:55:57 +020014#include "gr2d.h"
Thierry Redingc1bef812013-09-26 16:09:43 +020015
Terje Bergstromd43f81c2013-03-22 16:34:09 +020016struct gr2d {
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030017 struct iommu_group *group;
Thierry Reding53fa7f72013-09-24 15:35:40 +020018 struct tegra_drm_client client;
Terje Bergstromd43f81c2013-03-22 16:34:09 +020019 struct host1x_channel *channel;
Thierry Redingc1bef812013-09-26 16:09:43 +020020 struct clk *clk;
21
22 DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
Terje Bergstromd43f81c2013-03-22 16:34:09 +020023};
24
Thierry Reding53fa7f72013-09-24 15:35:40 +020025static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
Terje Bergstromd43f81c2013-03-22 16:34:09 +020026{
27 return container_of(client, struct gr2d, client);
28}
29
Thierry Reding776dc382013-10-14 14:43:22 +020030static int gr2d_init(struct host1x_client *client)
Terje Bergstromd43f81c2013-03-22 16:34:09 +020031{
Thierry Reding776dc382013-10-14 14:43:22 +020032 struct tegra_drm_client *drm = host1x_to_drm_client(client);
Thierry Reding9910f5c2014-05-22 09:57:15 +020033 struct drm_device *dev = dev_get_drvdata(client->parent);
Arto Merilainen61644dc2013-10-14 15:21:55 +030034 unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030035 struct tegra_drm *tegra = dev->dev_private;
Thierry Reding776dc382013-10-14 14:43:22 +020036 struct gr2d *gr2d = to_gr2d(drm);
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030037 int err;
Thierry Reding776dc382013-10-14 14:43:22 +020038
39 gr2d->channel = host1x_channel_request(client->dev);
40 if (!gr2d->channel)
41 return -ENOMEM;
42
Thierry Reding617dd7c2017-08-30 12:48:31 +020043 client->syncpts[0] = host1x_syncpt_request(client, flags);
Thierry Reding776dc382013-10-14 14:43:22 +020044 if (!client->syncpts[0]) {
Thierry Redingdd99b4b2018-05-04 14:58:26 +020045 err = -ENOMEM;
46 dev_err(client->dev, "failed to request syncpoint: %d\n", err);
47 goto put;
Thierry Reding776dc382013-10-14 14:43:22 +020048 }
49
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030050 if (tegra->domain) {
51 gr2d->group = iommu_group_get(client->dev);
52
53 if (gr2d->group) {
54 err = iommu_attach_group(tegra->domain, gr2d->group);
55 if (err < 0) {
56 dev_err(client->dev,
57 "failed to attach to domain: %d\n",
58 err);
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030059 iommu_group_put(gr2d->group);
Thierry Redingdd99b4b2018-05-04 14:58:26 +020060 goto free;
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030061 }
62 }
63 }
64
Thierry Redingdd99b4b2018-05-04 14:58:26 +020065 err = tegra_drm_register_client(tegra, drm);
66 if (err < 0) {
67 dev_err(client->dev, "failed to register client: %d\n", err);
68 goto detach;
69 }
70
71 return 0;
72
73detach:
74 if (gr2d->group) {
75 iommu_detach_group(tegra->domain, gr2d->group);
76 iommu_group_put(gr2d->group);
77 }
78free:
79 host1x_syncpt_free(client->syncpts[0]);
80put:
81 host1x_channel_put(gr2d->channel);
82 return err;
Terje Bergstromd43f81c2013-03-22 16:34:09 +020083}
84
Thierry Reding776dc382013-10-14 14:43:22 +020085static int gr2d_exit(struct host1x_client *client)
Terje Bergstromd43f81c2013-03-22 16:34:09 +020086{
Thierry Reding776dc382013-10-14 14:43:22 +020087 struct tegra_drm_client *drm = host1x_to_drm_client(client);
Thierry Reding9910f5c2014-05-22 09:57:15 +020088 struct drm_device *dev = dev_get_drvdata(client->parent);
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030089 struct tegra_drm *tegra = dev->dev_private;
Thierry Reding776dc382013-10-14 14:43:22 +020090 struct gr2d *gr2d = to_gr2d(drm);
91 int err;
92
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +030093 err = tegra_drm_unregister_client(tegra, drm);
Thierry Reding776dc382013-10-14 14:43:22 +020094 if (err < 0)
95 return err;
96
97 host1x_syncpt_free(client->syncpts[0]);
Mikko Perttunen8474b022017-06-15 02:18:42 +030098 host1x_channel_put(gr2d->channel);
Thierry Reding776dc382013-10-14 14:43:22 +020099
Dmitry Osipenko5fda01b2018-05-04 02:47:20 +0300100 if (gr2d->group) {
101 iommu_detach_group(tegra->domain, gr2d->group);
102 iommu_group_put(gr2d->group);
103 }
104
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200105 return 0;
106}
107
Thierry Reding53fa7f72013-09-24 15:35:40 +0200108static const struct host1x_client_ops gr2d_client_ops = {
Thierry Reding776dc382013-10-14 14:43:22 +0200109 .init = gr2d_init,
110 .exit = gr2d_exit,
Thierry Reding53fa7f72013-09-24 15:35:40 +0200111};
112
113static int gr2d_open_channel(struct tegra_drm_client *client,
Thierry Redingc88c3632013-09-26 16:08:22 +0200114 struct tegra_drm_context *context)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200115{
116 struct gr2d *gr2d = to_gr2d(client);
117
118 context->channel = host1x_channel_get(gr2d->channel);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200119 if (!context->channel)
120 return -ENOMEM;
121
122 return 0;
123}
124
Thierry Redingc88c3632013-09-26 16:08:22 +0200125static void gr2d_close_channel(struct tegra_drm_context *context)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200126{
127 host1x_channel_put(context->channel);
128}
129
Thierry Redingc1bef812013-09-26 16:09:43 +0200130static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset)
131{
132 struct gr2d *gr2d = dev_get_drvdata(dev);
133
134 switch (class) {
135 case HOST1X_CLASS_HOST1X:
136 if (offset == 0x2b)
137 return 1;
138
139 break;
140
141 case HOST1X_CLASS_GR2D:
142 case HOST1X_CLASS_GR2D_SB:
143 if (offset >= GR2D_NUM_REGS)
144 break;
145
146 if (test_bit(offset, gr2d->addr_regs))
147 return 1;
148
149 break;
150 }
151
152 return 0;
153}
154
Dmitry Osipenko0f563a42017-06-15 02:18:37 +0300155static int gr2d_is_valid_class(u32 class)
156{
157 return (class == HOST1X_CLASS_GR2D ||
158 class == HOST1X_CLASS_GR2D_SB);
159}
160
Thierry Reding53fa7f72013-09-24 15:35:40 +0200161static const struct tegra_drm_client_ops gr2d_ops = {
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200162 .open_channel = gr2d_open_channel,
163 .close_channel = gr2d_close_channel,
Thierry Redingc40f0f12013-10-10 11:00:33 +0200164 .is_addr_reg = gr2d_is_addr_reg,
Dmitry Osipenko0f563a42017-06-15 02:18:37 +0300165 .is_valid_class = gr2d_is_valid_class,
Thierry Redingc40f0f12013-10-10 11:00:33 +0200166 .submit = tegra_drm_submit,
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200167};
168
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200169static const struct of_device_id gr2d_match[] = {
170 { .compatible = "nvidia,tegra30-gr2d" },
171 { .compatible = "nvidia,tegra20-gr2d" },
172 { },
173};
Stephen Warrenef707282014-06-18 16:21:55 -0600174MODULE_DEVICE_TABLE(of, gr2d_match);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200175
Thierry Redingc1bef812013-09-26 16:09:43 +0200176static const u32 gr2d_addr_regs[] = {
Thierry Reding497c56a2013-10-07 09:55:57 +0200177 GR2D_UA_BASE_ADDR,
178 GR2D_VA_BASE_ADDR,
179 GR2D_PAT_BASE_ADDR,
180 GR2D_DSTA_BASE_ADDR,
181 GR2D_DSTB_BASE_ADDR,
182 GR2D_DSTC_BASE_ADDR,
183 GR2D_SRCA_BASE_ADDR,
184 GR2D_SRCB_BASE_ADDR,
185 GR2D_SRC_BASE_ADDR_SB,
186 GR2D_DSTA_BASE_ADDR_SB,
187 GR2D_DSTB_BASE_ADDR_SB,
188 GR2D_UA_BASE_ADDR_SB,
189 GR2D_VA_BASE_ADDR_SB,
Thierry Redingc1bef812013-09-26 16:09:43 +0200190};
191
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200192static int gr2d_probe(struct platform_device *pdev)
193{
194 struct device *dev = &pdev->dev;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200195 struct host1x_syncpt **syncpts;
Thierry Redingc1bef812013-09-26 16:09:43 +0200196 struct gr2d *gr2d;
197 unsigned int i;
198 int err;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200199
200 gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
201 if (!gr2d)
202 return -ENOMEM;
203
204 syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
205 if (!syncpts)
206 return -ENOMEM;
207
208 gr2d->clk = devm_clk_get(dev, NULL);
209 if (IS_ERR(gr2d->clk)) {
210 dev_err(dev, "cannot get clock\n");
211 return PTR_ERR(gr2d->clk);
212 }
213
214 err = clk_prepare_enable(gr2d->clk);
215 if (err) {
216 dev_err(dev, "cannot turn on clock\n");
217 return err;
218 }
219
Thierry Reding53fa7f72013-09-24 15:35:40 +0200220 INIT_LIST_HEAD(&gr2d->client.base.list);
221 gr2d->client.base.ops = &gr2d_client_ops;
222 gr2d->client.base.dev = dev;
223 gr2d->client.base.class = HOST1X_CLASS_GR2D;
224 gr2d->client.base.syncpts = syncpts;
225 gr2d->client.base.num_syncpts = 1;
Thierry Reding776dc382013-10-14 14:43:22 +0200226
227 INIT_LIST_HEAD(&gr2d->client.list);
Thierry Reding53fa7f72013-09-24 15:35:40 +0200228 gr2d->client.ops = &gr2d_ops;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200229
Thierry Reding776dc382013-10-14 14:43:22 +0200230 err = host1x_client_register(&gr2d->client.base);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200231 if (err < 0) {
232 dev_err(dev, "failed to register host1x client: %d\n", err);
Wei Yongjunb0084032013-10-21 13:38:34 +0800233 clk_disable_unprepare(gr2d->clk);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200234 return err;
235 }
236
Thierry Redingc1bef812013-09-26 16:09:43 +0200237 /* initialize address register map */
238 for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
239 set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200240
241 platform_set_drvdata(pdev, gr2d);
242
243 return 0;
244}
245
Thierry Redingc1bef812013-09-26 16:09:43 +0200246static int gr2d_remove(struct platform_device *pdev)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200247{
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200248 struct gr2d *gr2d = platform_get_drvdata(pdev);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200249 int err;
250
Thierry Reding776dc382013-10-14 14:43:22 +0200251 err = host1x_client_unregister(&gr2d->client.base);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200252 if (err < 0) {
Thierry Redingc1bef812013-09-26 16:09:43 +0200253 dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
254 err);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200255 return err;
256 }
257
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200258 clk_disable_unprepare(gr2d->clk);
259
260 return 0;
261}
262
263struct platform_driver tegra_gr2d_driver = {
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200264 .driver = {
Thierry Redinga137ce32013-10-14 14:44:54 +0200265 .name = "tegra-gr2d",
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200266 .of_match_table = gr2d_match,
Thierry Redingc1bef812013-09-26 16:09:43 +0200267 },
268 .probe = gr2d_probe,
269 .remove = gr2d_remove,
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200270};