/*
* drivers/video/tegra/host/host1x/host1x_syncpt.c
*
* Tegra Graphics Host Syncpoints for HOST1X
*
* Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include "nvhost_syncpt.h"
#include "nvhost_acm.h"
#include "dev.h"
#include "host1x_syncpt.h"
#include "host1x_hardware.h"
/**
* Write the current syncpoint value back to hw.
*/
static void t20_syncpt_reset(struct nvhost_syncpt *sp, u32 id)
{
struct nvhost_master *dev = syncpt_to_dev(sp);
int min = nvhost_syncpt_read_min(sp, id);
writel(min, dev->sync_aperture + (HOST1X_SYNC_SYNCPT_0 + id * 4));
}
/**
* Write the current waitbase value back to hw.
*/
static void t20_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id)
{
struct nvhost_master *dev = syncpt_to_dev(sp);
writel(sp->base_val[id],
dev->sync_aperture + (HOST1X_SYNC_SYNCPT_BASE_0 + id * 4));
}
/**
* Read waitbase value from hw.
*/
static void t20_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id)
{
struct nvhost_master *dev = syncpt_to_dev(sp);
sp->base_val[id] = readl(dev->sync_aperture +
(HOST1X_SYNC_SYNCPT_BASE_0 + id * 4));
}
/**
* Updates the last value read from hardware.
* (was nvhost_syncpt_update_min)
*/
static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id)
{
struct nvhost_master *dev = syncpt_to_dev(sp);
void __iomem *sync_regs = dev->sync_aperture;
u32 old, live;
do {
old = nvhost_syncpt_read_min(sp, id);
live = readl(sync_regs + (HOST1X_SYNC_SYNCPT_0 + id * 4));
} while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old);
if (!nvhost_syncpt_check_max(sp, id, live))
dev_err(&syncpt_to_dev(sp)->dev->dev,
"%s failed: id=%u, min=%d, max=%d\n",
__func__,
nvhost_syncpt_read_min(sp, id),
nvhost_syncpt_read_max(sp, id),
id);
return live;
}
/**
* Write a cpu syncpoint increment to the hardware, without touching
* the cache. Caller is responsible for host being powered.
*/
static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id)
{
struct nvhost_master *dev = syncpt_to_dev(sp);
BUG_ON(!nvhost_module_powered(dev->dev));
if (!client_managed(id) && nvhost_syncpt_min_eq_max(sp, id)) {
dev_err(&syncpt_to_dev(sp)->dev->dev,
"Trying to increment syncpoint id %d beyond max\n",
id);
nvhost_debug_dump(syncpt_to_dev(sp));
return;
}
writel(BIT(id), dev->sync_aperture + HOST1X_SYNC_SYNCPT_CPU_INCR);
wmb();
}
/* check for old WAITs to be removed (avoiding a wrap) */
static int t20_syncpt_wait_check(struct nvhost_syncpt *sp,
struct nvmap_client *nvmap,
u32 waitchk_mask,
struct nvhost_waitchk *wait,
int num_waitchk)
{
u32 idx;
int err = 0;
/* get current syncpt values */
for (idx = 0; idx < NV_HOST1X_SYNCPT_NB_PTS; idx++) {
if (BIT(idx) & waitchk_mask)
nvhost_syncpt_update_min(sp, idx);
}
BUG_ON(!wait && !num_waitchk);
/* compare syncpt vs wait threshold */
while (num_waitchk) {
u32 override;
BUG_ON(wait->syncpt_id >= NV_HOST1X_SYNCPT_NB_PTS);
if (nvhost_syncpt_is_expired(sp,
wait->syncpt_id, wait->thresh)) {
/*
* NULL an already satisfied WAIT_SYNCPT host method,
* by patching its args in the command stream. The
* method data is changed to reference a reserved
* (never given out or incr) NVSYNCPT_GRAPHICS_HOST
* syncpt with a matching threshold value of 0, so
* is guaranteed to be popped by the host HW.
*/
dev_dbg(&syncpt_to_dev(sp)->dev->dev,
"drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n",
wait->syncpt_id,
syncpt_op(sp).name(sp, wait->syncpt_id),
wait->thresh,
nvhost_syncpt_read_min(sp, wait->syncpt_id));
/* patch the wait */
override = nvhost_class_host_wait_syncpt(
NVSYNCPT_GRAPHICS_HOST, 0);
err = nvmap_patch_word(nvmap,
(struct nvmap_handle *)wait->mem,
wait->offset, override);
if (err)
break;
}
wait++;
num_waitchk--;
}
return err;
}
static const char *s_syncpt_names[32] = {
"gfx_host",
"", "", "", "", "", "", "",
"disp0_a", "disp1_a", "avp_0",
"csi_vi_0", "csi_vi_1",
"vi_isp_0", "vi_isp_1", "vi_isp_2", "vi_isp_3", "vi_isp_4",
"2d_0", "2d_1",
"disp0_b", "disp1_b",
"3d",
"mpe",
"disp0_c", "disp1_c",
"vblank0", "vblank1",
"mpe_ebm_eof", "mpe_wr_safe",
"2d_tinyblt",
"dsi"
};
static const char *t20_syncpt_name(struct nvhost_syncpt *s, u32 id)
{
BUG_ON(id >= ARRAY_SIZE(s_syncpt_names));
return s_syncpt_names[id];
}
static void t20_syncpt_debug(struct nvhost_syncpt *sp)
{
u32 i;
for (i = 0; i < NV_HOST1X_SYNCPT_NB_PTS; i++) {
u32 max = nvhost_syncpt_read_max(sp, i);
u32 min = nvhost_syncpt_update_min(sp, i);
if (!max && !min)
continue;
dev_info(&syncpt_to_dev(sp)->dev->dev,
"id %d (%s) min %d max %d\n",
i, syncpt_op(sp).name(sp, i),
min, max);
}
for (i = 0; i < NV_HOST1X_SYNCPT_NB_BASES; i++) {
u32 base_val;
t20_syncpt_read_wait_base(sp, i);
base_val = sp->base_val[i];
if (base_val)
dev_info(&syncpt_to_dev(sp)->dev->dev,
"waitbase id %d val %d\n",
i, base_val);
}
}
static int syncpt_mutex_try_lock(struct nvhost_syncpt *sp,
unsigned int idx)
{
void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture;
/* mlock registers returns 0 when the lock is aquired.
* writing 0 clears the lock. */
return !!readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4));
}
static void syncpt_mutex_unlock(struct nvhost_syncpt *sp,
unsigned int idx)
{
void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture;
writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4));
}
int host1x_init_syncpt_support(struct nvhost_master *host)
{
host->sync_aperture = host->aperture +
(NV_HOST1X_CHANNEL0_BASE +
HOST1X_CHANNEL_SYNC_REG_BASE);
host->op.syncpt.reset = t20_syncpt_reset;
host->op.syncpt.reset_wait_base = t20_syncpt_reset_wait_base;
host->op.syncpt.read_wait_base = t20_syncpt_read_wait_base;
host->op.syncpt.update_min = t20_syncpt_update_min;
host->op.syncpt.cpu_incr = t20_syncpt_cpu_incr;
host->op.syncpt.wait_check = t20_syncpt_wait_check;
host->op.syncpt.debug = t20_syncpt_debug;
host->op.syncpt.name = t20_syncpt_name;
host->op.syncpt.mutex_try_lock = syncpt_mutex_try_lock;
host->op.syncpt.mutex_unlock = syncpt_mutex_unlock;
host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS;
host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES;
host->syncpt.client_managed = NVSYNCPTS_CLIENT_MANAGED;
host->syncpt.nb_mlocks = NV_HOST1X_SYNC_MLOCK_NUM;
return 0;
}