summaryrefslogtreecommitdiff
path: root/drivers/video/tegra
diff options
context:
space:
mode:
authorAndrew Howe <ahowe@nvidia.com>2010-12-09 17:09:50 +0200
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:44:56 -0800
commit952956f5baf9611d4ef99fb97f8a82078172c5c2 (patch)
tree1683e86b022d2691ca911d8d429e641df6463153 /drivers/video/tegra
parenta9fde4bbe27d66a8b0639363de2bbf1f04d27939 (diff)
[ARM/tegra] nvhost: Merge NVIDIA changes into Google's nvhost
Google's 2.6.36 nvhost driver branched from NVIDIA's 2.6.32 nvhost at some point before it was actually committed to 2.6.32, but the former's original commit included some fixes that were added to the latter after that. Confusing... Also Google's version has some changes that we like so they will remain, but that makes merging difficult. Anyway, this commit brings the rest of our 2.6.32 changes into 2.6.36 and cleans it up a bit. It might be nicer to break this up into a load of smaller commits but it turned out to be very difficult to do that. Original-Change-Id: I828b624b089b811d6130173e55258da8f52a5cc2 Reviewed-on: http://git-master/r/12563 Tested-by: Jussi Rasanen <jrasanen@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Tested-by: Wei Sun <wsun@nvidia.com> Original-Change-Id: I72fbcff16ec6df62cc7299052a84b91db252c8d1 Rebase-Id: Rb3aa29f2ecbecf889bbc6ccf7d0b26925f7b94fb
Diffstat (limited to 'drivers/video/tegra')
-rw-r--r--drivers/video/tegra/host/debug.c16
-rw-r--r--drivers/video/tegra/host/dev.c21
-rw-r--r--drivers/video/tegra/host/dev.h1
-rw-r--r--drivers/video/tegra/host/nvhost_acm.c8
-rw-r--r--drivers/video/tegra/host/nvhost_cdma.c78
-rw-r--r--drivers/video/tegra/host/nvhost_cdma.h10
-rw-r--r--drivers/video/tegra/host/nvhost_channel.c20
-rw-r--r--drivers/video/tegra/host/nvhost_channel.h13
-rw-r--r--drivers/video/tegra/host/nvhost_hardware.h5
-rw-r--r--drivers/video/tegra/host/nvhost_intr.c315
-rw-r--r--drivers/video/tegra/host/nvhost_intr.h8
-rw-r--r--drivers/video/tegra/host/nvhost_syncpt.c2
12 files changed, 305 insertions, 192 deletions
diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c
index 4f0b941a7ab1..1277c1bc8324 100644
--- a/drivers/video/tegra/host/debug.c
+++ b/drivers/video/tegra/host/debug.c
@@ -1,5 +1,5 @@
/*
- * drivers/video/tegra/dc/dc.c
+ * drivers/video/tegra/host/debug.c
*
* Copyright (C) 2010 Google, Inc.
* Author: Erik Gilling <konkers@android.com>
@@ -66,7 +66,7 @@ static int nvhost_debug_handle_cmd(struct seq_file *s, u32 val, int *count)
case 0x4:
seq_printf(s, "IMM(offset=%03x, data=%03x)\n",
- val >> 16 & 0x3ff, val & 0xffff);
+ val >> 16 & 0xfff, val & 0xffff);
return NVHOST_DBG_STATE_CMD;
case 0x5:
@@ -101,7 +101,7 @@ static int nvhost_debug_handle_cmd(struct seq_file *s, u32 val, int *count)
}
static void nvhost_debug_handle_word(struct seq_file *s, int *state, int *count,
- unsigned long addr, int channel, u32 val)
+ u32 addr, int channel, u32 val)
{
switch (*state) {
case NVHOST_DBG_STATE_CMD:
@@ -210,14 +210,14 @@ static int nvhost_debug_show(struct seq_file *s, void *unused)
break;
case 0x00010009: /* HOST_WAIT_SYNCPT_BASE */
- base = cbread >> 15 & 0xf;
+ base = cbread >> 16 & 0xf;
offset = cbread & 0xffff;
val = readl(m->aperture + HOST1X_SYNC_SYNCPT_BASE(base)) & 0xffff;
val += offset;
- seq_printf(s, "waiting on syncpt %d val %d (base %d, offset %d)\n",
- cbread >> 24, val, base, offset);
+ seq_printf(s, "waiting on syncpt %d val %d (base %d = %d, offset %d)\n",
+ cbread >> 24, val, base, val - offset, offset);
break;
default:
@@ -233,7 +233,7 @@ static int nvhost_debug_show(struct seq_file *s, void *unused)
* it. */
if (size) {
u32 map_base = phys_addr & PAGE_MASK;
- u32 map_size = (size * 4 + PAGE_SIZE - 1) & PAGE_MASK;
+ u32 map_size = ((phys_addr + size * 4 + PAGE_SIZE - 1) & PAGE_MASK) - map_base;
u32 map_offset = phys_addr - map_base;
void *map_addr = ioremap_nocache(map_base, map_size);
@@ -244,7 +244,7 @@ static int nvhost_debug_show(struct seq_file *s, void *unused)
state = NVHOST_DBG_STATE_CMD;
for (ii = 0; ii < size; ii++) {
val = readl(map_addr + map_offset + ii*sizeof(u32));
- nvhost_debug_handle_word(s, &state, &count, phys_addr + ii, i, val);
+ nvhost_debug_handle_word(s, &state, &count, phys_addr + ii * 4, i, val);
}
iounmap(map_addr);
}
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c
index 8b577cc9045f..a5c4ec59fcc6 100644
--- a/drivers/video/tegra/host/dev.c
+++ b/drivers/video/tegra/host/dev.c
@@ -30,7 +30,6 @@
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/file.h>
-#include <linux/clk.h>
#include <asm/io.h>
@@ -50,6 +49,7 @@ struct nvhost_channel_userctx {
u32 syncpt_incrs;
u32 cmdbufs_pending;
u32 relocs_pending;
+ u32 null_kickoff;
struct nvmap_handle_ref *gather_mem;
struct nvhost_op_pair *gathers;
int num_gathers;
@@ -580,13 +580,17 @@ static void power_host(struct nvhost_module *mod, enum nvhost_power_action actio
struct nvhost_master *dev = container_of(mod, struct nvhost_master, mod);
if (action == NVHOST_POWER_ACTION_ON) {
- nvhost_intr_configure(&dev->intr, clk_get_rate(mod->clk[0]));
- }
- else if (action == NVHOST_POWER_ACTION_OFF) {
+ nvhost_intr_start(&dev->intr, clk_get_rate(mod->clk[0]));
+ /* don't do it, as display may have changed syncpt
+ * after the last save
+ * nvhost_syncpt_reset(&dev->syncpt);
+ */
+ } else if (action == NVHOST_POWER_ACTION_OFF) {
int i;
for (i = 0; i < NVHOST_NUMCHANNELS; i++)
nvhost_channel_suspend(&dev->channels[i]);
nvhost_syncpt_save(&dev->syncpt);
+ nvhost_intr_stop(&dev->intr);
}
}
@@ -617,6 +621,11 @@ static int __devinit nvhost_user_init(struct nvhost_master *host)
for (i = 0; i < NVHOST_NUMCHANNELS; i++) {
struct nvhost_channel *ch = &host->channels[i];
+ if (!strcmp(ch->desc->name, "display") &&
+ !nvhost_access_module_regs(&host->cpuaccess,
+ NVHOST_MODULE_DISPLAY_A))
+ continue;
+
cdev_init(&ch->cdev, &nvhost_channelops);
ch->cdev.owner = THIS_MODULE;
@@ -717,12 +726,12 @@ static int __devinit nvhost_probe(struct platform_device *pdev)
err = nvhost_module_init(&host->mod, "host1x", power_host, NULL, &pdev->dev);
if (err) goto fail;
- platform_set_drvdata(pdev, host);
-
clk_enable(host->mod.clk[0]);
nvhost_syncpt_reset(&host->syncpt);
clk_disable(host->mod.clk[0]);
+ platform_set_drvdata(pdev, host);
+
nvhost_bus_register(host);
nvhost_debug_init(host);
diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h
index 4f71ff5d9a9d..f39ea75681e4 100644
--- a/drivers/video/tegra/host/dev.h
+++ b/drivers/video/tegra/host/dev.h
@@ -22,6 +22,7 @@
#ifndef __NVHOST_DEV_H
#define __NVHOST_DEV_H
+
#include "nvhost_acm.h"
#include "nvhost_syncpt.h"
#include "nvhost_intr.h"
diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c
index c2152287506f..51fef8e8240e 100644
--- a/drivers/video/tegra/host/nvhost_acm.c
+++ b/drivers/video/tegra/host/nvhost_acm.c
@@ -28,8 +28,6 @@
#include <mach/powergate.h>
#include <mach/clk.h>
-#include "dev.h"
-
#define ACM_TIMEOUT 1*HZ
#define DISABLE_3D_POWERGATING
@@ -88,7 +86,8 @@ void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
mutex_lock(&mod->lock);
if (atomic_sub_return(refs, &mod->refcount) == 0) {
BUG_ON(!mod->powered);
- schedule_delayed_work(&mod->powerdown, ACM_TIMEOUT);
+ schedule_delayed_work(
+ &mod->powerdown, msecs_to_jiffies(ACM_TIMEOUT_MSEC));
kick = true;
}
mutex_unlock(&mod->lock);
@@ -126,6 +125,7 @@ int nvhost_module_init(struct nvhost_module *mod, const char *name,
struct device *dev)
{
int i = 0;
+
mod->name = name;
while (i < NVHOST_MODULE_MAX_CLOCKS) {
@@ -140,7 +140,9 @@ int nvhost_module_init(struct nvhost_module *mod, const char *name,
break;
}
if (rate != clk_get_rate(mod->clk[i])) {
+ clk_enable(mod->clk[i]);
clk_set_rate(mod->clk[i], rate);
+ clk_disable(mod->clk[i]);
}
i++;
}
diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c
index d2a91d117b52..ad5b3853c8dc 100644
--- a/drivers/video/tegra/host/nvhost_cdma.c
+++ b/drivers/video/tegra/host/nvhost_cdma.c
@@ -174,9 +174,9 @@ static u32 push_buffer_putptr(struct push_buffer *pb)
* The queue must not be left with less than SYNC_QUEUE_MIN_ENTRY words
* of space at the end of the array.
*
- * We want to pass contiguous arrays of handles to NrRmMemUnpin, so arrays
- * that would wrap at the end of the buffer will be split into two (or more)
- * entries.
+ * We want to pass contiguous arrays of handles to nvmap_unpin_handles,
+ * so arrays that would wrap at the end of the buffer will be split into
+ * two (or more) entries.
*/
/* Number of words needed to store an entry containing one handle */
@@ -327,6 +327,37 @@ dequeue_sync_queue_head(struct sync_queue *queue)
/*** Cdma internal stuff ***/
/**
+ * Start channel DMA
+ */
+static void start_cdma(struct nvhost_cdma *cdma)
+{
+ void __iomem *chan_regs = cdma_to_channel(cdma)->aperture;
+
+ if (cdma->running)
+ return;
+
+ cdma->last_put = push_buffer_putptr(&cdma->push_buffer);
+
+ writel(nvhost_channel_dmactrl(true, false, false),
+ chan_regs + HOST1X_CHANNEL_DMACTRL);
+
+ /* set base, put, end pointer (all of memory) */
+ writel(0, chan_regs + HOST1X_CHANNEL_DMASTART);
+ writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT);
+ writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND);
+
+ /* reset GET */
+ writel(nvhost_channel_dmactrl(true, true, true),
+ chan_regs + HOST1X_CHANNEL_DMACTRL);
+
+ /* start the command DMA */
+ writel(nvhost_channel_dmactrl(false, false, false),
+ chan_regs + HOST1X_CHANNEL_DMACTRL);
+
+ cdma->running = true;
+}
+
+/**
* Kick channel DMA into action by writing its PUT offset (if it has changed)
*/
static void kick_cdma(struct nvhost_cdma *cdma)
@@ -489,35 +520,6 @@ void nvhost_cdma_deinit(struct nvhost_cdma *cdma)
destroy_push_buffer(&cdma->push_buffer);
}
-static void start_cdma(struct nvhost_cdma *cdma)
-{
- void __iomem *chan_regs = cdma_to_channel(cdma)->aperture;
-
- if (cdma->running)
- return;
-
- cdma->last_put = push_buffer_putptr(&cdma->push_buffer);
-
- writel(nvhost_channel_dmactrl(true, false, false),
- chan_regs + HOST1X_CHANNEL_DMACTRL);
-
- /* set base, put, end pointer (all of memory) */
- writel(0, chan_regs + HOST1X_CHANNEL_DMASTART);
- writel(cdma->last_put, chan_regs + HOST1X_CHANNEL_DMAPUT);
- writel(0xFFFFFFFF, chan_regs + HOST1X_CHANNEL_DMAEND);
-
- /* reset GET */
- writel(nvhost_channel_dmactrl(true, true, true),
- chan_regs + HOST1X_CHANNEL_DMACTRL);
-
- /* start the command DMA */
- writel(nvhost_channel_dmactrl(false, false, false),
- chan_regs + HOST1X_CHANNEL_DMACTRL);
-
- cdma->running = true;
-
-}
-
void nvhost_cdma_stop(struct nvhost_cdma *cdma)
{
void __iomem *chan_regs = cdma_to_channel(cdma)->aperture;
@@ -568,9 +570,10 @@ void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2)
* The handles for a submit must all be pinned at the same time, but they
* can be unpinned in smaller chunks.
*/
-void nvhost_cdma_end(struct nvmap_client *user_nvmap, struct nvhost_cdma *cdma,
- u32 sync_point_id, u32 sync_point_value,
- struct nvmap_handle **handles, unsigned int nr_handles)
+void nvhost_cdma_end(struct nvhost_cdma *cdma,
+ struct nvmap_client *user_nvmap,
+ u32 sync_point_id, u32 sync_point_value,
+ struct nvmap_handle **handles, unsigned int nr_handles)
{
kick_cdma(cdma);
@@ -582,10 +585,7 @@ void nvhost_cdma_end(struct nvmap_client *user_nvmap, struct nvhost_cdma *cdma,
*/
count = wait_cdma(cdma, CDMA_EVENT_SYNC_QUEUE_SPACE);
- /*
- * Add reloc entries to sync queue (as many as will fit)
- * and unlock it
- */
+ /* Add reloc entries to sync queue (as many as will fit) */
if (count > nr_handles)
count = nr_handles;
add_to_sync_queue(&cdma->sync_queue, sync_point_id,
diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h
index a7f17d0413d5..59a45690de73 100644
--- a/drivers/video/tegra/host/nvhost_cdma.h
+++ b/drivers/video/tegra/host/nvhost_cdma.h
@@ -91,13 +91,13 @@ void nvhost_cdma_deinit(struct nvhost_cdma *cdma);
void nvhost_cdma_stop(struct nvhost_cdma *cdma);
void nvhost_cdma_begin(struct nvhost_cdma *cdma);
void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2);
-void nvhost_cdma_end(struct nvmap_client *user_nvmap,
- struct nvhost_cdma *cdma,
- u32 sync_point_id, u32 sync_point_value,
- struct nvmap_handle **handles, unsigned int nr_handles);
+void nvhost_cdma_end(struct nvhost_cdma *cdma,
+ struct nvmap_client *user_nvmap,
+ u32 sync_point_id, u32 sync_point_value,
+ struct nvmap_handle **handles, unsigned int nr_handles);
void nvhost_cdma_update(struct nvhost_cdma *cdma);
void nvhost_cdma_flush(struct nvhost_cdma *cdma);
void nvhost_cdma_find_gather(struct nvhost_cdma *cdma, u32 dmaget,
- u32 *addr, u32 *size);
+ u32 *addr, u32 *size);
#endif
diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c
index c1f440577ab0..b4e49a0cdc67 100644
--- a/drivers/video/tegra/host/nvhost_channel.c
+++ b/drivers/video/tegra/host/nvhost_channel.c
@@ -78,6 +78,7 @@ static const struct nvhost_channeldesc channelmap[] = {
BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) |
BIT(NVSYNCPT_VI_ISP_4) | BIT(NVSYNCPT_VI_ISP_5),
.modulemutexes = BIT(NVMODMUTEX_VI),
+ .exclusive = true,
},
{
/* channel 5 */
@@ -87,6 +88,7 @@ static const struct nvhost_channeldesc channelmap[] = {
.waitbases = BIT(NVWAITBASE_MPE),
.class = NV_VIDEO_ENCODE_MPEG_CLASS_ID,
.power = power_mpe,
+ .exclusive = true,
},
{
/* channel 6 */
@@ -130,6 +132,8 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch)
if (err)
nvhost_module_deinit(&ch->mod);
}
+ } else if (ch->desc->exclusive) {
+ err = -EBUSY;
}
if (!err) {
ch->refcount++;
@@ -169,12 +173,12 @@ void nvhost_channel_suspend(struct nvhost_channel *ch)
}
void nvhost_channel_submit(struct nvhost_channel *ch,
- struct nvmap_client *user_nvmap,
- struct nvhost_op_pair *ops, int num_pairs,
- struct nvhost_cpuinterrupt *intrs, int num_intrs,
- struct nvmap_handle **unpins, int num_unpins,
- u32 syncpt_id, u32 syncpt_val,
- int num_nulled_incrs)
+ struct nvmap_client *user_nvmap,
+ struct nvhost_op_pair *ops, int num_pairs,
+ struct nvhost_cpuinterrupt *intrs, int num_intrs,
+ struct nvmap_handle **unpins, int num_unpins,
+ u32 syncpt_id, u32 syncpt_val,
+ int num_nulled_incrs)
{
int i;
struct nvhost_op_pair* p;
@@ -218,8 +222,8 @@ void nvhost_channel_submit(struct nvhost_channel *ch,
}
/* end CDMA submit & stash pinned hMems into sync queue for later cleanup */
- nvhost_cdma_end(user_nvmap, &ch->cdma, syncpt_id, syncpt_val,
- unpins, num_unpins);
+ nvhost_cdma_end(&ch->cdma, user_nvmap, syncpt_id, syncpt_val,
+ unpins, num_unpins);
}
static void power_2d(struct nvhost_module *mod, enum nvhost_power_action action)
diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h
index 59ba06543a48..cb117b5a0cee 100644
--- a/drivers/video/tegra/host/nvhost_channel.h
+++ b/drivers/video/tegra/host/nvhost_channel.h
@@ -44,6 +44,7 @@ struct nvhost_channeldesc {
u32 waitbases;
u32 modulemutexes;
u32 class;
+ bool exclusive;
};
struct nvhost_channel {
@@ -76,12 +77,12 @@ int nvhost_channel_init(
struct nvhost_master *dev, int index);
void nvhost_channel_submit(struct nvhost_channel *ch,
- struct nvmap_client *user_nvmap,
- struct nvhost_op_pair *ops, int num_pairs,
- struct nvhost_cpuinterrupt *intrs, int num_intrs,
- struct nvmap_handle **unpins, int num_unpins,
- u32 syncpt_id, u32 syncpt_val,
- int num_nulled_incrs);
+ struct nvmap_client *user_nvmap,
+ struct nvhost_op_pair *ops, int num_pairs,
+ struct nvhost_cpuinterrupt *intrs, int num_intrs,
+ struct nvmap_handle **unpins, int num_unpins,
+ u32 syncpt_id, u32 syncpt_val,
+ int num_nulled_incrs);
struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch);
void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx);
diff --git a/drivers/video/tegra/host/nvhost_hardware.h b/drivers/video/tegra/host/nvhost_hardware.h
index a7663489727e..7a5f4ceb2cb6 100644
--- a/drivers/video/tegra/host/nvhost_hardware.h
+++ b/drivers/video/tegra/host/nvhost_hardware.h
@@ -88,8 +88,7 @@ enum {
HOST1X_SYNC_HINTSTATUS_EXT = 0x28,
HOST1X_SYNC_HINTMASK_EXT = 0x2c,
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS = 0x40,
- HOST1X_SYNC_SYNCPT_THRESH_INT_MASK_0 = 0x50,
- HOST1X_SYNC_SYNCPT_THRESH_INT_MASK_1 = 0x54,
+ HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS = 0x48,
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE = 0x60,
HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0 = 0x68,
HOST1X_SYNC_USEC_CLK = 0x1a4,
@@ -227,7 +226,5 @@ static inline u32 nvhost_opcode_gather_incr(unsigned offset, unsigned count)
#define NVHOST_OPCODE_NOOP nvhost_opcode_nonincr(0, 0)
-
-
#endif /* __NVHOST_HARDWARE_H */
diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c
index 51387ee62f8e..8acd09820f72 100644
--- a/drivers/video/tegra/host/nvhost_intr.c
+++ b/drivers/video/tegra/host/nvhost_intr.c
@@ -29,7 +29,26 @@
#define intr_to_dev(x) container_of(x, struct nvhost_master, intr)
-/*** HW sync point threshold interrupt management ***/
+/*** HW host sync management ***/
+
+void init_host_sync(void __iomem *sync_regs)
+{
+ /* disable the ip_busy_timeout. this prevents write drops, etc.
+ * there's no real way to recover from a hung client anyway.
+ */
+ writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT);
+
+ /* increase the auto-ack timout to the maximum value. 2d will hang
+ * otherwise on ap20.
+ */
+ writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
+}
+
+void set_host_clocks_per_microsecond(void __iomem *sync_regs, u32 cpm)
+{
+ /* write microsecond clock register */
+ writel(cpm, sync_regs + HOST1X_SYNC_USEC_CLK);
+}
static void set_syncpt_threshold(void __iomem *sync_regs, u32 id, u32 thresh)
{
@@ -42,6 +61,18 @@ static void enable_syncpt_interrupt(void __iomem *sync_regs, u32 id)
writel(BIT(id), sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0);
}
+void disable_all_syncpt_interrupts(void __iomem *sync_regs)
+{
+ /* disable interrupts for both cpu's */
+ writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE);
+
+ /* clear status for both cpu's */
+ writel(0xfffffffful, sync_regs +
+ HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS);
+ writel(0xfffffffful, sync_regs +
+ HOST1X_SYNC_SYNCPT_THRESH_CPU1_INT_STATUS);
+}
+
/*** Wait list management ***/
@@ -68,7 +99,7 @@ static void waiter_release(struct kref *kref)
kfree(container_of(kref, struct nvhost_waitlist, refcount));
}
-/*
+/**
* add a waiter to a waiter queue, sorted by threshold
* returns true if it was added at the head of the queue
*/
@@ -88,7 +119,7 @@ static bool add_waiter_to_queue(struct nvhost_waitlist *waiter,
return true;
}
-/*
+/**
* run through a waiter queue for a single sync point ID
* and gather all completed waiters into lists by actions
*/
@@ -125,6 +156,17 @@ static void remove_completed_waiters(struct list_head *head, u32 sync,
}
}
+void reset_threshold_interrupt(struct list_head *head,
+ unsigned int id, void __iomem *sync_regs)
+{
+ u32 thresh = list_first_entry(head,
+ struct nvhost_waitlist, list)->thresh;
+
+ set_syncpt_threshold(sync_regs, id, thresh);
+ enable_syncpt_interrupt(sync_regs, id);
+}
+
+
static void action_submit_complete(struct nvhost_waitlist *waiter)
{
struct nvhost_channel *channel = waiter->data;
@@ -184,40 +226,38 @@ static void run_handlers(struct list_head completed[NVHOST_INTR_ACTION_COUNT])
}
}
-
-/*** Interrupt service functions ***/
-
/**
- * Host1x intterrupt service function
- * Handles read / write failures
+ * Remove & handle all waiters that have completed for the given syncpt
*/
-static irqreturn_t host1x_isr(int irq, void *dev_id)
+int process_wait_list(struct nvhost_intr_syncpt *syncpt,
+ u32 threshold, void __iomem *sync_regs)
{
- struct nvhost_intr *intr = dev_id;
- void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
- u32 stat;
- u32 ext_stat;
- u32 addr;
+ struct list_head completed[NVHOST_INTR_ACTION_COUNT];
+ unsigned int i;
+ int empty;
- stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS);
- ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
+ for (i = 0; i < NVHOST_INTR_ACTION_COUNT; ++i)
+ INIT_LIST_HEAD(completed + i);
- if (nvhost_sync_hintstatus_ext_ip_read_int(ext_stat)) {
- addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR);
- pr_err("Host read timeout at address %x\n", addr);
- }
+ spin_lock(&syncpt->lock);
- if (nvhost_sync_hintstatus_ext_ip_write_int(ext_stat)) {
- addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR);
- pr_err("Host write timeout at address %x\n", addr);
- }
+ remove_completed_waiters(&syncpt->wait_head, threshold, completed);
- writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
- writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS);
+ empty = list_empty(&syncpt->wait_head);
+ if (!empty)
+ reset_threshold_interrupt(&syncpt->wait_head,
+ syncpt->id, sync_regs);
- return IRQ_HANDLED;
+ spin_unlock(&syncpt->lock);
+
+ run_handlers(completed);
+
+ return empty;
}
+
+/*** host syncpt interrupt service functions ***/
+
/**
* Sync point threshold interrupt service function
* Handles sync point threshold triggers, in interrupt context
@@ -238,7 +278,6 @@ static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
-
/**
* Sync point threshold interrupt service thread function
* Handles sync point threshold triggers, in thread context
@@ -250,56 +289,129 @@ static irqreturn_t syncpt_thresh_fn(int irq, void *dev_id)
struct nvhost_intr *intr = container_of(syncpt, struct nvhost_intr,
syncpt[id]);
struct nvhost_master *dev = intr_to_dev(intr);
- void __iomem *sync_regs = dev->sync_aperture;
- struct list_head completed[NVHOST_INTR_ACTION_COUNT];
- u32 sync;
- unsigned int i;
+ (void)process_wait_list(syncpt,
+ nvhost_syncpt_update_min(&dev->syncpt, id),
+ dev->sync_aperture);
- for (i = 0; i < NVHOST_INTR_ACTION_COUNT; ++i)
- INIT_LIST_HEAD(completed + i);
+ return IRQ_HANDLED;
+}
- sync = nvhost_syncpt_update_min(&dev->syncpt, id);
+/**
+ * lazily request a syncpt's irq
+ */
+static int request_syncpt_irq(struct nvhost_intr_syncpt *syncpt)
+{
+ int err;
- spin_lock(&syncpt->lock);
+ if (syncpt->irq_requested)
+ return 0;
- remove_completed_waiters(&syncpt->wait_head, sync, completed);
+ err = request_threaded_irq(syncpt->irq,
+ syncpt_thresh_isr, syncpt_thresh_fn,
+ 0, syncpt->thresh_irq_name, syncpt);
+ if (err)
+ return err;
- if (!list_empty(&syncpt->wait_head)) {
- u32 thresh = list_first_entry(&syncpt->wait_head,
- struct nvhost_waitlist, list)->thresh;
+ syncpt->irq_requested = 1;
+ return 0;
+}
- set_syncpt_threshold(sync_regs, id, thresh);
- enable_syncpt_interrupt(sync_regs, id);
+/**
+ * free a syncpt's irq. syncpt interrupt should be disabled first.
+ */
+static void free_syncpt_irq(struct nvhost_intr_syncpt *syncpt)
+{
+ if (syncpt->irq_requested) {
+ free_irq(syncpt->irq, syncpt);
+ syncpt->irq_requested = 0;
}
+}
- spin_unlock(&syncpt->lock);
- run_handlers(completed);
+/*** host general interrupt service functions ***/
+
+/**
+ * Host general interrupt service function
+ * Handles read / write failures
+ */
+static irqreturn_t host1x_isr(int irq, void *dev_id)
+{
+ struct nvhost_intr *intr = dev_id;
+ void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
+ u32 stat;
+ u32 ext_stat;
+ u32 addr;
+
+ stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS);
+ ext_stat = readl(sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
+
+ if (nvhost_sync_hintstatus_ext_ip_read_int(ext_stat)) {
+ addr = readl(sync_regs + HOST1X_SYNC_IP_READ_TIMEOUT_ADDR);
+ pr_err("Host read timeout at address %x\n", addr);
+ }
+
+ if (nvhost_sync_hintstatus_ext_ip_write_int(ext_stat)) {
+ addr = readl(sync_regs + HOST1X_SYNC_IP_WRITE_TIMEOUT_ADDR);
+ pr_err("Host write timeout at address %x\n", addr);
+ }
+
+ writel(ext_stat, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
+ writel(stat, sync_regs + HOST1X_SYNC_HINTSTATUS);
return IRQ_HANDLED;
}
-/*
- * lazily request a syncpt's irq
- */
-static int request_syncpt_irq(struct nvhost_intr_syncpt *syncpt)
+static int request_host_general_irq(struct nvhost_intr *intr)
{
- static DEFINE_MUTEX(mutex);
+ void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
int err;
- mutex_lock(&mutex);
- if (!syncpt->irq_requested) {
- err = request_threaded_irq(syncpt->irq,
- syncpt_thresh_isr, syncpt_thresh_fn,
- 0, syncpt->thresh_irq_name, syncpt);
- if (!err)
- syncpt->irq_requested = 1;
- }
- mutex_unlock(&mutex);
+ if (intr->host_general_irq_requested)
+ return 0;
+
+ /* master disable for general (not syncpt) host interrupts */
+ writel(0, sync_regs + HOST1X_SYNC_INTMASK);
+
+ /* clear status & extstatus */
+ writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS_EXT);
+ writel(0xfffffffful, sync_regs + HOST1X_SYNC_HINTSTATUS);
+
+ err = request_irq(intr->host_general_irq, host1x_isr, 0,
+ "host_status", intr);
+ if (err)
+ return err;
+
+ /* enable extra interrupt sources IP_READ_INT and IP_WRITE_INT */
+ writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT);
+
+ /* enable extra interrupt sources */
+ writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK);
+
+ /* enable host module interrupt to CPU0 */
+ writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK);
+
+ /* master enable for general (not syncpt) host interrupts */
+ writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK);
+
+ intr->host_general_irq_requested = true;
+
return err;
}
+static void free_host_general_irq(struct nvhost_intr *intr)
+{
+ if (intr->host_general_irq_requested) {
+ void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
+
+ /* master disable for general (not syncpt) host interrupts */
+ writel(0, sync_regs + HOST1X_SYNC_INTMASK);
+
+ free_irq(intr->host_general_irq, intr);
+ intr->host_general_irq_requested = false;
+ }
+}
+
/*** Main API ***/
@@ -337,7 +449,10 @@ int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh,
if (!syncpt->irq_requested) {
spin_unlock(&syncpt->lock);
+ mutex_lock(&intr->mutex);
err = request_syncpt_irq(syncpt);
+ mutex_unlock(&intr->mutex);
+
if (err) {
kfree(waiter);
return err;
@@ -382,13 +497,10 @@ int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync)
{
unsigned int id;
struct nvhost_intr_syncpt *syncpt;
- int err;
- err = request_irq(irq_gen, host1x_isr, 0, "host_status", intr);
- if (err)
- goto fail;
- intr->host1x_irq = irq_gen;
- intr->host1x_isr_started = true;
+ mutex_init(&intr->mutex);
+ intr->host_general_irq = irq_gen;
+ intr->host_general_irq_requested = false;
for (id = 0, syncpt = intr->syncpt;
id < NV_HOST1X_SYNCPT_NB_PTS;
@@ -399,22 +511,43 @@ int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync)
spin_lock_init(&syncpt->lock);
INIT_LIST_HEAD(&syncpt->wait_head);
snprintf(syncpt->thresh_irq_name,
- sizeof(syncpt->thresh_irq_name),
- "%s", nvhost_syncpt_name(id));
+ sizeof(syncpt->thresh_irq_name),
+ "host_sp_%02d", id);
}
return 0;
-
-fail:
- nvhost_intr_deinit(intr);
- return err;
}
void nvhost_intr_deinit(struct nvhost_intr *intr)
{
+ nvhost_intr_stop(intr);
+}
+
+void nvhost_intr_start(struct nvhost_intr *intr, u32 hz)
+{
+ struct nvhost_master *dev = intr_to_dev(intr);
+ void __iomem *sync_regs = dev->sync_aperture;
+
+ mutex_lock(&intr->mutex);
+
+ init_host_sync(sync_regs);
+ set_host_clocks_per_microsecond(sync_regs, (hz + 1000000 - 1)/1000000);
+
+ request_host_general_irq(intr);
+
+ mutex_unlock(&intr->mutex);
+}
+
+void nvhost_intr_stop(struct nvhost_intr *intr)
+{
+ void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
unsigned int id;
struct nvhost_intr_syncpt *syncpt;
+ mutex_lock(&intr->mutex);
+
+ disable_all_syncpt_interrupts(sync_regs);
+
for (id = 0, syncpt = intr->syncpt;
id < NV_HOST1X_SYNCPT_NB_PTS;
++id, ++syncpt) {
@@ -432,46 +565,10 @@ void nvhost_intr_deinit(struct nvhost_intr *intr)
BUG_ON(1);
}
- if (syncpt->irq_requested)
- free_irq(syncpt->irq, syncpt);
+ free_syncpt_irq(syncpt);
}
- if (intr->host1x_isr_started) {
- free_irq(intr->host1x_irq, intr);
- intr->host1x_isr_started = false;
- }
-}
+ free_host_general_irq(intr);
-void nvhost_intr_configure (struct nvhost_intr *intr, u32 hz)
-{
- void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture;
-
- /* write microsecond clock register */
- writel((hz + 1000000 - 1)/1000000, sync_regs + HOST1X_SYNC_USEC_CLK);
-
- /* disable the ip_busy_timeout. this prevents write drops, etc.
- * there's no real way to recover from a hung client anyway.
- */
- writel(0, sync_regs + HOST1X_SYNC_IP_BUSY_TIMEOUT);
-
- /* increase the auto-ack timout to the maximum value. 2d will hang
- * otherwise on ap20.
- */
- writel(0xff, sync_regs + HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
-
- /* disable interrupts for both cpu's */
- writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_MASK_0);
- writel(0, sync_regs + HOST1X_SYNC_SYNCPT_THRESH_INT_MASK_1);
-
- /* masking all of the interrupts actually means "enable" */
- writel(BIT(0), sync_regs + HOST1X_SYNC_INTMASK);
-
- /* enable HOST_INT_C0MASK */
- writel(BIT(0), sync_regs + HOST1X_SYNC_INTC0MASK);
-
- /* enable HINTMASK_EXT */
- writel(BIT(31), sync_regs + HOST1X_SYNC_HINTMASK);
-
- /* enable IP_READ_INT and IP_WRITE_INT */
- writel(BIT(30) | BIT(31), sync_regs + HOST1X_SYNC_HINTMASK_EXT);
+ mutex_unlock(&intr->mutex);
}
diff --git a/drivers/video/tegra/host/nvhost_intr.h b/drivers/video/tegra/host/nvhost_intr.h
index b546c54dde06..fb3e613d70da 100644
--- a/drivers/video/tegra/host/nvhost_intr.h
+++ b/drivers/video/tegra/host/nvhost_intr.h
@@ -69,8 +69,9 @@ struct nvhost_intr_syncpt {
struct nvhost_intr {
struct nvhost_intr_syncpt syncpt[NV_HOST1X_SYNCPT_NB_PTS];
- int host1x_irq;
- bool host1x_isr_started;
+ struct mutex mutex;
+ int host_general_irq;
+ bool host_general_irq_requested;
};
/**
@@ -97,6 +98,7 @@ void nvhost_intr_put_ref(struct nvhost_intr *intr, void *ref);
int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync);
void nvhost_intr_deinit(struct nvhost_intr *intr);
-void nvhost_intr_configure(struct nvhost_intr *intr, u32 hz);
+void nvhost_intr_start(struct nvhost_intr *intr, u32 hz);
+void nvhost_intr_stop(struct nvhost_intr *intr);
#endif
diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c
index dd2ab0d379e0..e623830d826e 100644
--- a/drivers/video/tegra/host/nvhost_syncpt.c
+++ b/drivers/video/tegra/host/nvhost_syncpt.c
@@ -217,7 +217,7 @@ int nvhost_syncpt_wait_timeout(struct nvhost_syncpt *sp, u32 id,
id, nvhost_syncpt_name(id), thresh);
nvhost_syncpt_debug(sp);
}
- };
+ }
nvhost_intr_put_ref(&(syncpt_to_dev(sp)->intr), ref);
done: