aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/tegra/host/bus_client.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/host/bus_client.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/video/tegra/host/bus_client.c')
-rw-r--r--drivers/video/tegra/host/bus_client.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c
new file mode 100644
index 00000000000..940f04a40e8
--- /dev/null
+++ b/drivers/video/tegra/host/bus_client.c
@@ -0,0 +1,606 @@
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/nvmap.h>
40#include <mach/gpufuse.h>
41#include <mach/hardware.h>
42#include <mach/iomap.h>
43
44#include "debug.h"
45#include "bus_client.h"
46#include "dev.h"
47
48void nvhost_read_module_regs(struct nvhost_device *ndev,
49 u32 offset, int count, u32 *values)
50{
51 void __iomem *p = ndev->aperture + offset;
52
53 nvhost_module_busy(ndev);
54 while (count--) {
55 *(values++) = readl(p);
56 p += 4;
57 }
58 rmb();
59 nvhost_module_idle(ndev);
60}
61
62void nvhost_write_module_regs(struct nvhost_device *ndev,
63 u32 offset, int count, const u32 *values)
64{
65 void __iomem *p = ndev->aperture + offset;
66
67 nvhost_module_busy(ndev);
68 while (count--) {
69 writel(*(values++), p);
70 p += 4;
71 }
72 wmb();
73 nvhost_module_idle(ndev);
74}
75
76struct nvhost_channel_userctx {
77 struct nvhost_channel *ch;
78 struct nvhost_hwctx *hwctx;
79 struct nvhost_submit_hdr_ext hdr;
80 int num_relocshifts;
81 struct nvhost_job *job;
82 struct nvmap_client *nvmap;
83 u32 timeout;
84 u32 priority;
85 int clientid;
86};
87
88/*
89 * Write cmdbuf to ftrace output. Checks if cmdbuf contents should be output
90 * and mmaps the cmdbuf contents if required.
91 */
92static void trace_write_cmdbufs(struct nvhost_job *job)
93{
94 struct nvmap_handle_ref handle;
95 void *mem = NULL;
96 int i = 0;
97
98 for (i = 0; i < job->num_gathers; i++) {
99 struct nvhost_channel_gather *gather = &job->gathers[i];
100 if (nvhost_debug_trace_cmdbuf) {
101 handle.handle = nvmap_id_to_handle(gather->mem_id);
102 mem = nvmap_mmap(&handle);
103 if (IS_ERR_OR_NULL(mem))
104 mem = NULL;
105 };
106
107 if (mem) {
108 u32 i;
109 /*
110 * Write in batches of 128 as there seems to be a limit
111 * of how much you can output to ftrace at once.
112 */
113 for (i = 0; i < gather->words; i += TRACE_MAX_LENGTH) {
114 trace_nvhost_channel_write_cmdbuf_data(
115 job->ch->dev->name,
116 gather->mem_id,
117 min(gather->words - i,
118 TRACE_MAX_LENGTH),
119 gather->offset + i * sizeof(u32),
120 mem);
121 }
122 nvmap_munmap(&handle, mem);
123 }
124 }
125}
126
127static int nvhost_channelrelease(struct inode *inode, struct file *filp)
128{
129 struct nvhost_channel_userctx *priv = filp->private_data;
130
131 trace_nvhost_channel_release(priv->ch->dev->name);
132
133 filp->private_data = NULL;
134
135 nvhost_module_remove_client(priv->ch->dev, priv);
136 nvhost_putchannel(priv->ch, priv->hwctx);
137
138 if (priv->hwctx)
139 priv->ch->ctxhandler->put(priv->hwctx);
140
141 if (priv->job)
142 nvhost_job_put(priv->job);
143
144 nvmap_client_put(priv->nvmap);
145 kfree(priv);
146 return 0;
147}
148
149static int nvhost_channelopen(struct inode *inode, struct file *filp)
150{
151 struct nvhost_channel_userctx *priv;
152 struct nvhost_channel *ch;
153
154 ch = container_of(inode->i_cdev, struct nvhost_channel, cdev);
155 ch = nvhost_getchannel(ch);
156 if (!ch)
157 return -ENOMEM;
158 trace_nvhost_channel_open(ch->dev->name);
159
160 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
161 if (!priv) {
162 nvhost_putchannel(ch, NULL);
163 return -ENOMEM;
164 }
165 filp->private_data = priv;
166 priv->ch = ch;
167 nvhost_module_add_client(ch->dev, priv);
168
169 if (ch->ctxhandler && ch->ctxhandler->alloc) {
170 priv->hwctx = ch->ctxhandler->alloc(ch->ctxhandler, ch);
171 if (!priv->hwctx)
172 goto fail;
173 }
174 priv->priority = NVHOST_PRIORITY_MEDIUM;
175 priv->clientid = atomic_add_return(1,
176 &nvhost_get_host(ch->dev)->clientid);
177
178 priv->job = nvhost_job_alloc(ch, priv->hwctx, &priv->hdr,
179 NULL, priv->priority, priv->clientid);
180 if (!priv->job)
181 goto fail;
182
183 return 0;
184fail:
185 nvhost_channelrelease(inode, filp);
186 return -ENOMEM;
187}
188
189static int set_submit(struct nvhost_channel_userctx *ctx)
190{
191 struct device *device = &ctx->ch->dev->dev;
192
193 /* submit should have at least 1 cmdbuf */
194 if (!ctx->hdr.num_cmdbufs)
195 return -EIO;
196
197 if (!ctx->nvmap) {
198 dev_err(device, "no nvmap context set\n");
199 return -EFAULT;
200 }
201
202 ctx->job = nvhost_job_realloc(ctx->job,
203 ctx->hwctx,
204 &ctx->hdr,
205 ctx->nvmap,
206 ctx->priority,
207 ctx->clientid);
208 if (!ctx->job)
209 return -ENOMEM;
210 ctx->job->timeout = ctx->timeout;
211
212 if (ctx->hdr.submit_version >= NVHOST_SUBMIT_VERSION_V2)
213 ctx->num_relocshifts = ctx->hdr.num_relocs;
214
215 return 0;
216}
217
218static void reset_submit(struct nvhost_channel_userctx *ctx)
219{
220 ctx->hdr.num_cmdbufs = 0;
221 ctx->hdr.num_relocs = 0;
222 ctx->num_relocshifts = 0;
223 ctx->hdr.num_waitchks = 0;
224}
225
226static 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 consumed = sizeof(struct nvhost_reloc);
275 if (remaining < consumed)
276 break;
277 if (copy_from_user(&job->pinarray[job->num_pins],
278 buf, consumed)) {
279 err = -EFAULT;
280 break;
281 }
282 trace_nvhost_channel_write_reloc(chname);
283 job->num_pins++;
284 hdr->num_relocs--;
285 } else if (hdr->num_waitchks) {
286 int numwaitchks =
287 (remaining / sizeof(struct nvhost_waitchk));
288 if (!numwaitchks)
289 break;
290 numwaitchks = min_t(int,
291 numwaitchks, hdr->num_waitchks);
292 consumed = numwaitchks * sizeof(struct nvhost_waitchk);
293 if (copy_from_user(&job->waitchk[job->num_waitchk],
294 buf, consumed)) {
295 err = -EFAULT;
296 break;
297 }
298 trace_nvhost_channel_write_waitchks(
299 chname, numwaitchks,
300 hdr->waitchk_mask);
301 job->num_waitchk += numwaitchks;
302 hdr->num_waitchks -= numwaitchks;
303 } else if (priv->num_relocshifts) {
304 int next_shift =
305 job->num_pins - priv->num_relocshifts;
306 consumed = sizeof(struct nvhost_reloc_shift);
307 if (remaining < consumed)
308 break;
309 if (copy_from_user(
310 &job->pinarray[next_shift].reloc_shift,
311 buf, consumed)) {
312 err = -EFAULT;
313 break;
314 }
315 priv->num_relocshifts--;
316 } else {
317 err = -EFAULT;
318 break;
319 }
320 remaining -= consumed;
321 buf += consumed;
322 }
323
324 if (err < 0) {
325 dev_err(&priv->ch->dev->dev, "channel write error\n");
326 reset_submit(priv);
327 return err;
328 }
329
330 return count - remaining;
331}
332
333static int nvhost_ioctl_channel_flush(
334 struct nvhost_channel_userctx *ctx,
335 struct nvhost_get_param_args *args,
336 int null_kickoff)
337{
338 struct device *device = &ctx->ch->dev->dev;
339 int err;
340
341 trace_nvhost_ioctl_channel_flush(ctx->ch->dev->name);
342
343 if (!ctx->job ||
344 ctx->hdr.num_relocs ||
345 ctx->hdr.num_cmdbufs ||
346 ctx->hdr.num_waitchks) {
347 reset_submit(ctx);
348 dev_err(device, "channel submit out of sync\n");
349 return -EFAULT;
350 }
351
352 err = nvhost_job_pin(ctx->job);
353 if (err) {
354 dev_warn(device, "nvhost_job_pin failed: %d\n", err);
355 return err;
356 }
357
358 if (nvhost_debug_null_kickoff_pid == current->tgid)
359 null_kickoff = 1;
360 ctx->job->null_kickoff = null_kickoff;
361
362 if ((nvhost_debug_force_timeout_pid == current->tgid) &&
363 (nvhost_debug_force_timeout_channel == ctx->ch->chid)) {
364 ctx->timeout = nvhost_debug_force_timeout_val;
365 }
366
367 trace_write_cmdbufs(ctx->job);
368
369 /* context switch if needed, and submit user's gathers to the channel */
370 err = nvhost_channel_submit(ctx->job);
371 args->value = ctx->job->syncpt_end;
372 if (err)
373 nvhost_job_unpin(ctx->job);
374
375 return err;
376}
377
378static int nvhost_ioctl_channel_read_3d_reg(
379 struct nvhost_channel_userctx *ctx,
380 struct nvhost_read_3d_reg_args *args)
381{
382 BUG_ON(!channel_op(ctx->ch).read3dreg);
383 return channel_op(ctx->ch).read3dreg(ctx->ch, ctx->hwctx,
384 args->offset, &args->value);
385}
386
387static long nvhost_channelctl(struct file *filp,
388 unsigned int cmd, unsigned long arg)
389{
390 struct nvhost_channel_userctx *priv = filp->private_data;
391 u8 buf[NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE];
392 int err = 0;
393
394 if ((_IOC_TYPE(cmd) != NVHOST_IOCTL_MAGIC) ||
395 (_IOC_NR(cmd) == 0) ||
396 (_IOC_NR(cmd) > NVHOST_IOCTL_CHANNEL_LAST))
397 return -EFAULT;
398
399 BUG_ON(_IOC_SIZE(cmd) > NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE);
400
401 if (_IOC_DIR(cmd) & _IOC_WRITE) {
402 if (copy_from_user(buf, (void __user *)arg, _IOC_SIZE(cmd)))
403 return -EFAULT;
404 }
405
406 switch (cmd) {
407 case NVHOST_IOCTL_CHANNEL_FLUSH:
408 err = nvhost_ioctl_channel_flush(priv, (void *)buf, 0);
409 break;
410 case NVHOST_IOCTL_CHANNEL_NULL_KICKOFF:
411 err = nvhost_ioctl_channel_flush(priv, (void *)buf, 1);
412 break;
413 case NVHOST_IOCTL_CHANNEL_SUBMIT_EXT:
414 {
415 struct nvhost_submit_hdr_ext *hdr;
416
417 if (priv->hdr.num_relocs ||
418 priv->num_relocshifts ||
419 priv->hdr.num_cmdbufs ||
420 priv->hdr.num_waitchks) {
421 reset_submit(priv);
422 dev_err(&priv->ch->dev->dev,
423 "channel submit out of sync\n");
424 err = -EIO;
425 break;
426 }
427
428 hdr = (struct nvhost_submit_hdr_ext *)buf;
429 if (hdr->submit_version > NVHOST_SUBMIT_VERSION_MAX_SUPPORTED) {
430 dev_err(&priv->ch->dev->dev,
431 "submit version %d > max supported %d\n",
432 hdr->submit_version,
433 NVHOST_SUBMIT_VERSION_MAX_SUPPORTED);
434 err = -EINVAL;
435 break;
436 }
437 memcpy(&priv->hdr, hdr, sizeof(struct nvhost_submit_hdr_ext));
438 err = set_submit(priv);
439 trace_nvhost_ioctl_channel_submit(priv->ch->dev->name,
440 priv->hdr.submit_version,
441 priv->hdr.num_cmdbufs, priv->hdr.num_relocs,
442 priv->hdr.num_waitchks,
443 priv->hdr.syncpt_id, priv->hdr.syncpt_incrs);
444 break;
445 }
446 case NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS:
447 /* host syncpt ID is used by the RM (and never be given out) */
448 BUG_ON(priv->ch->dev->syncpts & (1 << NVSYNCPT_GRAPHICS_HOST));
449 ((struct nvhost_get_param_args *)buf)->value =
450 priv->ch->dev->syncpts;
451 break;
452 case NVHOST_IOCTL_CHANNEL_GET_WAITBASES:
453 ((struct nvhost_get_param_args *)buf)->value =
454 priv->ch->dev->waitbases;
455 break;
456 case NVHOST_IOCTL_CHANNEL_GET_MODMUTEXES:
457 ((struct nvhost_get_param_args *)buf)->value =
458 priv->ch->dev->modulemutexes;
459 break;
460 case NVHOST_IOCTL_CHANNEL_SET_NVMAP_FD:
461 {
462 int fd = (int)((struct nvhost_set_nvmap_fd_args *)buf)->fd;
463 struct nvmap_client *new_client = nvmap_client_get_file(fd);
464
465 if (IS_ERR(new_client)) {
466 err = PTR_ERR(new_client);
467 break;
468 }
469
470 if (priv->nvmap)
471 nvmap_client_put(priv->nvmap);
472
473 priv->nvmap = new_client;
474 break;
475 }
476 case NVHOST_IOCTL_CHANNEL_READ_3D_REG:
477 err = nvhost_ioctl_channel_read_3d_reg(priv, (void *)buf);
478 break;
479 case NVHOST_IOCTL_CHANNEL_GET_CLK_RATE:
480 {
481 unsigned long rate;
482 struct nvhost_clk_rate_args *arg =
483 (struct nvhost_clk_rate_args *)buf;
484
485 err = nvhost_module_get_rate(priv->ch->dev, &rate, 0);
486 if (err == 0)
487 arg->rate = rate;
488 break;
489 }
490 case NVHOST_IOCTL_CHANNEL_SET_CLK_RATE:
491 {
492 struct nvhost_clk_rate_args *arg =
493 (struct nvhost_clk_rate_args *)buf;
494 unsigned long rate = (unsigned long)arg->rate;
495
496 err = nvhost_module_set_rate(priv->ch->dev, priv, rate, 0);
497 break;
498 }
499 case NVHOST_IOCTL_CHANNEL_SET_TIMEOUT:
500 priv->timeout =
501 (u32)((struct nvhost_set_timeout_args *)buf)->timeout;
502 dev_dbg(&priv->ch->dev->dev,
503 "%s: setting buffer timeout (%d ms) for userctx 0x%p\n",
504 __func__, priv->timeout, priv);
505 break;
506 case NVHOST_IOCTL_CHANNEL_GET_TIMEDOUT:
507 ((struct nvhost_get_param_args *)buf)->value =
508 priv->hwctx->has_timedout;
509 break;
510 case NVHOST_IOCTL_CHANNEL_SET_PRIORITY:
511 priv->priority =
512 (u32)((struct nvhost_set_priority_args *)buf)->priority;
513 break;
514 default:
515 err = -ENOTTY;
516 break;
517 }
518
519 if ((err == 0) && (_IOC_DIR(cmd) & _IOC_READ))
520 err = copy_to_user((void __user *)arg, buf, _IOC_SIZE(cmd));
521
522 return err;
523}
524
525static const struct file_operations nvhost_channelops = {
526 .owner = THIS_MODULE,
527 .release = nvhost_channelrelease,
528 .open = nvhost_channelopen,
529 .write = nvhost_channelwrite,
530 .unlocked_ioctl = nvhost_channelctl
531};
532
533int nvhost_client_user_init(struct nvhost_device *dev)
534{
535 int err, devno;
536
537 struct nvhost_channel *ch = dev->channel;
538
539 cdev_init(&ch->cdev, &nvhost_channelops);
540 ch->cdev.owner = THIS_MODULE;
541
542 devno = MKDEV(nvhost_major, nvhost_minor + dev->index);
543 err = cdev_add(&ch->cdev, devno, 1);
544 if (err < 0) {
545 dev_err(&dev->dev,
546 "failed to add chan %i cdev\n", dev->index);
547 goto fail;
548 }
549 ch->node = device_create(nvhost_get_host(dev)->nvhost_class, NULL, devno, NULL,
550 IFACE_NAME "-%s", dev->name);
551 if (IS_ERR(ch->node)) {
552 err = PTR_ERR(ch->node);
553 dev_err(&dev->dev,
554 "failed to create %s channel device\n", dev->name);
555 goto fail;
556 }
557
558 return 0;
559fail:
560 return err;
561}
562
563int nvhost_client_device_init(struct nvhost_device *dev)
564{
565 int err;
566 struct nvhost_master *nvhost_master = nvhost_get_host(dev);
567 struct nvhost_channel *ch = &nvhost_master->channels[dev->index];
568
569 /* store the pointer to this device for channel */
570 ch->dev = dev;
571
572 err = nvhost_channel_init(ch, nvhost_master, dev->index);
573 if (err)
574 goto fail;
575
576 err = nvhost_client_user_init(dev);
577 if (err)
578 goto fail;
579
580 err = nvhost_module_init(dev);
581 if (err)
582 goto fail;
583
584 dev_info(&dev->dev, "initialized\n");
585
586 return 0;
587
588fail:
589 /* Add clean-up */
590 return err;
591}
592
593int nvhost_client_device_suspend(struct nvhost_device *dev)
594{
595 int ret = 0;
596
597 dev_info(&dev->dev, "suspending\n");
598
599 ret = nvhost_channel_suspend(dev->channel);
600 if (ret)
601 return ret;
602
603 dev_info(&dev->dev, "suspend status: %d\n", ret);
604
605 return ret;
606}