]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/host1x/dev.c
b386a0bf828abf24457c442c8153e32e77387a07
[karo-tx-linux.git] / drivers / gpu / host1x / dev.c
1 /*
2  * Tegra host1x driver
3  *
4  * Copyright (c) 2010-2013, NVIDIA Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <linux/module.h>
20 #include <linux/list.h>
21 #include <linux/slab.h>
22 #include <linux/of.h>
23 #include <linux/of_device.h>
24 #include <linux/clk.h>
25 #include <linux/io.h>
26 #include <linux/dma-mapping.h>
27
28 #define CREATE_TRACE_POINTS
29 #include <trace/events/host1x.h>
30 #undef CREATE_TRACE_POINTS
31
32 #include "bus.h"
33 #include "dev.h"
34 #include "intr.h"
35 #include "channel.h"
36 #include "debug.h"
37 #include "hw/host1x01.h"
38 #include "hw/host1x02.h"
39 #include "hw/host1x04.h"
40 #include "hw/host1x05.h"
41
42 void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
43 {
44         void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset;
45
46         writel(v, sync_regs + r);
47 }
48
49 u32 host1x_sync_readl(struct host1x *host1x, u32 r)
50 {
51         void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset;
52
53         return readl(sync_regs + r);
54 }
55
56 void host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r)
57 {
58         writel(v, ch->regs + r);
59 }
60
61 u32 host1x_ch_readl(struct host1x_channel *ch, u32 r)
62 {
63         return readl(ch->regs + r);
64 }
65
66 static const struct host1x_info host1x01_info = {
67         .nb_channels = 8,
68         .nb_pts = 32,
69         .nb_mlocks = 16,
70         .nb_bases = 8,
71         .init = host1x01_init,
72         .sync_offset = 0x3000,
73         .dma_mask = DMA_BIT_MASK(32),
74 };
75
76 static const struct host1x_info host1x02_info = {
77         .nb_channels = 9,
78         .nb_pts = 32,
79         .nb_mlocks = 16,
80         .nb_bases = 12,
81         .init = host1x02_init,
82         .sync_offset = 0x3000,
83         .dma_mask = DMA_BIT_MASK(32),
84 };
85
86 static const struct host1x_info host1x04_info = {
87         .nb_channels = 12,
88         .nb_pts = 192,
89         .nb_mlocks = 16,
90         .nb_bases = 64,
91         .init = host1x04_init,
92         .sync_offset = 0x2100,
93         .dma_mask = DMA_BIT_MASK(34),
94 };
95
96 static const struct host1x_info host1x05_info = {
97         .nb_channels = 14,
98         .nb_pts = 192,
99         .nb_mlocks = 16,
100         .nb_bases = 64,
101         .init = host1x05_init,
102         .sync_offset = 0x2100,
103         .dma_mask = DMA_BIT_MASK(34),
104 };
105
106 static const struct of_device_id host1x_of_match[] = {
107         { .compatible = "nvidia,tegra210-host1x", .data = &host1x05_info, },
108         { .compatible = "nvidia,tegra124-host1x", .data = &host1x04_info, },
109         { .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },
110         { .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, },
111         { .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, },
112         { },
113 };
114 MODULE_DEVICE_TABLE(of, host1x_of_match);
115
116 static int host1x_probe(struct platform_device *pdev)
117 {
118         const struct of_device_id *id;
119         struct host1x *host;
120         struct resource *regs;
121         int syncpt_irq;
122         int err;
123
124         id = of_match_device(host1x_of_match, &pdev->dev);
125         if (!id)
126                 return -EINVAL;
127
128         regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
129         if (!regs) {
130                 dev_err(&pdev->dev, "failed to get registers\n");
131                 return -ENXIO;
132         }
133
134         syncpt_irq = platform_get_irq(pdev, 0);
135         if (syncpt_irq < 0) {
136                 dev_err(&pdev->dev, "failed to get IRQ\n");
137                 return -ENXIO;
138         }
139
140         host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
141         if (!host)
142                 return -ENOMEM;
143
144         mutex_init(&host->devices_lock);
145         INIT_LIST_HEAD(&host->devices);
146         INIT_LIST_HEAD(&host->list);
147         host->dev = &pdev->dev;
148         host->info = id->data;
149
150         /* set common host1x device data */
151         platform_set_drvdata(pdev, host);
152
153         host->regs = devm_ioremap_resource(&pdev->dev, regs);
154         if (IS_ERR(host->regs))
155                 return PTR_ERR(host->regs);
156
157         dma_set_mask_and_coherent(host->dev, host->info->dma_mask);
158
159         if (host->info->init) {
160                 err = host->info->init(host);
161                 if (err)
162                         return err;
163         }
164
165         host->clk = devm_clk_get(&pdev->dev, NULL);
166         if (IS_ERR(host->clk)) {
167                 dev_err(&pdev->dev, "failed to get clock\n");
168                 err = PTR_ERR(host->clk);
169                 return err;
170         }
171
172         if (iommu_present(&platform_bus_type)) {
173                 struct iommu_domain_geometry *geometry;
174                 unsigned long order;
175
176                 host->domain = iommu_domain_alloc(&platform_bus_type);
177                 if (!host->domain)
178                         return -ENOMEM;
179
180                 err = iommu_attach_device(host->domain, &pdev->dev);
181                 if (err)
182                         goto fail_free_domain;
183
184                 geometry = &host->domain->geometry;
185
186                 order = __ffs(host->domain->pgsize_bitmap);
187                 init_iova_domain(&host->iova, 1UL << order,
188                                  geometry->aperture_start >> order,
189                                  geometry->aperture_end >> order);
190                 host->iova_end = geometry->aperture_end;
191         }
192
193         err = host1x_channel_list_init(host);
194         if (err) {
195                 dev_err(&pdev->dev, "failed to initialize channel list\n");
196                 goto fail_detach_device;
197         }
198
199         err = clk_prepare_enable(host->clk);
200         if (err < 0) {
201                 dev_err(&pdev->dev, "failed to enable clock\n");
202                 goto fail_detach_device;
203         }
204
205         err = host1x_syncpt_init(host);
206         if (err) {
207                 dev_err(&pdev->dev, "failed to initialize syncpts\n");
208                 goto fail_unprepare_disable;
209         }
210
211         err = host1x_intr_init(host, syncpt_irq);
212         if (err) {
213                 dev_err(&pdev->dev, "failed to initialize interrupts\n");
214                 goto fail_deinit_syncpt;
215         }
216
217         host1x_debug_init(host);
218
219         err = host1x_register(host);
220         if (err < 0)
221                 goto fail_deinit_intr;
222
223         return 0;
224
225 fail_deinit_intr:
226         host1x_intr_deinit(host);
227 fail_deinit_syncpt:
228         host1x_syncpt_deinit(host);
229 fail_unprepare_disable:
230         clk_disable_unprepare(host->clk);
231 fail_detach_device:
232         if (host->domain) {
233                 put_iova_domain(&host->iova);
234                 iommu_detach_device(host->domain, &pdev->dev);
235         }
236 fail_free_domain:
237         if (host->domain)
238                 iommu_domain_free(host->domain);
239
240         return err;
241 }
242
243 static int host1x_remove(struct platform_device *pdev)
244 {
245         struct host1x *host = platform_get_drvdata(pdev);
246
247         host1x_unregister(host);
248         host1x_intr_deinit(host);
249         host1x_syncpt_deinit(host);
250         clk_disable_unprepare(host->clk);
251
252         if (host->domain) {
253                 put_iova_domain(&host->iova);
254                 iommu_detach_device(host->domain, &pdev->dev);
255                 iommu_domain_free(host->domain);
256         }
257
258         return 0;
259 }
260
261 static struct platform_driver tegra_host1x_driver = {
262         .driver = {
263                 .name = "tegra-host1x",
264                 .of_match_table = host1x_of_match,
265         },
266         .probe = host1x_probe,
267         .remove = host1x_remove,
268 };
269
270 static struct platform_driver * const drivers[] = {
271         &tegra_host1x_driver,
272         &tegra_mipi_driver,
273 };
274
275 static int __init tegra_host1x_init(void)
276 {
277         int err;
278
279         err = bus_register(&host1x_bus_type);
280         if (err < 0)
281                 return err;
282
283         err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
284         if (err < 0)
285                 bus_unregister(&host1x_bus_type);
286
287         return err;
288 }
289 module_init(tegra_host1x_init);
290
291 static void __exit tegra_host1x_exit(void)
292 {
293         platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
294         bus_unregister(&host1x_bus_type);
295 }
296 module_exit(tegra_host1x_exit);
297
298 MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
299 MODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>");
300 MODULE_DESCRIPTION("Host1x driver for Tegra products");
301 MODULE_LICENSE("GPL");