aaa0382219718afd108723d6328b09a04065bed7
[linux-2.6.git] / drivers / video / tegra / host / bus_client.c
1 /*
2  * drivers/video/tegra/host/bus_client.c
3  *
4  * Tegra Graphics Host Client Module
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 <trace/events/nvhost.h>
32
33 #include <linux/io.h>
34 #include <linux/string.h>
35
36 #include <linux/nvhost.h>
37 #include <linux/nvhost_ioctl.h>
38
39 #include <mach/gpufuse.h>
40 #include <mach/hardware.h>
41 #include <mach/iomap.h>
42
43 #include "debug.h"
44 #include "bus_client.h"
45 #include "dev.h"
46 #include "nvhost_memmgr.h"
47 #include "chip_support.h"
48 #include "nvhost_acm.h"
49
50 #include "nvhost_channel.h"
51 #include "nvhost_job.h"
52 #include "nvhost_hwctx.h"
53
54 static int validate_reg(struct nvhost_device *ndev, u32 offset, int count)
55 {
56         struct resource *r = nvhost_get_resource(ndev, IORESOURCE_MEM, 0);
57         int err = 0;
58
59         if (offset + 4 * count > resource_size(r)
60                         || (offset + 4 * count < offset))
61                 err = -EPERM;
62
63         return err;
64 }
65
66 int nvhost_read_module_regs(struct nvhost_device *ndev,
67                         u32 offset, int count, u32 *values)
68 {
69         void __iomem *p = ndev->aperture + offset;
70         int err;
71
72         /* verify offset */
73         err = validate_reg(ndev, offset, count);
74         if (err)
75                 return err;
76
77         nvhost_module_busy(ndev);
78         while (count--) {
79                 *(values++) = readl(p);
80                 p += 4;
81         }
82         rmb();
83         nvhost_module_idle(ndev);
84
85         return 0;
86 }
87
88 int nvhost_write_module_regs(struct nvhost_device *ndev,
89                         u32 offset, int count, const u32 *values)
90 {
91         void __iomem *p = ndev->aperture + offset;
92         int err;
93
94         /* verify offset */
95         err = validate_reg(ndev, offset, count);
96         if (err)
97                 return err;
98
99         nvhost_module_busy(ndev);
100         while (count--) {
101                 writel(*(values++), p);
102                 p += 4;
103         }
104         wmb();
105         nvhost_module_idle(ndev);
106
107         return 0;
108 }
109
110 struct nvhost_channel_userctx {
111         struct nvhost_channel *ch;
112         struct nvhost_hwctx *hwctx;
113         struct nvhost_submit_hdr_ext hdr;
114         int num_relocshifts;
115         struct nvhost_job *job;
116         struct mem_mgr *memmgr;
117         u32 timeout;
118         u32 priority;
119         int clientid;
120 };
121
122 static int nvhost_channelrelease(struct inode *inode, struct file *filp)
123 {
124         struct nvhost_channel_userctx *priv = filp->private_data;
125
126         trace_nvhost_channel_release(priv->ch->dev->name);
127
128         filp->private_data = NULL;
129
130         nvhost_module_remove_client(priv->ch->dev, priv);
131         nvhost_putchannel(priv->ch, priv->hwctx);
132
133         if (priv->hwctx)
134                 priv->ch->ctxhandler->put(priv->hwctx);
135
136         if (priv->job)
137                 nvhost_job_put(priv->job);
138
139         mem_op().put_mgr(priv->memmgr);
140         kfree(priv);
141         return 0;
142 }
143
144 static int nvhost_channelopen(struct inode *inode, struct file *filp)
145 {
146         struct nvhost_channel_userctx *priv;
147         struct nvhost_channel *ch;
148
149         ch = container_of(inode->i_cdev, struct nvhost_channel, cdev);
150         ch = nvhost_getchannel(ch);
151         if (!ch)
152                 return -ENOMEM;
153         trace_nvhost_channel_open(ch->dev->name);
154
155         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
156         if (!priv) {
157                 nvhost_putchannel(ch, NULL);
158                 return -ENOMEM;
159         }
160         filp->private_data = priv;
161         priv->ch = ch;
162         if(nvhost_module_add_client(ch->dev, priv))
163                 goto fail;
164
165         if (ch->ctxhandler && ch->ctxhandler->alloc) {
166                 priv->hwctx = ch->ctxhandler->alloc(ch->ctxhandler, ch);
167                 if (!priv->hwctx)
168                         goto fail;
169         }
170         priv->priority = NVHOST_PRIORITY_MEDIUM;
171         priv->clientid = atomic_add_return(1,
172                         &nvhost_get_host(ch->dev)->clientid);
173         priv->timeout = CONFIG_TEGRA_GRHOST_DEFAULT_TIMEOUT;
174
175         return 0;
176 fail:
177         nvhost_channelrelease(inode, filp);
178         return -ENOMEM;
179 }
180
181 static int set_submit(struct nvhost_channel_userctx *ctx)
182 {
183         struct nvhost_device *ndev = ctx->ch->dev;
184         struct nvhost_master *host = nvhost_get_host(ndev);
185
186         /* submit should have at least 1 cmdbuf */
187         if (!ctx->hdr.num_cmdbufs ||
188                         !nvhost_syncpt_is_valid(&host->syncpt,
189                                 ctx->hdr.syncpt_id))
190                 return -EIO;
191
192         if (!ctx->memmgr) {
193                 dev_err(&ndev->dev, "no nvmap context set\n");
194                 return -EFAULT;
195         }
196
197         ctx->job = nvhost_job_alloc(ctx->ch,
198                         ctx->hwctx,
199                         &ctx->hdr,
200                         ctx->memmgr,
201                         ctx->priority,
202                         ctx->clientid);
203         if (!ctx->job)
204                 return -ENOMEM;
205         ctx->job->timeout = ctx->timeout;
206
207         if (ctx->hdr.submit_version >= NVHOST_SUBMIT_VERSION_V2)
208                 ctx->num_relocshifts = ctx->hdr.num_relocs;
209
210         return 0;
211 }
212
213 static void reset_submit(struct nvhost_channel_userctx *ctx)
214 {
215         ctx->hdr.num_cmdbufs = 0;
216         ctx->hdr.num_relocs = 0;
217         ctx->num_relocshifts = 0;
218         ctx->hdr.num_waitchks = 0;
219
220         if (ctx->job) {
221                 nvhost_job_put(ctx->job);
222                 ctx->job = NULL;
223         }
224 }
225
226 static ssize_t nvhost_channelwrite(struct file *filp, const char __user *buf,
227                                 size_t count, loff_t *offp)
228 {
229         struct nvhost_channel_userctx *priv = filp->private_data;
230         size_t remaining = count;
231         int err = 0;
232         struct nvhost_job *job = priv->job;
233         struct nvhost_submit_hdr_ext *hdr = &priv->hdr;
234         const char *chname = priv->ch->dev->name;
235
236         if (!job)
237                 return -EIO;
238
239         while (remaining) {
240                 size_t consumed;
241                 if (!hdr->num_relocs &&
242                     !priv->num_relocshifts &&
243                     !hdr->num_cmdbufs &&
244                     !hdr->num_waitchks) {
245                         consumed = sizeof(struct nvhost_submit_hdr);
246                         if (remaining < consumed)
247                                 break;
248                         if (copy_from_user(hdr, buf, consumed)) {
249                                 err = -EFAULT;
250                                 break;
251                         }
252                         hdr->submit_version = NVHOST_SUBMIT_VERSION_V0;
253                         err = set_submit(priv);
254                         if (err)
255                                 break;
256                         trace_nvhost_channel_write_submit(chname,
257                           count, hdr->num_cmdbufs, hdr->num_relocs,
258                           hdr->syncpt_id, hdr->syncpt_incrs);
259                 } else if (hdr->num_cmdbufs) {
260                         struct nvhost_cmdbuf cmdbuf;
261                         consumed = sizeof(cmdbuf);
262                         if (remaining < consumed)
263                                 break;
264                         if (copy_from_user(&cmdbuf, buf, consumed)) {
265                                 err = -EFAULT;
266                                 break;
267                         }
268                         trace_nvhost_channel_write_cmdbuf(chname,
269                                 cmdbuf.mem, cmdbuf.words, cmdbuf.offset);
270                         nvhost_job_add_gather(job,
271                                 cmdbuf.mem, cmdbuf.words, cmdbuf.offset);
272                         hdr->num_cmdbufs--;
273                 } else if (hdr->num_relocs) {
274                         int numrelocs = remaining / sizeof(struct nvhost_reloc);
275                         if (!numrelocs)
276                                 break;
277                         numrelocs = min_t(int, numrelocs, priv->hdr.num_relocs);
278                         consumed = numrelocs * sizeof(struct nvhost_reloc);
279                         if (copy_from_user(&job->relocarray[job->num_relocs],
280                                         buf, consumed)) {
281                                 err = -EFAULT;
282                                 break;
283                         }
284                         while (numrelocs) {
285                                 struct nvhost_reloc *reloc =
286                                         &job->relocarray[job->num_relocs];
287                                 trace_nvhost_channel_write_reloc(chname,
288                                         reloc->cmdbuf_mem,
289                                         reloc->cmdbuf_offset,
290                                         reloc->target,
291                                         reloc->target_offset);
292                                 job->num_relocs++;
293                                 hdr->num_relocs--;
294                                 numrelocs--;
295                         }
296                 } else if (hdr->num_waitchks) {
297                         int numwaitchks =
298                                 (remaining / sizeof(struct nvhost_waitchk));
299                         if (!numwaitchks)
300                                 break;
301                         numwaitchks = min_t(int,
302                                 numwaitchks, hdr->num_waitchks);
303                         consumed = numwaitchks * sizeof(struct nvhost_waitchk);
304                         if (copy_from_user(&job->waitchk[job->num_waitchk],
305                                         buf, consumed)) {
306                                 err = -EFAULT;
307                                 break;
308                         }
309                         trace_nvhost_channel_write_waitchks(
310                           chname, numwaitchks,
311                           hdr->waitchk_mask);
312                         job->num_waitchk += numwaitchks;
313                         hdr->num_waitchks -= numwaitchks;
314                 } else if (priv->num_relocshifts) {
315                         int next_shift =
316                                 job->num_relocs - priv->num_relocshifts;
317                         int num =
318                                 (remaining / sizeof(struct nvhost_reloc_shift));
319                         if (!num)
320                                 break;
321                         num = min_t(int, num, priv->num_relocshifts);
322                         consumed = num * sizeof(struct nvhost_reloc_shift);
323                         if (copy_from_user(&job->relocshiftarray[next_shift],
324                                         buf, consumed)) {
325                                 err = -EFAULT;
326                                 break;
327                         }
328                         priv->num_relocshifts -= num;
329                 } else {
330                         err = -EFAULT;
331                         break;
332                 }
333                 remaining -= consumed;
334                 buf += consumed;
335         }
336
337         if (err < 0) {
338                 dev_err(&priv->ch->dev->dev, "channel write error\n");
339                 reset_submit(priv);
340                 return err;
341         }
342
343         return count - remaining;
344 }
345
346 static int nvhost_ioctl_channel_flush(
347         struct nvhost_channel_userctx *ctx,
348         struct nvhost_get_param_args *args,
349         int null_kickoff)
350 {
351         struct nvhost_device *ndev = to_nvhost_device(&ctx->ch->dev->dev);
352         int err;
353
354         trace_nvhost_ioctl_channel_flush(ctx->ch->dev->name);
355
356         if (!ctx->job ||
357             ctx->hdr.num_relocs ||
358             ctx->hdr.num_cmdbufs ||
359             ctx->hdr.num_waitchks) {
360                 reset_submit(ctx);
361                 dev_err(&ndev->dev, "channel submit out of sync\n");
362                 return -EFAULT;
363         }
364
365         err = nvhost_job_pin(ctx->job, &nvhost_get_host(ndev)->syncpt);
366         if (err) {
367                 dev_warn(&ndev->dev, "nvhost_job_pin failed: %d\n", err);
368                 return err;
369         }
370
371         if (nvhost_debug_null_kickoff_pid == current->tgid)
372                 null_kickoff = 1;
373         ctx->job->null_kickoff = null_kickoff;
374
375         if ((nvhost_debug_force_timeout_pid == current->tgid) &&
376             (nvhost_debug_force_timeout_channel == ctx->ch->chid)) {
377                 ctx->timeout = nvhost_debug_force_timeout_val;
378         }
379
380         /* context switch if needed, and submit user's gathers to the channel */
381         err = nvhost_channel_submit(ctx->job);
382         args->value = ctx->job->syncpt_end;
383         if (err)
384                 nvhost_job_unpin(ctx->job);
385
386         nvhost_job_put(ctx->job);
387         ctx->job = NULL;
388
389         return err;
390 }
391
392 static int nvhost_ioctl_channel_read_3d_reg(struct nvhost_channel_userctx *ctx,
393         struct nvhost_read_3d_reg_args *args)
394 {
395         BUG_ON(!channel_op().read3dreg);
396         return channel_op().read3dreg(ctx->ch, ctx->hwctx,
397                         args->offset, &args->value);
398 }
399
400 static long nvhost_channelctl(struct file *filp,
401         unsigned int cmd, unsigned long arg)
402 {
403         struct nvhost_channel_userctx *priv = filp->private_data;
404         u8 buf[NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE];
405         int err = 0;
406
407         if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) ||
408                 (_IOC_NR(cmd) == 0) ||
409                 (_IOC_NR(cmd) > NVHOST_IOCTL_CHANNEL_LAST) ||
410                 (_IOC_SIZE(cmd) > NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE))
411                 return -EFAULT;
412
413         if (_IOC_DIR(cmd) & _IOC_WRITE) {
414                 if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd)))
415                         return -EFAULT;
416         }
417
418         switch (cmd) {
419         case NVHOST_IOCTL_CHANNEL_FLUSH:
420                 err = nvhost_ioctl_channel_flush(priv, (void *)buf, 0);
421                 break;
422         case NVHOST_IOCTL_CHANNEL_NULL_KICKOFF:
423                 err = nvhost_ioctl_channel_flush(priv, (void *)buf, 1);
424                 break;
425         case NVHOST_IOCTL_CHANNEL_SUBMIT_EXT:
426         {
427                 struct nvhost_submit_hdr_ext *hdr;
428
429                 if (priv->hdr.num_relocs ||
430                     priv->num_relocshifts ||
431                     priv->hdr.num_cmdbufs ||
432                     priv->hdr.num_waitchks) {
433                         reset_submit(priv);
434                         dev_err(&priv->ch->dev->dev,
435                                 "channel submit out of sync\n");
436                         err = -EIO;
437                         break;
438                 }
439
440                 hdr = (struct nvhost_submit_hdr_ext *)buf;
441                 if (hdr->submit_version > NVHOST_SUBMIT_VERSION_MAX_SUPPORTED) {
442                         dev_err(&priv->ch->dev->dev,
443                                 "submit version %d > max supported %d\n",
444                                 hdr->submit_version,
445                                 NVHOST_SUBMIT_VERSION_MAX_SUPPORTED);
446                         err = -EINVAL;
447                         break;
448                 }
449                 memcpy(&priv->hdr, hdr, sizeof(struct nvhost_submit_hdr_ext));
450                 err = set_submit(priv);
451                 trace_nvhost_ioctl_channel_submit(priv->ch->dev->name,
452                         priv->hdr.submit_version,
453                         priv->hdr.num_cmdbufs, priv->hdr.num_relocs,
454                         priv->hdr.num_waitchks,
455                         priv->hdr.syncpt_id, priv->hdr.syncpt_incrs);
456                 break;
457         }
458         case NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS:
459                 /* host syncpt ID is used by the RM (and never be given out) */
460                 BUG_ON(priv->ch->dev->syncpts & (1 << NVSYNCPT_GRAPHICS_HOST));
461                 ((struct nvhost_get_param_args *)buf)->value =
462                         priv->ch->dev->syncpts;
463                 break;
464         case NVHOST_IOCTL_CHANNEL_GET_WAITBASES:
465                 ((struct nvhost_get_param_args *)buf)->value =
466                         priv->ch->dev->waitbases;
467                 break;
468         case NVHOST_IOCTL_CHANNEL_GET_MODMUTEXES:
469                 ((struct nvhost_get_param_args *)buf)->value =
470                         priv->ch->dev->modulemutexes;
471                 break;
472         case NVHOST_IOCTL_CHANNEL_SET_NVMAP_FD:
473         {
474                 int fd = (int)((struct nvhost_set_nvmap_fd_args *)buf)->fd;
475                 struct mem_mgr *new_client = mem_op().get_mgr_file(fd);
476
477                 if (IS_ERR(new_client)) {
478                         err = PTR_ERR(new_client);
479                         break;
480                 }
481
482                 if (priv->memmgr)
483                         mem_op().put_mgr(priv->memmgr);
484
485                 priv->memmgr = new_client;
486                 break;
487         }
488         case NVHOST_IOCTL_CHANNEL_READ_3D_REG:
489                 err = nvhost_ioctl_channel_read_3d_reg(priv, (void *)buf);
490                 break;
491         case NVHOST_IOCTL_CHANNEL_GET_CLK_RATE:
492         {
493                 unsigned long rate;
494                 struct nvhost_clk_rate_args *arg =
495                                 (struct nvhost_clk_rate_args *)buf;
496
497                 err = nvhost_module_get_rate(priv->ch->dev, &rate, 0);
498                 if (err == 0)
499                         arg->rate = rate;
500                 break;
501         }
502         case NVHOST_IOCTL_CHANNEL_SET_CLK_RATE:
503         {
504                 struct nvhost_clk_rate_args *arg =
505                                 (struct nvhost_clk_rate_args *)buf;
506                 unsigned long rate = (unsigned long)arg->rate;
507
508                 err = nvhost_module_set_rate(priv->ch->dev, priv, rate, 0);
509                 break;
510         }
511         case NVHOST_IOCTL_CHANNEL_SET_TIMEOUT:
512                 priv->timeout =
513                         (u32)((struct nvhost_set_timeout_args *)buf)->timeout;
514                 dev_dbg(&priv->ch->dev->dev,
515                         "%s: setting buffer timeout (%d ms) for userctx 0x%p\n",
516                         __func__, priv->timeout, priv);
517                 break;
518         case NVHOST_IOCTL_CHANNEL_GET_TIMEDOUT:
519                 ((struct nvhost_get_param_args *)buf)->value =
520                                 priv->hwctx->has_timedout;
521                 break;
522         case NVHOST_IOCTL_CHANNEL_SET_PRIORITY:
523                 priv->priority =
524                         (u32)((struct nvhost_set_priority_args *)buf)->priority;
525                 break;
526         default:
527                 err = -ENOTTY;
528                 break;
529         }
530
531         if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ))
532                 err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd));
533
534         return err;
535 }
536
537 static const struct file_operations nvhost_channelops = {
538         .owner = THIS_MODULE,
539         .release = nvhost_channelrelease,
540         .open = nvhost_channelopen,
541         .write = nvhost_channelwrite,
542         .unlocked_ioctl = nvhost_channelctl
543 };
544
545 int nvhost_client_user_init(struct nvhost_device *dev)
546 {
547         int err, devno;
548
549         struct nvhost_channel *ch = dev->channel;
550         err = alloc_chrdev_region(&devno, 0, 1, IFACE_NAME);
551         if (err < 0) {
552                 dev_err(&dev->dev, "failed to allocate devno\n");
553                 goto fail;
554         }
555
556         cdev_init(&ch->cdev, &nvhost_channelops);
557         ch->cdev.owner = THIS_MODULE;
558
559         err = cdev_add(&ch->cdev, devno, 1);
560         if (err < 0) {
561                 dev_err(&dev->dev,
562                         "failed to add chan %i cdev\n", dev->index);
563                 goto fail;
564         }
565         ch->node = device_create(nvhost_get_host(dev)->nvhost_class,
566                         NULL, devno, NULL,
567                         IFACE_NAME "-%s", dev->name);
568         if (IS_ERR(ch->node)) {
569                 err = PTR_ERR(ch->node);
570                 dev_err(&dev->dev,
571                         "failed to create %s channel device\n", dev->name);
572                 goto fail;
573         }
574
575         return 0;
576 fail:
577         return err;
578 }
579
580 int nvhost_client_device_init(struct nvhost_device *dev)
581 {
582         int err;
583         struct nvhost_master *nvhost_master = nvhost_get_host(dev);
584         struct nvhost_channel *ch;
585
586         ch = nvhost_alloc_channel(dev);
587         if (ch == NULL)
588                 return -ENODEV;
589
590         /* store the pointer to this device for channel */
591         ch->dev = dev;
592
593         err = nvhost_channel_init(ch, nvhost_master, dev->index);
594         if (err)
595                 goto fail;
596
597         err = nvhost_client_user_init(dev);
598         if (err)
599                 goto fail;
600
601         err = nvhost_module_init(dev);
602         if (err)
603                 goto fail;
604
605         dev_info(&dev->dev, "initialized\n");
606
607         return 0;
608
609 fail:
610         /* Add clean-up */
611         nvhost_free_channel(ch);
612         return err;
613 }
614
615 int nvhost_client_device_suspend(struct nvhost_device *dev)
616 {
617         int ret = 0;
618
619         ret = nvhost_channel_suspend(dev->channel);
620         if (ret)
621                 return ret;
622
623         dev_info(&dev->dev, "suspend status: %d\n", ret);
624
625         return ret;
626 }
627
628 int nvhost_client_device_get_resources(struct nvhost_device *dev)
629 {
630         struct resource *r = NULL;
631         void __iomem *regs = NULL;
632         struct resource *reg_mem = NULL;
633
634         r = nvhost_get_resource(dev, IORESOURCE_MEM, 0);
635         if (!r)
636                 goto fail;
637
638         reg_mem = request_mem_region(r->start, resource_size(r), dev->name);
639         if (!reg_mem)
640                 goto fail;
641
642         regs = ioremap(r->start, resource_size(r));
643         if (!regs)
644                 goto fail;
645
646         dev->reg_mem = reg_mem;
647         dev->aperture = regs;
648
649         return 0;
650
651 fail:
652         if (reg_mem)
653                 release_mem_region(r->start, resource_size(r));
654         if (regs)
655                 iounmap(regs);
656
657         dev_err(&dev->dev, "failed to get register memory\n");
658
659         return -ENXIO;
660 }