aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/tegra/host/dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/tegra/host/dev.c')
-rw-r--r--drivers/video/tegra/host/dev.c635
1 files changed, 635 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c
new file mode 100644
index 00000000000..8f0c0393401
--- /dev/null
+++ b/drivers/video/tegra/host/dev.c
@@ -0,0 +1,635 @@
1/*
2 * drivers/video/tegra/host/dev.c
3 *
4 * Tegra Graphics Host Driver Entrypoint
5 *
6 * Copyright (c) 2010-2012, NVIDIA Corporation.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <linux/slab.h>
22#include <linux/string.h>
23#include <linux/spinlock.h>
24#include <linux/fs.h>
25#include <linux/cdev.h>
26#include <linux/uaccess.h>
27#include <linux/file.h>
28#include <linux/clk.h>
29#include <linux/hrtimer.h>
30
31#include "dev.h"
32#define CREATE_TRACE_POINTS
33#include <trace/events/nvhost.h>
34
35#include <linux/io.h>
36
37#include <linux/nvhost.h>
38#include <linux/nvhost_ioctl.h>
39#include <mach/nvmap.h>
40#include <mach/gpufuse.h>
41#include <mach/hardware.h>
42#include <mach/iomap.h>
43
44#include "debug.h"
45#include "nvhost_job.h"
46#include "t20/t20.h"
47#include "t30/t30.h"
48#include "bus_client.h"
49
50#define DRIVER_NAME "host1x"
51
52int nvhost_major;
53int nvhost_minor;
54
55static unsigned int register_sets;
56
57struct nvhost_ctrl_userctx {
58 struct nvhost_master *dev;
59 u32 *mod_locks;
60};
61
62static int nvhost_ctrlrelease(struct inode *inode, struct file *filp)
63{
64 struct nvhost_ctrl_userctx *priv = filp->private_data;
65 int i;
66
67 trace_nvhost_ctrlrelease(priv->dev->dev->name);
68
69 filp->private_data = NULL;
70 if (priv->mod_locks[0])
71 nvhost_module_idle(priv->dev->dev);
72 for (i = 1; i < priv->dev->syncpt.nb_mlocks; i++)
73 if (priv->mod_locks[i])
74 nvhost_mutex_unlock(&priv->dev->syncpt, i);
75 kfree(priv->mod_locks);
76 kfree(priv);
77 return 0;
78}
79
80static int nvhost_ctrlopen(struct inode *inode, struct file *filp)
81{
82 struct nvhost_master *host = container_of(inode->i_cdev, struct nvhost_master, cdev);
83 struct nvhost_ctrl_userctx *priv;
84 u32 *mod_locks;
85
86 trace_nvhost_ctrlopen(host->dev->name);
87
88 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
89 mod_locks = kzalloc(sizeof(u32) * host->syncpt.nb_mlocks, GFP_KERNEL);
90
91 if (!(priv && mod_locks)) {
92 kfree(priv);
93 kfree(mod_locks);
94 return -ENOMEM;
95 }
96
97 priv->dev = host;
98 priv->mod_locks = mod_locks;
99 filp->private_data = priv;
100 return 0;
101}
102
103static int nvhost_ioctl_ctrl_syncpt_read(struct nvhost_ctrl_userctx *ctx,
104 struct nvhost_ctrl_syncpt_read_args *args)
105{
106 if (args->id >= ctx->dev->syncpt.nb_pts)
107 return -EINVAL;
108 args->value = nvhost_syncpt_read(&ctx->dev->syncpt, args->id);
109 trace_nvhost_ioctl_ctrl_syncpt_read(args->id, args->value);
110 return 0;
111}
112
113static int nvhost_ioctl_ctrl_syncpt_incr(struct nvhost_ctrl_userctx *ctx,
114 struct nvhost_ctrl_syncpt_incr_args *args)
115{
116 if (args->id >= ctx->dev->syncpt.nb_pts)
117 return -EINVAL;
118 trace_nvhost_ioctl_ctrl_syncpt_incr(args->id);
119 nvhost_syncpt_incr(&ctx->dev->syncpt, args->id);
120 return 0;
121}
122
123static int nvhost_ioctl_ctrl_syncpt_waitex(struct nvhost_ctrl_userctx *ctx,
124 struct nvhost_ctrl_syncpt_waitex_args *args)
125{
126 u32 timeout;
127 int err;
128 if (args->id >= ctx->dev->syncpt.nb_pts)
129 return -EINVAL;
130 if (args->timeout == NVHOST_NO_TIMEOUT)
131 timeout = MAX_SCHEDULE_TIMEOUT;
132 else
133 timeout = (u32)msecs_to_jiffies(args->timeout);
134
135 err = nvhost_syncpt_wait_timeout(&ctx->dev->syncpt, args->id,
136 args->thresh, timeout, &args->value);
137 trace_nvhost_ioctl_ctrl_syncpt_wait(args->id, args->thresh,
138 args->timeout, args->value, err);
139
140 return err;
141}
142
143static int nvhost_ioctl_ctrl_module_mutex(struct nvhost_ctrl_userctx *ctx,
144 struct nvhost_ctrl_module_mutex_args *args)
145{
146 int err = 0;
147 if (args->id >= ctx->dev->syncpt.nb_mlocks ||
148 args->lock > 1)
149 return -EINVAL;
150
151 trace_nvhost_ioctl_ctrl_module_mutex(args->lock, args->id);
152 if (args->lock && !ctx->mod_locks[args->id]) {
153 if (args->id == 0)
154 nvhost_module_busy(ctx->dev->dev);
155 else
156 err = nvhost_mutex_try_lock(&ctx->dev->syncpt,
157 args->id);
158 if (!err)
159 ctx->mod_locks[args->id] = 1;
160 } else if (!args->lock && ctx->mod_locks[args->id]) {
161 if (args->id == 0)
162 nvhost_module_idle(ctx->dev->dev);
163 else
164 nvhost_mutex_unlock(&ctx->dev->syncpt, args->id);
165 ctx->mod_locks[args->id] = 0;
166 }
167 return err;
168}
169
170static struct nvhost_device *get_ndev_by_moduleid(struct nvhost_master *host,
171 u32 id)
172{
173 int i;
174
175 for (i = 0; i < host->nb_channels; i++) {
176 struct nvhost_device *ndev = host->channels[i].dev;
177
178 /* display and dsi do not use channel for register programming.
179 * so their channels do not have device instance.
180 * hence skip such channels from here. */
181 if (ndev == NULL)
182 continue;
183
184 if (id == ndev->moduleid)
185 return ndev;
186 }
187 return NULL;
188}
189
190static int nvhost_ioctl_ctrl_module_regrdwr(struct nvhost_ctrl_userctx *ctx,
191 struct nvhost_ctrl_module_regrdwr_args *args)
192{
193 u32 num_offsets = args->num_offsets;
194 u32 *offsets = args->offsets;
195 u32 *values = args->values;
196 u32 vals[64];
197 struct nvhost_device *ndev;
198
199 trace_nvhost_ioctl_ctrl_module_regrdwr(args->id,
200 args->num_offsets, args->write);
201 /* Check that there is something to read and that block size is
202 * u32 aligned */
203 if (num_offsets == 0 || args->block_size & 3)
204 return -EINVAL;
205
206 ndev = get_ndev_by_moduleid(ctx->dev, args->id);
207 if (!ndev)
208 return -EINVAL;
209
210 while (num_offsets--) {
211 int remaining = args->block_size >> 2;
212 u32 offs;
213 if (get_user(offs, offsets))
214 return -EFAULT;
215 offsets++;
216 while (remaining) {
217 int batch = min(remaining, 64);
218 if (args->write) {
219 if (copy_from_user(vals, values,
220 batch*sizeof(u32)))
221 return -EFAULT;
222 nvhost_write_module_regs(ndev,
223 offs, batch, vals);
224 } else {
225 nvhost_read_module_regs(ndev,
226 offs, batch, vals);
227 if (copy_to_user(values, vals,
228 batch*sizeof(u32)))
229 return -EFAULT;
230 }
231 remaining -= batch;
232 offs += batch;
233 values += batch;
234 }
235 }
236
237 return 0;
238}
239
240static int nvhost_ioctl_ctrl_get_version(struct nvhost_ctrl_userctx *ctx,
241 struct nvhost_get_param_args *args)
242{
243 args->value = NVHOST_SUBMIT_VERSION_MAX_SUPPORTED;
244 return 0;
245}
246
247static long nvhost_ctrlctl(struct file *filp,
248 unsigned int cmd, unsigned long arg)
249{
250 struct nvhost_ctrl_userctx *priv = filp->private_data;
251 u8 buf[NVHOST_IOCTL_CTRL_MAX_ARG_SIZE];
252 int err = 0;
253
254 if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) ||
255 (_IOC_NR(cmd) == 0) ||
256 (_IOC_NR(cmd) > NVHOST_IOCTL_CTRL_LAST))
257 return -EFAULT;
258
259 BUG_ON(_IOC_SIZE(cmd) > NVHOST_IOCTL_CTRL_MAX_ARG_SIZE);
260
261 if (_IOC_DIR(cmd) & _IOC_WRITE) {
262 if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd)))
263 return -EFAULT;
264 }
265
266 switch (cmd) {
267 case NVHOST_IOCTL_CTRL_SYNCPT_READ:
268 err = nvhost_ioctl_ctrl_syncpt_read(priv, (void *)buf);
269 break;
270 case NVHOST_IOCTL_CTRL_SYNCPT_INCR:
271 err = nvhost_ioctl_ctrl_syncpt_incr(priv, (void *)buf);
272 break;
273 case NVHOST_IOCTL_CTRL_SYNCPT_WAIT:
274 err = nvhost_ioctl_ctrl_syncpt_waitex(priv, (void *)buf);
275 break;
276 case NVHOST_IOCTL_CTRL_MODULE_MUTEX:
277 err = nvhost_ioctl_ctrl_module_mutex(priv, (void *)buf);
278 break;
279 case NVHOST_IOCTL_CTRL_MODULE_REGRDWR:
280 err = nvhost_ioctl_ctrl_module_regrdwr(priv, (void *)buf);
281 break;
282 case NVHOST_IOCTL_CTRL_SYNCPT_WAITEX:
283 err = nvhost_ioctl_ctrl_syncpt_waitex(priv, (void *)buf);
284 break;
285 case NVHOST_IOCTL_CTRL_GET_VERSION:
286 err = nvhost_ioctl_ctrl_get_version(priv, (void *)buf);
287 break;
288 default:
289 err = -ENOTTY;
290 break;
291 }
292
293 if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ))
294 err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd));
295
296 return err;
297}
298
299static const struct file_operations nvhost_ctrlops = {
300 .owner = THIS_MODULE,
301 .release = nvhost_ctrlrelease,
302 .open = nvhost_ctrlopen,
303 .unlocked_ioctl = nvhost_ctrlctl
304};
305
306static void power_on_host(struct nvhost_device *dev)
307{
308 struct nvhost_master *host = nvhost_get_drvdata(dev);
309 nvhost_syncpt_reset(&host->syncpt);
310 nvhost_intr_start(&host->intr, clk_get_rate(dev->clk[0]));
311}
312
313static int power_off_host(struct nvhost_device *dev)
314{
315 struct nvhost_master *host = nvhost_get_drvdata(dev);
316 nvhost_syncpt_save(&host->syncpt);
317 nvhost_intr_stop(&host->intr);
318 return 0;
319}
320
321static int __devinit nvhost_user_init(struct nvhost_master *host)
322{
323 int err, devno;
324
325 host->nvhost_class = class_create(THIS_MODULE, IFACE_NAME);
326 if (IS_ERR(host->nvhost_class)) {
327 err = PTR_ERR(host->nvhost_class);
328 dev_err(&host->dev->dev, "failed to create class\n");
329 goto fail;
330 }
331
332 err = alloc_chrdev_region(&devno, nvhost_minor,
333 host->nb_channels + 1, IFACE_NAME);
334 nvhost_major = MAJOR(devno);
335 if (err < 0) {
336 dev_err(&host->dev->dev, "failed to reserve chrdev region\n");
337 goto fail;
338 }
339
340 cdev_init(&host->cdev, &nvhost_ctrlops);
341 host->cdev.owner = THIS_MODULE;
342 devno = MKDEV(nvhost_major, nvhost_minor + host->nb_channels);
343 err = cdev_add(&host->cdev, devno, 1);
344 if (err < 0)
345 goto fail;
346 host->ctrl = device_create(host->nvhost_class, NULL, devno, NULL,
347 IFACE_NAME "-ctrl");
348 if (IS_ERR(host->ctrl)) {
349 err = PTR_ERR(host->ctrl);
350 dev_err(&host->dev->dev, "failed to create ctrl device\n");
351 goto fail;
352 }
353
354 return 0;
355fail:
356 return err;
357}
358
359struct nvhost_device *nvhost_get_device(char *name)
360{
361 BUG_ON(!host_device_op(nvhost).get_nvhost_device);
362 return host_device_op(nvhost).get_nvhost_device(nvhost, name);
363}
364
365static void nvhost_remove_chip_support(struct nvhost_master *host)
366{
367 kfree(host->channels);
368 host->channels = 0;
369
370 kfree(host->syncpt.min_val);
371 host->syncpt.min_val = 0;
372
373 kfree(host->syncpt.max_val);
374 host->syncpt.max_val = 0;
375
376 kfree(host->syncpt.base_val);
377 host->syncpt.base_val = 0;
378
379 kfree(host->intr.syncpt);
380 host->intr.syncpt = 0;
381
382 kfree(host->syncpt.lock_counts);
383 host->syncpt.lock_counts = 0;
384}
385
386static int __devinit nvhost_init_chip_support(struct nvhost_master *host)
387{
388 int err;
389 switch (tegra_get_chipid()) {
390 case TEGRA_CHIPID_TEGRA2:
391 err = nvhost_init_t20_support(host);
392 break;
393
394 case TEGRA_CHIPID_TEGRA3:
395 err = nvhost_init_t30_support(host);
396 break;
397 default:
398 return -ENODEV;
399 }
400
401 if (err)
402 return err;
403
404 /* allocate items sized in chip specific support init */
405 host->channels = kzalloc(sizeof(struct nvhost_channel) *
406 host->nb_channels, GFP_KERNEL);
407
408 host->syncpt.min_val = kzalloc(sizeof(atomic_t) *
409 host->syncpt.nb_pts, GFP_KERNEL);
410
411 host->syncpt.max_val = kzalloc(sizeof(atomic_t) *
412 host->syncpt.nb_pts, GFP_KERNEL);
413
414 host->syncpt.base_val = kzalloc(sizeof(u32) *
415 host->syncpt.nb_bases, GFP_KERNEL);
416
417 host->intr.syncpt = kzalloc(sizeof(struct nvhost_intr_syncpt) *
418 host->syncpt.nb_pts, GFP_KERNEL);
419
420 host->syncpt.lock_counts = kzalloc(sizeof(atomic_t) *
421 host->syncpt.nb_mlocks, GFP_KERNEL);
422
423 if (!(host->channels && host->syncpt.min_val &&
424 host->syncpt.max_val && host->syncpt.base_val &&
425 host->intr.syncpt && host->syncpt.lock_counts)) {
426 /* frees happen in the support removal phase */
427 return -ENOMEM;
428 }
429
430 return 0;
431}
432
433static struct resource nvhost_resources[] = {
434 {
435 .start = TEGRA_HOST1X_BASE,
436 .end = TEGRA_HOST1X_BASE + TEGRA_HOST1X_SIZE - 1,
437 .flags = IORESOURCE_MEM,
438 },
439 {
440 .start = TEGRA_DISPLAY_BASE,
441 .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE - 1,
442 .flags = IORESOURCE_MEM,
443 },
444 {
445 .start = TEGRA_DISPLAY2_BASE,
446 .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
447 .flags = IORESOURCE_MEM,
448 },
449 {
450 .start = TEGRA_VI_BASE,
451 .end = TEGRA_VI_BASE + TEGRA_VI_SIZE - 1,
452 .flags = IORESOURCE_MEM,
453 },
454 {
455 .start = TEGRA_ISP_BASE,
456 .end = TEGRA_ISP_BASE + TEGRA_ISP_SIZE - 1,
457 .flags = IORESOURCE_MEM,
458 },
459 {
460 .start = TEGRA_MPE_BASE,
461 .end = TEGRA_MPE_BASE + TEGRA_MPE_SIZE - 1,
462 .flags = IORESOURCE_MEM,
463 },
464 {
465 .start = INT_SYNCPT_THRESH_BASE,
466 .end = INT_SYNCPT_THRESH_BASE + INT_SYNCPT_THRESH_NR - 1,
467 .flags = IORESOURCE_IRQ,
468 },
469 {
470 .start = INT_HOST1X_MPCORE_GENERAL,
471 .end = INT_HOST1X_MPCORE_GENERAL,
472 .flags = IORESOURCE_IRQ,
473 },
474};
475
476struct nvhost_device tegra_grhost_device = {
477 .name = DRIVER_NAME,
478 .id = -1,
479 .resource = nvhost_resources,
480 .num_resources = ARRAY_SIZE(nvhost_resources),
481 .finalize_poweron = power_on_host,
482 .prepare_poweroff = power_off_host,
483 .clocks = {{"host1x", UINT_MAX}, {} },
484 NVHOST_MODULE_NO_POWERGATE_IDS,
485};
486
487static int __devinit nvhost_probe(struct nvhost_device *dev)
488{
489 struct nvhost_master *host;
490 struct resource *regs, *intr0, *intr1;
491 int i, err;
492
493 regs = nvhost_get_resource(dev, IORESOURCE_MEM, 0);
494 intr0 = nvhost_get_resource(dev, IORESOURCE_IRQ, 0);
495 intr1 = nvhost_get_resource(dev, IORESOURCE_IRQ, 1);
496
497 if (!regs || !intr0 || !intr1) {
498 dev_err(&dev->dev, "missing required platform resources\n");
499 return -ENXIO;
500 }
501
502 host = kzalloc(sizeof(*host), GFP_KERNEL);
503 if (!host)
504 return -ENOMEM;
505
506 host->nvmap = nvmap_create_client(nvmap_dev, "nvhost");
507 if (!host->nvmap) {
508 dev_err(&dev->dev, "unable to create nvmap client\n");
509 err = -EIO;
510 goto fail;
511 }
512
513 host->reg_mem = request_mem_region(regs->start,
514 resource_size(regs), dev->name);
515 if (!host->reg_mem) {
516 dev_err(&dev->dev, "failed to get host register memory\n");
517 err = -ENXIO;
518 goto fail;
519 }
520
521 host->aperture = ioremap(regs->start, resource_size(regs));
522 if (!host->aperture) {
523 dev_err(&dev->dev, "failed to remap host registers\n");
524 err = -ENXIO;
525 goto fail;
526 }
527
528 err = nvhost_init_chip_support(host);
529 if (err) {
530 dev_err(&dev->dev, "failed to init chip support\n");
531 goto fail;
532 }
533
534 /* Register host1x device as bus master */
535 host->dev = dev;
536
537 /* Give pointer to host1x via driver */
538 nvhost_set_drvdata(dev, host);
539
540 nvhost_bus_add_host(host);
541
542 err = nvhost_intr_init(&host->intr, intr1->start, intr0->start);
543 if (err)
544 goto fail;
545
546 err = nvhost_user_init(host);
547 if (err)
548 goto fail;
549
550 err = nvhost_module_init(&tegra_grhost_device);
551 if (err)
552 goto fail;
553
554 for (i = 0; i < host->dev->num_clks; i++)
555 clk_enable(host->dev->clk[i]);
556 nvhost_syncpt_reset(&host->syncpt);
557 for (i = 0; i < host->dev->num_clks; i++)
558 clk_disable(host->dev->clk[0]);
559
560 nvhost_debug_init(host);
561
562 dev_info(&dev->dev, "initialized\n");
563 return 0;
564
565fail:
566 nvhost_remove_chip_support(host);
567 if (host->nvmap)
568 nvmap_client_put(host->nvmap);
569 kfree(host);
570 return err;
571}
572
573static int __exit nvhost_remove(struct nvhost_device *dev)
574{
575 struct nvhost_master *host = nvhost_get_drvdata(dev);
576 nvhost_intr_deinit(&host->intr);
577 nvhost_remove_chip_support(host);
578 return 0;
579}
580
581static int nvhost_suspend(struct nvhost_device *dev, pm_message_t state)
582{
583 struct nvhost_master *host = nvhost_get_drvdata(dev);
584 int ret = 0;
585
586 dev_info(&dev->dev, "suspending\n");
587 ret = nvhost_module_suspend(host->dev, true);
588 dev_info(&dev->dev, "suspend status: %d\n", ret);
589
590 return ret;
591}
592
593static int nvhost_resume(struct nvhost_device *dev)
594{
595 dev_info(&dev->dev, "resuming\n");
596 return 0;
597}
598
599static struct nvhost_driver nvhost_driver = {
600 .probe = nvhost_probe,
601 .remove = __exit_p(nvhost_remove),
602 .suspend = nvhost_suspend,
603 .resume = nvhost_resume,
604 .driver = {
605 .owner = THIS_MODULE,
606 .name = DRIVER_NAME
607 }
608};
609
610static int __init nvhost_mod_init(void)
611{
612 register_sets = tegra_gpu_register_sets();
613 return nvhost_driver_register(&nvhost_driver);
614}
615
616static void __exit nvhost_mod_exit(void)
617{
618 nvhost_driver_unregister(&nvhost_driver);
619}
620
621/* host1x master device needs nvmap to be instantiated first.
622 * nvmap is instantiated via fs_initcall.
623 * Hence instantiate host1x master device using rootfs_initcall
624 * which is one level after fs_initcall. */
625rootfs_initcall(nvhost_mod_init);
626module_exit(nvhost_mod_exit);
627
628module_param_call(register_sets, NULL, param_get_uint, &register_sets, 0444);
629MODULE_PARM_DESC(register_sets, "Number of register sets");
630
631MODULE_AUTHOR("NVIDIA");
632MODULE_DESCRIPTION("Graphics host driver for Tegra products");
633MODULE_VERSION("1.0");
634MODULE_LICENSE("GPL");
635MODULE_ALIAS("platform-nvhost");