diff options
| author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
|---|---|---|
| committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
| commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
| tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/video/tegra/host/host1x | |
| parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) | |
Diffstat (limited to 'drivers/video/tegra/host/host1x')
| -rw-r--r-- | drivers/video/tegra/host/host1x/Makefile | 12 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_cdma.c | 665 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_cdma.h | 41 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_channel.c | 627 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_channel.h | 45 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_debug.c | 404 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_hardware.h | 274 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_hwctx.h | 65 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_intr.c | 218 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_syncpt.c | 248 | ||||
| -rw-r--r-- | drivers/video/tegra/host/host1x/host1x_syncpt.h | 77 |
11 files changed, 2676 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/host1x/Makefile b/drivers/video/tegra/host/host1x/Makefile new file mode 100644 index 00000000000..c3214ffe147 --- /dev/null +++ b/drivers/video/tegra/host/host1x/Makefile | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | GCOV_PROFILE := y | ||
| 2 | |||
| 3 | EXTRA_CFLAGS += -Idrivers/video/tegra/host | ||
| 4 | |||
| 5 | nvhost-host1x-objs = \ | ||
| 6 | host1x_syncpt.o \ | ||
| 7 | host1x_channel.o \ | ||
| 8 | host1x_intr.o \ | ||
| 9 | host1x_cdma.o \ | ||
| 10 | host1x_debug.o | ||
| 11 | |||
| 12 | obj-$(CONFIG_TEGRA_GRHOST) += nvhost-host1x.o | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.c b/drivers/video/tegra/host/host1x/host1x_cdma.c new file mode 100644 index 00000000000..cdd6026718b --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_cdma.c | |||
| @@ -0,0 +1,665 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_cdma.c | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Command DMA | ||
| 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 "nvhost_cdma.h" | ||
| 23 | #include "dev.h" | ||
| 24 | |||
| 25 | #include "host1x_hardware.h" | ||
| 26 | #include "host1x_syncpt.h" | ||
| 27 | #include "host1x_cdma.h" | ||
| 28 | #include "host1x_hwctx.h" | ||
| 29 | |||
| 30 | static inline u32 host1x_channel_dmactrl(int stop, int get_rst, int init_get) | ||
| 31 | { | ||
| 32 | return HOST1X_CREATE(CHANNEL_DMACTRL, DMASTOP, stop) | ||
| 33 | | HOST1X_CREATE(CHANNEL_DMACTRL, DMAGETRST, get_rst) | ||
| 34 | | HOST1X_CREATE(CHANNEL_DMACTRL, DMAINITGET, init_get); | ||
| 35 | } | ||
| 36 | |||
| 37 | static void cdma_timeout_handler(struct work_struct *work); | ||
| 38 | |||
| 39 | /* | ||
| 40 | * push_buffer | ||
| 41 | * | ||
| 42 | * The push buffer is a circular array of words to be fetched by command DMA. | ||
| 43 | * Note that it works slightly differently to the sync queue; fence == cur | ||
| 44 | * means that the push buffer is full, not empty. | ||
| 45 | */ | ||
| 46 | |||
| 47 | |||
| 48 | /** | ||
| 49 | * Reset to empty push buffer | ||
| 50 | */ | ||
| 51 | static void push_buffer_reset(struct push_buffer *pb) | ||
| 52 | { | ||
| 53 | pb->fence = PUSH_BUFFER_SIZE - 8; | ||
| 54 | pb->cur = 0; | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Init push buffer resources | ||
| 59 | */ | ||
| 60 | static int push_buffer_init(struct push_buffer *pb) | ||
| 61 | { | ||
| 62 | struct nvhost_cdma *cdma = pb_to_cdma(pb); | ||
| 63 | struct nvmap_client *nvmap = cdma_to_nvmap(cdma); | ||
| 64 | pb->mem = NULL; | ||
| 65 | pb->mapped = NULL; | ||
| 66 | pb->phys = 0; | ||
| 67 | pb->nvmap = NULL; | ||
| 68 | |||
| 69 | BUG_ON(!cdma_pb_op(cdma).reset); | ||
| 70 | cdma_pb_op(cdma).reset(pb); | ||
| 71 | |||
| 72 | /* allocate and map pushbuffer memory */ | ||
| 73 | pb->mem = nvmap_alloc(nvmap, PUSH_BUFFER_SIZE + 4, 32, | ||
| 74 | NVMAP_HANDLE_WRITE_COMBINE, 0); | ||
| 75 | if (IS_ERR_OR_NULL(pb->mem)) { | ||
| 76 | pb->mem = NULL; | ||
| 77 | goto fail; | ||
| 78 | } | ||
| 79 | pb->mapped = nvmap_mmap(pb->mem); | ||
| 80 | if (pb->mapped == NULL) | ||
| 81 | goto fail; | ||
| 82 | |||
| 83 | /* pin pushbuffer and get physical address */ | ||
| 84 | pb->phys = nvmap_pin(nvmap, pb->mem); | ||
| 85 | if (pb->phys >= 0xfffff000) { | ||
| 86 | pb->phys = 0; | ||
| 87 | goto fail; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* memory for storing nvmap client and handles for each opcode pair */ | ||
| 91 | pb->nvmap = kzalloc(NVHOST_GATHER_QUEUE_SIZE * | ||
| 92 | sizeof(struct nvmap_client_handle), | ||
| 93 | GFP_KERNEL); | ||
| 94 | if (!pb->nvmap) | ||
| 95 | goto fail; | ||
| 96 | |||
| 97 | /* put the restart at the end of pushbuffer memory */ | ||
| 98 | *(pb->mapped + (PUSH_BUFFER_SIZE >> 2)) = | ||
| 99 | nvhost_opcode_restart(pb->phys); | ||
| 100 | |||
| 101 | return 0; | ||
| 102 | |||
| 103 | fail: | ||
| 104 | cdma_pb_op(cdma).destroy(pb); | ||
| 105 | return -ENOMEM; | ||
| 106 | } | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Clean up push buffer resources | ||
| 110 | */ | ||
| 111 | static void push_buffer_destroy(struct push_buffer *pb) | ||
| 112 | { | ||
| 113 | struct nvhost_cdma *cdma = pb_to_cdma(pb); | ||
| 114 | struct nvmap_client *nvmap = cdma_to_nvmap(cdma); | ||
| 115 | if (pb->mapped) | ||
| 116 | nvmap_munmap(pb->mem, pb->mapped); | ||
| 117 | |||
| 118 | if (pb->phys != 0) | ||
| 119 | nvmap_unpin(nvmap, pb->mem); | ||
| 120 | |||
| 121 | if (pb->mem) | ||
| 122 | nvmap_free(nvmap, pb->mem); | ||
| 123 | |||
| 124 | kfree(pb->nvmap); | ||
| 125 | |||
| 126 | pb->mem = NULL; | ||
| 127 | pb->mapped = NULL; | ||
| 128 | pb->phys = 0; | ||
| 129 | pb->nvmap = 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Push two words to the push buffer | ||
| 134 | * Caller must ensure push buffer is not full | ||
| 135 | */ | ||
| 136 | static void push_buffer_push_to(struct push_buffer *pb, | ||
| 137 | struct nvmap_client *client, | ||
| 138 | struct nvmap_handle *handle, u32 op1, u32 op2) | ||
| 139 | { | ||
| 140 | u32 cur = pb->cur; | ||
| 141 | u32 *p = (u32 *)((u32)pb->mapped + cur); | ||
| 142 | u32 cur_nvmap = (cur/8) & (NVHOST_GATHER_QUEUE_SIZE - 1); | ||
| 143 | BUG_ON(cur == pb->fence); | ||
| 144 | *(p++) = op1; | ||
| 145 | *(p++) = op2; | ||
| 146 | pb->nvmap[cur_nvmap].client = client; | ||
| 147 | pb->nvmap[cur_nvmap].handle = handle; | ||
| 148 | pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1); | ||
| 149 | } | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Pop a number of two word slots from the push buffer | ||
| 153 | * Caller must ensure push buffer is not empty | ||
| 154 | */ | ||
| 155 | static void push_buffer_pop_from(struct push_buffer *pb, | ||
| 156 | unsigned int slots) | ||
| 157 | { | ||
| 158 | /* Clear the nvmap references for old items from pb */ | ||
| 159 | unsigned int i; | ||
| 160 | u32 fence_nvmap = pb->fence/8; | ||
| 161 | for (i = 0; i < slots; i++) { | ||
| 162 | int cur_fence_nvmap = (fence_nvmap+i) | ||
| 163 | & (NVHOST_GATHER_QUEUE_SIZE - 1); | ||
| 164 | struct nvmap_client_handle *h = | ||
| 165 | &pb->nvmap[cur_fence_nvmap]; | ||
| 166 | h->client = NULL; | ||
| 167 | h->handle = NULL; | ||
| 168 | } | ||
| 169 | /* Advance the next write position */ | ||
| 170 | pb->fence = (pb->fence + slots * 8) & (PUSH_BUFFER_SIZE - 1); | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Return the number of two word slots free in the push buffer | ||
| 175 | */ | ||
| 176 | static u32 push_buffer_space(struct push_buffer *pb) | ||
| 177 | { | ||
| 178 | return ((pb->fence - pb->cur) & (PUSH_BUFFER_SIZE - 1)) / 8; | ||
| 179 | } | ||
| 180 | |||
| 181 | static u32 push_buffer_putptr(struct push_buffer *pb) | ||
| 182 | { | ||
| 183 | return pb->phys + pb->cur; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* | ||
| 187 | * The syncpt incr buffer is filled with methods to increment syncpts, which | ||
| 188 | * is later GATHER-ed into the mainline PB. It's used when a timed out context | ||
| 189 | * is interleaved with other work, so needs to inline the syncpt increments | ||
| 190 | * to maintain the count (but otherwise does no work). | ||
| 191 | */ | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Init timeout and syncpt incr buffer resources | ||
| 195 | */ | ||
| 196 | static int cdma_timeout_init(struct nvhost_cdma *cdma, | ||
| 197 | u32 syncpt_id) | ||
| 198 | { | ||
| 199 | struct nvhost_master *dev = cdma_to_dev(cdma); | ||
| 200 | struct nvmap_client *nvmap = cdma_to_nvmap(cdma); | ||
| 201 | struct syncpt_buffer *sb = &cdma->syncpt_buffer; | ||
| 202 | struct nvhost_channel *ch = cdma_to_channel(cdma); | ||
| 203 | u32 i = 0; | ||
| 204 | |||
| 205 | if (syncpt_id == NVSYNCPT_INVALID) | ||
| 206 | return -EINVAL; | ||
| 207 | |||
| 208 | /* allocate and map syncpt incr memory */ | ||
| 209 | sb->mem = nvmap_alloc(nvmap, | ||
| 210 | (SYNCPT_INCR_BUFFER_SIZE_WORDS * sizeof(u32)), 32, | ||
| 211 | NVMAP_HANDLE_WRITE_COMBINE, 0); | ||
| 212 | if (IS_ERR_OR_NULL(sb->mem)) { | ||
| 213 | sb->mem = NULL; | ||
| 214 | goto fail; | ||
| 215 | } | ||
| 216 | sb->mapped = nvmap_mmap(sb->mem); | ||
| 217 | if (sb->mapped == NULL) | ||
| 218 | goto fail; | ||
| 219 | |||
| 220 | /* pin syncpt buffer and get physical address */ | ||
| 221 | sb->phys = nvmap_pin(nvmap, sb->mem); | ||
| 222 | if (sb->phys >= 0xfffff000) { | ||
| 223 | sb->phys = 0; | ||
| 224 | goto fail; | ||
| 225 | } | ||
| 226 | |||
| 227 | dev_dbg(&dev->dev->dev, "%s: SYNCPT_INCR buffer at 0x%x\n", | ||
| 228 | __func__, sb->phys); | ||
| 229 | |||
| 230 | sb->words_per_incr = (syncpt_id == NVSYNCPT_3D) ? 5 : 3; | ||
| 231 | sb->incr_per_buffer = (SYNCPT_INCR_BUFFER_SIZE_WORDS / | ||
| 232 | sb->words_per_incr); | ||
| 233 | |||
| 234 | /* init buffer with SETCL and INCR_SYNCPT methods */ | ||
| 235 | while (i < sb->incr_per_buffer) { | ||
| 236 | sb->mapped[i++] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
| 237 | 0, 0); | ||
| 238 | sb->mapped[i++] = nvhost_opcode_imm_incr_syncpt( | ||
| 239 | NV_SYNCPT_IMMEDIATE, | ||
| 240 | syncpt_id); | ||
| 241 | if (syncpt_id == NVSYNCPT_3D) { | ||
| 242 | /* also contains base increments */ | ||
| 243 | sb->mapped[i++] = nvhost_opcode_nonincr( | ||
| 244 | NV_CLASS_HOST_INCR_SYNCPT_BASE, | ||
| 245 | 1); | ||
| 246 | sb->mapped[i++] = nvhost_class_host_incr_syncpt_base( | ||
| 247 | NVWAITBASE_3D, 1); | ||
| 248 | } | ||
| 249 | sb->mapped[i++] = nvhost_opcode_setclass(ch->dev->class, | ||
| 250 | 0, 0); | ||
| 251 | } | ||
| 252 | wmb(); | ||
| 253 | |||
| 254 | INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler); | ||
| 255 | cdma->timeout.initialized = true; | ||
| 256 | |||
| 257 | return 0; | ||
| 258 | fail: | ||
| 259 | cdma_op(cdma).timeout_destroy(cdma); | ||
| 260 | return -ENOMEM; | ||
| 261 | } | ||
| 262 | |||
| 263 | /** | ||
| 264 | * Clean up timeout syncpt buffer resources | ||
| 265 | */ | ||
| 266 | static void cdma_timeout_destroy(struct nvhost_cdma *cdma) | ||
| 267 | { | ||
| 268 | struct nvmap_client *nvmap = cdma_to_nvmap(cdma); | ||
| 269 | struct syncpt_buffer *sb = &cdma->syncpt_buffer; | ||
| 270 | |||
| 271 | if (sb->mapped) | ||
| 272 | nvmap_munmap(sb->mem, sb->mapped); | ||
| 273 | |||
| 274 | if (sb->phys != 0) | ||
| 275 | nvmap_unpin(nvmap, sb->mem); | ||
| 276 | |||
| 277 | if (sb->mem) | ||
| 278 | nvmap_free(nvmap, sb->mem); | ||
| 279 | |||
| 280 | sb->mem = NULL; | ||
| 281 | sb->mapped = NULL; | ||
| 282 | sb->phys = 0; | ||
| 283 | |||
| 284 | if (cdma->timeout.initialized) | ||
| 285 | cancel_delayed_work(&cdma->timeout.wq); | ||
| 286 | cdma->timeout.initialized = false; | ||
| 287 | } | ||
| 288 | |||
| 289 | /** | ||
| 290 | * Increment timedout buffer's syncpt via CPU. | ||
| 291 | */ | ||
| 292 | static void cdma_timeout_cpu_incr(struct nvhost_cdma *cdma, u32 getptr, | ||
| 293 | u32 syncpt_incrs, u32 syncval, u32 nr_slots) | ||
| 294 | { | ||
| 295 | struct nvhost_master *dev = cdma_to_dev(cdma); | ||
| 296 | struct push_buffer *pb = &cdma->push_buffer; | ||
| 297 | u32 i, getidx; | ||
| 298 | |||
| 299 | for (i = 0; i < syncpt_incrs; i++) | ||
| 300 | nvhost_syncpt_cpu_incr(&dev->syncpt, cdma->timeout.syncpt_id); | ||
| 301 | |||
| 302 | /* after CPU incr, ensure shadow is up to date */ | ||
| 303 | nvhost_syncpt_update_min(&dev->syncpt, cdma->timeout.syncpt_id); | ||
| 304 | |||
| 305 | /* update WAITBASE_3D by same number of incrs */ | ||
| 306 | if (cdma->timeout.syncpt_id == NVSYNCPT_3D) { | ||
| 307 | void __iomem *p; | ||
| 308 | p = dev->sync_aperture + HOST1X_SYNC_SYNCPT_BASE_0 + | ||
| 309 | (NVWAITBASE_3D * sizeof(u32)); | ||
| 310 | writel(syncval, p); | ||
| 311 | } | ||
| 312 | |||
| 313 | /* NOP all the PB slots */ | ||
| 314 | getidx = getptr - pb->phys; | ||
| 315 | while (nr_slots--) { | ||
| 316 | u32 *p = (u32 *)((u32)pb->mapped + getidx); | ||
| 317 | *(p++) = NVHOST_OPCODE_NOOP; | ||
| 318 | *(p++) = NVHOST_OPCODE_NOOP; | ||
| 319 | dev_dbg(&dev->dev->dev, "%s: NOP at 0x%x\n", | ||
| 320 | __func__, pb->phys + getidx); | ||
| 321 | getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); | ||
| 322 | } | ||
| 323 | wmb(); | ||
| 324 | } | ||
| 325 | |||
| 326 | /** | ||
| 327 | * This routine is called at the point we transition back into a timed | ||
| 328 | * ctx. The syncpts are incremented via pushbuffer with a flag indicating | ||
| 329 | * whether there's a CTXSAVE that should be still executed (for the | ||
| 330 | * preceding HW ctx). | ||
| 331 | */ | ||
| 332 | static void cdma_timeout_pb_incr(struct nvhost_cdma *cdma, u32 getptr, | ||
| 333 | u32 syncpt_incrs, u32 nr_slots, | ||
| 334 | bool exec_ctxsave) | ||
| 335 | { | ||
| 336 | struct nvhost_master *dev = cdma_to_dev(cdma); | ||
| 337 | struct syncpt_buffer *sb = &cdma->syncpt_buffer; | ||
| 338 | struct push_buffer *pb = &cdma->push_buffer; | ||
| 339 | struct host1x_hwctx *hwctx = to_host1x_hwctx(cdma->timeout.ctx); | ||
| 340 | u32 getidx, *p; | ||
| 341 | |||
| 342 | /* should have enough slots to incr to desired count */ | ||
| 343 | BUG_ON(syncpt_incrs > (nr_slots * sb->incr_per_buffer)); | ||
| 344 | |||
| 345 | getidx = getptr - pb->phys; | ||
| 346 | if (exec_ctxsave) { | ||
| 347 | /* don't disrupt the CTXSAVE of a good/non-timed out ctx */ | ||
| 348 | nr_slots -= hwctx->save_slots; | ||
| 349 | syncpt_incrs -= hwctx->save_incrs; | ||
| 350 | |||
| 351 | getidx += (hwctx->save_slots * 8); | ||
| 352 | getidx &= (PUSH_BUFFER_SIZE - 1); | ||
| 353 | |||
| 354 | dev_dbg(&dev->dev->dev, | ||
| 355 | "%s: exec CTXSAVE of prev ctx (slots %d, incrs %d)\n", | ||
| 356 | __func__, nr_slots, syncpt_incrs); | ||
| 357 | } | ||
| 358 | |||
| 359 | while (syncpt_incrs) { | ||
| 360 | u32 incrs, count; | ||
| 361 | |||
| 362 | /* GATHER count are incrs * number of DWORDs per incr */ | ||
| 363 | incrs = min(syncpt_incrs, sb->incr_per_buffer); | ||
| 364 | count = incrs * sb->words_per_incr; | ||
| 365 | |||
| 366 | p = (u32 *)((u32)pb->mapped + getidx); | ||
| 367 | *(p++) = nvhost_opcode_gather(count); | ||
| 368 | *(p++) = sb->phys; | ||
| 369 | |||
| 370 | dev_dbg(&dev->dev->dev, | ||
| 371 | "%s: GATHER at 0x%x, from 0x%x, dcount = %d\n", | ||
| 372 | __func__, | ||
| 373 | pb->phys + getidx, sb->phys, | ||
| 374 | (incrs * sb->words_per_incr)); | ||
| 375 | |||
| 376 | syncpt_incrs -= incrs; | ||
| 377 | getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); | ||
| 378 | nr_slots--; | ||
| 379 | } | ||
| 380 | |||
| 381 | /* NOP remaining slots */ | ||
| 382 | while (nr_slots--) { | ||
| 383 | p = (u32 *)((u32)pb->mapped + getidx); | ||
| 384 | *(p++) = NVHOST_OPCODE_NOOP; | ||
| 385 | *(p++) = NVHOST_OPCODE_NOOP; | ||
| 386 | dev_dbg(&dev->dev->dev, "%s: NOP at 0x%x\n", | ||
| 387 | __func__, pb->phys + getidx); | ||
| 388 | getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); | ||
| 389 | } | ||
| 390 | wmb(); | ||
| 391 | } | ||
| 392 | |||
| 393 | /** | ||
| 394 | * Start channel DMA | ||
| 395 | */ | ||
| 396 | static void cdma_start(struct nvhost_cdma *cdma) | ||
| 397 | { | ||
| 398 | void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; | ||
| 399 | |||
| 400 | if (cdma->running) | ||
| 401 | return; | ||
| 402 | |||
| 403 | BUG_ON(!cdma_pb_op(cdma).putptr); | ||
| 404 | cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); | ||
| 405 | |||
| 406 | writel(host1x_channel_dmactrl(true, false, false), | ||
| 407 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 408 | |||
| 409 | /* set base, put, end pointer (all of memory) */ | ||
| 410 | writel(0, chan_regs + HOST1X_CHANNEL_DMASTART); | ||
| 411 | writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT); | ||
| 412 | writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND); | ||
| 413 | |||
| 414 | /* reset GET */ | ||
| 415 | writel(host1x_channel_dmactrl(true, true, true), | ||
| 416 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 417 | |||
| 418 | /* start the command DMA */ | ||
| 419 | writel(host1x_channel_dmactrl(false, false, false), | ||
| 420 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 421 | |||
| 422 | cdma->running = true; | ||
| 423 | } | ||
| 424 | |||
| 425 | /** | ||
| 426 | * Similar to cdma_start(), but rather than starting from an idle | ||
| 427 | * state (where DMA GET is set to DMA PUT), on a timeout we restore | ||
| 428 | * DMA GET from an explicit value (so DMA may again be pending). | ||
| 429 | */ | ||
| 430 | static void cdma_timeout_restart(struct nvhost_cdma *cdma, u32 getptr) | ||
| 431 | { | ||
| 432 | struct nvhost_master *dev = cdma_to_dev(cdma); | ||
| 433 | void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; | ||
| 434 | |||
| 435 | if (cdma->running) | ||
| 436 | return; | ||
| 437 | |||
| 438 | BUG_ON(!cdma_pb_op(cdma).putptr); | ||
| 439 | cdma->last_put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); | ||
| 440 | |||
| 441 | writel(host1x_channel_dmactrl(true, false, false), | ||
| 442 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 443 | |||
| 444 | /* set base, end pointer (all of memory) */ | ||
| 445 | writel(0, chan_regs + HOST1X_CHANNEL_DMASTART); | ||
| 446 | writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND); | ||
| 447 | |||
| 448 | /* set GET, by loading the value in PUT (then reset GET) */ | ||
| 449 | writel(getptr, chan_regs + HOST1X_CHANNEL_DMAPUT); | ||
| 450 | writel(host1x_channel_dmactrl(true, true, true), | ||
| 451 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 452 | |||
| 453 | dev_dbg(&dev->dev->dev, | ||
| 454 | "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", | ||
| 455 | __func__, | ||
| 456 | readl(chan_regs + HOST1X_CHANNEL_DMAGET), | ||
| 457 | readl(chan_regs + HOST1X_CHANNEL_DMAPUT), | ||
| 458 | cdma->last_put); | ||
| 459 | |||
| 460 | /* deassert GET reset and set PUT */ | ||
| 461 | writel(host1x_channel_dmactrl(true, false, false), | ||
| 462 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 463 | writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT); | ||
| 464 | |||
| 465 | /* start the command DMA */ | ||
| 466 | writel(host1x_channel_dmactrl(false, false, false), | ||
| 467 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 468 | |||
| 469 | cdma->running = true; | ||
| 470 | } | ||
| 471 | |||
| 472 | /** | ||
| 473 | * Kick channel DMA into action by writing its PUT offset (if it has changed) | ||
| 474 | */ | ||
| 475 | static void cdma_kick(struct nvhost_cdma *cdma) | ||
| 476 | { | ||
| 477 | u32 put; | ||
| 478 | BUG_ON(!cdma_pb_op(cdma).putptr); | ||
| 479 | |||
| 480 | put = cdma_pb_op(cdma).putptr(&cdma->push_buffer); | ||
| 481 | |||
| 482 | if (put != cdma->last_put) { | ||
| 483 | void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; | ||
| 484 | wmb(); | ||
| 485 | writel(put, chan_regs + HOST1X_CHANNEL_DMAPUT); | ||
| 486 | cdma->last_put = put; | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | static void cdma_stop(struct nvhost_cdma *cdma) | ||
| 491 | { | ||
| 492 | void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; | ||
| 493 | |||
| 494 | mutex_lock(&cdma->lock); | ||
| 495 | if (cdma->running) { | ||
| 496 | nvhost_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); | ||
| 497 | writel(host1x_channel_dmactrl(true, false, false), | ||
| 498 | chan_regs + HOST1X_CHANNEL_DMACTRL); | ||
| 499 | cdma->running = false; | ||
| 500 | } | ||
| 501 | mutex_unlock(&cdma->lock); | ||
| 502 | } | ||
| 503 | |||
| 504 | /** | ||
| 505 | * Retrieve the op pair at a slot offset from a DMA address | ||
| 506 | */ | ||
| 507 | void cdma_peek(struct nvhost_cdma *cdma, | ||
| 508 | u32 dmaget, int slot, u32 *out) | ||
| 509 | { | ||
| 510 | u32 offset = dmaget - cdma->push_buffer.phys; | ||
| 511 | u32 *p = cdma->push_buffer.mapped; | ||
| 512 | |||
| 513 | offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; | ||
| 514 | out[0] = p[offset]; | ||
| 515 | out[1] = p[offset + 1]; | ||
| 516 | } | ||
| 517 | |||
| 518 | /** | ||
| 519 | * Stops both channel's command processor and CDMA immediately. | ||
| 520 | * Also, tears down the channel and resets corresponding module. | ||
| 521 | */ | ||
| 522 | void cdma_timeout_teardown_begin(struct nvhost_cdma *cdma) | ||
| 523 | { | ||
| 524 | struct nvhost_master *dev = cdma_to_dev(cdma); | ||
| 525 | struct nvhost_channel *ch = cdma_to_channel(cdma); | ||
| 526 | u32 cmdproc_stop; | ||
| 527 | |||
| 528 | BUG_ON(cdma->torndown); | ||
| 529 | |||
| 530 | dev_dbg(&dev->dev->dev, | ||
| 531 | "begin channel teardown (channel id %d)\n", ch->chid); | ||
| 532 | |||
| 533 | cmdproc_stop = readl(dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 534 | cmdproc_stop |= BIT(ch->chid); | ||
| 535 | writel(cmdproc_stop, dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 536 | |||
| 537 | dev_dbg(&dev->dev->dev, | ||
| 538 | "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", | ||
| 539 | __func__, | ||
| 540 | readl(ch->aperture + HOST1X_CHANNEL_DMAGET), | ||
| 541 | readl(ch->aperture + HOST1X_CHANNEL_DMAPUT), | ||
| 542 | cdma->last_put); | ||
| 543 | |||
| 544 | writel(host1x_channel_dmactrl(true, false, false), | ||
| 545 | ch->aperture + HOST1X_CHANNEL_DMACTRL); | ||
| 546 | |||
| 547 | writel(BIT(ch->chid), dev->sync_aperture + HOST1X_SYNC_CH_TEARDOWN); | ||
| 548 | nvhost_module_reset(ch->dev); | ||
| 549 | |||
| 550 | cdma->running = false; | ||
| 551 | cdma->torndown = true; | ||
| 552 | } | ||
| 553 | |||
| 554 | void cdma_timeout_teardown_end(struct nvhost_cdma *cdma, u32 getptr) | ||
| 555 | { | ||
| 556 | struct nvhost_master *dev = cdma_to_dev(cdma); | ||
| 557 | struct nvhost_channel *ch = cdma_to_channel(cdma); | ||
| 558 | u32 cmdproc_stop; | ||
| 559 | |||
| 560 | BUG_ON(!cdma->torndown || cdma->running); | ||
| 561 | |||
| 562 | dev_dbg(&dev->dev->dev, | ||
| 563 | "end channel teardown (id %d, DMAGET restart = 0x%x)\n", | ||
| 564 | ch->chid, getptr); | ||
| 565 | |||
| 566 | cmdproc_stop = readl(dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 567 | cmdproc_stop &= ~(BIT(ch->chid)); | ||
| 568 | writel(cmdproc_stop, dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 569 | |||
| 570 | cdma->torndown = false; | ||
| 571 | cdma_timeout_restart(cdma, getptr); | ||
| 572 | } | ||
| 573 | |||
| 574 | /** | ||
| 575 | * If this timeout fires, it indicates the current sync_queue entry has | ||
| 576 | * exceeded its TTL and the userctx should be timed out and remaining | ||
| 577 | * submits already issued cleaned up (future submits return an error). | ||
| 578 | */ | ||
| 579 | static void cdma_timeout_handler(struct work_struct *work) | ||
| 580 | { | ||
| 581 | struct nvhost_cdma *cdma; | ||
| 582 | struct nvhost_master *dev; | ||
| 583 | struct nvhost_syncpt *sp; | ||
| 584 | struct nvhost_channel *ch; | ||
| 585 | |||
| 586 | u32 syncpt_val; | ||
| 587 | |||
| 588 | u32 prev_cmdproc, cmdproc_stop; | ||
| 589 | |||
| 590 | cdma = container_of(to_delayed_work(work), struct nvhost_cdma, | ||
| 591 | timeout.wq); | ||
| 592 | dev = cdma_to_dev(cdma); | ||
| 593 | sp = &dev->syncpt; | ||
| 594 | ch = cdma_to_channel(cdma); | ||
| 595 | |||
| 596 | mutex_lock(&cdma->lock); | ||
| 597 | |||
| 598 | if (!cdma->timeout.clientid) { | ||
| 599 | dev_dbg(&dev->dev->dev, | ||
| 600 | "cdma_timeout: expired, but has no clientid\n"); | ||
| 601 | mutex_unlock(&cdma->lock); | ||
| 602 | return; | ||
| 603 | } | ||
| 604 | |||
| 605 | /* stop processing to get a clean snapshot */ | ||
| 606 | prev_cmdproc = readl(dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 607 | cmdproc_stop = prev_cmdproc | BIT(ch->chid); | ||
| 608 | writel(cmdproc_stop, dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 609 | |||
| 610 | dev_dbg(&dev->dev->dev, "cdma_timeout: cmdproc was 0x%x is 0x%x\n", | ||
| 611 | prev_cmdproc, cmdproc_stop); | ||
| 612 | |||
| 613 | syncpt_val = nvhost_syncpt_update_min(&dev->syncpt, | ||
| 614 | cdma->timeout.syncpt_id); | ||
| 615 | |||
| 616 | /* has buffer actually completed? */ | ||
| 617 | if ((s32)(syncpt_val - cdma->timeout.syncpt_val) >= 0) { | ||
| 618 | dev_dbg(&dev->dev->dev, | ||
| 619 | "cdma_timeout: expired, but buffer had completed\n"); | ||
| 620 | /* restore */ | ||
| 621 | cmdproc_stop = prev_cmdproc & ~(BIT(ch->chid)); | ||
| 622 | writel(cmdproc_stop, | ||
| 623 | dev->sync_aperture + HOST1X_SYNC_CMDPROC_STOP); | ||
| 624 | mutex_unlock(&cdma->lock); | ||
| 625 | return; | ||
| 626 | } | ||
| 627 | |||
| 628 | dev_warn(&dev->dev->dev, | ||
| 629 | "%s: timeout: %d (%s) ctx 0x%p, HW thresh %d, done %d\n", | ||
| 630 | __func__, | ||
| 631 | cdma->timeout.syncpt_id, | ||
| 632 | syncpt_op(sp).name(sp, cdma->timeout.syncpt_id), | ||
| 633 | cdma->timeout.ctx, | ||
| 634 | syncpt_val, cdma->timeout.syncpt_val); | ||
| 635 | |||
| 636 | /* stop HW, resetting channel/module */ | ||
| 637 | cdma_op(cdma).timeout_teardown_begin(cdma); | ||
| 638 | |||
| 639 | nvhost_cdma_update_sync_queue(cdma, sp, &dev->dev->dev); | ||
| 640 | mutex_unlock(&cdma->lock); | ||
| 641 | } | ||
| 642 | |||
| 643 | int host1x_init_cdma_support(struct nvhost_master *host) | ||
| 644 | { | ||
| 645 | host->op.cdma.start = cdma_start; | ||
| 646 | host->op.cdma.stop = cdma_stop; | ||
| 647 | host->op.cdma.kick = cdma_kick; | ||
| 648 | |||
| 649 | host->op.cdma.timeout_init = cdma_timeout_init; | ||
| 650 | host->op.cdma.timeout_destroy = cdma_timeout_destroy; | ||
| 651 | host->op.cdma.timeout_teardown_begin = cdma_timeout_teardown_begin; | ||
| 652 | host->op.cdma.timeout_teardown_end = cdma_timeout_teardown_end; | ||
| 653 | host->op.cdma.timeout_cpu_incr = cdma_timeout_cpu_incr; | ||
| 654 | host->op.cdma.timeout_pb_incr = cdma_timeout_pb_incr; | ||
| 655 | |||
| 656 | host->op.push_buffer.reset = push_buffer_reset; | ||
| 657 | host->op.push_buffer.init = push_buffer_init; | ||
| 658 | host->op.push_buffer.destroy = push_buffer_destroy; | ||
| 659 | host->op.push_buffer.push_to = push_buffer_push_to; | ||
| 660 | host->op.push_buffer.pop_from = push_buffer_pop_from; | ||
| 661 | host->op.push_buffer.space = push_buffer_space; | ||
| 662 | host->op.push_buffer.putptr = push_buffer_putptr; | ||
| 663 | |||
| 664 | return 0; | ||
| 665 | } | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.h b/drivers/video/tegra/host/host1x/host1x_cdma.h new file mode 100644 index 00000000000..60909236a7c --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_cdma.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_cdma.h | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Channel | ||
| 5 | * | ||
| 6 | * Copyright (c) 2011-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 | #ifndef __NVHOST_HOST1X_HOST1X_CDMA_H | ||
| 22 | #define __NVHOST_HOST1X_HOST1X_CDMA_H | ||
| 23 | |||
| 24 | /* Size of the sync queue. If it is too small, we won't be able to queue up | ||
| 25 | * many command buffers. If it is too large, we waste memory. */ | ||
| 26 | #define NVHOST_SYNC_QUEUE_SIZE 512 | ||
| 27 | |||
| 28 | /* Number of gathers we allow to be queued up per channel. Must be a | ||
| 29 | * power of two. Currently sized such that pushbuffer is 4KB (512*8B). */ | ||
| 30 | #define NVHOST_GATHER_QUEUE_SIZE 512 | ||
| 31 | |||
| 32 | /* 8 bytes per slot. (This number does not include the final RESTART.) */ | ||
| 33 | #define PUSH_BUFFER_SIZE (NVHOST_GATHER_QUEUE_SIZE * 8) | ||
| 34 | |||
| 35 | /* 4K page containing GATHERed methods to increment channel syncpts | ||
| 36 | * and replaces the original timed out contexts GATHER slots */ | ||
| 37 | #define SYNCPT_INCR_BUFFER_SIZE_WORDS (4096 / sizeof(u32)) | ||
| 38 | |||
| 39 | int host1x_init_cdma_support(struct nvhost_master *); | ||
| 40 | |||
| 41 | #endif | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c new file mode 100644 index 00000000000..b16a34f416a --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_channel.c | |||
| @@ -0,0 +1,627 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/channel_host1x.c | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Channel | ||
| 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 "nvhost_channel.h" | ||
| 22 | #include "dev.h" | ||
| 23 | #include "nvhost_hwctx.h" | ||
| 24 | #include <trace/events/nvhost.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | |||
| 27 | #include "host1x_syncpt.h" | ||
| 28 | #include "host1x_channel.h" | ||
| 29 | #include "host1x_hardware.h" | ||
| 30 | #include "host1x_hwctx.h" | ||
| 31 | #include "nvhost_intr.h" | ||
| 32 | |||
| 33 | #define NV_FIFO_READ_TIMEOUT 200000 | ||
| 34 | |||
| 35 | static void sync_waitbases(struct nvhost_channel *ch, u32 syncpt_val) | ||
| 36 | { | ||
| 37 | unsigned long waitbase; | ||
| 38 | unsigned long int waitbase_mask = ch->dev->waitbases; | ||
| 39 | if (ch->dev->waitbasesync) { | ||
| 40 | waitbase = find_first_bit(&waitbase_mask, BITS_PER_LONG); | ||
| 41 | nvhost_cdma_push(&ch->cdma, | ||
| 42 | nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
| 43 | NV_CLASS_HOST_LOAD_SYNCPT_BASE, | ||
| 44 | 1), | ||
| 45 | nvhost_class_host_load_syncpt_base(waitbase, | ||
| 46 | syncpt_val)); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | static void *pre_submit_ctxsave(struct nvhost_job *job, | ||
| 51 | struct nvhost_hwctx *cur_ctx) | ||
| 52 | { | ||
| 53 | struct nvhost_channel *ch = job->ch; | ||
| 54 | void *ctxsave_waiter = NULL; | ||
| 55 | |||
| 56 | /* Is a save needed? */ | ||
| 57 | if (!cur_ctx || ch->cur_ctx == job->hwctx) | ||
| 58 | return NULL; | ||
| 59 | |||
| 60 | if (cur_ctx->has_timedout) { | ||
| 61 | dev_dbg(&ch->dev->dev, | ||
| 62 | "%s: skip save of timed out context (0x%p)\n", | ||
| 63 | __func__, ch->cur_ctx); | ||
| 64 | |||
| 65 | return NULL; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Allocate save waiter if needed */ | ||
| 69 | if (ch->ctxhandler->save_service) { | ||
| 70 | ctxsave_waiter = nvhost_intr_alloc_waiter(); | ||
| 71 | if (!ctxsave_waiter) | ||
| 72 | return ERR_PTR(-ENOMEM); | ||
| 73 | } | ||
| 74 | |||
| 75 | return ctxsave_waiter; | ||
| 76 | } | ||
| 77 | |||
| 78 | static void submit_ctxsave(struct nvhost_job *job, void *ctxsave_waiter, | ||
| 79 | struct nvhost_hwctx *cur_ctx) | ||
| 80 | { | ||
| 81 | struct nvhost_master *host = nvhost_get_host(job->ch->dev); | ||
| 82 | struct nvhost_channel *ch = job->ch; | ||
| 83 | u32 syncval; | ||
| 84 | int err; | ||
| 85 | u32 save_thresh = 0; | ||
| 86 | |||
| 87 | /* Is a save needed? */ | ||
| 88 | if (!cur_ctx || cur_ctx == job->hwctx || cur_ctx->has_timedout) | ||
| 89 | return; | ||
| 90 | |||
| 91 | /* Retrieve save threshold if we have a waiter */ | ||
| 92 | if (ctxsave_waiter) | ||
| 93 | save_thresh = | ||
| 94 | nvhost_syncpt_read_max(&host->syncpt, job->syncpt_id) | ||
| 95 | + to_host1x_hwctx(cur_ctx)->save_thresh; | ||
| 96 | |||
| 97 | /* Adjust the syncpoint max */ | ||
| 98 | job->syncpt_incrs += to_host1x_hwctx(cur_ctx)->save_incrs; | ||
| 99 | syncval = nvhost_syncpt_incr_max(&host->syncpt, | ||
| 100 | job->syncpt_id, | ||
| 101 | to_host1x_hwctx(cur_ctx)->save_incrs); | ||
| 102 | |||
| 103 | /* Send the save to channel */ | ||
| 104 | cur_ctx->valid = true; | ||
| 105 | ch->ctxhandler->save_push(cur_ctx, &ch->cdma); | ||
| 106 | nvhost_job_get_hwctx(job, cur_ctx); | ||
| 107 | |||
| 108 | /* Notify save service */ | ||
| 109 | if (ctxsave_waiter) { | ||
| 110 | err = nvhost_intr_add_action(&host->intr, | ||
| 111 | job->syncpt_id, | ||
| 112 | save_thresh, | ||
| 113 | NVHOST_INTR_ACTION_CTXSAVE, cur_ctx, | ||
| 114 | ctxsave_waiter, | ||
| 115 | NULL); | ||
| 116 | ctxsave_waiter = NULL; | ||
| 117 | WARN(err, "Failed to set ctx save interrupt"); | ||
| 118 | } | ||
| 119 | |||
| 120 | trace_nvhost_channel_context_save(ch->dev->name, cur_ctx); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void submit_ctxrestore(struct nvhost_job *job) | ||
| 124 | { | ||
| 125 | struct nvhost_master *host = nvhost_get_host(job->ch->dev); | ||
| 126 | struct nvhost_channel *ch = job->ch; | ||
| 127 | u32 syncval; | ||
| 128 | struct host1x_hwctx *ctx = | ||
| 129 | job->hwctx ? to_host1x_hwctx(job->hwctx) : NULL; | ||
| 130 | |||
| 131 | /* First check if we have a valid context to restore */ | ||
| 132 | if(ch->cur_ctx == job->hwctx || !job->hwctx || !job->hwctx->valid) | ||
| 133 | return; | ||
| 134 | |||
| 135 | /* Increment syncpt max */ | ||
| 136 | job->syncpt_incrs += ctx->restore_incrs; | ||
| 137 | syncval = nvhost_syncpt_incr_max(&host->syncpt, | ||
| 138 | job->syncpt_id, | ||
| 139 | ctx->restore_incrs); | ||
| 140 | |||
| 141 | /* Send restore buffer to channel */ | ||
| 142 | nvhost_cdma_push_gather(&ch->cdma, | ||
| 143 | host->nvmap, | ||
| 144 | nvmap_ref_to_handle(ctx->restore), | ||
| 145 | nvhost_opcode_gather(ctx->restore_size), | ||
| 146 | ctx->restore_phys); | ||
| 147 | |||
| 148 | trace_nvhost_channel_context_restore(ch->dev->name, &ctx->hwctx); | ||
| 149 | } | ||
| 150 | |||
| 151 | void submit_nullkickoff(struct nvhost_job *job, int user_syncpt_incrs) | ||
| 152 | { | ||
| 153 | struct nvhost_channel *ch = job->ch; | ||
| 154 | int incr; | ||
| 155 | u32 op_incr; | ||
| 156 | |||
| 157 | /* push increments that correspond to nulled out commands */ | ||
| 158 | op_incr = nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, | ||
| 159 | job->syncpt_id); | ||
| 160 | for (incr = 0; incr < (user_syncpt_incrs >> 1); incr++) | ||
| 161 | nvhost_cdma_push(&ch->cdma, op_incr, op_incr); | ||
| 162 | if (user_syncpt_incrs & 1) | ||
| 163 | nvhost_cdma_push(&ch->cdma, op_incr, NVHOST_OPCODE_NOOP); | ||
| 164 | |||
| 165 | /* for 3d, waitbase needs to be incremented after each submit */ | ||
| 166 | if (ch->dev->class == NV_GRAPHICS_3D_CLASS_ID) { | ||
| 167 | u32 waitbase = to_host1x_hwctx_handler(job->hwctx->h)->waitbase; | ||
| 168 | nvhost_cdma_push(&ch->cdma, | ||
| 169 | nvhost_opcode_setclass( | ||
| 170 | NV_HOST1X_CLASS_ID, | ||
| 171 | NV_CLASS_HOST_INCR_SYNCPT_BASE, | ||
| 172 | 1), | ||
| 173 | nvhost_class_host_incr_syncpt_base( | ||
| 174 | waitbase, | ||
| 175 | user_syncpt_incrs)); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | void submit_gathers(struct nvhost_job *job) | ||
| 180 | { | ||
| 181 | /* push user gathers */ | ||
| 182 | int i = 0; | ||
| 183 | for ( ; i < job->num_gathers; i++) { | ||
| 184 | u32 op1 = nvhost_opcode_gather(job->gathers[i].words); | ||
| 185 | u32 op2 = job->gathers[i].mem; | ||
| 186 | nvhost_cdma_push_gather(&job->ch->cdma, | ||
| 187 | job->nvmap, job->unpins[i/2], | ||
| 188 | op1, op2); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | int host1x_channel_submit(struct nvhost_job *job) | ||
| 193 | { | ||
| 194 | struct nvhost_channel *ch = job->ch; | ||
| 195 | struct nvhost_syncpt *sp = &nvhost_get_host(job->ch->dev)->syncpt; | ||
| 196 | u32 user_syncpt_incrs = job->syncpt_incrs; | ||
| 197 | u32 prev_max = 0; | ||
| 198 | u32 syncval; | ||
| 199 | int err; | ||
| 200 | void *completed_waiter = NULL, *ctxsave_waiter = NULL; | ||
| 201 | |||
| 202 | /* Bail out on timed out contexts */ | ||
| 203 | if (job->hwctx && job->hwctx->has_timedout) | ||
| 204 | return -ETIMEDOUT; | ||
| 205 | |||
| 206 | /* Turn on the client module and host1x */ | ||
| 207 | nvhost_module_busy(ch->dev); | ||
| 208 | if (ch->dev->busy) | ||
| 209 | ch->dev->busy(ch->dev); | ||
| 210 | |||
| 211 | /* before error checks, return current max */ | ||
| 212 | prev_max = job->syncpt_end = | ||
| 213 | nvhost_syncpt_read_max(sp, job->syncpt_id); | ||
| 214 | |||
| 215 | /* get submit lock */ | ||
| 216 | err = mutex_lock_interruptible(&ch->submitlock); | ||
| 217 | if (err) { | ||
| 218 | nvhost_module_idle(ch->dev); | ||
| 219 | goto error; | ||
| 220 | } | ||
| 221 | |||
| 222 | /* Do the needed allocations */ | ||
| 223 | ctxsave_waiter = pre_submit_ctxsave(job, ch->cur_ctx); | ||
| 224 | if (IS_ERR(ctxsave_waiter)) { | ||
| 225 | err = PTR_ERR(ctxsave_waiter); | ||
| 226 | nvhost_module_idle(ch->dev); | ||
| 227 | mutex_unlock(&ch->submitlock); | ||
| 228 | goto error; | ||
| 229 | } | ||
| 230 | |||
| 231 | completed_waiter = nvhost_intr_alloc_waiter(); | ||
| 232 | if (!completed_waiter) { | ||
| 233 | nvhost_module_idle(ch->dev); | ||
| 234 | mutex_unlock(&ch->submitlock); | ||
| 235 | err = -ENOMEM; | ||
| 236 | goto error; | ||
| 237 | } | ||
| 238 | |||
| 239 | /* remove stale waits */ | ||
| 240 | if (job->num_waitchk) { | ||
| 241 | err = nvhost_syncpt_wait_check(sp, | ||
| 242 | job->nvmap, | ||
| 243 | job->waitchk_mask, | ||
| 244 | job->waitchk, | ||
| 245 | job->num_waitchk); | ||
| 246 | if (err) { | ||
| 247 | dev_warn(&ch->dev->dev, | ||
| 248 | "nvhost_syncpt_wait_check failed: %d\n", err); | ||
| 249 | mutex_unlock(&ch->submitlock); | ||
| 250 | nvhost_module_idle(ch->dev); | ||
| 251 | goto error; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | /* begin a CDMA submit */ | ||
| 256 | err = nvhost_cdma_begin(&ch->cdma, job); | ||
| 257 | if (err) { | ||
| 258 | mutex_unlock(&ch->submitlock); | ||
| 259 | nvhost_module_idle(ch->dev); | ||
| 260 | goto error; | ||
| 261 | } | ||
| 262 | |||
| 263 | submit_ctxsave(job, ctxsave_waiter, ch->cur_ctx); | ||
| 264 | submit_ctxrestore(job); | ||
| 265 | ch->cur_ctx = job->hwctx; | ||
| 266 | |||
| 267 | syncval = nvhost_syncpt_incr_max(sp, | ||
| 268 | job->syncpt_id, user_syncpt_incrs); | ||
| 269 | |||
| 270 | job->syncpt_end = syncval; | ||
| 271 | |||
| 272 | /* add a setclass for modules that require it */ | ||
| 273 | if (ch->dev->class) | ||
| 274 | nvhost_cdma_push(&ch->cdma, | ||
| 275 | nvhost_opcode_setclass(ch->dev->class, 0, 0), | ||
| 276 | NVHOST_OPCODE_NOOP); | ||
| 277 | |||
| 278 | if (job->null_kickoff) | ||
| 279 | submit_nullkickoff(job, user_syncpt_incrs); | ||
| 280 | else | ||
| 281 | submit_gathers(job); | ||
| 282 | |||
| 283 | sync_waitbases(ch, job->syncpt_end); | ||
| 284 | |||
| 285 | /* end CDMA submit & stash pinned hMems into sync queue */ | ||
| 286 | nvhost_cdma_end(&ch->cdma, job); | ||
| 287 | |||
| 288 | trace_nvhost_channel_submitted(ch->dev->name, | ||
| 289 | prev_max, syncval); | ||
| 290 | |||
| 291 | /* schedule a submit complete interrupt */ | ||
| 292 | err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr, | ||
| 293 | job->syncpt_id, syncval, | ||
| 294 | NVHOST_INTR_ACTION_SUBMIT_COMPLETE, ch, | ||
| 295 | completed_waiter, | ||
| 296 | NULL); | ||
| 297 | completed_waiter = NULL; | ||
| 298 | WARN(err, "Failed to set submit complete interrupt"); | ||
| 299 | |||
| 300 | mutex_unlock(&ch->submitlock); | ||
| 301 | |||
| 302 | return 0; | ||
| 303 | |||
| 304 | error: | ||
| 305 | kfree(ctxsave_waiter); | ||
| 306 | kfree(completed_waiter); | ||
| 307 | return err; | ||
| 308 | } | ||
| 309 | |||
| 310 | int host1x_channel_read_3d_reg( | ||
| 311 | struct nvhost_channel *channel, | ||
| 312 | struct nvhost_hwctx *hwctx, | ||
| 313 | u32 offset, | ||
| 314 | u32 *value) | ||
| 315 | { | ||
| 316 | struct host1x_hwctx *hwctx_to_save = NULL; | ||
| 317 | struct nvhost_hwctx_handler *h = hwctx->h; | ||
| 318 | struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h); | ||
| 319 | bool need_restore = false; | ||
| 320 | u32 syncpt_incrs = 4; | ||
| 321 | unsigned int pending = 0; | ||
| 322 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); | ||
| 323 | void *ref; | ||
| 324 | void *ctx_waiter, *read_waiter, *completed_waiter; | ||
| 325 | struct nvhost_job *job; | ||
| 326 | u32 syncval; | ||
| 327 | int err; | ||
| 328 | |||
| 329 | if (hwctx && hwctx->has_timedout) | ||
| 330 | return -ETIMEDOUT; | ||
| 331 | |||
| 332 | ctx_waiter = nvhost_intr_alloc_waiter(); | ||
| 333 | read_waiter = nvhost_intr_alloc_waiter(); | ||
| 334 | completed_waiter = nvhost_intr_alloc_waiter(); | ||
| 335 | if (!ctx_waiter || !read_waiter || !completed_waiter) { | ||
| 336 | err = -ENOMEM; | ||
| 337 | goto done; | ||
| 338 | } | ||
| 339 | |||
| 340 | job = nvhost_job_alloc(channel, hwctx, | ||
| 341 | NULL, | ||
| 342 | nvhost_get_host(channel->dev)->nvmap, 0, 0); | ||
| 343 | if (!job) { | ||
| 344 | err = -ENOMEM; | ||
| 345 | goto done; | ||
| 346 | } | ||
| 347 | |||
| 348 | /* keep module powered */ | ||
| 349 | nvhost_module_busy(channel->dev); | ||
| 350 | |||
| 351 | /* get submit lock */ | ||
| 352 | err = mutex_lock_interruptible(&channel->submitlock); | ||
| 353 | if (err) { | ||
| 354 | nvhost_module_idle(channel->dev); | ||
| 355 | return err; | ||
| 356 | } | ||
| 357 | |||
| 358 | /* context switch */ | ||
| 359 | if (channel->cur_ctx != hwctx) { | ||
| 360 | hwctx_to_save = channel->cur_ctx ? | ||
| 361 | to_host1x_hwctx(channel->cur_ctx) : NULL; | ||
| 362 | if (hwctx_to_save) { | ||
| 363 | syncpt_incrs += hwctx_to_save->save_incrs; | ||
| 364 | hwctx_to_save->hwctx.valid = true; | ||
| 365 | channel->ctxhandler->get(&hwctx_to_save->hwctx); | ||
| 366 | } | ||
| 367 | channel->cur_ctx = hwctx; | ||
| 368 | if (channel->cur_ctx && channel->cur_ctx->valid) { | ||
| 369 | need_restore = true; | ||
| 370 | syncpt_incrs += to_host1x_hwctx(channel->cur_ctx) | ||
| 371 | ->restore_incrs; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | syncval = nvhost_syncpt_incr_max(&nvhost_get_host(channel->dev)->syncpt, | ||
| 376 | p->syncpt, syncpt_incrs); | ||
| 377 | |||
| 378 | job->syncpt_id = p->syncpt; | ||
| 379 | job->syncpt_incrs = syncpt_incrs; | ||
| 380 | job->syncpt_end = syncval; | ||
| 381 | |||
| 382 | /* begin a CDMA submit */ | ||
| 383 | nvhost_cdma_begin(&channel->cdma, job); | ||
| 384 | |||
| 385 | /* push save buffer (pre-gather setup depends on unit) */ | ||
| 386 | if (hwctx_to_save) | ||
| 387 | h->save_push(&hwctx_to_save->hwctx, &channel->cdma); | ||
| 388 | |||
| 389 | /* gather restore buffer */ | ||
| 390 | if (need_restore) | ||
| 391 | nvhost_cdma_push(&channel->cdma, | ||
| 392 | nvhost_opcode_gather(to_host1x_hwctx(channel->cur_ctx) | ||
| 393 | ->restore_size), | ||
| 394 | to_host1x_hwctx(channel->cur_ctx)->restore_phys); | ||
| 395 | |||
| 396 | /* Switch to 3D - wait for it to complete what it was doing */ | ||
| 397 | nvhost_cdma_push(&channel->cdma, | ||
| 398 | nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0), | ||
| 399 | nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_OP_DONE, | ||
| 400 | p->syncpt)); | ||
| 401 | nvhost_cdma_push(&channel->cdma, | ||
| 402 | nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, | ||
| 403 | NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1), | ||
| 404 | nvhost_class_host_wait_syncpt_base(p->syncpt, | ||
| 405 | p->waitbase, 1)); | ||
| 406 | /* Tell 3D to send register value to FIFO */ | ||
| 407 | nvhost_cdma_push(&channel->cdma, | ||
| 408 | nvhost_opcode_nonincr(NV_CLASS_HOST_INDOFF, 1), | ||
| 409 | nvhost_class_host_indoff_reg_read(NV_HOST_MODULE_GR3D, | ||
| 410 | offset, false)); | ||
| 411 | nvhost_cdma_push(&channel->cdma, | ||
| 412 | nvhost_opcode_imm(NV_CLASS_HOST_INDDATA, 0), | ||
| 413 | NVHOST_OPCODE_NOOP); | ||
| 414 | /* Increment syncpt to indicate that FIFO can be read */ | ||
| 415 | nvhost_cdma_push(&channel->cdma, | ||
| 416 | nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE, | ||
| 417 | p->syncpt), | ||
| 418 | NVHOST_OPCODE_NOOP); | ||
| 419 | /* Wait for value to be read from FIFO */ | ||
| 420 | nvhost_cdma_push(&channel->cdma, | ||
| 421 | nvhost_opcode_nonincr(NV_CLASS_HOST_WAIT_SYNCPT_BASE, 1), | ||
| 422 | nvhost_class_host_wait_syncpt_base(p->syncpt, | ||
| 423 | p->waitbase, 3)); | ||
| 424 | /* Indicate submit complete */ | ||
| 425 | nvhost_cdma_push(&channel->cdma, | ||
| 426 | nvhost_opcode_nonincr(NV_CLASS_HOST_INCR_SYNCPT_BASE, 1), | ||
| 427 | nvhost_class_host_incr_syncpt_base(p->waitbase, 4)); | ||
| 428 | nvhost_cdma_push(&channel->cdma, | ||
| 429 | NVHOST_OPCODE_NOOP, | ||
| 430 | nvhost_opcode_imm_incr_syncpt(NV_SYNCPT_IMMEDIATE, | ||
| 431 | p->syncpt)); | ||
| 432 | |||
| 433 | /* end CDMA submit */ | ||
| 434 | nvhost_cdma_end(&channel->cdma, job); | ||
| 435 | nvhost_job_put(job); | ||
| 436 | job = NULL; | ||
| 437 | |||
| 438 | /* | ||
| 439 | * schedule a context save interrupt (to drain the host FIFO | ||
| 440 | * if necessary, and to release the restore buffer) | ||
| 441 | */ | ||
| 442 | if (hwctx_to_save) { | ||
| 443 | err = nvhost_intr_add_action( | ||
| 444 | &nvhost_get_host(channel->dev)->intr, | ||
| 445 | p->syncpt, | ||
| 446 | syncval - syncpt_incrs | ||
| 447 | + hwctx_to_save->save_incrs | ||
| 448 | - 1, | ||
| 449 | NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, | ||
| 450 | ctx_waiter, | ||
| 451 | NULL); | ||
| 452 | ctx_waiter = NULL; | ||
| 453 | WARN(err, "Failed to set context save interrupt"); | ||
| 454 | } | ||
| 455 | |||
| 456 | /* Wait for FIFO to be ready */ | ||
| 457 | err = nvhost_intr_add_action(&nvhost_get_host(channel->dev)->intr, | ||
| 458 | p->syncpt, syncval - 2, | ||
| 459 | NVHOST_INTR_ACTION_WAKEUP, &wq, | ||
| 460 | read_waiter, | ||
| 461 | &ref); | ||
| 462 | read_waiter = NULL; | ||
| 463 | WARN(err, "Failed to set wakeup interrupt"); | ||
| 464 | wait_event(wq, | ||
| 465 | nvhost_syncpt_is_expired(&nvhost_get_host(channel->dev)->syncpt, | ||
| 466 | p->syncpt, syncval - 2)); | ||
| 467 | nvhost_intr_put_ref(&nvhost_get_host(channel->dev)->intr, ref); | ||
| 468 | |||
| 469 | /* Read the register value from FIFO */ | ||
| 470 | err = host1x_drain_read_fifo(channel->aperture, | ||
| 471 | value, 1, &pending); | ||
| 472 | |||
| 473 | /* Indicate we've read the value */ | ||
| 474 | nvhost_syncpt_cpu_incr(&nvhost_get_host(channel->dev)->syncpt, | ||
| 475 | p->syncpt); | ||
| 476 | |||
| 477 | /* Schedule a submit complete interrupt */ | ||
| 478 | err = nvhost_intr_add_action(&nvhost_get_host(channel->dev)->intr, | ||
| 479 | p->syncpt, syncval, | ||
| 480 | NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel, | ||
| 481 | completed_waiter, NULL); | ||
| 482 | completed_waiter = NULL; | ||
| 483 | WARN(err, "Failed to set submit complete interrupt"); | ||
| 484 | |||
| 485 | mutex_unlock(&channel->submitlock); | ||
| 486 | |||
| 487 | done: | ||
| 488 | kfree(ctx_waiter); | ||
| 489 | kfree(read_waiter); | ||
| 490 | kfree(completed_waiter); | ||
| 491 | return err; | ||
| 492 | } | ||
| 493 | |||
| 494 | |||
| 495 | int host1x_drain_read_fifo(void __iomem *chan_regs, | ||
| 496 | u32 *ptr, unsigned int count, unsigned int *pending) | ||
| 497 | { | ||
| 498 | unsigned int entries = *pending; | ||
| 499 | unsigned long timeout = jiffies + NV_FIFO_READ_TIMEOUT; | ||
| 500 | while (count) { | ||
| 501 | unsigned int num; | ||
| 502 | |||
| 503 | while (!entries && time_before(jiffies, timeout)) { | ||
| 504 | /* query host for number of entries in fifo */ | ||
| 505 | entries = HOST1X_VAL(CHANNEL_FIFOSTAT, OUTFENTRIES, | ||
| 506 | readl(chan_regs + HOST1X_CHANNEL_FIFOSTAT)); | ||
| 507 | if (!entries) | ||
| 508 | cpu_relax(); | ||
| 509 | } | ||
| 510 | |||
| 511 | /* timeout -> return error */ | ||
| 512 | if (!entries) | ||
| 513 | return -EIO; | ||
| 514 | |||
| 515 | num = min(entries, count); | ||
| 516 | entries -= num; | ||
| 517 | count -= num; | ||
| 518 | |||
| 519 | while (num & ~0x3) { | ||
| 520 | u32 arr[4]; | ||
| 521 | arr[0] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); | ||
| 522 | arr[1] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); | ||
| 523 | arr[2] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); | ||
| 524 | arr[3] = readl(chan_regs + HOST1X_CHANNEL_INDDATA); | ||
| 525 | memcpy(ptr, arr, 4*sizeof(u32)); | ||
| 526 | ptr += 4; | ||
| 527 | num -= 4; | ||
| 528 | } | ||
| 529 | while (num--) | ||
| 530 | *ptr++ = readl(chan_regs + HOST1X_CHANNEL_INDDATA); | ||
| 531 | } | ||
| 532 | *pending = entries; | ||
| 533 | |||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id) | ||
| 538 | { | ||
| 539 | struct nvhost_channel *ch = dev->channel; | ||
| 540 | struct nvhost_hwctx *hwctx_to_save; | ||
| 541 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); | ||
| 542 | u32 syncpt_incrs, syncpt_val; | ||
| 543 | int err = 0; | ||
| 544 | void *ref; | ||
| 545 | void *ctx_waiter = NULL, *wakeup_waiter = NULL; | ||
| 546 | struct nvhost_job *job; | ||
| 547 | |||
| 548 | ctx_waiter = nvhost_intr_alloc_waiter(); | ||
| 549 | wakeup_waiter = nvhost_intr_alloc_waiter(); | ||
| 550 | if (!ctx_waiter || !wakeup_waiter) { | ||
| 551 | err = -ENOMEM; | ||
| 552 | goto done; | ||
| 553 | } | ||
| 554 | |||
| 555 | if (dev->busy) | ||
| 556 | dev->busy(dev); | ||
| 557 | |||
| 558 | mutex_lock(&ch->submitlock); | ||
| 559 | hwctx_to_save = ch->cur_ctx; | ||
| 560 | if (!hwctx_to_save) { | ||
| 561 | mutex_unlock(&ch->submitlock); | ||
| 562 | goto done; | ||
| 563 | } | ||
| 564 | |||
| 565 | job = nvhost_job_alloc(ch, hwctx_to_save, | ||
| 566 | NULL, | ||
| 567 | nvhost_get_host(ch->dev)->nvmap, 0, 0); | ||
| 568 | if (IS_ERR_OR_NULL(job)) { | ||
| 569 | err = PTR_ERR(job); | ||
| 570 | mutex_unlock(&ch->submitlock); | ||
| 571 | goto done; | ||
| 572 | } | ||
| 573 | |||
| 574 | hwctx_to_save->valid = true; | ||
| 575 | ch->ctxhandler->get(hwctx_to_save); | ||
| 576 | ch->cur_ctx = NULL; | ||
| 577 | |||
| 578 | syncpt_incrs = to_host1x_hwctx(hwctx_to_save)->save_incrs; | ||
| 579 | syncpt_val = nvhost_syncpt_incr_max(&nvhost_get_host(ch->dev)->syncpt, | ||
| 580 | syncpt_id, syncpt_incrs); | ||
| 581 | |||
| 582 | job->syncpt_id = syncpt_id; | ||
| 583 | job->syncpt_incrs = syncpt_incrs; | ||
| 584 | job->syncpt_end = syncpt_val; | ||
| 585 | |||
| 586 | err = nvhost_cdma_begin(&ch->cdma, job); | ||
| 587 | if (err) { | ||
| 588 | mutex_unlock(&ch->submitlock); | ||
| 589 | goto done; | ||
| 590 | } | ||
| 591 | |||
| 592 | ch->ctxhandler->save_push(hwctx_to_save, &ch->cdma); | ||
| 593 | nvhost_cdma_end(&ch->cdma, job); | ||
| 594 | nvhost_job_put(job); | ||
| 595 | job = NULL; | ||
| 596 | |||
| 597 | err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr, syncpt_id, | ||
| 598 | syncpt_val - syncpt_incrs + | ||
| 599 | to_host1x_hwctx(hwctx_to_save)->save_thresh, | ||
| 600 | NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, | ||
| 601 | ctx_waiter, | ||
| 602 | NULL); | ||
| 603 | ctx_waiter = NULL; | ||
| 604 | WARN(err, "Failed to set context save interrupt"); | ||
| 605 | |||
| 606 | err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr, | ||
| 607 | syncpt_id, syncpt_val, | ||
| 608 | NVHOST_INTR_ACTION_WAKEUP, &wq, | ||
| 609 | wakeup_waiter, | ||
| 610 | &ref); | ||
| 611 | wakeup_waiter = NULL; | ||
| 612 | WARN(err, "Failed to set wakeup interrupt"); | ||
| 613 | wait_event(wq, | ||
| 614 | nvhost_syncpt_is_expired(&nvhost_get_host(ch->dev)->syncpt, | ||
| 615 | syncpt_id, syncpt_val)); | ||
| 616 | |||
| 617 | nvhost_intr_put_ref(&nvhost_get_host(ch->dev)->intr, ref); | ||
| 618 | |||
| 619 | nvhost_cdma_update(&ch->cdma); | ||
| 620 | |||
| 621 | mutex_unlock(&ch->submitlock); | ||
| 622 | |||
| 623 | done: | ||
| 624 | kfree(ctx_waiter); | ||
| 625 | kfree(wakeup_waiter); | ||
| 626 | return err; | ||
| 627 | } | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_channel.h b/drivers/video/tegra/host/host1x/host1x_channel.h new file mode 100644 index 00000000000..4113dbcada2 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_channel.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_channel.h | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Channel | ||
| 5 | * | ||
| 6 | * Copyright (c) 2011-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 | #ifndef __NVHOST_HOST1X_CHANNEL_H | ||
| 22 | #define __NVHOST_HOST1X_CHANNEL_H | ||
| 23 | |||
| 24 | struct nvhost_job; | ||
| 25 | struct nvhost_channel; | ||
| 26 | struct nvhost_hwctx; | ||
| 27 | struct nvhost_device; | ||
| 28 | |||
| 29 | /* Submit job to a host1x client */ | ||
| 30 | int host1x_channel_submit(struct nvhost_job *job); | ||
| 31 | |||
| 32 | /* Read 3d register via FIFO */ | ||
| 33 | int host1x_channel_read_3d_reg( | ||
| 34 | struct nvhost_channel *channel, | ||
| 35 | struct nvhost_hwctx *hwctx, | ||
| 36 | u32 offset, | ||
| 37 | u32 *value); | ||
| 38 | |||
| 39 | /* Reads words from FIFO */ | ||
| 40 | int host1x_drain_read_fifo(void __iomem *chan_regs, | ||
| 41 | u32 *ptr, unsigned int count, unsigned int *pending); | ||
| 42 | |||
| 43 | int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id); | ||
| 44 | |||
| 45 | #endif | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c new file mode 100644 index 00000000000..1a1d764bbd6 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_debug.c | |||
| @@ -0,0 +1,404 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_debug.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Google, Inc. | ||
| 5 | * Author: Erik Gilling <konkers@android.com> | ||
| 6 | * | ||
| 7 | * Copyright (C) 2011 NVIDIA Corporation | ||
| 8 | * | ||
| 9 | * This software is licensed under the terms of the GNU General Public | ||
| 10 | * License version 2, as published by the Free Software Foundation, and | ||
| 11 | * may be copied, distributed, and modified under those terms. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/debugfs.h> | ||
| 21 | #include <linux/seq_file.h> | ||
| 22 | #include <linux/mm.h> | ||
| 23 | |||
| 24 | #include <linux/io.h> | ||
| 25 | |||
| 26 | #include "dev.h" | ||
| 27 | #include "debug.h" | ||
| 28 | #include "nvhost_cdma.h" | ||
| 29 | #include "../../nvmap/nvmap.h" | ||
| 30 | |||
| 31 | #include "host1x_hardware.h" | ||
| 32 | #include "host1x_cdma.h" | ||
| 33 | |||
| 34 | #define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 | ||
| 35 | |||
| 36 | enum { | ||
| 37 | NVHOST_DBG_STATE_CMD = 0, | ||
| 38 | NVHOST_DBG_STATE_DATA = 1, | ||
| 39 | NVHOST_DBG_STATE_GATHER = 2 | ||
| 40 | }; | ||
| 41 | |||
| 42 | static int show_channel_command(struct output *o, u32 addr, u32 val, int *count) | ||
| 43 | { | ||
| 44 | unsigned mask; | ||
| 45 | unsigned subop; | ||
| 46 | |||
| 47 | switch (val >> 28) { | ||
| 48 | case 0x0: | ||
| 49 | mask = val & 0x3f; | ||
| 50 | if (mask) { | ||
| 51 | nvhost_debug_output(o, | ||
| 52 | "SETCL(class=%03x, offset=%03x, mask=%02x, [", | ||
| 53 | val >> 6 & 0x3ff, val >> 16 & 0xfff, mask); | ||
| 54 | *count = hweight8(mask); | ||
| 55 | return NVHOST_DBG_STATE_DATA; | ||
| 56 | } else { | ||
| 57 | nvhost_debug_output(o, "SETCL(class=%03x)\n", | ||
| 58 | val >> 6 & 0x3ff); | ||
| 59 | return NVHOST_DBG_STATE_CMD; | ||
| 60 | } | ||
| 61 | |||
| 62 | case 0x1: | ||
| 63 | nvhost_debug_output(o, "INCR(offset=%03x, [", | ||
| 64 | val >> 16 & 0xfff); | ||
| 65 | *count = val & 0xffff; | ||
| 66 | return NVHOST_DBG_STATE_DATA; | ||
| 67 | |||
| 68 | case 0x2: | ||
| 69 | nvhost_debug_output(o, "NONINCR(offset=%03x, [", | ||
| 70 | val >> 16 & 0xfff); | ||
| 71 | *count = val & 0xffff; | ||
| 72 | return NVHOST_DBG_STATE_DATA; | ||
| 73 | |||
| 74 | case 0x3: | ||
| 75 | mask = val & 0xffff; | ||
| 76 | nvhost_debug_output(o, "MASK(offset=%03x, mask=%03x, [", | ||
| 77 | val >> 16 & 0xfff, mask); | ||
| 78 | *count = hweight16(mask); | ||
| 79 | return NVHOST_DBG_STATE_DATA; | ||
| 80 | |||
| 81 | case 0x4: | ||
| 82 | nvhost_debug_output(o, "IMM(offset=%03x, data=%03x)\n", | ||
| 83 | val >> 16 & 0xfff, val & 0xffff); | ||
| 84 | return NVHOST_DBG_STATE_CMD; | ||
| 85 | |||
| 86 | case 0x5: | ||
| 87 | nvhost_debug_output(o, "RESTART(offset=%08x)\n", val << 4); | ||
| 88 | return NVHOST_DBG_STATE_CMD; | ||
| 89 | |||
| 90 | case 0x6: | ||
| 91 | nvhost_debug_output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[", | ||
| 92 | val >> 16 & 0xfff, val >> 15 & 0x1, val >> 14 & 0x1, | ||
| 93 | val & 0x3fff); | ||
| 94 | *count = val & 0x3fff; /* TODO: insert */ | ||
| 95 | return NVHOST_DBG_STATE_GATHER; | ||
| 96 | |||
| 97 | case 0xe: | ||
| 98 | subop = val >> 24 & 0xf; | ||
| 99 | if (subop == 0) | ||
| 100 | nvhost_debug_output(o, "ACQUIRE_MLOCK(index=%d)\n", | ||
| 101 | val & 0xff); | ||
| 102 | else if (subop == 1) | ||
| 103 | nvhost_debug_output(o, "RELEASE_MLOCK(index=%d)\n", | ||
| 104 | val & 0xff); | ||
| 105 | else | ||
| 106 | nvhost_debug_output(o, "EXTEND_UNKNOWN(%08x)\n", val); | ||
| 107 | return NVHOST_DBG_STATE_CMD; | ||
| 108 | |||
| 109 | default: | ||
| 110 | return NVHOST_DBG_STATE_CMD; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | static void show_channel_gather(struct output *o, u32 addr, | ||
| 115 | phys_addr_t phys_addr, u32 words, struct nvhost_cdma *cdma); | ||
| 116 | |||
| 117 | static void show_channel_word(struct output *o, int *state, int *count, | ||
| 118 | u32 addr, u32 val, struct nvhost_cdma *cdma) | ||
| 119 | { | ||
| 120 | static int start_count, dont_print; | ||
| 121 | |||
| 122 | switch (*state) { | ||
| 123 | case NVHOST_DBG_STATE_CMD: | ||
| 124 | if (addr) | ||
| 125 | nvhost_debug_output(o, "%08x: %08x:", addr, val); | ||
| 126 | else | ||
| 127 | nvhost_debug_output(o, "%08x:", val); | ||
| 128 | |||
| 129 | *state = show_channel_command(o, addr, val, count); | ||
| 130 | dont_print = 0; | ||
| 131 | start_count = *count; | ||
| 132 | if (*state == NVHOST_DBG_STATE_DATA && *count == 0) { | ||
| 133 | *state = NVHOST_DBG_STATE_CMD; | ||
| 134 | nvhost_debug_output(o, "])\n"); | ||
| 135 | } | ||
| 136 | break; | ||
| 137 | |||
| 138 | case NVHOST_DBG_STATE_DATA: | ||
| 139 | (*count)--; | ||
| 140 | if (start_count - *count < 64) | ||
| 141 | nvhost_debug_output(o, "%08x%s", | ||
| 142 | val, *count > 0 ? ", " : "])\n"); | ||
| 143 | else if (!dont_print && (*count > 0)) { | ||
| 144 | nvhost_debug_output(o, "[truncated; %d more words]\n", | ||
| 145 | *count); | ||
| 146 | dont_print = 1; | ||
| 147 | } | ||
| 148 | if (*count == 0) | ||
| 149 | *state = NVHOST_DBG_STATE_CMD; | ||
| 150 | break; | ||
| 151 | |||
| 152 | case NVHOST_DBG_STATE_GATHER: | ||
| 153 | *state = NVHOST_DBG_STATE_CMD; | ||
| 154 | nvhost_debug_output(o, "%08x]):\n", val); | ||
| 155 | if (cdma) { | ||
| 156 | show_channel_gather(o, addr, val, | ||
| 157 | *count, cdma); | ||
| 158 | } | ||
| 159 | break; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | static void show_channel_gather(struct output *o, u32 addr, | ||
| 164 | phys_addr_t phys_addr, | ||
| 165 | u32 words, struct nvhost_cdma *cdma) | ||
| 166 | { | ||
| 167 | #if defined(CONFIG_TEGRA_NVMAP) | ||
| 168 | /* Map dmaget cursor to corresponding nvmap_handle */ | ||
| 169 | struct push_buffer *pb = &cdma->push_buffer; | ||
| 170 | u32 cur = addr - pb->phys; | ||
| 171 | struct nvmap_client_handle *nvmap = &pb->nvmap[cur/8]; | ||
| 172 | struct nvmap_handle_ref ref; | ||
| 173 | u32 *map_addr, offset; | ||
| 174 | phys_addr_t pin_addr; | ||
| 175 | int state, count, i; | ||
| 176 | |||
| 177 | if ((u32)nvmap->handle == NVHOST_CDMA_PUSH_GATHER_CTXSAVE) { | ||
| 178 | nvhost_debug_output(o, "[context save]\n"); | ||
| 179 | return; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (!nvmap->handle || !nvmap->client | ||
| 183 | || atomic_read(&nvmap->handle->ref) < 1) { | ||
| 184 | nvhost_debug_output(o, "[already deallocated]\n"); | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Create a fake nvmap_handle_ref - nvmap requires it | ||
| 189 | * but accesses only the first field - nvmap_handle */ | ||
| 190 | ref.handle = nvmap->handle; | ||
| 191 | |||
| 192 | map_addr = nvmap_mmap(&ref); | ||
| 193 | if (!map_addr) { | ||
| 194 | nvhost_debug_output(o, "[could not mmap]\n"); | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* Get base address from nvmap */ | ||
| 199 | pin_addr = nvmap_pin(nvmap->client, &ref); | ||
| 200 | if (IS_ERR_VALUE(pin_addr)) { | ||
| 201 | nvhost_debug_output(o, "[couldn't pin]\n"); | ||
| 202 | nvmap_munmap(&ref, map_addr); | ||
| 203 | return; | ||
| 204 | } | ||
| 205 | |||
| 206 | offset = phys_addr - pin_addr; | ||
| 207 | /* | ||
| 208 | * Sometimes we're given different hardware address to the same | ||
| 209 | * page - in these cases the offset will get an invalid number and | ||
| 210 | * we just have to bail out. | ||
| 211 | */ | ||
| 212 | if (offset > NVHOST_DEBUG_MAX_PAGE_OFFSET) { | ||
| 213 | nvhost_debug_output(o, "[address mismatch]\n"); | ||
| 214 | } else { | ||
| 215 | /* GATHER buffer starts always with commands */ | ||
| 216 | state = NVHOST_DBG_STATE_CMD; | ||
| 217 | for (i = 0; i < words; i++) | ||
| 218 | show_channel_word(o, &state, &count, | ||
| 219 | phys_addr + i * 4, | ||
| 220 | *(map_addr + offset/4 + i), | ||
| 221 | cdma); | ||
| 222 | } | ||
| 223 | nvmap_unpin(nvmap->client, &ref); | ||
| 224 | nvmap_munmap(&ref, map_addr); | ||
| 225 | #endif | ||
| 226 | } | ||
| 227 | |||
| 228 | static void show_channel_pair(struct output *o, u32 addr, | ||
| 229 | u32 w0, u32 w1, struct nvhost_cdma *cdma) | ||
| 230 | { | ||
| 231 | int state = NVHOST_DBG_STATE_CMD; | ||
| 232 | int count; | ||
| 233 | |||
| 234 | show_channel_word(o, &state, &count, addr, w0, cdma); | ||
| 235 | show_channel_word(o, &state, &count, addr+4, w1, cdma); | ||
| 236 | } | ||
| 237 | |||
| 238 | /** | ||
| 239 | * Retrieve the op pair at a slot offset from a DMA address | ||
| 240 | */ | ||
| 241 | static void cdma_peek(struct nvhost_cdma *cdma, | ||
| 242 | u32 dmaget, int slot, u32 *out) | ||
| 243 | { | ||
| 244 | u32 offset = dmaget - cdma->push_buffer.phys; | ||
| 245 | u32 *p = cdma->push_buffer.mapped; | ||
| 246 | |||
| 247 | offset = ((offset + slot * 8) & (PUSH_BUFFER_SIZE - 1)) >> 2; | ||
| 248 | out[0] = p[offset]; | ||
| 249 | out[1] = p[offset + 1]; | ||
| 250 | } | ||
| 251 | |||
| 252 | u32 previous_oppair(struct nvhost_cdma *cdma, u32 cur) | ||
| 253 | { | ||
| 254 | u32 pb = cdma->push_buffer.phys; | ||
| 255 | u32 prev = cur-8; | ||
| 256 | if (prev < pb) | ||
| 257 | prev += PUSH_BUFFER_SIZE; | ||
| 258 | return prev; | ||
| 259 | } | ||
| 260 | |||
| 261 | static void t20_debug_show_channel_cdma(struct nvhost_master *m, | ||
| 262 | struct nvhost_channel *ch, struct output *o, int chid) | ||
| 263 | { | ||
| 264 | struct nvhost_channel *channel = ch; | ||
| 265 | struct nvhost_cdma *cdma = &channel->cdma; | ||
| 266 | u32 dmaput, dmaget, dmactrl; | ||
| 267 | u32 cbstat, cbread; | ||
| 268 | u32 val, base, baseval; | ||
| 269 | u32 pbw[2]; | ||
| 270 | |||
| 271 | dmaput = readl(channel->aperture + HOST1X_CHANNEL_DMAPUT); | ||
| 272 | dmaget = readl(channel->aperture + HOST1X_CHANNEL_DMAGET); | ||
| 273 | dmactrl = readl(channel->aperture + HOST1X_CHANNEL_DMACTRL); | ||
| 274 | cbread = readl(m->sync_aperture + HOST1X_SYNC_CBREAD_x(chid)); | ||
| 275 | cbstat = readl(m->sync_aperture + HOST1X_SYNC_CBSTAT_x(chid)); | ||
| 276 | |||
| 277 | nvhost_debug_output(o, "%d-%s (%d): ", chid, | ||
| 278 | channel->dev->name, | ||
| 279 | channel->dev->refcount); | ||
| 280 | |||
| 281 | if (HOST1X_VAL(CHANNEL_DMACTRL, DMASTOP, dmactrl) | ||
| 282 | || !channel->cdma.push_buffer.mapped) { | ||
| 283 | nvhost_debug_output(o, "inactive\n\n"); | ||
| 284 | return; | ||
| 285 | } | ||
| 286 | |||
| 287 | switch (cbstat) { | ||
| 288 | case 0x00010008: | ||
| 289 | nvhost_debug_output(o, "waiting on syncpt %d val %d\n", | ||
| 290 | cbread >> 24, cbread & 0xffffff); | ||
| 291 | break; | ||
| 292 | |||
| 293 | case 0x00010009: | ||
| 294 | base = (cbread >> 16) & 0xff; | ||
| 295 | val = readl(m->sync_aperture + | ||
| 296 | HOST1X_SYNC_SYNCPT_BASE_x(base)); | ||
| 297 | baseval = HOST1X_VAL(SYNC_SYNCPT_BASE_0, BASE, val); | ||
| 298 | val = cbread & 0xffff; | ||
| 299 | nvhost_debug_output(o, "waiting on syncpt %d val %d " | ||
| 300 | "(base %d = %d; offset = %d)\n", | ||
| 301 | cbread >> 24, baseval + val, | ||
| 302 | base, baseval, val); | ||
| 303 | break; | ||
| 304 | |||
| 305 | default: | ||
| 306 | nvhost_debug_output(o, | ||
| 307 | "active class %02x, offset %04x, val %08x\n", | ||
| 308 | HOST1X_VAL(SYNC_CBSTAT_0, CBCLASS0, cbstat), | ||
| 309 | HOST1X_VAL(SYNC_CBSTAT_0, CBOFFSET0, cbstat), | ||
| 310 | cbread); | ||
| 311 | break; | ||
| 312 | } | ||
| 313 | |||
| 314 | nvhost_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n", | ||
| 315 | dmaput, dmaget, dmactrl); | ||
| 316 | nvhost_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); | ||
| 317 | |||
| 318 | cdma_peek(cdma, dmaget, -1, pbw); | ||
| 319 | show_channel_pair(o, previous_oppair(cdma, dmaget), | ||
| 320 | pbw[0], pbw[1], &channel->cdma); | ||
| 321 | nvhost_debug_output(o, "\n"); | ||
| 322 | } | ||
| 323 | |||
| 324 | void t20_debug_show_channel_fifo(struct nvhost_master *m, | ||
| 325 | struct nvhost_channel *ch, struct output *o, int chid) | ||
| 326 | { | ||
| 327 | u32 val, rd_ptr, wr_ptr, start, end; | ||
| 328 | struct nvhost_channel *channel = ch; | ||
| 329 | int state, count; | ||
| 330 | |||
| 331 | nvhost_debug_output(o, "%d: fifo:\n", chid); | ||
| 332 | |||
| 333 | val = readl(channel->aperture + HOST1X_CHANNEL_FIFOSTAT); | ||
| 334 | nvhost_debug_output(o, "FIFOSTAT %08x\n", val); | ||
| 335 | if (HOST1X_VAL(CHANNEL_FIFOSTAT, CFEMPTY, val)) { | ||
| 336 | nvhost_debug_output(o, "[empty]\n"); | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | |||
| 340 | writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
| 341 | writel(HOST1X_CREATE(SYNC_CFPEEK_CTRL, ENA, 1) | ||
| 342 | | HOST1X_CREATE(SYNC_CFPEEK_CTRL, CHANNR, chid), | ||
| 343 | m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
| 344 | |||
| 345 | val = readl(m->sync_aperture + HOST1X_SYNC_CFPEEK_PTRS); | ||
| 346 | rd_ptr = HOST1X_VAL(SYNC_CFPEEK_PTRS, CF_RD_PTR, val); | ||
| 347 | wr_ptr = HOST1X_VAL(SYNC_CFPEEK_PTRS, CF_WR_PTR, val); | ||
| 348 | |||
| 349 | val = readl(m->sync_aperture + HOST1X_SYNC_CFx_SETUP(chid)); | ||
| 350 | start = HOST1X_VAL(SYNC_CF0_SETUP, BASE, val); | ||
| 351 | end = HOST1X_VAL(SYNC_CF0_SETUP, LIMIT, val); | ||
| 352 | |||
| 353 | state = NVHOST_DBG_STATE_CMD; | ||
| 354 | |||
| 355 | do { | ||
| 356 | writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
| 357 | writel(HOST1X_CREATE(SYNC_CFPEEK_CTRL, ENA, 1) | ||
| 358 | | HOST1X_CREATE(SYNC_CFPEEK_CTRL, CHANNR, chid) | ||
| 359 | | HOST1X_CREATE(SYNC_CFPEEK_CTRL, ADDR, rd_ptr), | ||
| 360 | m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
| 361 | val = readl(m->sync_aperture + HOST1X_SYNC_CFPEEK_READ); | ||
| 362 | |||
| 363 | show_channel_word(o, &state, &count, 0, val, NULL); | ||
| 364 | |||
| 365 | if (rd_ptr == end) | ||
| 366 | rd_ptr = start; | ||
| 367 | else | ||
| 368 | rd_ptr++; | ||
| 369 | } while (rd_ptr != wr_ptr); | ||
| 370 | |||
| 371 | if (state == NVHOST_DBG_STATE_DATA) | ||
| 372 | nvhost_debug_output(o, ", ...])\n"); | ||
| 373 | nvhost_debug_output(o, "\n"); | ||
| 374 | |||
| 375 | writel(0x0, m->sync_aperture + HOST1X_SYNC_CFPEEK_CTRL); | ||
| 376 | } | ||
| 377 | |||
| 378 | static void t20_debug_show_mlocks(struct nvhost_master *m, struct output *o) | ||
| 379 | { | ||
| 380 | u32 __iomem *mlo_regs = m->sync_aperture + HOST1X_SYNC_MLOCK_OWNER_0; | ||
| 381 | int i; | ||
| 382 | |||
| 383 | nvhost_debug_output(o, "---- mlocks ----\n"); | ||
| 384 | for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) { | ||
| 385 | u32 owner = readl(mlo_regs + i); | ||
| 386 | if (HOST1X_VAL(SYNC_MLOCK_OWNER_0, CH_OWNS, owner)) | ||
| 387 | nvhost_debug_output(o, "%d: locked by channel %d\n", | ||
| 388 | i, HOST1X_VAL(SYNC_MLOCK_OWNER_0, CHID, owner)); | ||
| 389 | else if (HOST1X_VAL(SYNC_MLOCK_OWNER_0, CPU_OWNS, owner)) | ||
| 390 | nvhost_debug_output(o, "%d: locked by cpu\n", i); | ||
| 391 | else | ||
| 392 | nvhost_debug_output(o, "%d: unlocked\n", i); | ||
| 393 | } | ||
| 394 | nvhost_debug_output(o, "\n"); | ||
| 395 | } | ||
| 396 | |||
| 397 | int nvhost_init_t20_debug_support(struct nvhost_master *host) | ||
| 398 | { | ||
| 399 | host->op.debug.show_channel_cdma = t20_debug_show_channel_cdma; | ||
| 400 | host->op.debug.show_channel_fifo = t20_debug_show_channel_fifo; | ||
| 401 | host->op.debug.show_mlocks = t20_debug_show_mlocks; | ||
| 402 | |||
| 403 | return 0; | ||
| 404 | } | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_hardware.h b/drivers/video/tegra/host/host1x/host1x_hardware.h new file mode 100644 index 00000000000..d13d5752364 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_hardware.h | |||
| @@ -0,0 +1,274 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_hardware.h | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Register Offsets | ||
| 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 | #ifndef __NVHOST_HOST1X_HOST1X_HARDWARE_H | ||
| 22 | #define __NVHOST_HOST1X_HOST1X_HARDWARE_H | ||
| 23 | |||
| 24 | #include <linux/types.h> | ||
| 25 | #include <linux/bitops.h> | ||
| 26 | |||
| 27 | /* class ids */ | ||
| 28 | enum { | ||
| 29 | NV_HOST1X_CLASS_ID = 0x1, | ||
| 30 | NV_VIDEO_ENCODE_MPEG_CLASS_ID = 0x20, | ||
| 31 | NV_GRAPHICS_3D_CLASS_ID = 0x60 | ||
| 32 | }; | ||
| 33 | |||
| 34 | |||
| 35 | /* channel registers */ | ||
| 36 | #define NV_HOST1X_CHANNELS 8 | ||
| 37 | #define NV_HOST1X_CHANNEL0_BASE 0 | ||
| 38 | #define NV_HOST1X_CHANNEL_MAP_SIZE_BYTES 16384 | ||
| 39 | #define NV_HOST1X_SYNC_MLOCK_NUM 16 | ||
| 40 | |||
| 41 | #define HOST1X_VAL(reg, field, regdata) \ | ||
| 42 | ((regdata >> HOST1X_##reg##_##field##_SHIFT) \ | ||
| 43 | & HOST1X_##reg##_##field##_MASK) | ||
| 44 | #define HOST1X_CREATE(reg, field, data) \ | ||
| 45 | ((data & HOST1X_##reg##_##field##_MASK) \ | ||
| 46 | << HOST1X_##reg##_##field##_SHIFT) \ | ||
| 47 | |||
| 48 | #define HOST1X_CHANNEL_FIFOSTAT 0x00 | ||
| 49 | #define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_SHIFT 10 | ||
| 50 | #define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_MASK 0x1 | ||
| 51 | #define HOST1X_CHANNEL_FIFOSTAT_OUTFENTRIES_SHIFT 24 | ||
| 52 | #define HOST1X_CHANNEL_FIFOSTAT_OUTFENTRIES_MASK 0x1f | ||
| 53 | #define HOST1X_CHANNEL_INDDATA 0x0c | ||
| 54 | #define HOST1X_CHANNEL_DMASTART 0x14 | ||
| 55 | #define HOST1X_CHANNEL_DMAPUT 0x18 | ||
| 56 | #define HOST1X_CHANNEL_DMAGET 0x1c | ||
| 57 | #define HOST1X_CHANNEL_DMAEND 0x20 | ||
| 58 | #define HOST1X_CHANNEL_DMACTRL 0x24 | ||
| 59 | #define HOST1X_CHANNEL_DMACTRL_DMASTOP_SHIFT 0 | ||
| 60 | #define HOST1X_CHANNEL_DMACTRL_DMASTOP_MASK 0x1 | ||
| 61 | #define HOST1X_CHANNEL_DMACTRL_DMAGETRST_SHIFT 1 | ||
| 62 | #define HOST1X_CHANNEL_DMACTRL_DMAGETRST_MASK 0x1 | ||
| 63 | #define HOST1X_CHANNEL_DMACTRL_DMAINITGET_SHIFT 2 | ||
| 64 | #define HOST1X_CHANNEL_DMACTRL_DMAINITGET_MASK 0x1 | ||
| 65 | |||
| 66 | #define HOST1X_CHANNEL_SYNC_REG_BASE 0x3000 | ||
| 67 | |||
| 68 | #define HOST1X_SYNC_INTMASK 0x4 | ||
| 69 | #define HOST1X_SYNC_INTC0MASK 0x8 | ||
| 70 | #define HOST1X_SYNC_HINTSTATUS 0x20 | ||
| 71 | #define HOST1X_SYNC_HINTMASK 0x24 | ||
| 72 | #define HOST1X_SYNC_HINTSTATUS_EXT 0x28 | ||
| 73 | #define HOST1X_SYNC_HINTSTATUS_EXT_IP_READ_INT_SHIFT 30 | ||
| 74 | #define HOST1X_SYNC_HINTSTATUS_EXT_IP_READ_INT_MASK 0x1 | ||
| 75 | #define HOST1X_SYNC_HINTSTATUS_EXT_IP_WRITE_INT_SHIFT 31 | ||
| 76 | #define HOST1X_SYNC_HINTSTATUS_EXT_IP_WRITE_INT_MASK 0x1 | ||
| 77 | #define HOST1X_SYNC_HINTMASK_EXT 0x2c | ||
| 78 | #define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS 0x40 | ||
| 79 | #define HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS 0x48 | ||
| 80 | #define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE 0x60 | ||
| 81 | #define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0 0x68 | ||
| 82 | #define HOST1X_SYNC_CF0_SETUP 0x80 | ||
| 83 | #define HOST1X_SYNC_CF0_SETUP_BASE_SHIFT 0 | ||
| 84 | #define HOST1X_SYNC_CF0_SETUP_BASE_MASK 0x1ff | ||
| 85 | #define HOST1X_SYNC_CF0_SETUP_LIMIT_SHIFT 16 | ||
| 86 | #define HOST1X_SYNC_CF0_SETUP_LIMIT_MASK 0x1ff | ||
| 87 | #define HOST1X_SYNC_CFx_SETUP(x) (HOST1X_SYNC_CF0_SETUP + (4 * (x))) | ||
| 88 | |||
| 89 | #define HOST1X_SYNC_CMDPROC_STOP 0xac | ||
| 90 | #define HOST1X_SYNC_CH_TEARDOWN 0xb0 | ||
| 91 | #define HOST1X_SYNC_USEC_CLK 0x1a4 | ||
| 92 | #define HOST1X_SYNC_CTXSW_TIMEOUT_CFG 0x1a8 | ||
| 93 | #define HOST1X_SYNC_IP_BUSY_TIMEOUT 0x1bc | ||
| 94 | #define HOST1X_SYNC_IP_READ_TIMEOUT_ADDR 0x1c0 | ||
| 95 | #define HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR 0x1c4 | ||
| 96 | #define HOST1X_SYNC_MLOCK_0 0x2c0 | ||
| 97 | #define HOST1X_SYNC_MLOCK_OWNER_0 0x340 | ||
| 98 | #define HOST1X_SYNC_MLOCK_OWNER_0_CHID_SHIFT 8 | ||
| 99 | #define HOST1X_SYNC_MLOCK_OWNER_0_CHID_MASK 0xf | ||
| 100 | #define HOST1X_SYNC_MLOCK_OWNER_0_CPU_OWNS_SHIFT 1 | ||
| 101 | #define HOST1X_SYNC_MLOCK_OWNER_0_CPU_OWNS_MASK 0x1 | ||
| 102 | #define HOST1X_SYNC_MLOCK_OWNER_0_CH_OWNS_SHIFT 0 | ||
| 103 | #define HOST1X_SYNC_MLOCK_OWNER_0_CH_OWNS_MASK 0x1 | ||
| 104 | #define HOST1X_SYNC_SYNCPT_0 0x400 | ||
| 105 | #define HOST1X_SYNC_SYNCPT_INT_THRESH_0 0x500 | ||
| 106 | |||
| 107 | #define HOST1X_SYNC_SYNCPT_BASE_0 0x600 | ||
| 108 | #define HOST1X_SYNC_SYNCPT_BASE_0_BASE_SHIFT 0 | ||
| 109 | #define HOST1X_SYNC_SYNCPT_BASE_0_BASE_MASK 0xffff | ||
| 110 | #define HOST1X_SYNC_SYNCPT_BASE_x(x) (HOST1X_SYNC_SYNCPT_BASE_0 + (4 * (x))) | ||
| 111 | |||
| 112 | #define HOST1X_SYNC_SYNCPT_CPU_INCR 0x700 | ||
| 113 | |||
| 114 | #define HOST1X_SYNC_CBREAD_0 0x720 | ||
| 115 | #define HOST1X_SYNC_CBREAD_x(x) (HOST1X_SYNC_CBREAD_0 + (4 * (x))) | ||
| 116 | #define HOST1X_SYNC_CFPEEK_CTRL 0x74c | ||
| 117 | #define HOST1X_SYNC_CFPEEK_CTRL_ADDR_SHIFT 0 | ||
| 118 | #define HOST1X_SYNC_CFPEEK_CTRL_ADDR_MASK 0x1ff | ||
| 119 | #define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_SHIFT 16 | ||
| 120 | #define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_MASK 0x7 | ||
| 121 | #define HOST1X_SYNC_CFPEEK_CTRL_ENA_SHIFT 31 | ||
| 122 | #define HOST1X_SYNC_CFPEEK_CTRL_ENA_MASK 0x1 | ||
| 123 | #define HOST1X_SYNC_CFPEEK_READ 0x750 | ||
| 124 | #define HOST1X_SYNC_CFPEEK_PTRS 0x754 | ||
| 125 | #define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_SHIFT 0 | ||
| 126 | #define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_MASK 0x1ff | ||
| 127 | #define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_SHIFT 16 | ||
| 128 | #define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_MASK 0x1ff | ||
| 129 | #define HOST1X_SYNC_CBSTAT_0 0x758 | ||
| 130 | #define HOST1X_SYNC_CBSTAT_0_CBOFFSET0_SHIFT 0 | ||
| 131 | #define HOST1X_SYNC_CBSTAT_0_CBOFFSET0_MASK 0xffff | ||
| 132 | #define HOST1X_SYNC_CBSTAT_0_CBCLASS0_SHIFT 16 | ||
| 133 | #define HOST1X_SYNC_CBSTAT_0_CBCLASS0_MASK 0xffff | ||
| 134 | #define HOST1X_SYNC_CBSTAT_x(x) (HOST1X_SYNC_CBSTAT_0 + (4 * (x))) | ||
| 135 | |||
| 136 | /* sync registers */ | ||
| 137 | #define NV_HOST1X_SYNCPT_NB_PTS 32 | ||
| 138 | #define NV_HOST1X_SYNCPT_NB_BASES 8 | ||
| 139 | #define NV_HOST1X_NB_MLOCKS 16 | ||
| 140 | |||
| 141 | /* host class methods */ | ||
| 142 | enum { | ||
| 143 | NV_CLASS_HOST_INCR_SYNCPT = 0x0, | ||
| 144 | NV_CLASS_HOST_WAIT_SYNCPT = 0x8, | ||
| 145 | NV_CLASS_HOST_WAIT_SYNCPT_BASE = 0x9, | ||
| 146 | NV_CLASS_HOST_LOAD_SYNCPT_BASE = 0xb, | ||
| 147 | NV_CLASS_HOST_INCR_SYNCPT_BASE = 0xc, | ||
| 148 | NV_CLASS_HOST_INDOFF = 0x2d, | ||
| 149 | NV_CLASS_HOST_INDDATA = 0x2e | ||
| 150 | }; | ||
| 151 | /* sync point conditionals */ | ||
| 152 | enum { | ||
| 153 | NV_SYNCPT_IMMEDIATE = 0x0, | ||
| 154 | NV_SYNCPT_OP_DONE = 0x1, | ||
| 155 | NV_SYNCPT_RD_DONE = 0x2, | ||
| 156 | NV_SYNCPT_REG_WR_SAFE = 0x3, | ||
| 157 | }; | ||
| 158 | |||
| 159 | static inline u32 nvhost_class_host_wait_syncpt( | ||
| 160 | unsigned indx, unsigned threshold) | ||
| 161 | { | ||
| 162 | return (indx << 24) | (threshold & 0xffffff); | ||
| 163 | } | ||
| 164 | |||
| 165 | static inline u32 nvhost_class_host_load_syncpt_base( | ||
| 166 | unsigned indx, unsigned threshold) | ||
| 167 | { | ||
| 168 | return (indx << 24) | (threshold & 0xffffff); | ||
| 169 | } | ||
| 170 | |||
| 171 | static inline u32 nvhost_class_host_wait_syncpt_base( | ||
| 172 | unsigned indx, unsigned base_indx, unsigned offset) | ||
| 173 | { | ||
| 174 | return (indx << 24) | (base_indx << 16) | offset; | ||
| 175 | } | ||
| 176 | |||
| 177 | static inline u32 nvhost_class_host_incr_syncpt_base( | ||
| 178 | unsigned base_indx, unsigned offset) | ||
| 179 | { | ||
| 180 | return (base_indx << 24) | offset; | ||
| 181 | } | ||
| 182 | |||
| 183 | static inline u32 nvhost_class_host_incr_syncpt( | ||
| 184 | unsigned cond, unsigned indx) | ||
| 185 | { | ||
| 186 | return (cond << 8) | indx; | ||
| 187 | } | ||
| 188 | |||
| 189 | enum { | ||
| 190 | NV_HOST_MODULE_HOST1X = 0, | ||
| 191 | NV_HOST_MODULE_MPE = 1, | ||
| 192 | NV_HOST_MODULE_GR3D = 6 | ||
| 193 | }; | ||
| 194 | |||
| 195 | static inline u32 nvhost_class_host_indoff_reg_write( | ||
| 196 | unsigned mod_id, unsigned offset, bool auto_inc) | ||
| 197 | { | ||
| 198 | u32 v = (0xf << 28) | (mod_id << 18) | (offset << 2); | ||
| 199 | if (auto_inc) | ||
| 200 | v |= BIT(27); | ||
| 201 | return v; | ||
| 202 | } | ||
| 203 | |||
| 204 | static inline u32 nvhost_class_host_indoff_reg_read( | ||
| 205 | unsigned mod_id, unsigned offset, bool auto_inc) | ||
| 206 | { | ||
| 207 | u32 v = (mod_id << 18) | (offset << 2) | 1; | ||
| 208 | if (auto_inc) | ||
| 209 | v |= BIT(27); | ||
| 210 | return v; | ||
| 211 | } | ||
| 212 | |||
| 213 | |||
| 214 | /* cdma opcodes */ | ||
| 215 | static inline u32 nvhost_opcode_setclass( | ||
| 216 | unsigned class_id, unsigned offset, unsigned mask) | ||
| 217 | { | ||
| 218 | return (0 << 28) | (offset << 16) | (class_id << 6) | mask; | ||
| 219 | } | ||
| 220 | |||
| 221 | static inline u32 nvhost_opcode_incr(unsigned offset, unsigned count) | ||
| 222 | { | ||
| 223 | return (1 << 28) | (offset << 16) | count; | ||
| 224 | } | ||
| 225 | |||
| 226 | static inline u32 nvhost_opcode_nonincr(unsigned offset, unsigned count) | ||
| 227 | { | ||
| 228 | return (2 << 28) | (offset << 16) | count; | ||
| 229 | } | ||
| 230 | |||
| 231 | static inline u32 nvhost_opcode_mask(unsigned offset, unsigned mask) | ||
| 232 | { | ||
| 233 | return (3 << 28) | (offset << 16) | mask; | ||
| 234 | } | ||
| 235 | |||
| 236 | static inline u32 nvhost_opcode_imm(unsigned offset, unsigned value) | ||
| 237 | { | ||
| 238 | return (4 << 28) | (offset << 16) | value; | ||
| 239 | } | ||
| 240 | |||
| 241 | static inline u32 nvhost_opcode_imm_incr_syncpt(unsigned cond, unsigned indx) | ||
| 242 | { | ||
| 243 | return nvhost_opcode_imm(NV_CLASS_HOST_INCR_SYNCPT, | ||
| 244 | nvhost_class_host_incr_syncpt(cond, indx)); | ||
| 245 | } | ||
| 246 | |||
| 247 | static inline u32 nvhost_opcode_restart(unsigned address) | ||
| 248 | { | ||
| 249 | return (5 << 28) | (address >> 4); | ||
| 250 | } | ||
| 251 | |||
| 252 | static inline u32 nvhost_opcode_gather(unsigned count) | ||
| 253 | { | ||
| 254 | return (6 << 28) | count; | ||
| 255 | } | ||
| 256 | |||
| 257 | static inline u32 nvhost_opcode_gather_nonincr(unsigned offset, unsigned count) | ||
| 258 | { | ||
| 259 | return (6 << 28) | (offset << 16) | BIT(15) | count; | ||
| 260 | } | ||
| 261 | |||
| 262 | static inline u32 nvhost_opcode_gather_incr(unsigned offset, unsigned count) | ||
| 263 | { | ||
| 264 | return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; | ||
| 265 | } | ||
| 266 | |||
| 267 | #define NVHOST_OPCODE_NOOP nvhost_opcode_nonincr(0, 0) | ||
| 268 | |||
| 269 | static inline u32 nvhost_mask2(unsigned x, unsigned y) | ||
| 270 | { | ||
| 271 | return 1 | (1 << (y - x)); | ||
| 272 | } | ||
| 273 | |||
| 274 | #endif | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_hwctx.h b/drivers/video/tegra/host/host1x/host1x_hwctx.h new file mode 100644 index 00000000000..7587642d0e1 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_hwctx.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_hwctx.h | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host HOST1X Hardware Context Interface | ||
| 5 | * | ||
| 6 | * Copyright (c) 2012, NVIDIA Corporation. | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 16 | * more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License along | ||
| 19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
| 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #ifndef __NVHOST_HOST1X_HWCTX_H | ||
| 24 | #define __NVHOST_HOST1X_HWCTX_H | ||
| 25 | |||
| 26 | #include <linux/kref.h> | ||
| 27 | |||
| 28 | struct nvhost_hwctx_handler; | ||
| 29 | struct nvhost_channel; | ||
| 30 | |||
| 31 | #define to_host1x_hwctx_handler(handler) \ | ||
| 32 | container_of((handler), struct host1x_hwctx_handler, h) | ||
| 33 | #define to_host1x_hwctx(h) container_of((h), struct host1x_hwctx, hwctx) | ||
| 34 | #define host1x_hwctx_handler(_hwctx) to_host1x_hwctx_handler((_hwctx)->hwctx.h) | ||
| 35 | |||
| 36 | struct host1x_hwctx { | ||
| 37 | struct nvhost_hwctx hwctx; | ||
| 38 | |||
| 39 | u32 save_incrs; | ||
| 40 | u32 save_thresh; | ||
| 41 | u32 save_slots; | ||
| 42 | |||
| 43 | struct nvmap_handle_ref *restore; | ||
| 44 | u32 *restore_virt; | ||
| 45 | phys_addr_t restore_phys; | ||
| 46 | u32 restore_size; | ||
| 47 | u32 restore_incrs; | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct host1x_hwctx_handler { | ||
| 51 | struct nvhost_hwctx_handler h; | ||
| 52 | |||
| 53 | u32 syncpt; | ||
| 54 | u32 waitbase; | ||
| 55 | u32 restore_size; | ||
| 56 | u32 restore_incrs; | ||
| 57 | struct nvmap_handle_ref *save_buf; | ||
| 58 | u32 save_incrs; | ||
| 59 | u32 save_thresh; | ||
| 60 | u32 save_slots; | ||
| 61 | phys_addr_t save_phys; | ||
| 62 | u32 save_size; | ||
| 63 | }; | ||
| 64 | |||
| 65 | #endif | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_intr.c b/drivers/video/tegra/host/host1x/host1x_intr.c new file mode 100644 index 00000000000..47e984e2943 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_intr.c | |||
| @@ -0,0 +1,218 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_intr.c | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Interrupt Management | ||
| 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/interrupt.h> | ||
| 22 | #include <linux/irq.h> | ||
| 23 | |||
| 24 | #include "nvhost_intr.h" | ||
| 25 | #include "dev.h" | ||
| 26 | #include "host1x_hardware.h" | ||
| 27 | |||
| 28 | |||
| 29 | /*** HW host sync management ***/ | ||
| 30 | |||
| 31 | static void t20_intr_init_host_sync(struct nvhost_intr *intr) | ||
| 32 | { | ||
| 33 | struct nvhost_master *dev = intr_to_dev(intr); | ||
| 34 | void __iomem *sync_regs = dev->sync_aperture; | ||
| 35 | /* disable the ip_busy_timeout. this prevents write drops, etc. | ||
| 36 | * there's no real way to recover from a hung client anyway. | ||
| 37 | */ | ||
| 38 | writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT); | ||
| 39 | |||
| 40 | /* increase the auto-ack timout to the maximum value. 2d will hang | ||
| 41 | * otherwise on ap20. | ||
| 42 | */ | ||
| 43 | writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG); | ||
| 44 | } | ||
| 45 | |||
| 46 | static void t20_intr_set_host_clocks_per_usec(struct nvhost_intr *intr, u32 cpm) | ||
| 47 | { | ||
| 48 | struct nvhost_master *dev = intr_to_dev(intr); | ||
| 49 | void __iomem *sync_regs = dev->sync_aperture; | ||
| 50 | /* write microsecond clock register */ | ||
| 51 | writel(cpm, sync_regs + HOST1X_SYNC_USEC_CLK); | ||
| 52 | } | ||
| 53 | |||
| 54 | static void t20_intr_set_syncpt_threshold(struct nvhost_intr *intr, | ||
| 55 | u32 id, u32 thresh) | ||
| 56 | { | ||
| 57 | struct nvhost_master *dev = intr_to_dev(intr); | ||
| 58 | void __iomem *sync_regs = dev->sync_aperture; | ||
| 59 | thresh &= 0xffff; | ||
| 60 | writel(thresh, sync_regs + (HOST1X_SYNC_SYNCPT_INT_THRESH_0 + id * 4)); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void t20_intr_enable_syncpt_intr(struct nvhost_intr *intr, u32 id) | ||
| 64 | { | ||
| 65 | struct nvhost_master *dev = intr_to_dev(intr); | ||
| 66 | void __iomem *sync_regs = dev->sync_aperture; | ||
| 67 | writel(BIT(id), sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0); | ||
| 68 | } | ||
| 69 | |||
| 70 | static void t20_intr_disable_all_syncpt_intrs(struct nvhost_intr *intr) | ||
| 71 | { | ||
| 72 | struct nvhost_master *dev = intr_to_dev(intr); | ||
| 73 | void __iomem *sync_regs = dev->sync_aperture; | ||
| 74 | /* disable interrupts for both cpu's */ | ||
| 75 | writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); | ||
| 76 | |||
| 77 | /* clear status for both cpu's */ | ||
| 78 | writel(0xffffffffu, sync_regs + | ||
| 79 | HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); | ||
| 80 | writel(0xffffffffu, sync_regs + | ||
| 81 | HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS); | ||
| 82 | } | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Sync point threshold interrupt service function | ||
| 86 | * Handles sync point threshold triggers, in interrupt context | ||
| 87 | */ | ||
| 88 | irqreturn_t t20_intr_syncpt_thresh_isr(int irq, void *dev_id) | ||
| 89 | { | ||
| 90 | struct nvhost_intr_syncpt *syncpt = dev_id; | ||
| 91 | unsigned int id = syncpt->id; | ||
| 92 | struct nvhost_intr *intr = intr_syncpt_to_intr(syncpt); | ||
| 93 | |||
| 94 | void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; | ||
| 95 | |||
| 96 | writel(BIT(id), | ||
| 97 | sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE); | ||
| 98 | writel(BIT(id), | ||
| 99 | sync_regs + HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS); | ||
| 100 | |||
| 101 | return IRQ_WAKE_THREAD; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Host general interrupt service function | ||
| 106 | * Handles read / write failures | ||
| 107 | */ | ||
| 108 | static irqreturn_t t20_intr_host1x_isr(int irq, void *dev_id) | ||
| 109 | { | ||
| 110 | struct nvhost_intr *intr = dev_id; | ||
| 111 | void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; | ||
| 112 | u32 stat; | ||
| 113 | u32 ext_stat; | ||
| 114 | u32 addr; | ||
| 115 | |||
| 116 | stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS); | ||
| 117 | ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); | ||
| 118 | |||
| 119 | if (HOST1X_VAL(SYNC_HINTSTATUS_EXT, IP_READ_INT, ext_stat)) { | ||
| 120 | addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR); | ||
| 121 | pr_err("Host read timeout at address %x\n", addr); | ||
| 122 | } | ||
| 123 | |||
| 124 | if (HOST1X_VAL(SYNC_HINTSTATUS_EXT, IP_WRITE_INT, ext_stat)) { | ||
| 125 | addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR); | ||
| 126 | pr_err("Host write timeout at address %x\n", addr); | ||
| 127 | } | ||
| 128 | |||
| 129 | writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); | ||
| 130 | writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS); | ||
| 131 | |||
| 132 | return IRQ_HANDLED; | ||
| 133 | } | ||
| 134 | static int t20_intr_request_host_general_irq(struct nvhost_intr *intr) | ||
| 135 | { | ||
| 136 | void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; | ||
| 137 | int err; | ||
| 138 | |||
| 139 | if (intr->host_general_irq_requested) | ||
| 140 | return 0; | ||
| 141 | |||
| 142 | /* master disable for general (not syncpt) host interrupts */ | ||
| 143 | writel(0, sync_regs + HOST1X_SYNC_INTMASK); | ||
| 144 | |||
| 145 | /* clear status & extstatus */ | ||
| 146 | writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT); | ||
| 147 | writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS); | ||
| 148 | |||
| 149 | err = request_irq(intr->host_general_irq, t20_intr_host1x_isr, 0, | ||
| 150 | "host_status", intr); | ||
| 151 | if (err) | ||
| 152 | return err; | ||
| 153 | |||
| 154 | /* enable extra interrupt sources IP_READ_INT and IP_WRITE_INT */ | ||
| 155 | writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT); | ||
| 156 | |||
| 157 | /* enable extra interrupt sources */ | ||
| 158 | writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK); | ||
| 159 | |||
| 160 | /* enable host module interrupt to CPU0 */ | ||
| 161 | writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK); | ||
| 162 | |||
| 163 | /* master enable for general (not syncpt) host interrupts */ | ||
| 164 | writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK); | ||
| 165 | |||
| 166 | intr->host_general_irq_requested = true; | ||
| 167 | |||
| 168 | return err; | ||
| 169 | } | ||
| 170 | |||
| 171 | static void t20_intr_free_host_general_irq(struct nvhost_intr *intr) | ||
| 172 | { | ||
| 173 | if (intr->host_general_irq_requested) { | ||
| 174 | void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; | ||
| 175 | |||
| 176 | /* master disable for general (not syncpt) host interrupts */ | ||
| 177 | writel(0, sync_regs + HOST1X_SYNC_INTMASK); | ||
| 178 | |||
| 179 | free_irq(intr->host_general_irq, intr); | ||
| 180 | intr->host_general_irq_requested = false; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | static int t20_request_syncpt_irq(struct nvhost_intr_syncpt *syncpt) | ||
| 185 | { | ||
| 186 | int err; | ||
| 187 | if (syncpt->irq_requested) | ||
| 188 | return 0; | ||
| 189 | |||
| 190 | err = request_threaded_irq(syncpt->irq, | ||
| 191 | t20_intr_syncpt_thresh_isr, | ||
| 192 | nvhost_syncpt_thresh_fn, | ||
| 193 | 0, syncpt->thresh_irq_name, syncpt); | ||
| 194 | if (err) | ||
| 195 | return err; | ||
| 196 | |||
| 197 | syncpt->irq_requested = 1; | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | int nvhost_init_t20_intr_support(struct nvhost_master *host) | ||
| 202 | { | ||
| 203 | host->op.intr.init_host_sync = t20_intr_init_host_sync; | ||
| 204 | host->op.intr.set_host_clocks_per_usec = | ||
| 205 | t20_intr_set_host_clocks_per_usec; | ||
| 206 | host->op.intr.set_syncpt_threshold = t20_intr_set_syncpt_threshold; | ||
| 207 | host->op.intr.enable_syncpt_intr = t20_intr_enable_syncpt_intr; | ||
| 208 | host->op.intr.disable_all_syncpt_intrs = | ||
| 209 | t20_intr_disable_all_syncpt_intrs; | ||
| 210 | host->op.intr.request_host_general_irq = | ||
| 211 | t20_intr_request_host_general_irq; | ||
| 212 | host->op.intr.free_host_general_irq = | ||
| 213 | t20_intr_free_host_general_irq; | ||
| 214 | host->op.intr.request_syncpt_irq = | ||
| 215 | t20_request_syncpt_irq; | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c new file mode 100644 index 00000000000..b0fd9970aaa --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_syncpt.c | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Syncpoints for HOST1X | ||
| 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/nvhost_ioctl.h> | ||
| 22 | #include "nvhost_syncpt.h" | ||
| 23 | #include "dev.h" | ||
| 24 | #include "host1x_syncpt.h" | ||
| 25 | #include "host1x_hardware.h" | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Write the current syncpoint value back to hw. | ||
| 29 | */ | ||
| 30 | static void t20_syncpt_reset(struct nvhost_syncpt *sp, u32 id) | ||
| 31 | { | ||
| 32 | struct nvhost_master *dev = syncpt_to_dev(sp); | ||
| 33 | int min = nvhost_syncpt_read_min(sp, id); | ||
| 34 | writel(min, dev->sync_aperture + (HOST1X_SYNC_SYNCPT_0 + id * 4)); | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Write the current waitbase value back to hw. | ||
| 39 | */ | ||
| 40 | static void t20_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id) | ||
| 41 | { | ||
| 42 | struct nvhost_master *dev = syncpt_to_dev(sp); | ||
| 43 | writel(sp->base_val[id], | ||
| 44 | dev->sync_aperture + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); | ||
| 45 | } | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Read waitbase value from hw. | ||
| 49 | */ | ||
| 50 | static void t20_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) | ||
| 51 | { | ||
| 52 | struct nvhost_master *dev = syncpt_to_dev(sp); | ||
| 53 | sp->base_val[id] = readl(dev->sync_aperture + | ||
| 54 | (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4)); | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Updates the last value read from hardware. | ||
| 59 | * (was nvhost_syncpt_update_min) | ||
| 60 | */ | ||
| 61 | static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) | ||
| 62 | { | ||
| 63 | struct nvhost_master *dev = syncpt_to_dev(sp); | ||
| 64 | void __iomem *sync_regs = dev->sync_aperture; | ||
| 65 | u32 old, live; | ||
| 66 | |||
| 67 | do { | ||
| 68 | old = nvhost_syncpt_read_min(sp, id); | ||
| 69 | live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4)); | ||
| 70 | } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); | ||
| 71 | |||
| 72 | if (!nvhost_syncpt_check_max(sp, id, live)) | ||
| 73 | dev_err(&syncpt_to_dev(sp)->dev->dev, | ||
| 74 | "%s failed: id=%u\n", | ||
| 75 | __func__, | ||
| 76 | id); | ||
| 77 | |||
| 78 | return live; | ||
| 79 | } | ||
| 80 | |||
| 81 | /** | ||
| 82 | * Write a cpu syncpoint increment to the hardware, without touching | ||
| 83 | * the cache. Caller is responsible for host being powered. | ||
| 84 | */ | ||
| 85 | static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) | ||
| 86 | { | ||
| 87 | struct nvhost_master *dev = syncpt_to_dev(sp); | ||
| 88 | BUG_ON(!nvhost_module_powered(dev->dev)); | ||
| 89 | if (!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)) { | ||
| 90 | dev_err(&syncpt_to_dev(sp)->dev->dev, | ||
| 91 | "Trying to increment syncpoint id %d beyond max\n", | ||
| 92 | id); | ||
| 93 | nvhost_debug_dump(syncpt_to_dev(sp)); | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR); | ||
| 97 | wmb(); | ||
| 98 | } | ||
| 99 | |||
| 100 | /* check for old WAITs to be removed (avoiding a wrap) */ | ||
| 101 | static int t20_syncpt_wait_check(struct nvhost_syncpt *sp, | ||
| 102 | struct nvmap_client *nvmap, | ||
| 103 | u32 waitchk_mask, | ||
| 104 | struct nvhost_waitchk *wait, | ||
| 105 | int num_waitchk) | ||
| 106 | { | ||
| 107 | u32 idx; | ||
| 108 | int err = 0; | ||
| 109 | |||
| 110 | /* get current syncpt values */ | ||
| 111 | for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) { | ||
| 112 | if (BIT(idx) & waitchk_mask) | ||
| 113 | nvhost_syncpt_update_min(sp, idx); | ||
| 114 | } | ||
| 115 | |||
| 116 | BUG_ON(!wait && !num_waitchk); | ||
| 117 | |||
| 118 | /* compare syncpt vs wait threshold */ | ||
| 119 | while (num_waitchk) { | ||
| 120 | u32 override; | ||
| 121 | |||
| 122 | BUG_ON(wait->syncpt_id >= NV_HOST1X_SYNCPT_NB_PTS); | ||
| 123 | if (nvhost_syncpt_is_expired(sp, | ||
| 124 | wait->syncpt_id, wait->thresh)) { | ||
| 125 | /* | ||
| 126 | * NULL an already satisfied WAIT_SYNCPT host method, | ||
| 127 | * by patching its args in the command stream. The | ||
| 128 | * method data is changed to reference a reserved | ||
| 129 | * (never given out or incr) NVSYNCPT_GRAPHICS_HOST | ||
| 130 | * syncpt with a matching threshold value of 0, so | ||
| 131 | * is guaranteed to be popped by the host HW. | ||
| 132 | */ | ||
| 133 | dev_dbg(&syncpt_to_dev(sp)->dev->dev, | ||
| 134 | "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", | ||
| 135 | wait->syncpt_id, | ||
| 136 | syncpt_op(sp).name(sp, wait->syncpt_id), | ||
| 137 | wait->thresh, | ||
| 138 | nvhost_syncpt_read_min(sp, wait->syncpt_id)); | ||
| 139 | |||
| 140 | /* patch the wait */ | ||
| 141 | override = nvhost_class_host_wait_syncpt( | ||
| 142 | NVSYNCPT_GRAPHICS_HOST, 0); | ||
| 143 | err = nvmap_patch_word(nvmap, | ||
| 144 | (struct nvmap_handle *)wait->mem, | ||
| 145 | wait->offset, override); | ||
| 146 | if (err) | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | |||
| 150 | wait++; | ||
| 151 | num_waitchk--; | ||
| 152 | } | ||
| 153 | return err; | ||
| 154 | } | ||
| 155 | |||
| 156 | |||
| 157 | static const char *s_syncpt_names[32] = { | ||
| 158 | "gfx_host", | ||
| 159 | "", "", "", "", "", "", "", | ||
| 160 | "disp0_a", "disp1_a", "avp_0", | ||
| 161 | "csi_vi_0", "csi_vi_1", | ||
| 162 | "vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4", | ||
| 163 | "2d_0", "2d_1", | ||
| 164 | "disp0_b", "disp1_b", | ||
| 165 | "3d", | ||
| 166 | "mpe", | ||
| 167 | "disp0_c", "disp1_c", | ||
| 168 | "vblank0", "vblank1", | ||
| 169 | "mpe_ebm_eof", "mpe_wr_safe", | ||
| 170 | "2d_tinyblt", | ||
| 171 | "dsi" | ||
| 172 | }; | ||
| 173 | |||
| 174 | static const char *t20_syncpt_name(struct nvhost_syncpt *s, u32 id) | ||
| 175 | { | ||
| 176 | BUG_ON(id >= ARRAY_SIZE(s_syncpt_names)); | ||
| 177 | return s_syncpt_names[id]; | ||
| 178 | } | ||
| 179 | |||
| 180 | static void t20_syncpt_debug(struct nvhost_syncpt *sp) | ||
| 181 | { | ||
| 182 | u32 i; | ||
| 183 | for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) { | ||
| 184 | u32 max = nvhost_syncpt_read_max(sp, i); | ||
| 185 | u32 min = nvhost_syncpt_update_min(sp, i); | ||
| 186 | if (!max && !min) | ||
| 187 | continue; | ||
| 188 | dev_info(&syncpt_to_dev(sp)->dev->dev, | ||
| 189 | "id %d (%s) min %d max %d\n", | ||
| 190 | i, syncpt_op(sp).name(sp, i), | ||
| 191 | min, max); | ||
| 192 | |||
| 193 | } | ||
| 194 | |||
| 195 | for (i = 0; i < NV_HOST1X_SYNCPT_NB_BASES; i++) { | ||
| 196 | u32 base_val; | ||
| 197 | t20_syncpt_read_wait_base(sp, i); | ||
| 198 | base_val = sp->base_val[i]; | ||
| 199 | if (base_val) | ||
| 200 | dev_info(&syncpt_to_dev(sp)->dev->dev, | ||
| 201 | "waitbase id %d val %d\n", | ||
| 202 | i, base_val); | ||
| 203 | |||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | static int syncpt_mutex_try_lock(struct nvhost_syncpt *sp, | ||
| 208 | unsigned int idx) | ||
| 209 | { | ||
| 210 | void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture; | ||
| 211 | /* mlock registers returns 0 when the lock is aquired. | ||
| 212 | * writing 0 clears the lock. */ | ||
| 213 | return !!readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); | ||
| 214 | } | ||
| 215 | |||
| 216 | static void syncpt_mutex_unlock(struct nvhost_syncpt *sp, | ||
| 217 | unsigned int idx) | ||
| 218 | { | ||
| 219 | void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture; | ||
| 220 | |||
| 221 | writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4)); | ||
| 222 | } | ||
| 223 | |||
| 224 | int host1x_init_syncpt_support(struct nvhost_master *host) | ||
| 225 | { | ||
| 226 | |||
| 227 | host->sync_aperture = host->aperture + | ||
| 228 | (NV_HOST1X_CHANNEL0_BASE + | ||
| 229 | HOST1X_CHANNEL_SYNC_REG_BASE); | ||
| 230 | |||
| 231 | host->op.syncpt.reset = t20_syncpt_reset; | ||
| 232 | host->op.syncpt.reset_wait_base = t20_syncpt_reset_wait_base; | ||
| 233 | host->op.syncpt.read_wait_base = t20_syncpt_read_wait_base; | ||
| 234 | host->op.syncpt.update_min = t20_syncpt_update_min; | ||
| 235 | host->op.syncpt.cpu_incr = t20_syncpt_cpu_incr; | ||
| 236 | host->op.syncpt.wait_check = t20_syncpt_wait_check; | ||
| 237 | host->op.syncpt.debug = t20_syncpt_debug; | ||
| 238 | host->op.syncpt.name = t20_syncpt_name; | ||
| 239 | host->op.syncpt.mutex_try_lock = syncpt_mutex_try_lock; | ||
| 240 | host->op.syncpt.mutex_unlock = syncpt_mutex_unlock; | ||
| 241 | |||
| 242 | host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS; | ||
| 243 | host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES; | ||
| 244 | host->syncpt.client_managed = NVSYNCPTS_CLIENT_MANAGED; | ||
| 245 | host->syncpt.nb_mlocks = NV_HOST1X_SYNC_MLOCK_NUM; | ||
| 246 | |||
| 247 | return 0; | ||
| 248 | } | ||
diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.h b/drivers/video/tegra/host/host1x/host1x_syncpt.h new file mode 100644 index 00000000000..0d263dc92ed --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* | ||
| 2 | * drivers/video/tegra/host/host1x/host1x_syncpt.h | ||
| 3 | * | ||
| 4 | * Tegra Graphics Host Syncpoints for HOST1X | ||
| 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 | #ifndef __NVHOST_HOST1X_HOST1X_SYNCPT_H | ||
| 22 | #define __NVHOST_HOST1X_HOST1X_SYNCPT_H | ||
| 23 | |||
| 24 | #define NVSYNCPT_DISP0_A (8) | ||
| 25 | #define NVSYNCPT_DISP1_A (9) | ||
| 26 | #define NVSYNCPT_AVP_0 (10) | ||
| 27 | #define NVSYNCPT_CSI_VI_0 (11) | ||
| 28 | #define NVSYNCPT_CSI_VI_1 (12) | ||
| 29 | #define NVSYNCPT_VI_ISP_0 (13) | ||
| 30 | #define NVSYNCPT_VI_ISP_1 (14) | ||
| 31 | #define NVSYNCPT_VI_ISP_2 (15) | ||
| 32 | #define NVSYNCPT_VI_ISP_3 (16) | ||
| 33 | #define NVSYNCPT_VI_ISP_4 (17) | ||
| 34 | #define NVSYNCPT_2D_0 (18) | ||
| 35 | #define NVSYNCPT_2D_1 (19) | ||
| 36 | #define NVSYNCPT_DISP0_B (20) | ||
| 37 | #define NVSYNCPT_DISP1_B (21) | ||
| 38 | #define NVSYNCPT_3D (22) | ||
| 39 | #define NVSYNCPT_MPE (23) | ||
| 40 | #define NVSYNCPT_DISP0_C (24) | ||
| 41 | #define NVSYNCPT_DISP1_C (25) | ||
| 42 | #define NVSYNCPT_VBLANK0 (26) | ||
| 43 | #define NVSYNCPT_VBLANK1 (27) | ||
| 44 | #define NVSYNCPT_MPE_EBM_EOF (28) | ||
| 45 | #define NVSYNCPT_MPE_WR_SAFE (29) | ||
| 46 | #define NVSYNCPT_DSI (31) | ||
| 47 | |||
| 48 | |||
| 49 | /*#define NVSYNCPT_2D_CHANNEL2_0 (20) */ | ||
| 50 | /*#define NVSYNCPT_2D_CHANNEL2_1 (21) */ | ||
| 51 | /*#define NVSYNCPT_2D_TINYBLT_WAR (30)*/ | ||
| 52 | /*#define NVSYNCPT_2D_TINYBLT_RESTORE_CLASS_ID (30)*/ | ||
| 53 | |||
| 54 | /* sync points that are wholly managed by the client */ | ||
| 55 | #define NVSYNCPTS_CLIENT_MANAGED ( \ | ||
| 56 | BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) | \ | ||
| 57 | BIT(NVSYNCPT_DISP0_B) | BIT(NVSYNCPT_DISP1_B) | \ | ||
| 58 | BIT(NVSYNCPT_DISP0_C) | BIT(NVSYNCPT_DISP1_C) | \ | ||
| 59 | BIT(NVSYNCPT_DSI) | \ | ||
| 60 | BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1) | \ | ||
| 61 | BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) | \ | ||
| 62 | BIT(NVSYNCPT_VI_ISP_1) | BIT(NVSYNCPT_VI_ISP_2) | \ | ||
| 63 | BIT(NVSYNCPT_VI_ISP_3) | BIT(NVSYNCPT_VI_ISP_4) | \ | ||
| 64 | BIT(NVSYNCPT_MPE_EBM_EOF) | BIT(NVSYNCPT_MPE_WR_SAFE) | \ | ||
| 65 | BIT(NVSYNCPT_2D_1) | BIT(NVSYNCPT_AVP_0)) | ||
| 66 | |||
| 67 | |||
| 68 | #define NVWAITBASE_2D_0 (1) | ||
| 69 | #define NVWAITBASE_2D_1 (2) | ||
| 70 | #define NVWAITBASE_3D (3) | ||
| 71 | #define NVWAITBASE_MPE (4) | ||
| 72 | |||
| 73 | struct nvhost_master; | ||
| 74 | int host1x_init_syncpt(struct nvhost_master *host); | ||
| 75 | int host1x_init_syncpt_support(struct nvhost_master *host); | ||
| 76 | |||
| 77 | #endif | ||
