On Thu, Nov 22, 2012 at 01:16:49PM +0100, Terje Bergstrom wrote: > Add nvhost, the driver for host1x and 2D, which is a client device > for host1x. > > Change-Id: Id93a28491dc2d54049e0adce4ad287c5985b2bca > Signed-off-by: Terje Bergstrom <tbergstrom@xxxxxxxxxx> > Signed-off-by: Arto Merilainen <amerilainen@xxxxxxxxxx> > --- > drivers/video/Kconfig | 2 + > drivers/video/Makefile | 2 + > drivers/video/tegra/host/Kconfig | 20 + > drivers/video/tegra/host/Makefile | 21 + > drivers/video/tegra/host/bus_client.c | 101 ++++ > drivers/video/tegra/host/bus_client.h | 32 ++ > drivers/video/tegra/host/chip_support.c | 68 +++ > drivers/video/tegra/host/chip_support.h | 179 +++++++ > drivers/video/tegra/host/debug.c | 255 ++++++++++ > drivers/video/tegra/host/debug.h | 50 ++ > drivers/video/tegra/host/dev.c | 104 ++++ > drivers/video/tegra/host/dev.h | 33 ++ > drivers/video/tegra/host/dmabuf.c | 151 ++++++ > drivers/video/tegra/host/dmabuf.h | 51 ++ > drivers/video/tegra/host/host1x/Makefile | 6 + > drivers/video/tegra/host/host1x/host1x.c | 320 ++++++++++++ > drivers/video/tegra/host/host1x/host1x.h | 97 ++++ > .../video/tegra/host/host1x/host1x01_hardware.h | 157 ++++++ > drivers/video/tegra/host/host1x/host1x_cdma.c | 488 ++++++++++++++++++ > drivers/video/tegra/host/host1x/host1x_cdma.h | 39 ++ > drivers/video/tegra/host/host1x/host1x_channel.c | 157 ++++++ > drivers/video/tegra/host/host1x/host1x_debug.c | 405 +++++++++++++++ > drivers/video/tegra/host/host1x/host1x_intr.c | 263 ++++++++++ > drivers/video/tegra/host/host1x/host1x_syncpt.c | 170 +++++++ > .../video/tegra/host/host1x/hw_host1x01_channel.h | 182 +++++++ > drivers/video/tegra/host/host1x/hw_host1x01_sync.h | 398 +++++++++++++++ > .../video/tegra/host/host1x/hw_host1x01_uclass.h | 474 +++++++++++++++++ > drivers/video/tegra/host/nvhost_acm.c | 532 ++++++++++++++++++++ > drivers/video/tegra/host/nvhost_acm.h | 49 ++ > drivers/video/tegra/host/nvhost_cdma.c | 473 +++++++++++++++++ > drivers/video/tegra/host/nvhost_cdma.h | 116 +++++ > drivers/video/tegra/host/nvhost_channel.c | 129 +++++ > drivers/video/tegra/host/nvhost_channel.h | 65 +++ > drivers/video/tegra/host/nvhost_intr.c | 391 ++++++++++++++ > drivers/video/tegra/host/nvhost_intr.h | 110 ++++ > drivers/video/tegra/host/nvhost_job.c | 398 +++++++++++++++ > drivers/video/tegra/host/nvhost_memmgr.c | 252 ++++++++++ > drivers/video/tegra/host/nvhost_memmgr.h | 66 +++ > drivers/video/tegra/host/nvhost_syncpt.c | 453 +++++++++++++++++ > drivers/video/tegra/host/nvhost_syncpt.h | 148 ++++++ > drivers/video/tegra/host/t20/Makefile | 6 + > drivers/video/tegra/host/t20/t20.c | 78 +++ > drivers/video/tegra/host/t20/t20.h | 29 ++ > drivers/video/tegra/host/t30/Makefile | 6 + > drivers/video/tegra/host/t30/t30.c | 80 +++ > drivers/video/tegra/host/t30/t30.h | 29 ++ > include/linux/nvhost.h | 302 +++++++++++ > include/trace/events/nvhost.h | 249 +++++++++ > 48 files changed, 8186 insertions(+) > create mode 100644 drivers/video/tegra/host/Kconfig > create mode 100644 drivers/video/tegra/host/Makefile > create mode 100644 drivers/video/tegra/host/bus_client.c > create mode 100644 drivers/video/tegra/host/bus_client.h > create mode 100644 drivers/video/tegra/host/chip_support.c > create mode 100644 drivers/video/tegra/host/chip_support.h > create mode 100644 drivers/video/tegra/host/debug.c > create mode 100644 drivers/video/tegra/host/debug.h > create mode 100644 drivers/video/tegra/host/dev.c > create mode 100644 drivers/video/tegra/host/dev.h > create mode 100644 drivers/video/tegra/host/dmabuf.c > create mode 100644 drivers/video/tegra/host/dmabuf.h > create mode 100644 drivers/video/tegra/host/host1x/Makefile > create mode 100644 drivers/video/tegra/host/host1x/host1x.c > create mode 100644 drivers/video/tegra/host/host1x/host1x.h > create mode 100644 drivers/video/tegra/host/host1x/host1x01_hardware.h > create mode 100644 drivers/video/tegra/host/host1x/host1x_cdma.c > create mode 100644 drivers/video/tegra/host/host1x/host1x_cdma.h > create mode 100644 drivers/video/tegra/host/host1x/host1x_channel.c > create mode 100644 drivers/video/tegra/host/host1x/host1x_debug.c > create mode 100644 drivers/video/tegra/host/host1x/host1x_intr.c > create mode 100644 drivers/video/tegra/host/host1x/host1x_syncpt.c > create mode 100644 drivers/video/tegra/host/host1x/hw_host1x01_channel.h > create mode 100644 drivers/video/tegra/host/host1x/hw_host1x01_sync.h > create mode 100644 drivers/video/tegra/host/host1x/hw_host1x01_uclass.h > create mode 100644 drivers/video/tegra/host/nvhost_acm.c > create mode 100644 drivers/video/tegra/host/nvhost_acm.h > create mode 100644 drivers/video/tegra/host/nvhost_cdma.c > create mode 100644 drivers/video/tegra/host/nvhost_cdma.h > create mode 100644 drivers/video/tegra/host/nvhost_channel.c > create mode 100644 drivers/video/tegra/host/nvhost_channel.h > create mode 100644 drivers/video/tegra/host/nvhost_intr.c > create mode 100644 drivers/video/tegra/host/nvhost_intr.h > create mode 100644 drivers/video/tegra/host/nvhost_job.c > create mode 100644 drivers/video/tegra/host/nvhost_memmgr.c > create mode 100644 drivers/video/tegra/host/nvhost_memmgr.h > create mode 100644 drivers/video/tegra/host/nvhost_syncpt.c > create mode 100644 drivers/video/tegra/host/nvhost_syncpt.h > create mode 100644 drivers/video/tegra/host/t20/Makefile > create mode 100644 drivers/video/tegra/host/t20/t20.c > create mode 100644 drivers/video/tegra/host/t20/t20.h > create mode 100644 drivers/video/tegra/host/t30/Makefile > create mode 100644 drivers/video/tegra/host/t30/t30.c > create mode 100644 drivers/video/tegra/host/t30/t30.h > create mode 100644 include/linux/nvhost.h > create mode 100644 include/trace/events/nvhost.h > > diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig > index fb9a14e..94c861b 100644 > --- a/drivers/video/Kconfig > +++ b/drivers/video/Kconfig > @@ -2463,4 +2463,6 @@ config FB_SH_MOBILE_MERAM > Up to 4 memory channels can be configured, allowing 4 RGB or > 2 YCbCr framebuffers to be configured. > > +source "drivers/video/tegra/host/Kconfig" > + > endmenu > diff --git a/drivers/video/Makefile b/drivers/video/Makefile > index b936b00..aae33a1 100644 > --- a/drivers/video/Makefile > +++ b/drivers/video/Makefile > @@ -17,6 +17,8 @@ obj-y += backlight/ > > obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ > > +obj-$(CONFIG_TEGRA_GRHOST) += tegra/host/ > + > obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o > obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o > obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o > diff --git a/drivers/video/tegra/host/Kconfig b/drivers/video/tegra/host/Kconfig > new file mode 100644 > index 0000000..2954bea > --- /dev/null > +++ b/drivers/video/tegra/host/Kconfig > @@ -0,0 +1,20 @@ > +config TEGRA_GRHOST > + tristate "Tegra graphics host driver" > + help > + Driver for the Tegra graphics host hardware. > + > +config TEGRA_GRHOST_USE_DMABUF > + depends on TEGRA_GRHOST > + bool "Support dmabuf buffers" > + default y > + select DMA_SHARED_BUFFER > + help > + Support dmabuf buffers. > + > +config TEGRA_GRHOST_DEFAULT_TIMEOUT > + depends on TEGRA_GRHOST > + int "Default timeout for submits" > + default 10000 > + help > + Default timeout for jobs in milliseconds. Set to zero for no timeout. > + > diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile > new file mode 100644 > index 0000000..bd12f98 > --- /dev/null > +++ b/drivers/video/tegra/host/Makefile > @@ -0,0 +1,21 @@ > +ccflags-y = -Idrivers/video/tegra/host -Iarch/arm/mach-tegra > + > +nvhost-objs = \ > + nvhost_acm.o \ > + nvhost_syncpt.o \ > + nvhost_cdma.o \ > + nvhost_intr.o \ > + nvhost_channel.o \ > + nvhost_job.o \ > + dev.o \ > + debug.o \ > + bus_client.o \ > + chip_support.o \ > + nvhost_memmgr.o > + > +obj-$(CONFIG_TEGRA_GRHOST) += host1x/ > +obj-$(CONFIG_TEGRA_GRHOST) += t20/ > +obj-$(CONFIG_TEGRA_GRHOST) += t30/ > +obj-$(CONFIG_TEGRA_GRHOST) += nvhost.o > + > +obj-$(CONFIG_TEGRA_GRHOST_USE_DMABUF) += dmabuf.o > diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c > new file mode 100644 > index 0000000..886d3fe > --- /dev/null > +++ b/drivers/video/tegra/host/bus_client.c > @@ -0,0 +1,101 @@ > +/* > + * drivers/video/tegra/host/bus_client.c > + * > + * Tegra Graphics Host Client Module > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/string.h> > +#include <linux/spinlock.h> > +#include <linux/fs.h> > +#include <linux/cdev.h> > +#include <linux/uaccess.h> > +#include <linux/file.h> > +#include <linux/clk.h> > +#include <linux/hrtimer.h> > +#include <linux/export.h> > + > +#include <trace/events/nvhost.h> > + > +#include <linux/io.h> > +#include <linux/string.h> > + > +#include <linux/nvhost.h> > + > +#include "debug.h" > +#include "bus_client.h" > +#include "dev.h" > +#include "nvhost_memmgr.h" > +#include "chip_support.h" > +#include "nvhost_acm.h" > + > +#include "nvhost_channel.h" > + > +int nvhost_client_device_init(struct platform_device *dev) > +{ > + int err; > + struct nvhost_master *nvhost_master = nvhost_get_host(dev); > + struct nvhost_channel *ch; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + ch = nvhost_alloc_channel(dev); > + if (ch == NULL) > + return -ENODEV; > + > + /* store the pointer to this device for channel */ > + ch->dev = dev; > + > + err = nvhost_channel_init(ch, nvhost_master, pdata->index); > + if (err) > + goto fail; > + > + err = nvhost_module_init(dev); > + if (err) > + goto fail; > + > + err = nvhost_device_list_add(dev); > + if (err) > + goto fail; > + > + nvhost_device_debug_init(dev); > + > + dev_info(&dev->dev, "initialized\n"); > + > + return 0; > + > +fail: > + /* Add clean-up */ > + nvhost_free_channel(ch); > + return err; > +} > +EXPORT_SYMBOL(nvhost_client_device_init); > + > +int nvhost_client_device_suspend(struct platform_device *dev) > +{ > + int ret = 0; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + ret = nvhost_channel_suspend(pdata->channel); > + if (ret) > + return ret; > + > + dev_info(&dev->dev, "suspend status: %d\n", ret); Shouldn't this be placed before the if statement? > + > + return ret; > +} > +EXPORT_SYMBOL(nvhost_client_device_suspend); > diff --git a/drivers/video/tegra/host/bus_client.h b/drivers/video/tegra/host/bus_client.h > new file mode 100644 > index 0000000..2b56213 > --- /dev/null > +++ b/drivers/video/tegra/host/bus_client.h > @@ -0,0 +1,32 @@ > +/* > + * drivers/video/tegra/host/bus_client.h > + * > + * Tegra Graphics Host client > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_BUS_CLIENT_H > +#define __NVHOST_BUS_CLIENT_H > + > +#include <linux/types.h> > + > +struct platform_device; > + > +int nvhost_client_device_init(struct platform_device *dev); > + > +int nvhost_client_device_suspend(struct platform_device *dev); > + > +#endif > diff --git a/drivers/video/tegra/host/chip_support.c b/drivers/video/tegra/host/chip_support.c > new file mode 100644 > index 0000000..fca15a6 > --- /dev/null > +++ b/drivers/video/tegra/host/chip_support.c > @@ -0,0 +1,68 @@ > +/* > + * drivers/video/tegra/host/chip_support.c > + * > + * Tegra Graphics Host Chip support module > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/errno.h> > +#include <linux/types.h> > +#include <linux/bug.h> > +#include <linux/slab.h> > + > +#include "chip_support.h" > +#include "t20/t20.h" > +#include "t30/t30.h" > + > +#include "fuse.h" > + > +struct nvhost_chip_support *nvhost_chip_ops; Should this be static (considering that the below function exist)? > + > +struct nvhost_chip_support *nvhost_get_chip_ops(void) > +{ > + return nvhost_chip_ops; > +} > + > +int nvhost_init_chip_support(struct nvhost_master *host) > +{ > + int err = 0; > + > + if (nvhost_chip_ops == NULL) { > + nvhost_chip_ops = kzalloc(sizeof(*nvhost_chip_ops), GFP_KERNEL); > + if (nvhost_chip_ops == NULL) { > + pr_err("%s: Cannot allocate nvhost_chip_support\n", > + __func__); > + return 0; > + } > + } > + > + switch (tegra_chip_id) { > + case TEGRA20: > + nvhost_chip_ops->soc_name = "tegra2x"; > + err = nvhost_init_t20_support(host, nvhost_chip_ops); > + break; > + > + case TEGRA30: > + nvhost_chip_ops->soc_name = "tegra3x"; > + err = nvhost_init_t30_support(host, nvhost_chip_ops); > + break; > + > + default: > + err = -ENODEV; > + } > + > + return err; > +} > diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h > new file mode 100644 > index 0000000..ce2ac22 > --- /dev/null > +++ b/drivers/video/tegra/host/chip_support.h > @@ -0,0 +1,179 @@ > +/* > + * drivers/video/tegra/host/chip_support.h > + * > + * Tegra Graphics Host Chip Support > + * > + * Copyright (c) 2011-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 <http://www.gnu.org/licenses/>. > + */ > +#ifndef _NVHOST_CHIP_SUPPORT_H_ > +#define _NVHOST_CHIP_SUPPORT_H_ > + > +#include <linux/types.h> > + > +struct output; > + > +struct nvhost_master; > +struct nvhost_intr; > +struct nvhost_syncpt; > +struct nvhost_userctx_timeout; > +struct nvhost_channel; > +struct nvhost_cdma; > +struct nvhost_job; > +struct push_buffer; > +struct nvhost_syncpt; > +struct dentry; > +struct nvhost_job; > +struct nvhost_job_unpin_data; > +struct nvhost_intr_syncpt; > +struct mem_handle; > +struct mem_mgr; > +struct platform_device; > + > +struct nvhost_channel_ops { > + const char *soc_name; > + int (*init)(struct nvhost_channel *, > + struct nvhost_master *, > + int chid); > + int (*submit)(struct nvhost_job *job); > +}; > + > +struct nvhost_cdma_ops { > + void (*start)(struct nvhost_cdma *); > + void (*stop)(struct nvhost_cdma *); > + void (*kick)(struct nvhost_cdma *); > + int (*timeout_init)(struct nvhost_cdma *, > + u32 syncpt_id); > + void (*timeout_destroy)(struct nvhost_cdma *); > + void (*timeout_teardown_begin)(struct nvhost_cdma *); > + void (*timeout_teardown_end)(struct nvhost_cdma *, > + u32 getptr); > + void (*timeout_cpu_incr)(struct nvhost_cdma *, > + u32 getptr, > + u32 syncpt_incrs, > + u32 syncval, > + u32 nr_slots); > +}; > + > +struct nvhost_pushbuffer_ops { > + void (*reset)(struct push_buffer *); > + int (*init)(struct push_buffer *); > + void (*destroy)(struct push_buffer *); > + void (*push_to)(struct push_buffer *, > + struct mem_mgr *, struct mem_handle *, > + u32 op1, u32 op2); > + void (*pop_from)(struct push_buffer *, > + unsigned int slots); > + u32 (*space)(struct push_buffer *); > + u32 (*putptr)(struct push_buffer *); > +}; > + > +struct nvhost_debug_ops { > + void (*debug_init)(struct dentry *de); > + void (*show_channel_cdma)(struct nvhost_master *, > + struct nvhost_channel *, > + struct output *, > + int chid); > + void (*show_channel_fifo)(struct nvhost_master *, > + struct nvhost_channel *, > + struct output *, > + int chid); > + void (*show_mlocks)(struct nvhost_master *m, > + struct output *o); > + > +}; > + > +struct nvhost_syncpt_ops { > + void (*reset)(struct nvhost_syncpt *, u32 id); > + void (*reset_wait_base)(struct nvhost_syncpt *, u32 id); > + void (*read_wait_base)(struct nvhost_syncpt *, u32 id); > + u32 (*update_min)(struct nvhost_syncpt *, u32 id); > + void (*cpu_incr)(struct nvhost_syncpt *, u32 id); > + int (*patch_wait)(struct nvhost_syncpt *sp, > + void *patch_addr); > + void (*debug)(struct nvhost_syncpt *); > + const char * (*name)(struct nvhost_syncpt *, u32 id); > +}; > + > +struct nvhost_intr_ops { > + void (*init_host_sync)(struct nvhost_intr *); > + void (*set_host_clocks_per_usec)( > + struct nvhost_intr *, u32 clocks); > + void (*set_syncpt_threshold)( > + struct nvhost_intr *, u32 id, u32 thresh); > + void (*enable_syncpt_intr)(struct nvhost_intr *, u32 id); > + void (*disable_syncpt_intr)(struct nvhost_intr *, u32 id); > + void (*disable_all_syncpt_intrs)(struct nvhost_intr *); > + int (*request_host_general_irq)(struct nvhost_intr *); > + void (*free_host_general_irq)(struct nvhost_intr *); > + int (*free_syncpt_irq)(struct nvhost_intr *); > +}; > + > +struct nvhost_dev_ops { > + struct nvhost_channel *(*alloc_nvhost_channel)( > + struct platform_device *dev); > + void (*free_nvhost_channel)(struct nvhost_channel *ch); > +}; > + > +struct nvhost_mem_ops { > + struct mem_mgr *(*alloc_mgr)(void); > + void (*put_mgr)(struct mem_mgr *); > + struct mem_mgr *(*get_mgr)(struct mem_mgr *); > + struct mem_mgr *(*get_mgr_file)(int fd); > + struct mem_handle *(*alloc)(struct mem_mgr *, > + size_t size, size_t align, > + int flags); > + struct mem_handle *(*get)(struct mem_mgr *, > + u32 id, struct platform_device *); > + void (*put)(struct mem_mgr *, struct mem_handle *); > + struct sg_table *(*pin)(struct mem_mgr *, struct mem_handle *); > + void (*unpin)(struct mem_mgr *, struct mem_handle *, struct sg_table *); > + void *(*mmap)(struct mem_handle *); > + void (*munmap)(struct mem_handle *, void *); > + void *(*kmap)(struct mem_handle *, unsigned int); > + void (*kunmap)(struct mem_handle *, unsigned int, void *); > + int (*pin_array_ids)(struct mem_mgr *, > + struct platform_device *, > + long unsigned *, > + dma_addr_t *, > + u32, > + struct nvhost_job_unpin_data *); > +}; > + > +struct nvhost_chip_support { > + const char *soc_name; > + struct nvhost_channel_ops channel; > + struct nvhost_cdma_ops cdma; > + struct nvhost_pushbuffer_ops push_buffer; > + struct nvhost_debug_ops debug; > + struct nvhost_syncpt_ops syncpt; > + struct nvhost_intr_ops intr; > + struct nvhost_dev_ops nvhost_dev; > + struct nvhost_mem_ops mem; > +}; > + > +struct nvhost_chip_support *nvhost_get_chip_ops(void); > + > +#define host_device_op() (nvhost_get_chip_ops()->nvhost_dev) > +#define channel_cdma_op() (nvhost_get_chip_ops()->cdma) > +#define channel_op() (nvhost_get_chip_ops()->channel) > +#define syncpt_op() (nvhost_get_chip_ops()->syncpt) > +#define intr_op() (nvhost_get_chip_ops()->intr) > +#define cdma_op() (nvhost_get_chip_ops()->cdma) > +#define cdma_pb_op() (nvhost_get_chip_ops()->push_buffer) > +#define mem_op() (nvhost_get_chip_ops()->mem) > + > +int nvhost_init_chip_support(struct nvhost_master *host); > + > +#endif /* _NVHOST_CHIP_SUPPORT_H_ */ > diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c > new file mode 100644 > index 0000000..4f912f2 > --- /dev/null > +++ b/drivers/video/tegra/host/debug.c > @@ -0,0 +1,255 @@ > +/* > + * drivers/video/tegra/host/debug.c > + * > + * Copyright (C) 2010 Google, Inc. > + * Author: Erik Gilling <konkers@xxxxxxxxxxx> > + * > + * Copyright (C) 2011-2012 NVIDIA Corporation > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that 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. > + * > + */ > + > +#include <linux/debugfs.h> > +#include <linux/seq_file.h> > +#include <linux/uaccess.h> > + > +#include <linux/io.h> > + > +#include "dev.h" > +#include "debug.h" > +#include "nvhost_acm.h" > +#include "nvhost_channel.h" > +#include "chip_support.h" > + > +pid_t nvhost_debug_null_kickoff_pid; > +unsigned int nvhost_debug_trace_cmdbuf; > + > +pid_t nvhost_debug_force_timeout_pid; > +u32 nvhost_debug_force_timeout_val; > +u32 nvhost_debug_force_timeout_channel; > + > +void nvhost_debug_output(struct output *o, const char *fmt, ...) > +{ > + va_list args; > + int len; > + > + va_start(args, fmt); > + len = vsnprintf(o->buf, sizeof(o->buf), fmt, args); > + va_end(args); > + o->fn(o->ctx, o->buf, len); > +} > + > +static int show_channels(struct platform_device *pdev, void *data) > +{ > + struct nvhost_channel *ch; > + struct output *o = data; > + struct nvhost_master *m; > + struct nvhost_device_data *pdata; > + > + if (pdev == NULL) > + return 0; > + > + pdata = platform_get_drvdata(pdev); > + m = nvhost_get_host(pdev); > + ch = pdata->channel; > + if (ch) { > + mutex_lock(&ch->reflock); > + if (ch->refcount) { > + mutex_lock(&ch->cdma.lock); > + nvhost_get_chip_ops()->debug.show_channel_fifo( > + m, ch, o, pdata->index); > + nvhost_get_chip_ops()->debug.show_channel_cdma( > + m, ch, o, pdata->index); > + mutex_unlock(&ch->cdma.lock); > + } > + mutex_unlock(&ch->reflock); > + } > + > + return 0; > +} > + > +static void show_syncpts(struct nvhost_master *m, struct output *o) > +{ > + int i; > + nvhost_debug_output(o, "---- syncpts ----\n"); > + for (i = 0; i < nvhost_syncpt_nb_pts(&m->syncpt); i++) { > + u32 max = nvhost_syncpt_read_max(&m->syncpt, i); > + u32 min = nvhost_syncpt_update_min(&m->syncpt, i); > + if (!min && !max) > + continue; > + nvhost_debug_output(o, "id %d (%s) min %d max %d\n", > + i, nvhost_get_chip_ops()->syncpt.name(&m->syncpt, i), > + min, max); > + } > + > + for (i = 0; i < nvhost_syncpt_nb_bases(&m->syncpt); i++) { > + u32 base_val; > + base_val = nvhost_syncpt_read_wait_base(&m->syncpt, i); > + if (base_val) > + nvhost_debug_output(o, "waitbase id %d val %d\n", > + i, base_val); > + } > + > + nvhost_debug_output(o, "\n"); > +} > + > +static void show_all(struct nvhost_master *m, struct output *o) > +{ > + nvhost_module_busy(m->dev); > + > + nvhost_get_chip_ops()->debug.show_mlocks(m, o); > + show_syncpts(m, o); > + nvhost_debug_output(o, "---- channels ----\n"); > + nvhost_device_list_for_all(o, show_channels); > + > + nvhost_module_idle(m->dev); > +} > + > +#ifdef CONFIG_DEBUG_FS > +static int show_channels_no_fifo(struct platform_device *pdev, void *data) > +{ > + struct nvhost_channel *ch; > + struct output *o = data; > + struct nvhost_master *m; > + struct nvhost_device_data *pdata; > + > + if (pdev == NULL) > + return 0; > + > + pdata = platform_get_drvdata(pdev); > + m = nvhost_get_host(pdev); > + ch = pdata->channel; > + if (ch) { > + mutex_lock(&ch->reflock); > + if (ch->refcount) { > + mutex_lock(&ch->cdma.lock); > + nvhost_get_chip_ops()->debug.show_channel_cdma(m, > + ch, o, pdata->index); > + mutex_unlock(&ch->cdma.lock); > + } > + mutex_unlock(&ch->reflock); > + } > + > + return 0; > +} > + > +static void show_all_no_fifo(struct nvhost_master *m, struct output *o) > +{ > + nvhost_module_busy(m->dev); > + > + nvhost_get_chip_ops()->debug.show_mlocks(m, o); > + show_syncpts(m, o); > + nvhost_debug_output(o, "---- channels ----\n"); > + nvhost_device_list_for_all(o, show_channels_no_fifo); > + > + nvhost_module_idle(m->dev); > +} > + > +static int nvhost_debug_show_all(struct seq_file *s, void *unused) > +{ > + struct output o = { > + .fn = write_to_seqfile, > + .ctx = s > + }; > + show_all(s->private, &o); > + return 0; > +} > + > +static int nvhost_debug_show(struct seq_file *s, void *unused) > +{ > + struct output o = { > + .fn = write_to_seqfile, > + .ctx = s > + }; > + show_all_no_fifo(s->private, &o); > + return 0; > +} > + > +static int nvhost_debug_open_all(struct inode *inode, struct file *file) > +{ > + return single_open(file, nvhost_debug_show_all, inode->i_private); > +} > + > +static const struct file_operations nvhost_debug_all_fops = { > + .open = nvhost_debug_open_all, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static int nvhost_debug_open(struct inode *inode, struct file *file) > +{ > + return single_open(file, nvhost_debug_show, inode->i_private); > +} > + > +static const struct file_operations nvhost_debug_fops = { > + .open = nvhost_debug_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +void nvhost_device_debug_init(struct platform_device *dev) > +{ > + struct dentry *de = NULL; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + de = debugfs_create_dir(dev->name, de); > + > + pdata->debugfs = de; > +} > + > +void nvhost_debug_init(struct nvhost_master *master) > +{ > + struct nvhost_device_data *pdata; > + struct dentry *de = debugfs_create_dir("tegra_host", NULL); > + > + if (!de) > + return; > + > + pdata = platform_get_drvdata(master->dev); > + > + /* Store the created entry */ > + pdata->debugfs = de; > + > + debugfs_create_file("status", S_IRUGO, de, > + master, &nvhost_debug_fops); > + debugfs_create_file("status_all", S_IRUGO, de, > + master, &nvhost_debug_all_fops); > + > + debugfs_create_u32("null_kickoff_pid", S_IRUGO|S_IWUSR, de, > + &nvhost_debug_null_kickoff_pid); > + debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de, > + &nvhost_debug_trace_cmdbuf); > + > + if (nvhost_get_chip_ops()->debug.debug_init) > + nvhost_get_chip_ops()->debug.debug_init(de); > + > + debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de, > + &nvhost_debug_force_timeout_pid); > + debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de, > + &nvhost_debug_force_timeout_val); > + debugfs_create_u32("force_timeout_channel", S_IRUGO|S_IWUSR, de, > + &nvhost_debug_force_timeout_channel); > +} > +#else > +void nvhost_debug_init(struct nvhost_master *master) > +{ > +} > +#endif > + > +void nvhost_debug_dump(struct nvhost_master *master) > +{ > + struct output o = { > + .fn = write_to_printk > + }; > + show_all(master, &o); > +} > diff --git a/drivers/video/tegra/host/debug.h b/drivers/video/tegra/host/debug.h > new file mode 100644 > index 0000000..0d1110a > --- /dev/null > +++ b/drivers/video/tegra/host/debug.h > @@ -0,0 +1,50 @@ > +/* > + * drivers/video/tegra/host/debug.h > + * > + * Tegra Graphics Host Debug > + * > + * Copyright (c) 2011-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 <http://www.gnu.org/licenses/>. > + */ > +#ifndef __NVHOST_DEBUG_H > +#define __NVHOST_DEBUG_H > + > +#include <linux/debugfs.h> > +#include <linux/seq_file.h> > + > +struct output { > + void (*fn)(void *ctx, const char *str, size_t len); > + void *ctx; > + char buf[256]; > +}; > + > +static inline void write_to_seqfile(void *ctx, const char *str, size_t len) > +{ > + seq_write((struct seq_file *)ctx, str, len); > +} > + > +static inline void write_to_printk(void *ctx, const char *str, size_t len) > +{ > + pr_info("%s", str); > +} > + > +void nvhost_debug_output(struct output *o, const char *fmt, ...); > + > +extern pid_t nvhost_debug_null_kickoff_pid; > +extern pid_t nvhost_debug_force_timeout_pid; > +extern u32 nvhost_debug_force_timeout_val; > +extern u32 nvhost_debug_force_timeout_channel; > +extern unsigned int nvhost_debug_trace_cmdbuf; > + > +#endif /*__NVHOST_DEBUG_H */ > diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c > new file mode 100644 > index 0000000..4743039 > --- /dev/null > +++ b/drivers/video/tegra/host/dev.c > @@ -0,0 +1,104 @@ > +/* > + * drivers/video/tegra/host/dev.c > + * > + * Tegra Graphics Host Driver Entrypoint > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#define CREATE_TRACE_POINTS > +#include <trace/events/nvhost.h> > + > +#include <linux/module.h> > +#include <linux/io.h> > +#include <linux/nvhost.h> > +#include <linux/list.h> > +#include <linux/slab.h> > + > +/* host1x device list is used in 2 places: > + * 1. In ioctl(NVHOST_IOCTL_CTRL_MODULE_REGRDWR) of host1x device > + * 2. debug-fs dump of host1x and client device > + * as well as channel state */ > +struct nvhost_device_list { > + struct list_head list; > + struct platform_device *pdev; > +}; > + > +/* HEAD for the host1x device list */ > +static struct nvhost_device_list ndev_head; > + > +/* Constructor for the host1x device list */ > +void nvhost_device_list_init(void) > +{ > + INIT_LIST_HEAD(&ndev_head.list); > +} > + > +/* Adds a device to tail of host1x device list */ > +int nvhost_device_list_add(struct platform_device *pdev) > +{ > + struct nvhost_device_list *list; > + > + list = kzalloc(sizeof(struct nvhost_device_list), GFP_KERNEL); > + if (!list) > + return -ENOMEM; > + > + list->pdev = pdev; > + list_add_tail(&list->list, &ndev_head.list); > + > + return 0; > +} > + > +/* Iterator function for host1x device list > + * It takes a fptr as an argument and calls that function for each > + * device in the list */ > +void nvhost_device_list_for_all(void *data, > + int (*fptr)(struct platform_device *pdev, void *fdata)) > +{ > + struct list_head *pos; > + struct nvhost_device_list *nlist; > + int ret; > + > + list_for_each(pos, &ndev_head.list) { > + nlist = list_entry(pos, struct nvhost_device_list, list); > + if (nlist && nlist->pdev && fptr) { > + ret = fptr(nlist->pdev, data); > + if (ret) { > + pr_info("%s: iterator error\n", __func__); > + break; > + } > + } > + } > +} > + > +/* Removes a device from the host1x device list */ > +void nvhost_device_list_remove(struct platform_device *pdev) > +{ > + struct list_head *pos; > + struct nvhost_device_list *nlist; > + > + list_for_each(pos, &ndev_head.list) { > + nlist = list_entry(pos, struct nvhost_device_list, list); > + if (nlist && nlist->pdev == pdev) { > + list_del(&nlist->list); > + kfree(nlist); Can there be multiple matches? If yes, shouldn't you be using list_for_each_safe, instead of list_for_each? If no, then the function should return immediately? Also, I think you can use nlist instead of pos and remove calling list_entry()? > + } > + } > +} > + > +MODULE_AUTHOR("Terje Bergstrom <tbergstrom@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("Graphics host driver for Tegra products"); > +MODULE_VERSION("1.0"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform-nvhost"); > diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h > new file mode 100644 > index 0000000..12dfda5 > --- /dev/null > +++ b/drivers/video/tegra/host/dev.h > @@ -0,0 +1,33 @@ > +/* > + * drivers/video/tegra/host/dev.h > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef NVHOST_DEV_H > +#define NVHOST_DEV_H > + > +#include "host1x/host1x.h" > + > +struct platform_device; > + > +void nvhost_device_list_init(void); > +int nvhost_device_list_add(struct platform_device *pdev); > +void nvhost_device_list_for_all(void *data, > + int (*fptr)(struct platform_device *pdev, void *fdata)); > +struct platform_device *nvhost_device_list_match_by_id(u32 id); > +void nvhost_device_list_remove(struct platform_device *pdev); > + > +#endif > diff --git a/drivers/video/tegra/host/dmabuf.c b/drivers/video/tegra/host/dmabuf.c > new file mode 100644 > index 0000000..0ae9d78 > --- /dev/null > +++ b/drivers/video/tegra/host/dmabuf.c > @@ -0,0 +1,151 @@ > +/* > + * drivers/video/tegra/host/dmabuf.c > + * > + * Tegra Graphics Host DMA-BUF support > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/dma-buf.h> > +#include <linux/nvhost.h> > +#include "chip_support.h" > +#include "nvhost_memmgr.h" > + > +static inline struct dma_buf_attachment *to_dmabuf_att(struct mem_handle *h) > +{ > + return (struct dma_buf_attachment *)(((u32)h) & ~0x3); > +} > + > +static inline struct dma_buf *to_dmabuf(struct mem_handle *h) > +{ > + return to_dmabuf_att(h)->dmabuf; > +} > + > +static inline int to_dmabuf_fd(u32 id) > +{ > + return nvhost_memmgr_id(id) >> 2; > +} > +struct mem_handle *nvhost_dmabuf_alloc(size_t size, size_t align, int flags) > +{ > + /* TODO: Add allocation via DMA Mapping API */ > + return NULL; > +} > + > +void nvhost_dmabuf_put(struct mem_handle *handle) > +{ > + struct dma_buf_attachment *attach = to_dmabuf_att(handle); > + struct dma_buf *dmabuf = attach->dmabuf; > + dma_buf_detach(dmabuf, attach); > + dma_buf_put(dmabuf); > +} > + > +struct sg_table *nvhost_dmabuf_pin(struct mem_handle *handle) > +{ > + return dma_buf_map_attachment(to_dmabuf_att(handle), > + DMA_BIDIRECTIONAL); > +} > + > +void nvhost_dmabuf_unpin(struct mem_handle *handle, struct sg_table *sgt) > +{ > + dma_buf_unmap_attachment(to_dmabuf_att(handle), sgt, DMA_BIDIRECTIONAL); > +} > + > + > +void *nvhost_dmabuf_mmap(struct mem_handle *handle) > +{ > + return dma_buf_vmap(to_dmabuf(handle)); > +} > + > +void nvhost_dmabuf_munmap(struct mem_handle *handle, void *addr) > +{ > + dma_buf_vunmap(to_dmabuf(handle), addr); > +} > + > +void *nvhost_dmabuf_kmap(struct mem_handle *handle, unsigned int pagenum) > +{ > + return dma_buf_kmap(to_dmabuf(handle), pagenum); > +} > + > +void nvhost_dmabuf_kunmap(struct mem_handle *handle, unsigned int pagenum, > + void *addr) > +{ > + dma_buf_kunmap(to_dmabuf(handle), pagenum, addr); > +} > + > +struct mem_handle *nvhost_dmabuf_get(u32 id, struct platform_device *dev) > +{ > + struct mem_handle *h; > + struct dma_buf *buf; > + > + buf = dma_buf_get(to_dmabuf_fd(id)); > + if (IS_ERR_OR_NULL(buf)) > + return (struct mem_handle *)buf; > + else { Redundant 'else' > + h = (struct mem_handle *)dma_buf_attach(buf, &dev->dev); > + if (IS_ERR_OR_NULL(h)) > + dma_buf_put(buf); > + } > + > + return (struct mem_handle *) ((u32)h | mem_mgr_type_dmabuf); > +} > + > +int nvhost_dmabuf_pin_array_ids(struct platform_device *dev, > + long unsigned *ids, > + long unsigned id_type_mask, > + long unsigned id_type, > + u32 count, > + struct nvhost_job_unpin_data *unpin_data, > + dma_addr_t *phys_addr) { Nit: wrong bracket placement > + int i; > + int pin_count = 0; > + int err; > + > + for (i = 0; i < count; i++) { > + struct mem_handle *handle; > + struct sg_table *sgt; > + > + if ((ids[i] & id_type_mask) != id_type) > + continue; > + > + handle = nvhost_dmabuf_get(ids[i], dev); > + > + if (IS_ERR(handle)) { > + err = PTR_ERR(handle); > + goto fail; > + } > + > + sgt = nvhost_dmabuf_pin(handle); > + if (IS_ERR_OR_NULL(sgt)) { > + nvhost_dmabuf_put(handle); > + err = PTR_ERR(sgt); > + goto fail; > + } > + > + phys_addr[i] = sg_dma_address(sgt->sgl); > + > + unpin_data[pin_count].h = handle; > + unpin_data[pin_count].mem = sgt; > + pin_count++; > + } > + return pin_count; > +fail: > + while (pin_count) { > + pin_count--; > + nvhost_dmabuf_unpin(unpin_data[pin_count].h, > + unpin_data[pin_count].mem); > + nvhost_dmabuf_put(unpin_data[pin_count].h); > + } > + return err; > +} > diff --git a/drivers/video/tegra/host/dmabuf.h b/drivers/video/tegra/host/dmabuf.h > new file mode 100644 > index 0000000..fc26477 > --- /dev/null > +++ b/drivers/video/tegra/host/dmabuf.h > @@ -0,0 +1,51 @@ > +/* > + * drivers/video/tegra/host/dmabuf.h > + * > + * Tegra Graphics Host dmabuf memory manager > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_DMABUF_H > +#define __NVHOST_DMABUF_H > + > +#include "nvhost_memmgr.h" > + > +struct nvhost_chip_support; > +struct platform_device; > + > +struct mem_mgr *nvhost_dmabuf_alloc_mgr(void); > +void nvhost_dmabuf_put_mgr(struct mem_mgr *mgr); > +struct mem_mgr *nvhost_dmabuf_get_mgr(struct mem_mgr *mgr); > +struct mem_mgr *nvhost_dmabuf_get_mgr_file(int fd); > +struct mem_handle *nvhost_dmabuf_alloc(struct mem_mgr *mgr, > + size_t size, size_t align, int flags); > +void nvhost_dmabuf_put(struct mem_handle *handle); > +struct sg_table *nvhost_dmabuf_pin(struct mem_handle *handle); > +void nvhost_dmabuf_unpin(struct mem_handle *handle, struct sg_table *sgt); > +void *nvhost_dmabuf_mmap(struct mem_handle *handle); > +void nvhost_dmabuf_munmap(struct mem_handle *handle, void *addr); > +void *nvhost_dmabuf_kmap(struct mem_handle *handle, unsigned int pagenum); > +void nvhost_dmabuf_kunmap(struct mem_handle *handle, unsigned int pagenum, > + void *addr); > +int nvhost_dmabuf_get(u32 id, struct platform_device *dev); > +int nvhost_dmabuf_pin_array_ids(struct platform_device *dev, > + long unsigned *ids, > + long unsigned id_type_mask, > + long unsigned id_type, > + u32 count, > + struct nvhost_job_unpin_data *unpin_data, > + dma_addr_t *phys_addr); > +#endif > diff --git a/drivers/video/tegra/host/host1x/Makefile b/drivers/video/tegra/host/host1x/Makefile > new file mode 100644 > index 0000000..516e16f > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/Makefile > @@ -0,0 +1,6 @@ > +ccflags-y = -Idrivers/video/tegra/host > + > +nvhost-host1x-objs = \ > + host1x.o > + > +obj-$(CONFIG_TEGRA_GRHOST) += nvhost-host1x.o > diff --git a/drivers/video/tegra/host/host1x/host1x.c b/drivers/video/tegra/host/host1x/host1x.c > new file mode 100644 > index 0000000..db4389d > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x.c > @@ -0,0 +1,320 @@ > +/* > + * drivers/video/tegra/host/dev.c > + * > + * Tegra Graphics Host Driver Entrypoint > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/slab.h> > +#include <linux/fs.h> > +#include <linux/cdev.h> > +#include <linux/uaccess.h> > +#include <linux/file.h> > +#include <linux/clk.h> > +#include <linux/hrtimer.h> > +#include <linux/module.h> > +#include <linux/of.h> > + > +#include "dev.h" > +#include <trace/events/nvhost.h> > + > +#include <linux/nvhost.h> > + > +#include "debug.h" > +#include "bus_client.h" > +#include "nvhost_acm.h" > +#include "nvhost_channel.h" > +#include "chip_support.h" > + > +#define DRIVER_NAME "tegra-host1x" > + > +struct nvhost_master *nvhost; > + > +/* public sync point API */ > +u32 host1x_syncpt_incr_max(u32 id, u32 incrs) > +{ > + struct nvhost_syncpt *sp = &nvhost->syncpt; > + return nvhost_syncpt_incr_max(sp, id, incrs); > +} > +EXPORT_SYMBOL(host1x_syncpt_incr_max); > + > +void host1x_syncpt_incr(u32 id) > +{ > + struct nvhost_syncpt *sp = &nvhost->syncpt; > + nvhost_syncpt_incr(sp, id); > +} > +EXPORT_SYMBOL(host1x_syncpt_incr); > + > +u32 host1x_syncpt_read(u32 id) > +{ > + struct nvhost_syncpt *sp = &nvhost->syncpt; > + return nvhost_syncpt_read(sp, id); > +} > +EXPORT_SYMBOL(host1x_syncpt_read); > + > +int host1x_syncpt_wait(u32 id, u32 thresh, > + u32 timeout, u32 *value) > +{ > + struct nvhost_syncpt *sp = &nvhost->syncpt; > + return nvhost_syncpt_wait_timeout(sp, id, thresh, timeout, value); > +} > +EXPORT_SYMBOL(host1x_syncpt_wait); > + > +struct nvhost_ctrl_userctx { > + struct nvhost_master *dev; > + u32 *mod_locks; > +}; > + > +static void power_on_host(struct platform_device *dev) > +{ > + struct nvhost_master *host = nvhost_get_private_data(dev); > + > + nvhost_syncpt_reset(&host->syncpt); > +} > + > +static int power_off_host(struct platform_device *dev) > +{ > + struct nvhost_master *host = nvhost_get_private_data(dev); > + > + nvhost_syncpt_save(&host->syncpt); > + return 0; > +} > + > +static void clock_on_host(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + struct nvhost_master *host = nvhost_get_private_data(dev); > + nvhost_intr_start(&host->intr, clk_get_rate(pdata->clk[0])); > +} > + > +static int clock_off_host(struct platform_device *dev) > +{ > + struct nvhost_master *host = nvhost_get_private_data(dev); > + nvhost_intr_stop(&host->intr); > + return 0; > +} > + > +static int __devinit nvhost_user_init(struct nvhost_master *host) > +{ > + int err = 0; > + > + host->nvhost_class = class_create(THIS_MODULE, IFACE_NAME); > + if (IS_ERR(host->nvhost_class)) { > + err = PTR_ERR(host->nvhost_class); > + dev_err(&host->dev->dev, "failed to create class\n"); > + } > + > + return err; > +} > + > +struct nvhost_channel *nvhost_alloc_channel(struct platform_device *dev) > +{ > + return host_device_op().alloc_nvhost_channel(dev); > +} > + > +void nvhost_free_channel(struct nvhost_channel *ch) > +{ > + host_device_op().free_nvhost_channel(ch); > +} > + > +static void nvhost_free_resources(struct nvhost_master *host) > +{ > + kfree(host->intr.syncpt); > + host->intr.syncpt = 0; > +} > + > +static int __devinit nvhost_alloc_resources(struct nvhost_master *host) > +{ > + int err; > + > + err = nvhost_init_chip_support(host); > + if (err) > + return err; > + > + host->intr.syncpt = devm_kzalloc(&host->dev->dev, > + sizeof(struct nvhost_intr_syncpt) * > + nvhost_syncpt_nb_pts(&host->syncpt), > + GFP_KERNEL); > + > + if (!host->intr.syncpt) { > + /* frees happen in the support removal phase */ > + return -ENOMEM; > + } > + > + return 0; > +} > + > +static int __devinit nvhost_probe(struct platform_device *dev) > +{ > + struct nvhost_master *host; > + struct resource *regs, *intr0, *intr1; > + int i, err; > + struct nvhost_device_data *pdata = > + (struct nvhost_device_data *)dev->dev.platform_data; > + > + regs = platform_get_resource(dev, IORESOURCE_MEM, 0); > + intr0 = platform_get_resource(dev, IORESOURCE_IRQ, 0); > + intr1 = platform_get_resource(dev, IORESOURCE_IRQ, 1); > + > + if (!regs || !intr0 || !intr1) { > + dev_err(&dev->dev, "missing required platform resources\n"); > + return -ENXIO; > + } > + > + host = devm_kzalloc(&dev->dev, sizeof(*host), GFP_KERNEL); > + if (!host) > + return -ENOMEM; > + > + nvhost = host; > + > + host->dev = dev; > + > + /* Copy host1x parameters. The private_data gets replaced > + * by nvhost_master later */ > + memcpy(&host->info, pdata->private_data, > + sizeof(struct host1x_device_info)); > + > + pdata->finalize_poweron = power_on_host; > + pdata->prepare_poweroff = power_off_host; > + pdata->prepare_clockoff = clock_off_host; > + pdata->finalize_clockon = clock_on_host; > + > + pdata->pdev = dev; > + > + /* set common host1x device data */ > + platform_set_drvdata(dev, pdata); > + > + /* set private host1x device data */ > + nvhost_set_private_data(dev, host); > + > + host->aperture = devm_request_and_ioremap(&dev->dev, regs); > + if (!host->aperture) { > + dev_err(&dev->dev, "failed to remap host registers\n"); > + err = -ENXIO; > + goto fail; > + } > + > + err = nvhost_alloc_resources(host); > + if (err) { > + dev_err(&dev->dev, "failed to init chip support\n"); > + goto fail; > + } > + > + host->memmgr = mem_op().alloc_mgr(); > + if (!host->memmgr) { > + dev_err(&dev->dev, "unable to create memory client\n"); > + err = -EIO; > + goto fail; > + } > + > + err = nvhost_syncpt_init(dev, &host->syncpt); > + if (err) > + goto fail; > + > + err = nvhost_intr_init(&host->intr, intr1->start, intr0->start); > + if (err) > + goto fail; > + > + err = nvhost_user_init(host); > + if (err) > + goto fail; > + > + err = nvhost_module_init(dev); > + if (err) > + goto fail; > + > + for (i = 0; i < pdata->num_clks; i++) > + clk_prepare_enable(pdata->clk[i]); > + nvhost_syncpt_reset(&host->syncpt); > + for (i = 0; i < pdata->num_clks; i++) > + clk_disable_unprepare(pdata->clk[i]); > + > + nvhost_device_list_init(); > + err = nvhost_device_list_add(dev); > + if (err) > + goto fail; > + > + nvhost_debug_init(host); > + > + dev_info(&dev->dev, "initialized\n"); > + > + return 0; > + > +fail: > + nvhost_free_resources(host); > + if (host->memmgr) > + mem_op().put_mgr(host->memmgr); > + kfree(host); > + return err; > +} > + > +static int __exit nvhost_remove(struct platform_device *dev) > +{ > + struct nvhost_master *host = nvhost_get_private_data(dev); > + nvhost_intr_deinit(&host->intr); > + nvhost_syncpt_deinit(&host->syncpt); > + nvhost_free_resources(host); > + return 0; > +} > + > +static int nvhost_suspend(struct platform_device *dev, pm_message_t state) > +{ > + struct nvhost_master *host = nvhost_get_private_data(dev); > + int ret = 0; > + > + ret = nvhost_module_suspend(host->dev); > + dev_info(&dev->dev, "suspend status: %d\n", ret); > + > + return ret; > +} > + > +static int nvhost_resume(struct platform_device *dev) > +{ > + dev_info(&dev->dev, "resuming\n"); > + return 0; > +} > + > +static struct of_device_id host1x_match[] __devinitdata = { > + { .compatible = "nvidia,tegra20-host1x", }, > + { .compatible = "nvidia,tegra30-host1x", }, > + { }, > +}; > + > +static struct platform_driver platform_driver = { > + .probe = nvhost_probe, > + .remove = __exit_p(nvhost_remove), > + .suspend = nvhost_suspend, > + .resume = nvhost_resume, > + .driver = { > + .owner = THIS_MODULE, > + .name = DRIVER_NAME, > + .of_match_table = of_match_ptr(host1x_match), > + }, > +}; > + > +static int __init nvhost_mod_init(void) > +{ > + return platform_driver_register(&platform_driver); > +} > + > +static void __exit nvhost_mod_exit(void) > +{ > + platform_driver_unregister(&platform_driver); > +} > + > +module_init(nvhost_mod_init); > +module_exit(nvhost_mod_exit); > + > diff --git a/drivers/video/tegra/host/host1x/host1x.h b/drivers/video/tegra/host/host1x/host1x.h > new file mode 100644 > index 0000000..46ecdf4 > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x.h > @@ -0,0 +1,97 @@ > +/* > + * drivers/video/tegra/host/host1x/host1x.h > + * > + * Tegra Graphics Host Driver Entrypoint > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_HOST1X_H > +#define __NVHOST_HOST1X_H > + > +#include <linux/cdev.h> > +#include <linux/nvhost.h> > + > +#include "nvhost_syncpt.h" > +#include "nvhost_intr.h" > + > +#define TRACE_MAX_LENGTH 128U > +#define IFACE_NAME "nvhost" > + > +struct nvhost_channel; > +struct mem_mgr; > + > +struct nvhost_master { > + void __iomem *aperture; > + void __iomem *sync_aperture; > + struct class *nvhost_class; > + struct cdev cdev; > + struct device *ctrl; > + struct nvhost_syncpt syncpt; > + struct mem_mgr *memmgr; > + struct nvhost_intr intr; > + struct platform_device *dev; > + atomic_t clientid; > + struct host1x_device_info info; > +}; > + > +extern struct nvhost_master *nvhost; > + > +void nvhost_debug_init(struct nvhost_master *master); > +void nvhost_device_debug_init(struct platform_device *dev); > +void nvhost_debug_dump(struct nvhost_master *master); > + > +struct nvhost_channel *nvhost_alloc_channel(struct platform_device *dev); > +void nvhost_free_channel(struct nvhost_channel *ch); > + > +extern pid_t nvhost_debug_null_kickoff_pid; > + > +static inline void *nvhost_get_private_data(struct platform_device *_dev) > +{ > + struct nvhost_device_data *pdata = > + (struct nvhost_device_data *)platform_get_drvdata(_dev); > + WARN_ON(!pdata); > + return (pdata && pdata->private_data) ? pdata->private_data : NULL; > +} > + > +static inline void nvhost_set_private_data(struct platform_device *_dev, > + void *priv_data) > +{ > + struct nvhost_device_data *pdata = > + (struct nvhost_device_data *)platform_get_drvdata(_dev); > + WARN_ON(!pdata); > + if (pdata) > + pdata->private_data = priv_data; > +} > + > +static inline > +struct nvhost_master *nvhost_get_host(struct platform_device *_dev) > +{ > + struct platform_device *pdev; > + > + if (_dev->dev.parent) { > + pdev = to_platform_device(_dev->dev.parent); > + return nvhost_get_private_data(pdev); > + } else > + return nvhost_get_private_data(_dev); > +} > + > +static inline > +struct platform_device *nvhost_get_parent(struct platform_device *_dev) > +{ > + return _dev->dev.parent ? to_platform_device(_dev->dev.parent) : NULL; > +} > + > +#endif > diff --git a/drivers/video/tegra/host/host1x/host1x01_hardware.h b/drivers/video/tegra/host/host1x/host1x01_hardware.h > new file mode 100644 > index 0000000..75468de > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x01_hardware.h > @@ -0,0 +1,157 @@ > +/* > + * drivers/video/tegra/host/host1x/host1x01_hardware.h > + * > + * Tegra Graphics Host Register Offsets for T20/T30 > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_HOST1X01_HARDWARE_H > +#define __NVHOST_HOST1X01_HARDWARE_H > + > +#include <linux/types.h> > +#include <linux/bitops.h> > +#include "hw_host1x01_channel.h" > +#include "hw_host1x01_sync.h" > +#include "hw_host1x01_uclass.h" > + > +/* channel registers */ > +#define NV_HOST1X_CHANNEL_MAP_SIZE_BYTES 16384 > +#define NV_HOST1X_SYNC_MLOCK_NUM 16 > + > +/* sync registers */ > +#define HOST1X_CHANNEL_SYNC_REG_BASE 0x3000 > +#define NV_HOST1X_NB_MLOCKS 16 > + > +static inline u32 nvhost_class_host_wait_syncpt( > + unsigned indx, unsigned threshold) > +{ > + return host1x_uclass_wait_syncpt_indx_f(indx) > + | host1x_uclass_wait_syncpt_thresh_f(threshold); > +} > + > +static inline u32 nvhost_class_host_load_syncpt_base( > + unsigned indx, unsigned threshold) > +{ > + return host1x_uclass_load_syncpt_base_base_indx_f(indx) > + | host1x_uclass_load_syncpt_base_value_f(threshold); > +} > + > +static inline u32 nvhost_class_host_wait_syncpt_base( > + unsigned indx, unsigned base_indx, unsigned offset) > +{ > + return host1x_uclass_wait_syncpt_base_indx_f(indx) > + | host1x_uclass_wait_syncpt_base_base_indx_f(base_indx) > + | host1x_uclass_wait_syncpt_base_offset_f(offset); > +} > + > +static inline u32 nvhost_class_host_incr_syncpt_base( > + unsigned base_indx, unsigned offset) > +{ > + return host1x_uclass_incr_syncpt_base_base_indx_f(base_indx) > + | host1x_uclass_incr_syncpt_base_offset_f(offset); > +} > + > +static inline u32 nvhost_class_host_incr_syncpt( > + unsigned cond, unsigned indx) > +{ > + return host1x_uclass_incr_syncpt_cond_f(cond) > + | host1x_uclass_incr_syncpt_indx_f(indx); > +} > + > +static inline u32 nvhost_class_host_indoff_reg_write( > + unsigned mod_id, unsigned offset, bool auto_inc) > +{ > + u32 v = host1x_uclass_indoff_indbe_f(0xf) > + | host1x_uclass_indoff_indmodid_f(mod_id) > + | host1x_uclass_indoff_indroffset_f(offset); > + if (auto_inc) > + v |= host1x_uclass_indoff_autoinc_f(1); > + return v; > +} > + > +static inline u32 nvhost_class_host_indoff_reg_read( > + unsigned mod_id, unsigned offset, bool auto_inc) > +{ > + u32 v = host1x_uclass_indoff_indmodid_f(mod_id) > + | host1x_uclass_indoff_indroffset_f(offset) > + | host1x_uclass_indoff_rwn_read_v(); > + if (auto_inc) > + v |= host1x_uclass_indoff_autoinc_f(1); > + return v; > +} > + > + > +/* cdma opcodes */ > +static inline u32 nvhost_opcode_setclass( > + unsigned class_id, unsigned offset, unsigned mask) > +{ > + return (0 << 28) | (offset << 16) | (class_id << 6) | mask; > +} > + > +static inline u32 nvhost_opcode_incr(unsigned offset, unsigned count) > +{ > + return (1 << 28) | (offset << 16) | count; > +} > + > +static inline u32 nvhost_opcode_nonincr(unsigned offset, unsigned count) > +{ > + return (2 << 28) | (offset << 16) | count; > +} > + > +static inline u32 nvhost_opcode_mask(unsigned offset, unsigned mask) > +{ > + return (3 << 28) | (offset << 16) | mask; > +} > + > +static inline u32 nvhost_opcode_imm(unsigned offset, unsigned value) > +{ > + return (4 << 28) | (offset << 16) | value; > +} > + > +static inline u32 nvhost_opcode_imm_incr_syncpt(unsigned cond, unsigned indx) > +{ > + return nvhost_opcode_imm(host1x_uclass_incr_syncpt_r(), > + nvhost_class_host_incr_syncpt(cond, indx)); > +} > + > +static inline u32 nvhost_opcode_restart(unsigned address) > +{ > + return (5 << 28) | (address >> 4); > +} > + > +static inline u32 nvhost_opcode_gather(unsigned count) > +{ > + return (6 << 28) | count; > +} > + > +static inline u32 nvhost_opcode_gather_nonincr(unsigned offset, unsigned count) > +{ > + return (6 << 28) | (offset << 16) | BIT(15) | count; > +} > + > +static inline u32 nvhost_opcode_gather_incr(unsigned offset, unsigned count) > +{ > + return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count; > +} > + > +#define NVHOST_OPCODE_NOOP nvhost_opcode_nonincr(0, 0) > + > +static inline u32 nvhost_mask2(unsigned x, unsigned y) > +{ > + return 1 | (1 << (y - x)); > +} > + > +#endif > 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 0000000..224b721 > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x_cdma.c > @@ -0,0 +1,488 @@ > +/* > + * drivers/video/tegra/host/host1x/host1x_cdma.c > + * > + * Tegra Graphics Host Command DMA > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/slab.h> > +#include <linux/scatterlist.h> > +#include <linux/dma-mapping.h> > +#include "nvhost_acm.h" > +#include "nvhost_cdma.h" > +#include "nvhost_channel.h" > +#include "dev.h" > +#include "chip_support.h" > +#include "nvhost_memmgr.h" > + > +#include "host1x_cdma.h" > + > +static inline u32 host1x_channel_dmactrl(int stop, int get_rst, int init_get) > +{ > + return host1x_channel_dmactrl_dmastop_f(stop) > + | host1x_channel_dmactrl_dmagetrst_f(get_rst) > + | host1x_channel_dmactrl_dmainitget_f(init_get); > +} > + > +static void cdma_timeout_handler(struct work_struct *work); > + > +/* > + * push_buffer > + * > + * The push buffer is a circular array of words to be fetched by command DMA. > + * Note that it works slightly differently to the sync queue; fence == cur > + * means that the push buffer is full, not empty. > + */ > + > + > +/** > + * Reset to empty push buffer > + */ > +static void push_buffer_reset(struct push_buffer *pb) > +{ > + pb->fence = PUSH_BUFFER_SIZE - 8; > + pb->cur = 0; > +} > + > +/** > + * Init push buffer resources > + */ > +static void push_buffer_destroy(struct push_buffer *pb); > +static int push_buffer_init(struct push_buffer *pb) > +{ > + struct nvhost_cdma *cdma = pb_to_cdma(pb); > + struct nvhost_master *master = cdma_to_dev(cdma); > + pb->mapped = NULL; > + pb->phys = 0; > + pb->client_handle = NULL; > + > + cdma_pb_op().reset(pb); > + > + /* allocate and map pushbuffer memory */ > + pb->mapped = dma_alloc_writecombine(&master->dev->dev, > + PUSH_BUFFER_SIZE + 4, &pb->phys, GFP_KERNEL); > + if (IS_ERR_OR_NULL(pb->mapped)) { > + pb->mapped = NULL; > + goto fail; > + } > + > + /* memory for storing mem client and handles for each opcode pair */ > + pb->client_handle = kzalloc(NVHOST_GATHER_QUEUE_SIZE * > + sizeof(struct mem_mgr_handle), > + GFP_KERNEL); > + if (!pb->client_handle) > + goto fail; > + > + /* put the restart at the end of pushbuffer memory */ > + *(pb->mapped + (PUSH_BUFFER_SIZE >> 2)) = > + nvhost_opcode_restart(pb->phys); > + > + return 0; > + > +fail: > + push_buffer_destroy(pb); > + return -ENOMEM; > +} > + > +/** > + * Clean up push buffer resources > + */ > +static void push_buffer_destroy(struct push_buffer *pb) > +{ > + struct nvhost_cdma *cdma = pb_to_cdma(pb); > + struct nvhost_master *master = cdma_to_dev(cdma); > + > + if (pb->phys != 0) > + dma_free_writecombine(&master->dev->dev, > + PUSH_BUFFER_SIZE + 4, > + pb->mapped, pb->phys); > + > + kfree(pb->client_handle); > + > + pb->mapped = NULL; > + pb->phys = 0; > + pb->client_handle = 0; > +} > + > +/** > + * Push two words to the push buffer > + * Caller must ensure push buffer is not full > + */ > +static void push_buffer_push_to(struct push_buffer *pb, > + struct mem_mgr *client, struct mem_handle *handle, > + u32 op1, u32 op2) > +{ > + u32 cur = pb->cur; > + u32 *p = (u32 *)((u32)pb->mapped + cur); > + u32 cur_mem = (cur/8) & (NVHOST_GATHER_QUEUE_SIZE - 1); > + WARN_ON(cur == pb->fence); > + *(p++) = op1; > + *(p++) = op2; > + pb->client_handle[cur_mem].client = client; > + pb->client_handle[cur_mem].handle = handle; > + pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1); > +} > + > +/** > + * Pop a number of two word slots from the push buffer > + * Caller must ensure push buffer is not empty > + */ > +static void push_buffer_pop_from(struct push_buffer *pb, > + unsigned int slots) > +{ > + /* Clear the mem references for old items from pb */ > + unsigned int i; > + u32 fence_mem = pb->fence/8; > + for (i = 0; i < slots; i++) { > + int cur_fence_mem = (fence_mem+i) > + & (NVHOST_GATHER_QUEUE_SIZE - 1); > + struct mem_mgr_handle *h = &pb->client_handle[cur_fence_mem]; > + h->client = NULL; > + h->handle = NULL; > + } > + /* Advance the next write position */ > + pb->fence = (pb->fence + slots * 8) & (PUSH_BUFFER_SIZE - 1); > +} > + > +/** > + * Return the number of two word slots free in the push buffer > + */ > +static u32 push_buffer_space(struct push_buffer *pb) > +{ > + return ((pb->fence - pb->cur) & (PUSH_BUFFER_SIZE - 1)) / 8; > +} > + > +static u32 push_buffer_putptr(struct push_buffer *pb) > +{ > + return pb->phys + pb->cur; > +} > + > +/* > + * The syncpt incr buffer is filled with methods to increment syncpts, which > + * is later GATHER-ed into the mainline PB. It's used when a timed out context > + * is interleaved with other work, so needs to inline the syncpt increments > + * to maintain the count (but otherwise does no work). > + */ > + > +/** > + * Init timeout resources > + */ > +static int cdma_timeout_init(struct nvhost_cdma *cdma, > + u32 syncpt_id) > +{ > + if (syncpt_id == NVSYNCPT_INVALID) > + return -EINVAL; > + > + INIT_DELAYED_WORK(&cdma->timeout.wq, cdma_timeout_handler); > + cdma->timeout.initialized = true; > + > + return 0; > +} > + > +/** > + * Clean up timeout resources > + */ > +static void cdma_timeout_destroy(struct nvhost_cdma *cdma) > +{ > + if (cdma->timeout.initialized) > + cancel_delayed_work(&cdma->timeout.wq); > + cdma->timeout.initialized = false; > +} > + > +/** > + * Increment timedout buffer's syncpt via CPU. > + */ > +static void cdma_timeout_cpu_incr(struct nvhost_cdma *cdma, u32 getptr, > + u32 syncpt_incrs, u32 syncval, u32 nr_slots) > +{ > + struct nvhost_master *dev = cdma_to_dev(cdma); > + struct push_buffer *pb = &cdma->push_buffer; > + u32 i, getidx; > + > + for (i = 0; i < syncpt_incrs; i++) > + nvhost_syncpt_cpu_incr(&dev->syncpt, cdma->timeout.syncpt_id); > + > + /* after CPU incr, ensure shadow is up to date */ > + nvhost_syncpt_update_min(&dev->syncpt, cdma->timeout.syncpt_id); > + > + /* NOP all the PB slots */ > + getidx = getptr - pb->phys; > + while (nr_slots--) { > + u32 *p = (u32 *)((u32)pb->mapped + getidx); > + *(p++) = NVHOST_OPCODE_NOOP; > + *(p++) = NVHOST_OPCODE_NOOP; > + dev_dbg(&dev->dev->dev, "%s: NOP at 0x%x\n", > + __func__, pb->phys + getidx); > + getidx = (getidx + 8) & (PUSH_BUFFER_SIZE - 1); > + } > + wmb(); > +} > + > +/** > + * Start channel DMA > + */ > +static void cdma_start(struct nvhost_cdma *cdma) > +{ > + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; > + > + if (cdma->running) > + return; > + > + cdma->last_put = cdma_pb_op().putptr(&cdma->push_buffer); > + > + writel(host1x_channel_dmactrl(true, false, false), > + chan_regs + host1x_channel_dmactrl_r()); > + > + /* set base, put, end pointer (all of memory) */ > + writel(0, chan_regs + host1x_channel_dmastart_r()); > + writel(cdma->last_put, chan_regs + host1x_channel_dmaput_r()); > + writel(0xFFFFFFFF, chan_regs + host1x_channel_dmaend_r()); > + > + /* reset GET */ > + writel(host1x_channel_dmactrl(true, true, true), > + chan_regs + host1x_channel_dmactrl_r()); > + > + /* start the command DMA */ > + writel(host1x_channel_dmactrl(false, false, false), > + chan_regs + host1x_channel_dmactrl_r()); > + > + cdma->running = true; > +} > + > +/** > + * Similar to cdma_start(), but rather than starting from an idle > + * state (where DMA GET is set to DMA PUT), on a timeout we restore > + * DMA GET from an explicit value (so DMA may again be pending). > + */ > +static void cdma_timeout_restart(struct nvhost_cdma *cdma, u32 getptr) > +{ > + struct nvhost_master *dev = cdma_to_dev(cdma); > + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; > + > + if (cdma->running) > + return; > + > + cdma->last_put = cdma_pb_op().putptr(&cdma->push_buffer); > + > + writel(host1x_channel_dmactrl(true, false, false), > + chan_regs + host1x_channel_dmactrl_r()); > + > + /* set base, end pointer (all of memory) */ > + writel(0, chan_regs + host1x_channel_dmastart_r()); > + writel(0xFFFFFFFF, chan_regs + host1x_channel_dmaend_r()); > + > + /* set GET, by loading the value in PUT (then reset GET) */ > + writel(getptr, chan_regs + host1x_channel_dmaput_r()); > + writel(host1x_channel_dmactrl(true, true, true), > + chan_regs + host1x_channel_dmactrl_r()); > + > + dev_dbg(&dev->dev->dev, > + "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", > + __func__, > + readl(chan_regs + host1x_channel_dmaget_r()), > + readl(chan_regs + host1x_channel_dmaput_r()), > + cdma->last_put); > + > + /* deassert GET reset and set PUT */ > + writel(host1x_channel_dmactrl(true, false, false), > + chan_regs + host1x_channel_dmactrl_r()); > + writel(cdma->last_put, chan_regs + host1x_channel_dmaput_r()); > + > + /* start the command DMA */ > + writel(host1x_channel_dmactrl(false, false, false), > + chan_regs + host1x_channel_dmactrl_r()); > + > + cdma->running = true; > +} > + > +/** > + * Kick channel DMA into action by writing its PUT offset (if it has changed) > + */ > +static void cdma_kick(struct nvhost_cdma *cdma) > +{ > + u32 put; > + > + put = cdma_pb_op().putptr(&cdma->push_buffer); > + > + if (put != cdma->last_put) { > + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; > + writel(put, chan_regs + host1x_channel_dmaput_r()); > + cdma->last_put = put; > + } > +} > + > +static void cdma_stop(struct nvhost_cdma *cdma) > +{ > + void __iomem *chan_regs = cdma_to_channel(cdma)->aperture; > + > + mutex_lock(&cdma->lock); > + if (cdma->running) { > + nvhost_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY); > + writel(host1x_channel_dmactrl(true, false, false), > + chan_regs + host1x_channel_dmactrl_r()); > + cdma->running = false; > + } > + mutex_unlock(&cdma->lock); > +} > + > +/** > + * Stops both channel's command processor and CDMA immediately. > + * Also, tears down the channel and resets corresponding module. > + */ > +static void cdma_timeout_teardown_begin(struct nvhost_cdma *cdma) > +{ > + struct nvhost_master *dev = cdma_to_dev(cdma); > + struct nvhost_channel *ch = cdma_to_channel(cdma); > + u32 cmdproc_stop; > + > + if (cdma->torndown && !cdma->running) { > + dev_warn(&dev->dev->dev, "Already torn down\n"); > + return; > + } > + > + dev_dbg(&dev->dev->dev, > + "begin channel teardown (channel id %d)\n", ch->chid); > + > + cmdproc_stop = readl(dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + cmdproc_stop |= BIT(ch->chid); > + writel(cmdproc_stop, dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + > + dev_dbg(&dev->dev->dev, > + "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", > + __func__, > + readl(ch->aperture + host1x_channel_dmaget_r()), > + readl(ch->aperture + host1x_channel_dmaput_r()), > + cdma->last_put); > + > + writel(host1x_channel_dmactrl(true, false, false), > + ch->aperture + host1x_channel_dmactrl_r()); > + > + writel(BIT(ch->chid), dev->sync_aperture + host1x_sync_ch_teardown_r()); > + > + cdma->running = false; > + cdma->torndown = true; > +} > + > +static void cdma_timeout_teardown_end(struct nvhost_cdma *cdma, u32 getptr) > +{ > + struct nvhost_master *dev = cdma_to_dev(cdma); > + struct nvhost_channel *ch = cdma_to_channel(cdma); > + u32 cmdproc_stop; > + > + dev_dbg(&dev->dev->dev, > + "end channel teardown (id %d, DMAGET restart = 0x%x)\n", > + ch->chid, getptr); > + > + cmdproc_stop = readl(dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + cmdproc_stop &= ~(BIT(ch->chid)); > + writel(cmdproc_stop, dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + > + cdma->torndown = false; > + cdma_timeout_restart(cdma, getptr); > +} > + > +/** > + * If this timeout fires, it indicates the current sync_queue entry has > + * exceeded its TTL and the userctx should be timed out and remaining > + * submits already issued cleaned up (future submits return an error). > + */ > +static void cdma_timeout_handler(struct work_struct *work) > +{ > + struct nvhost_cdma *cdma; > + struct nvhost_master *dev; > + struct nvhost_syncpt *sp; > + struct nvhost_channel *ch; > + > + u32 syncpt_val; > + > + u32 prev_cmdproc, cmdproc_stop; > + > + cdma = container_of(to_delayed_work(work), struct nvhost_cdma, > + timeout.wq); > + dev = cdma_to_dev(cdma); > + sp = &dev->syncpt; > + ch = cdma_to_channel(cdma); > + > + nvhost_debug_dump(cdma_to_dev(cdma)); > + > + mutex_lock(&cdma->lock); > + > + if (!cdma->timeout.clientid) { > + dev_dbg(&dev->dev->dev, > + "cdma_timeout: expired, but has no clientid\n"); > + mutex_unlock(&cdma->lock); > + return; > + } > + > + /* stop processing to get a clean snapshot */ > + prev_cmdproc = readl(dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + cmdproc_stop = prev_cmdproc | BIT(ch->chid); > + writel(cmdproc_stop, dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + > + dev_dbg(&dev->dev->dev, "cdma_timeout: cmdproc was 0x%x is 0x%x\n", > + prev_cmdproc, cmdproc_stop); > + > + syncpt_val = nvhost_syncpt_update_min(&dev->syncpt, > + cdma->timeout.syncpt_id); > + > + /* has buffer actually completed? */ > + if ((s32)(syncpt_val - cdma->timeout.syncpt_val) >= 0) { > + dev_dbg(&dev->dev->dev, > + "cdma_timeout: expired, but buffer had completed\n"); > + /* restore */ > + cmdproc_stop = prev_cmdproc & ~(BIT(ch->chid)); > + writel(cmdproc_stop, > + dev->sync_aperture + host1x_sync_cmdproc_stop_r()); > + mutex_unlock(&cdma->lock); > + return; > + } > + > + dev_warn(&dev->dev->dev, > + "%s: timeout: %d (%s), HW thresh %d, done %d\n", > + __func__, > + cdma->timeout.syncpt_id, > + syncpt_op().name(sp, cdma->timeout.syncpt_id), > + syncpt_val, cdma->timeout.syncpt_val); > + > + /* stop HW, resetting channel/module */ > + cdma_op().timeout_teardown_begin(cdma); > + > + nvhost_cdma_update_sync_queue(cdma, sp, ch->dev); > + mutex_unlock(&cdma->lock); > +} > + > +static const struct nvhost_cdma_ops host1x_cdma_ops = { > + .start = cdma_start, > + .stop = cdma_stop, > + .kick = cdma_kick, > + > + .timeout_init = cdma_timeout_init, > + .timeout_destroy = cdma_timeout_destroy, > + .timeout_teardown_begin = cdma_timeout_teardown_begin, > + .timeout_teardown_end = cdma_timeout_teardown_end, > + .timeout_cpu_incr = cdma_timeout_cpu_incr, > +}; > + > +static const struct nvhost_pushbuffer_ops host1x_pushbuffer_ops = { > + .reset = push_buffer_reset, > + .init = push_buffer_init, > + .destroy = push_buffer_destroy, > + .push_to = push_buffer_push_to, > + .pop_from = push_buffer_pop_from, > + .space = push_buffer_space, > + .putptr = push_buffer_putptr, > +}; > + > 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 0000000..94bfc09 > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x_cdma.h > @@ -0,0 +1,39 @@ > +/* > + * drivers/video/tegra/host/host1x/host1x_cdma.h > + * > + * Tegra Graphics Host Command DMA > + * > + * Copyright (c) 2011-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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_HOST1X_HOST1X_CDMA_H > +#define __NVHOST_HOST1X_HOST1X_CDMA_H > + > +/* Size of the sync queue. If it is too small, we won't be able to queue up > + * many command buffers. If it is too large, we waste memory. */ > +#define NVHOST_SYNC_QUEUE_SIZE 512 > + > +/* Number of gathers we allow to be queued up per channel. Must be a > + * power of two. Currently sized such that pushbuffer is 4KB (512*8B). */ > +#define NVHOST_GATHER_QUEUE_SIZE 512 > + > +/* 8 bytes per slot. (This number does not include the final RESTART.) */ > +#define PUSH_BUFFER_SIZE (NVHOST_GATHER_QUEUE_SIZE * 8) > + > +/* 4K page containing GATHERed methods to increment channel syncpts > + * and replaces the original timed out contexts GATHER slots */ > +#define SYNCPT_INCR_BUFFER_SIZE_WORDS (4096 / sizeof(u32)) > + > +#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 0000000..d39fe60 > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x_channel.c > @@ -0,0 +1,157 @@ > +/* > + * drivers/video/tegra/host/host1x/channel_host1x.c > + * > + * Tegra Graphics Host Channel > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/nvhost.h> > +#include "nvhost_channel.h" > +#include "dev.h" > +#include "nvhost_acm.h" > +#include <trace/events/nvhost.h> > +#include <linux/slab.h> > +#include "nvhost_intr.h" > + > +#define NV_FIFO_READ_TIMEOUT 200000 > + > +static void submit_gathers(struct nvhost_job *job) > +{ > + /* push user gathers */ > + int i; > + for (i = 0 ; i < job->num_gathers; i++) { > + struct nvhost_job_gather *g = &job->gathers[i]; > + u32 op1 = nvhost_opcode_gather(g->words); > + u32 op2 = g->mem_base + g->offset; > + nvhost_cdma_push_gather(&job->ch->cdma, > + job->memmgr, > + job->gathers[i].ref, > + job->gathers[i].offset, > + op1, op2); > + } > +} > + > +static int host1x_channel_submit(struct nvhost_job *job) > +{ > + struct nvhost_channel *ch = job->ch; > + struct nvhost_syncpt *sp = &nvhost_get_host(job->ch->dev)->syncpt; > + u32 user_syncpt_incrs = job->syncpt_incrs; > + u32 prev_max = 0; > + u32 syncval; > + int err; > + void *completed_waiter = NULL; > + struct nvhost_device_data *pdata = platform_get_drvdata(ch->dev); > + > + /* Turn on the client module and host1x */ > + nvhost_module_busy(ch->dev); > + > + /* before error checks, return current max */ > + prev_max = job->syncpt_end = > + nvhost_syncpt_read_max(sp, job->syncpt_id); > + > + /* get submit lock */ > + err = mutex_lock_interruptible(&ch->submitlock); > + if (err) { > + nvhost_module_idle(ch->dev); > + goto error; > + } > + > + completed_waiter = nvhost_intr_alloc_waiter(); > + if (!completed_waiter) { > + nvhost_module_idle(ch->dev); > + mutex_unlock(&ch->submitlock); > + err = -ENOMEM; > + goto error; > + } > + > + /* begin a CDMA submit */ > + err = nvhost_cdma_begin(&ch->cdma, job); > + if (err) { > + mutex_unlock(&ch->submitlock); > + nvhost_module_idle(ch->dev); > + goto error; > + } > + > + if (pdata->serialize) { > + /* Force serialization by inserting a host wait for the > + * previous job to finish before this one can commence. */ > + nvhost_cdma_push(&ch->cdma, > + nvhost_opcode_setclass(NV_HOST1X_CLASS_ID, > + host1x_uclass_wait_syncpt_r(), > + 1), > + nvhost_class_host_wait_syncpt(job->syncpt_id, > + nvhost_syncpt_read_max(sp, > + job->syncpt_id))); > + } > + > + syncval = nvhost_syncpt_incr_max(sp, > + job->syncpt_id, user_syncpt_incrs); > + > + job->syncpt_end = syncval; > + > + /* add a setclass for modules that require it */ > + if (pdata->class) > + nvhost_cdma_push(&ch->cdma, > + nvhost_opcode_setclass(pdata->class, 0, 0), > + NVHOST_OPCODE_NOOP); > + > + submit_gathers(job); > + > + /* end CDMA submit & stash pinned hMems into sync queue */ > + nvhost_cdma_end(&ch->cdma, job); > + > + trace_nvhost_channel_submitted(ch->dev->name, > + prev_max, syncval); > + > + /* schedule a submit complete interrupt */ > + err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr, > + job->syncpt_id, syncval, > + NVHOST_INTR_ACTION_SUBMIT_COMPLETE, ch, > + completed_waiter, > + NULL); > + completed_waiter = NULL; > + WARN(err, "Failed to set submit complete interrupt"); > + > + mutex_unlock(&ch->submitlock); > + > + return 0; > + > +error: > + kfree(completed_waiter); > + return err; > +} > + > +static inline void __iomem *host1x_channel_aperture(void __iomem *p, int ndx) > +{ > + p += ndx * NV_HOST1X_CHANNEL_MAP_SIZE_BYTES; > + return p; > +} > + > +static int host1x_channel_init(struct nvhost_channel *ch, > + struct nvhost_master *dev, int index) > +{ > + ch->chid = index; > + mutex_init(&ch->reflock); > + mutex_init(&ch->submitlock); > + > + ch->aperture = host1x_channel_aperture(dev->aperture, index); > + return 0; > +} > + > +static const struct nvhost_channel_ops host1x_channel_ops = { > + .init = host1x_channel_init, > + .submit = host1x_channel_submit, > +}; > 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 0000000..ce6074e > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x_debug.c > @@ -0,0 +1,405 @@ > +/* > + * drivers/video/tegra/host/host1x/host1x_debug.c > + * > + * Copyright (C) 2010 Google, Inc. > + * Author: Erik Gilling <konkers@xxxxxxxxxxx> > + * > + * Copyright (C) 2011 NVIDIA Corporation > + * > + * This software is licensed under the terms of the GNU General Public > + * License version 2, as published by the Free Software Foundation, and > + * may be copied, distributed, and modified under those terms. > + * > + * This program is distributed in the hope that 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. > + * > + */ > + > +#include <linux/debugfs.h> > +#include <linux/seq_file.h> > +#include <linux/mm.h> > +#include <linux/scatterlist.h> > + > +#include <linux/io.h> > + > +#include "dev.h" > +#include "debug.h" > +#include "nvhost_cdma.h" > +#include "nvhost_channel.h" > +#include "chip_support.h" > +#include "nvhost_memmgr.h" > + > +#define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 > + > +enum { > + NVHOST_DBG_STATE_CMD = 0, > + NVHOST_DBG_STATE_DATA = 1, > + NVHOST_DBG_STATE_GATHER = 2 > +}; > + > +static int show_channel_command(struct output *o, u32 addr, u32 val, int *count) > +{ > + unsigned mask; > + unsigned subop; > + > + switch (val >> 28) { > + case 0x0: > + mask = val & 0x3f; > + if (mask) { > + nvhost_debug_output(o, > + "SETCL(class=%03x, offset=%03x, mask=%02x, [", > + val >> 6 & 0x3ff, val >> 16 & 0xfff, mask); > + *count = hweight8(mask); > + return NVHOST_DBG_STATE_DATA; > + } else { > + nvhost_debug_output(o, "SETCL(class=%03x)\n", > + val >> 6 & 0x3ff); > + return NVHOST_DBG_STATE_CMD; > + } > + > + case 0x1: > + nvhost_debug_output(o, "INCR(offset=%03x, [", > + val >> 16 & 0xfff); > + *count = val & 0xffff; > + return NVHOST_DBG_STATE_DATA; > + > + case 0x2: > + nvhost_debug_output(o, "NONINCR(offset=%03x, [", > + val >> 16 & 0xfff); > + *count = val & 0xffff; > + return NVHOST_DBG_STATE_DATA; > + > + case 0x3: > + mask = val & 0xffff; > + nvhost_debug_output(o, "MASK(offset=%03x, mask=%03x, [", > + val >> 16 & 0xfff, mask); > + *count = hweight16(mask); > + return NVHOST_DBG_STATE_DATA; > + > + case 0x4: > + nvhost_debug_output(o, "IMM(offset=%03x, data=%03x)\n", > + val >> 16 & 0xfff, val & 0xffff); > + return NVHOST_DBG_STATE_CMD; > + > + case 0x5: > + nvhost_debug_output(o, "RESTART(offset=%08x)\n", val << 4); > + return NVHOST_DBG_STATE_CMD; > + > + case 0x6: > + nvhost_debug_output(o, > + "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[", > + val >> 16 & 0xfff, val >> 15 & 0x1, val >> 14 & 0x1, > + val & 0x3fff); > + *count = val & 0x3fff; /* TODO: insert */ > + return NVHOST_DBG_STATE_GATHER; > + > + case 0xe: > + subop = val >> 24 & 0xf; > + if (subop == 0) > + nvhost_debug_output(o, "ACQUIRE_MLOCK(index=%d)\n", > + val & 0xff); > + else if (subop == 1) > + nvhost_debug_output(o, "RELEASE_MLOCK(index=%d)\n", > + val & 0xff); > + else > + nvhost_debug_output(o, "EXTEND_UNKNOWN(%08x)\n", val); > + return NVHOST_DBG_STATE_CMD; > + > + default: > + return NVHOST_DBG_STATE_CMD; > + } > +} > + > +static void show_channel_gather(struct output *o, u32 addr, > + phys_addr_t phys_addr, u32 words, struct nvhost_cdma *cdma); > + > +static void show_channel_word(struct output *o, int *state, int *count, > + u32 addr, u32 val, struct nvhost_cdma *cdma) > +{ > + static int start_count, dont_print; > + > + switch (*state) { > + case NVHOST_DBG_STATE_CMD: > + if (addr) > + nvhost_debug_output(o, "%08x: %08x:", addr, val); > + else > + nvhost_debug_output(o, "%08x:", val); > + > + *state = show_channel_command(o, addr, val, count); > + dont_print = 0; > + start_count = *count; > + if (*state == NVHOST_DBG_STATE_DATA && *count == 0) { > + *state = NVHOST_DBG_STATE_CMD; > + nvhost_debug_output(o, "])\n"); > + } > + break; > + > + case NVHOST_DBG_STATE_DATA: > + (*count)--; > + if (start_count - *count < 64) > + nvhost_debug_output(o, "%08x%s", > + val, *count > 0 ? ", " : "])\n"); > + else if (!dont_print && (*count > 0)) { > + nvhost_debug_output(o, "[truncated; %d more words]\n", > + *count); > + dont_print = 1; > + } > + if (*count == 0) > + *state = NVHOST_DBG_STATE_CMD; > + break; > + > + case NVHOST_DBG_STATE_GATHER: > + *state = NVHOST_DBG_STATE_CMD; > + nvhost_debug_output(o, "%08x]):\n", val); > + if (cdma) { > + show_channel_gather(o, addr, val, > + *count, cdma); > + } > + break; > + } > +} > + > +static void do_show_channel_gather(struct output *o, > + phys_addr_t phys_addr, > + u32 words, struct nvhost_cdma *cdma, > + phys_addr_t pin_addr, u32 *map_addr) > +{ > + /* Map dmaget cursor to corresponding mem handle */ > + u32 offset; > + int state, count, i; > + > + offset = phys_addr - pin_addr; > + /* > + * Sometimes we're given different hardware address to the same > + * page - in these cases the offset will get an invalid number and > + * we just have to bail out. > + */ > + if (offset > NVHOST_DEBUG_MAX_PAGE_OFFSET) { > + nvhost_debug_output(o, "[address mismatch]\n"); > + } else { > + /* GATHER buffer starts always with commands */ > + state = NVHOST_DBG_STATE_CMD; > + for (i = 0; i < words; i++) > + show_channel_word(o, &state, &count, > + phys_addr + i * 4, > + *(map_addr + offset/4 + i), > + cdma); > + } > +} > + > +static void show_channel_gather(struct output *o, u32 addr, > + phys_addr_t phys_addr, > + u32 words, struct nvhost_cdma *cdma) > +{ > + /* Map dmaget cursor to corresponding mem handle */ > + struct push_buffer *pb = &cdma->push_buffer; > + u32 cur = addr - pb->phys; > + struct mem_mgr_handle *mem = &pb->client_handle[cur/8]; > + u32 *map_addr, offset; > + struct sg_table *sgt; > + > + if (!mem || !mem->handle || !mem->client) { > + nvhost_debug_output(o, "[already deallocated]\n"); > + return; > + } > + > + map_addr = mem_op().mmap(mem->handle); > + if (!map_addr) { > + nvhost_debug_output(o, "[could not mmap]\n"); > + return; > + } > + > + /* Get base address from mem */ > + sgt = mem_op().pin(mem->client, mem->handle); > + if (IS_ERR(sgt)) { > + nvhost_debug_output(o, "[couldn't pin]\n"); > + mem_op().munmap(mem->handle, map_addr); > + return; > + } > + > + offset = phys_addr - sg_dma_address(sgt->sgl); > + do_show_channel_gather(o, phys_addr, words, cdma, > + sg_dma_address(sgt->sgl), map_addr); > + mem_op().unpin(mem->client, mem->handle, sgt); > + mem_op().munmap(mem->handle, map_addr); > +} > + > +static void show_channel_gathers(struct output *o, struct nvhost_cdma *cdma) > +{ > + struct nvhost_job *job; > + > + list_for_each_entry(job, &cdma->sync_queue, list) { > + int i; > + nvhost_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d," > + " first_get=%08x, timeout=%d" > + " num_slots=%d, num_handles=%d\n", > + job, > + job->syncpt_id, > + job->syncpt_end, > + job->first_get, > + job->timeout, > + job->num_slots, > + job->num_unpins); > + > + for (i = 0; i < job->num_gathers; i++) { > + struct nvhost_job_gather *g = &job->gathers[i]; > + u32 *mapped = mem_op().mmap(g->ref); > + if (!mapped) { > + nvhost_debug_output(o, "[could not mmap]\n"); > + continue; > + } > + > + nvhost_debug_output(o, > + " GATHER at %08x+%04x, %d words\n", > + g->mem_base, g->offset, g->words); > + > + do_show_channel_gather(o, g->mem_base + g->offset, > + g->words, cdma, g->mem_base, mapped); > + mem_op().munmap(g->ref, mapped); > + } > + } > +} > + > +static void host1x_debug_show_channel_cdma(struct nvhost_master *m, > + struct nvhost_channel *ch, struct output *o, int chid) > +{ > + struct nvhost_channel *channel = ch; > + struct nvhost_cdma *cdma = &channel->cdma; > + u32 dmaput, dmaget, dmactrl; > + u32 cbstat, cbread; > + u32 val, base, baseval; > + struct nvhost_device_data *pdata = platform_get_drvdata(channel->dev); > + > + dmaput = readl(channel->aperture + host1x_channel_dmaput_r()); > + dmaget = readl(channel->aperture + host1x_channel_dmaget_r()); > + dmactrl = readl(channel->aperture + host1x_channel_dmactrl_r()); > + cbread = readl(m->sync_aperture + host1x_sync_cbread0_r() + 4 * chid); > + cbstat = readl(m->sync_aperture + host1x_sync_cbstat_0_r() + 4 * chid); > + > + nvhost_debug_output(o, "%d-%s (%d): ", chid, > + channel->dev->name, > + pdata->refcount); > + > + if (host1x_channel_dmactrl_dmastop_v(dmactrl) > + || !channel->cdma.push_buffer.mapped) { > + nvhost_debug_output(o, "inactive\n\n"); > + return; > + } > + > + switch (cbstat) { > + case 0x00010008: > + nvhost_debug_output(o, "waiting on syncpt %d val %d\n", > + cbread >> 24, cbread & 0xffffff); > + break; > + > + case 0x00010009: > + base = (cbread >> 16) & 0xff; > + baseval = readl(m->sync_aperture + > + host1x_sync_syncpt_base_0_r() + 4 * base); > + val = cbread & 0xffff; > + nvhost_debug_output(o, "waiting on syncpt %d val %d " > + "(base %d = %d; offset = %d)\n", > + cbread >> 24, baseval + val, > + base, baseval, val); > + break; > + > + default: > + nvhost_debug_output(o, > + "active class %02x, offset %04x, val %08x\n", > + host1x_sync_cbstat_0_cbclass0_v(cbstat), > + host1x_sync_cbstat_0_cboffset0_v(cbstat), > + cbread); > + break; > + } > + > + nvhost_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n", > + dmaput, dmaget, dmactrl); > + nvhost_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); > + > + show_channel_gathers(o, cdma); > + nvhost_debug_output(o, "\n"); > +} > + > +static void host1x_debug_show_channel_fifo(struct nvhost_master *m, > + struct nvhost_channel *ch, struct output *o, int chid) > +{ > + u32 val, rd_ptr, wr_ptr, start, end; > + struct nvhost_channel *channel = ch; > + int state, count; > + > + nvhost_debug_output(o, "%d: fifo:\n", chid); > + > + val = readl(channel->aperture + host1x_channel_fifostat_r()); > + nvhost_debug_output(o, "FIFOSTAT %08x\n", val); > + if (host1x_channel_fifostat_cfempty_v(val)) { > + nvhost_debug_output(o, "[empty]\n"); > + return; > + } > + > + writel(0x0, m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); > + writel(host1x_sync_cfpeek_ctrl_cfpeek_ena_f(1) > + | host1x_sync_cfpeek_ctrl_cfpeek_channr_f(chid), > + m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); > + > + val = readl(m->sync_aperture + host1x_sync_cfpeek_ptrs_r()); > + rd_ptr = host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(val); > + wr_ptr = host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(val); > + > + val = readl(m->sync_aperture + host1x_sync_cf0_setup_r() + 4 * chid); > + start = host1x_sync_cf0_setup_cf0_base_v(val); > + end = host1x_sync_cf0_setup_cf0_limit_v(val); > + > + state = NVHOST_DBG_STATE_CMD; > + > + do { > + writel(0x0, m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); > + writel(host1x_sync_cfpeek_ctrl_cfpeek_ena_f(1) > + | host1x_sync_cfpeek_ctrl_cfpeek_channr_f(chid) > + | host1x_sync_cfpeek_ctrl_cfpeek_addr_f(rd_ptr), > + m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); > + val = readl(m->sync_aperture + host1x_sync_cfpeek_read_r()); > + > + show_channel_word(o, &state, &count, 0, val, NULL); > + > + if (rd_ptr == end) > + rd_ptr = start; > + else > + rd_ptr++; > + } while (rd_ptr != wr_ptr); > + > + if (state == NVHOST_DBG_STATE_DATA) > + nvhost_debug_output(o, ", ...])\n"); > + nvhost_debug_output(o, "\n"); > + > + writel(0x0, m->sync_aperture + host1x_sync_cfpeek_ctrl_r()); > +} > + > +static void host1x_debug_show_mlocks(struct nvhost_master *m, struct output *o) > +{ > + u32 __iomem *mlo_regs = m->sync_aperture + > + host1x_sync_mlock_owner_0_r(); > + int i; > + > + nvhost_debug_output(o, "---- mlocks ----\n"); > + for (i = 0; i < NV_HOST1X_NB_MLOCKS; i++) { > + u32 owner = readl(mlo_regs + i); > + if (host1x_sync_mlock_owner_0_mlock_ch_owns_0_v(owner)) > + nvhost_debug_output(o, "%d: locked by channel %d\n", > + i, > + host1x_sync_mlock_owner_0_mlock_owner_chid_0_f( > + owner)); > + else if (host1x_sync_mlock_owner_0_mlock_cpu_owns_0_v(owner)) > + nvhost_debug_output(o, "%d: locked by cpu\n", i); > + else > + nvhost_debug_output(o, "%d: unlocked\n", i); > + } > + nvhost_debug_output(o, "\n"); > +} > + > +static const struct nvhost_debug_ops host1x_debug_ops = { > + .show_channel_cdma = host1x_debug_show_channel_cdma, > + .show_channel_fifo = host1x_debug_show_channel_fifo, > + .show_mlocks = host1x_debug_show_mlocks, > +}; > 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 0000000..bf816bb > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x_intr.c > @@ -0,0 +1,263 @@ > +/* > + * drivers/video/tegra/host/host1x/host1x_intr.c > + * > + * Tegra Graphics Host Interrupt Management > + * > + * Copyright (C) 2010 Google, Inc. > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/io.h> > +#include <asm/mach/irq.h> > + > +#include "nvhost_intr.h" > +#include "dev.h" > + > +/* Spacing between sync registers */ > +#define REGISTER_STRIDE 4 > + > +static void host1x_intr_syncpt_thresh_isr(struct nvhost_intr_syncpt *syncpt); > + > +static void syncpt_thresh_cascade_fn(struct work_struct *work) > +{ > + struct nvhost_intr_syncpt *sp = > + container_of(work, struct nvhost_intr_syncpt, work); > + nvhost_syncpt_thresh_fn(sp->irq, sp); > +} > + > +static irqreturn_t syncpt_thresh_cascade_isr(int irq, void *dev_id) > +{ > + struct nvhost_master *dev = dev_id; > + void __iomem *sync_regs = dev->sync_aperture; > + struct nvhost_intr *intr = &dev->intr; > + unsigned long reg; > + int i, id; > + > + for (i = 0; i < dev->info.nb_pts / BITS_PER_LONG; i++) { > + reg = readl(sync_regs + > + host1x_sync_syncpt_thresh_cpu0_int_status_r() + > + i * REGISTER_STRIDE); > + for_each_set_bit(id, ®, BITS_PER_LONG) { > + struct nvhost_intr_syncpt *sp = > + intr->syncpt + (i * BITS_PER_LONG + id); > + host1x_intr_syncpt_thresh_isr(sp); > + queue_work(intr->wq, &sp->work); > + } > + } > + > + return IRQ_HANDLED; > +} > + > +static void host1x_intr_init_host_sync(struct nvhost_intr *intr) > +{ > + struct nvhost_master *dev = intr_to_dev(intr); > + void __iomem *sync_regs = dev->sync_aperture; > + int i, err, irq; > + > + writel(0xffffffffUL, > + sync_regs + host1x_sync_syncpt_thresh_int_disable_r()); > + writel(0xffffffffUL, > + sync_regs + host1x_sync_syncpt_thresh_cpu0_int_status_r()); > + > + for (i = 0; i < dev->info.nb_pts; i++) > + INIT_WORK(&intr->syncpt[i].work, syncpt_thresh_cascade_fn); > + > + irq = platform_get_irq(dev->dev, 0); > + WARN_ON(IS_ERR_VALUE(irq)); > + err = devm_request_irq(&dev->dev->dev, irq, > + syncpt_thresh_cascade_isr, > + IRQF_SHARED, "host_syncpt", dev); > + WARN_ON(IS_ERR_VALUE(err)); > + > + /* 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_r()); > + > + /* increase the auto-ack timout to the maximum value. 2d will hang > + * otherwise on Tegra2. > + */ > + writel(0xff, sync_regs + host1x_sync_ctxsw_timeout_cfg_r()); > +} > + > +static void host1x_intr_set_host_clocks_per_usec(struct nvhost_intr *intr, > + u32 cpm) > +{ > + struct nvhost_master *dev = intr_to_dev(intr); > + void __iomem *sync_regs = dev->sync_aperture; > + /* write microsecond clock register */ > + writel(cpm, sync_regs + host1x_sync_usec_clk_r()); > +} > + > +static void host1x_intr_set_syncpt_threshold(struct nvhost_intr *intr, > + u32 id, u32 thresh) > +{ > + struct nvhost_master *dev = intr_to_dev(intr); > + void __iomem *sync_regs = dev->sync_aperture; > + writel(thresh, sync_regs + > + (host1x_sync_syncpt_int_thresh_0_r() + id * REGISTER_STRIDE)); > +} > + > +static void host1x_intr_enable_syncpt_intr(struct nvhost_intr *intr, u32 id) > +{ > + struct nvhost_master *dev = intr_to_dev(intr); > + void __iomem *sync_regs = dev->sync_aperture; > + > + writel(BIT_MASK(id), sync_regs + > + host1x_sync_syncpt_thresh_int_enable_cpu0_r() + > + BIT_WORD(id) * REGISTER_STRIDE); > +} > + > +static void host1x_intr_disable_syncpt_intr(struct nvhost_intr *intr, u32 id) > +{ > + struct nvhost_master *dev = intr_to_dev(intr); > + void __iomem *sync_regs = dev->sync_aperture; > + > + writel(BIT_MASK(id), sync_regs + > + host1x_sync_syncpt_thresh_int_disable_r() + > + BIT_WORD(id) * REGISTER_STRIDE); > + > + writel(BIT_MASK(id), sync_regs + > + host1x_sync_syncpt_thresh_cpu0_int_status_r() + > + BIT_WORD(id) * REGISTER_STRIDE); > +} > + > +static void host1x_intr_disable_all_syncpt_intrs(struct nvhost_intr *intr) > +{ > + struct nvhost_master *dev = intr_to_dev(intr); > + void __iomem *sync_regs = dev->sync_aperture; > + u32 reg; > + > + for (reg = 0; reg <= BIT_WORD(dev->info.nb_pts) * REGISTER_STRIDE; > + reg += REGISTER_STRIDE) { > + writel(0xffffffffu, sync_regs + > + host1x_sync_syncpt_thresh_int_disable_r() + > + reg); > + > + writel(0xffffffffu, sync_regs + > + host1x_sync_syncpt_thresh_cpu0_int_status_r() + reg); > + } > +} > + > +/** > + * Sync point threshold interrupt service function > + * Handles sync point threshold triggers, in interrupt context > + */ > +static void host1x_intr_syncpt_thresh_isr(struct nvhost_intr_syncpt *syncpt) > +{ > + unsigned int id = syncpt->id; > + struct nvhost_intr *intr = intr_syncpt_to_intr(syncpt); > + > + void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; > + > + u32 reg = BIT_WORD(id) * REGISTER_STRIDE; > + > + writel(BIT_MASK(id), sync_regs + > + host1x_sync_syncpt_thresh_int_disable_r() + reg); > + writel(BIT_MASK(id), sync_regs + > + host1x_sync_syncpt_thresh_cpu0_int_status_r() + reg); > +} > + > +/** > + * Host general interrupt service function > + * Handles read / write failures > + */ > +static irqreturn_t host1x_intr_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_r()); > + ext_stat = readl(sync_regs + host1x_sync_hintstatus_ext_r()); > + > + if (host1x_sync_hintstatus_ext_ip_read_int_v(ext_stat)) { > + addr = readl(sync_regs + host1x_sync_ip_read_timeout_addr_r()); > + pr_err("Host read timeout at address %x\n", addr); > + } > + > + if (host1x_sync_hintstatus_ext_ip_write_int_v(ext_stat)) { > + addr = readl(sync_regs + host1x_sync_ip_write_timeout_addr_r()); > + pr_err("Host write timeout at address %x\n", addr); > + } > + > + writel(ext_stat, sync_regs + host1x_sync_hintstatus_ext_r()); > + writel(stat, sync_regs + host1x_sync_hintstatus_r()); > + > + return IRQ_HANDLED; > +} > +static int host1x_intr_request_host_general_irq(struct nvhost_intr *intr) > +{ > + void __iomem *sync_regs = intr_to_dev(intr)->sync_aperture; > + int err; > + > + /* master disable for general (not syncpt) host interrupts */ > + writel(0, sync_regs + host1x_sync_intmask_r()); > + > + /* clear status & extstatus */ > + writel(0xfffffffful, sync_regs + host1x_sync_hintstatus_ext_r()); > + writel(0xfffffffful, sync_regs + host1x_sync_hintstatus_r()); > + > + err = request_irq(intr->host_general_irq, host1x_intr_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_r()); > + > + /* enable extra interrupt sources */ > + writel(BIT(12) | BIT(31), sync_regs + host1x_sync_hintmask_r()); > + > + /* enable host module interrupt to CPU0 */ > + writel(BIT(0), sync_regs + host1x_sync_intc0mask_r()); > + > + /* master enable for general (not syncpt) host interrupts */ > + writel(BIT(0), sync_regs + host1x_sync_intmask_r()); > + > + return err; > +} > + > +static void host1x_intr_free_host_general_irq(struct nvhost_intr *intr) > +{ > + 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_r()); > + > + free_irq(intr->host_general_irq, intr); > +} > + > +static int host1x_free_syncpt_irq(struct nvhost_intr *intr) > +{ > + flush_workqueue(intr->wq); > + return 0; > +} > + > +static const struct nvhost_intr_ops host1x_intr_ops = { > + .init_host_sync = host1x_intr_init_host_sync, > + .set_host_clocks_per_usec = host1x_intr_set_host_clocks_per_usec, > + .set_syncpt_threshold = host1x_intr_set_syncpt_threshold, > + .enable_syncpt_intr = host1x_intr_enable_syncpt_intr, > + .disable_syncpt_intr = host1x_intr_disable_syncpt_intr, > + .disable_all_syncpt_intrs = host1x_intr_disable_all_syncpt_intrs, > + .request_host_general_irq = host1x_intr_request_host_general_irq, > + .free_host_general_irq = host1x_intr_free_host_general_irq, > + .free_syncpt_irq = host1x_free_syncpt_irq, > +}; > 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 0000000..ba0080a > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c > @@ -0,0 +1,170 @@ > +/* > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/io.h> > +#include <trace/events/nvhost.h> > +#include "nvhost_syncpt.h" > +#include "nvhost_acm.h" > +#include "host1x.h" > +#include "chip_support.h" > + > +/** > + * Write the current syncpoint value back to hw. > + */ > +static void host1x_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_r() + id * 4)); > +} > + > +/** > + * Write the current waitbase value back to hw. > + */ > +static void host1x_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_r() + id * 4)); > +} > + > +/** > + * Read waitbase value from hw. > + */ > +static void host1x_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_r() + id * 4)); > +} > + > +/** > + * Updates the last value read from hardware. > + * (was nvhost_syncpt_update_min) > + */ > +static u32 host1x_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_r() + 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__, > + id, > + nvhost_syncpt_read_min(sp, id), > + nvhost_syncpt_read_max(sp, id)); > + > + return live; > +} > + > +/** > + * Write a cpu syncpoint increment to the hardware, without touching > + * the cache. Caller is responsible for host being powered. > + */ > +static void host1x_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) > +{ > + struct nvhost_master *dev = syncpt_to_dev(sp); > + u32 reg_offset = id / 32; > + > + if (!nvhost_module_powered(dev->dev)) { > + dev_err(&syncpt_to_dev(sp)->dev->dev, > + "Trying to access host1x when it's off"); > + return; > + } > + > + if (!nvhost_syncpt_client_managed(sp, 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_MASK(id), dev->sync_aperture + > + host1x_sync_syncpt_cpu_incr_r() + reg_offset * 4); > + wmb(); > +} > + > +/* remove a wait pointed to by patch_addr */ > +static int host1x_syncpt_patch_wait(struct nvhost_syncpt *sp, > + void *patch_addr) > +{ > + u32 override = nvhost_class_host_wait_syncpt( > + NVSYNCPT_GRAPHICS_HOST, 0); > + __raw_writel(override, patch_addr); > + return 0; > +} > + > + > +static const char *host1x_syncpt_name(struct nvhost_syncpt *sp, u32 id) > +{ > + struct host1x_device_info *info = &syncpt_to_dev(sp)->info; > + const char *name = NULL; > + > + if (id < info->nb_pts) > + name = info->syncpt_names[id]; > + > + return name ? name : ""; > +} > + > +static void host1x_syncpt_debug(struct nvhost_syncpt *sp) > +{ > + u32 i; > + for (i = 0; i < nvhost_syncpt_nb_pts(sp); 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().name(sp, i), > + min, max); > + > + } > + > + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) { > + u32 base_val; > + host1x_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 const struct nvhost_syncpt_ops host1x_syncpt_ops = { > + .reset = host1x_syncpt_reset, > + .reset_wait_base = host1x_syncpt_reset_wait_base, > + .read_wait_base = host1x_syncpt_read_wait_base, > + .update_min = host1x_syncpt_update_min, > + .cpu_incr = host1x_syncpt_cpu_incr, > + .patch_wait = host1x_syncpt_patch_wait, > + .debug = host1x_syncpt_debug, > + .name = host1x_syncpt_name, > +}; > diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_channel.h b/drivers/video/tegra/host/host1x/hw_host1x01_channel.h > new file mode 100644 > index 0000000..ca2f9a0 > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/hw_host1x01_channel.h > @@ -0,0 +1,182 @@ > +/* > + * drivers/video/tegra/host/host1x/hw_host1x_channel_host1x.h > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + * > + */ > + > + /* > + * Function naming determines intended use: > + * > + * <x>_r(void) : Returns the offset for register <x>. > + * > + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. > + * > + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. > + * > + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted > + * and masked to place it at field <y> of register <x>. This value > + * can be |'d with others to produce a full register value for > + * register <x>. > + * > + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This > + * value can be ~'d and then &'d to clear the value of field <y> for > + * register <x>. > + * > + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted > + * to place it at field <y> of register <x>. This value can be |'d > + * with others to produce a full register value for <x>. > + * > + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register > + * <x> value 'r' after being shifted to place its LSB at bit 0. > + * This value is suitable for direct comparison with other unshifted > + * values appropriate for use in field <y> of register <x>. > + * > + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for > + * field <y> of register <x>. This value is suitable for direct > + * comparison with unshifted values appropriate for use in field <y> > + * of register <x>. > + */ > + > +#ifndef __hw_host1x_channel_host1x_h__ > +#define __hw_host1x_channel_host1x_h__ > +/*This file is autogenerated. Do not edit. */ > + > +static inline u32 host1x_channel_fifostat_r(void) > +{ > + return 0x0; > +} > +static inline u32 host1x_channel_fifostat_cfempty_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_channel_fifostat_cfempty_f(u32 v) > +{ > + return (v & 0x1) << 10; > +} > +static inline u32 host1x_channel_fifostat_cfempty_m(void) > +{ > + return 0x1 << 10; > +} > +static inline u32 host1x_channel_fifostat_cfempty_v(u32 r) > +{ > + return (r >> 10) & 0x1; > +} > +static inline u32 host1x_channel_fifostat_cfempty_notempty_v(void) > +{ > + return 0; > +} > +static inline u32 host1x_channel_fifostat_cfempty_empty_v(void) > +{ > + return 1; > +} > +static inline u32 host1x_channel_fifostat_outfentries_s(void) > +{ > + return 5; > +} > +static inline u32 host1x_channel_fifostat_outfentries_f(u32 v) > +{ > + return (v & 0x1f) << 24; > +} > +static inline u32 host1x_channel_fifostat_outfentries_m(void) > +{ > + return 0x1f << 24; > +} > +static inline u32 host1x_channel_fifostat_outfentries_v(u32 r) > +{ > + return (r >> 24) & 0x1f; > +} > +static inline u32 host1x_channel_inddata_r(void) > +{ > + return 0xc; > +} > +static inline u32 host1x_channel_dmastart_r(void) > +{ > + return 0x14; > +} > +static inline u32 host1x_channel_dmaput_r(void) > +{ > + return 0x18; > +} > +static inline u32 host1x_channel_dmaget_r(void) > +{ > + return 0x1c; > +} > +static inline u32 host1x_channel_dmaend_r(void) > +{ > + return 0x20; > +} > +static inline u32 host1x_channel_dmactrl_r(void) > +{ > + return 0x24; > +} > +static inline u32 host1x_channel_dmactrl_dmastop_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_channel_dmactrl_dmastop_f(u32 v) > +{ > + return (v & 0x1) << 0; > +} > +static inline u32 host1x_channel_dmactrl_dmastop_m(void) > +{ > + return 0x1 << 0; > +} > +static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r) > +{ > + return (r >> 0) & 0x1; > +} > +static inline u32 host1x_channel_dmactrl_dmastop_run_v(void) > +{ > + return 0; > +} > +static inline u32 host1x_channel_dmactrl_dmastop_stop_v(void) > +{ > + return 1; > +} > +static inline u32 host1x_channel_dmactrl_dmagetrst_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_channel_dmactrl_dmagetrst_f(u32 v) > +{ > + return (v & 0x1) << 1; > +} > +static inline u32 host1x_channel_dmactrl_dmagetrst_m(void) > +{ > + return 0x1 << 1; > +} > +static inline u32 host1x_channel_dmactrl_dmagetrst_v(u32 r) > +{ > + return (r >> 1) & 0x1; > +} > +static inline u32 host1x_channel_dmactrl_dmainitget_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_channel_dmactrl_dmainitget_f(u32 v) > +{ > + return (v & 0x1) << 2; > +} > +static inline u32 host1x_channel_dmactrl_dmainitget_m(void) > +{ > + return 0x1 << 2; > +} > +static inline u32 host1x_channel_dmactrl_dmainitget_v(u32 r) > +{ > + return (r >> 2) & 0x1; > +} > + > +#endif /* __hw_host1x_channel_host1x_h__ */ > diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_sync.h b/drivers/video/tegra/host/host1x/hw_host1x01_sync.h > new file mode 100644 > index 0000000..67f0cbf > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/hw_host1x01_sync.h > @@ -0,0 +1,398 @@ > +/* > + * drivers/video/tegra/host/host1x/hw_host1x_sync_host1x.h > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + * > + */ > + > + /* > + * Function naming determines intended use: > + * > + * <x>_r(void) : Returns the offset for register <x>. > + * > + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. > + * > + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. > + * > + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted > + * and masked to place it at field <y> of register <x>. This value > + * can be |'d with others to produce a full register value for > + * register <x>. > + * > + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This > + * value can be ~'d and then &'d to clear the value of field <y> for > + * register <x>. > + * > + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted > + * to place it at field <y> of register <x>. This value can be |'d > + * with others to produce a full register value for <x>. > + * > + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register > + * <x> value 'r' after being shifted to place its LSB at bit 0. > + * This value is suitable for direct comparison with other unshifted > + * values appropriate for use in field <y> of register <x>. > + * > + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for > + * field <y> of register <x>. This value is suitable for direct > + * comparison with unshifted values appropriate for use in field <y> > + * of register <x>. > + */ > + > +#ifndef __hw_host1x_sync_host1x_h__ > +#define __hw_host1x_sync_host1x_h__ > +/*This file is autogenerated. Do not edit. */ > + > +static inline u32 host1x_sync_intmask_r(void) > +{ > + return 0x4; > +} > +static inline u32 host1x_sync_intc0mask_r(void) > +{ > + return 0x8; > +} > +static inline u32 host1x_sync_hintstatus_r(void) > +{ > + return 0x20; > +} > +static inline u32 host1x_sync_hintmask_r(void) > +{ > + return 0x24; > +} > +static inline u32 host1x_sync_hintstatus_ext_r(void) > +{ > + return 0x28; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_f(u32 v) > +{ > + return (v & 0x1) << 30; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_m(void) > +{ > + return 0x1 << 30; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_read_int_v(u32 r) > +{ > + return (r >> 30) & 0x1; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_f(u32 v) > +{ > + return (v & 0x1) << 31; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_m(void) > +{ > + return 0x1 << 31; > +} > +static inline u32 host1x_sync_hintstatus_ext_ip_write_int_v(u32 r) > +{ > + return (r >> 31) & 0x1; > +} > +static inline u32 host1x_sync_hintmask_ext_r(void) > +{ > + return 0x2c; > +} > +static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(void) > +{ > + return 0x40; > +} > +static inline u32 host1x_sync_syncpt_thresh_cpu1_int_status_r(void) > +{ > + return 0x48; > +} > +static inline u32 host1x_sync_syncpt_thresh_int_disable_r(void) > +{ > + return 0x60; > +} > +static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(void) > +{ > + return 0x68; > +} > +static inline u32 host1x_sync_cf0_setup_r(void) > +{ > + return 0x80; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_base_s(void) > +{ > + return 9; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_base_f(u32 v) > +{ > + return (v & 0x1ff) << 0; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_base_m(void) > +{ > + return 0x1ff << 0; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_base_v(u32 r) > +{ > + return (r >> 0) & 0x1ff; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_limit_s(void) > +{ > + return 9; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_limit_f(u32 v) > +{ > + return (v & 0x1ff) << 16; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_limit_m(void) > +{ > + return 0x1ff << 16; > +} > +static inline u32 host1x_sync_cf0_setup_cf0_limit_v(u32 r) > +{ > + return (r >> 16) & 0x1ff; > +} > +static inline u32 host1x_sync_cmdproc_stop_r(void) > +{ > + return 0xac; > +} > +static inline u32 host1x_sync_ch_teardown_r(void) > +{ > + return 0xb0; > +} > +static inline u32 host1x_sync_usec_clk_r(void) > +{ > + return 0x1a4; > +} > +static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void) > +{ > + return 0x1a8; > +} > +static inline u32 host1x_sync_ip_busy_timeout_r(void) > +{ > + return 0x1bc; > +} > +static inline u32 host1x_sync_ip_read_timeout_addr_r(void) > +{ > + return 0x1c0; > +} > +static inline u32 host1x_sync_ip_write_timeout_addr_r(void) > +{ > + return 0x1c4; > +} > +static inline u32 host1x_sync_mlock_0_r(void) > +{ > + return 0x2c0; > +} > +static inline u32 host1x_sync_mlock_owner_0_r(void) > +{ > + return 0x340; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_s(void) > +{ > + return 4; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_f(u32 v) > +{ > + return (v & 0xf) << 8; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_m(void) > +{ > + return 0xf << 8; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_owner_chid_0_v(u32 r) > +{ > + return (r >> 8) & 0xf; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_f(u32 v) > +{ > + return (v & 0x1) << 1; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_m(void) > +{ > + return 0x1 << 1; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_cpu_owns_0_v(u32 r) > +{ > + return (r >> 1) & 0x1; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_f(u32 v) > +{ > + return (v & 0x1) << 0; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_m(void) > +{ > + return 0x1 << 0; > +} > +static inline u32 host1x_sync_mlock_owner_0_mlock_ch_owns_0_v(u32 r) > +{ > + return (r >> 0) & 0x1; > +} > +static inline u32 host1x_sync_syncpt_0_r(void) > +{ > + return 0x400; > +} > +static inline u32 host1x_sync_syncpt_int_thresh_0_r(void) > +{ > + return 0x500; > +} > +static inline u32 host1x_sync_syncpt_base_0_r(void) > +{ > + return 0x600; > +} > +static inline u32 host1x_sync_syncpt_cpu_incr_r(void) > +{ > + return 0x700; > +} > +static inline u32 host1x_sync_cbread0_r(void) > +{ > + return 0x720; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_r(void) > +{ > + return 0x74c; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_s(void) > +{ > + return 9; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_f(u32 v) > +{ > + return (v & 0x1ff) << 0; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_m(void) > +{ > + return 0x1ff << 0; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_addr_v(u32 r) > +{ > + return (r >> 0) & 0x1ff; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_s(void) > +{ > + return 3; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_f(u32 v) > +{ > + return (v & 0x7) << 16; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_m(void) > +{ > + return 0x7 << 16; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_channr_v(u32 r) > +{ > + return (r >> 16) & 0x7; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_f(u32 v) > +{ > + return (v & 0x1) << 31; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_m(void) > +{ > + return 0x1 << 31; > +} > +static inline u32 host1x_sync_cfpeek_ctrl_cfpeek_ena_v(u32 r) > +{ > + return (r >> 31) & 0x1; > +} > +static inline u32 host1x_sync_cfpeek_read_r(void) > +{ > + return 0x750; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_r(void) > +{ > + return 0x754; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_s(void) > +{ > + return 9; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_f(u32 v) > +{ > + return (v & 0x1ff) << 0; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_m(void) > +{ > + return 0x1ff << 0; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r) > +{ > + return (r >> 0) & 0x1ff; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_s(void) > +{ > + return 9; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_f(u32 v) > +{ > + return (v & 0x1ff) << 16; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_m(void) > +{ > + return 0x1ff << 16; > +} > +static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r) > +{ > + return (r >> 16) & 0x1ff; > +} > +static inline u32 host1x_sync_cbstat_0_r(void) > +{ > + return 0x758; > +} > +static inline u32 host1x_sync_cbstat_0_cboffset0_s(void) > +{ > + return 16; > +} > +static inline u32 host1x_sync_cbstat_0_cboffset0_f(u32 v) > +{ > + return (v & 0xffff) << 0; > +} > +static inline u32 host1x_sync_cbstat_0_cboffset0_m(void) > +{ > + return 0xffff << 0; > +} > +static inline u32 host1x_sync_cbstat_0_cboffset0_v(u32 r) > +{ > + return (r >> 0) & 0xffff; > +} > +static inline u32 host1x_sync_cbstat_0_cbclass0_s(void) > +{ > + return 10; > +} > +static inline u32 host1x_sync_cbstat_0_cbclass0_f(u32 v) > +{ > + return (v & 0x3ff) << 16; > +} > +static inline u32 host1x_sync_cbstat_0_cbclass0_m(void) > +{ > + return 0x3ff << 16; > +} > +static inline u32 host1x_sync_cbstat_0_cbclass0_v(u32 r) > +{ > + return (r >> 16) & 0x3ff; > +} > + > +#endif /* __hw_host1x_sync_host1x_h__ */ > diff --git a/drivers/video/tegra/host/host1x/hw_host1x01_uclass.h b/drivers/video/tegra/host/host1x/hw_host1x01_uclass.h > new file mode 100644 > index 0000000..ed6e4b7 > --- /dev/null > +++ b/drivers/video/tegra/host/host1x/hw_host1x01_uclass.h > @@ -0,0 +1,474 @@ > +/* > + * drivers/video/tegra/host/host1x/hw_host1x_uclass_host1x.h > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + * > + */ > + > + /* > + * Function naming determines intended use: > + * > + * <x>_r(void) : Returns the offset for register <x>. > + * > + * <x>_w(void) : Returns the word offset for word (4 byte) element <x>. > + * > + * <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits. > + * > + * <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted > + * and masked to place it at field <y> of register <x>. This value > + * can be |'d with others to produce a full register value for > + * register <x>. > + * > + * <x>_<y>_m(void) : Returns a mask for field <y> of register <x>. This > + * value can be ~'d and then &'d to clear the value of field <y> for > + * register <x>. > + * > + * <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted > + * to place it at field <y> of register <x>. This value can be |'d > + * with others to produce a full register value for <x>. > + * > + * <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register > + * <x> value 'r' after being shifted to place its LSB at bit 0. > + * This value is suitable for direct comparison with other unshifted > + * values appropriate for use in field <y> of register <x>. > + * > + * <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for > + * field <y> of register <x>. This value is suitable for direct > + * comparison with unshifted values appropriate for use in field <y> > + * of register <x>. > + */ > + > +#ifndef __hw_host1x_uclass_host1x_h__ > +#define __hw_host1x_uclass_host1x_h__ > +/*This file is autogenerated. Do not edit. */ > + > +static inline u32 host1x_uclass_incr_syncpt_r(void) > +{ > + return 0x0; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v) > +{ > + return (v & 0xff) << 8; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_m(void) > +{ > + return 0xff << 8; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_v(u32 r) > +{ > + return (r >> 8) & 0xff; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_immediate_v(void) > +{ > + return 0; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_op_done_v(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_rd_done_v(void) > +{ > + return 2; > +} > +static inline u32 host1x_uclass_incr_syncpt_cond_reg_wr_safe_v(void) > +{ > + return 3; > +} > +static inline u32 host1x_uclass_incr_syncpt_indx_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v) > +{ > + return (v & 0xff) << 0; > +} > +static inline u32 host1x_uclass_incr_syncpt_indx_m(void) > +{ > + return 0xff << 0; > +} > +static inline u32 host1x_uclass_incr_syncpt_indx_v(u32 r) > +{ > + return (r >> 0) & 0xff; > +} > +static inline u32 host1x_uclass_wait_syncpt_r(void) > +{ > + return 0x8; > +} > +static inline u32 host1x_uclass_wait_syncpt_indx_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v) > +{ > + return (v & 0xff) << 24; > +} > +static inline u32 host1x_uclass_wait_syncpt_indx_m(void) > +{ > + return 0xff << 24; > +} > +static inline u32 host1x_uclass_wait_syncpt_indx_v(u32 r) > +{ > + return (r >> 24) & 0xff; > +} > +static inline u32 host1x_uclass_wait_syncpt_thresh_s(void) > +{ > + return 24; > +} > +static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v) > +{ > + return (v & 0xffffff) << 0; > +} > +static inline u32 host1x_uclass_wait_syncpt_thresh_m(void) > +{ > + return 0xffffff << 0; > +} > +static inline u32 host1x_uclass_wait_syncpt_thresh_v(u32 r) > +{ > + return (r >> 0) & 0xffffff; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_r(void) > +{ > + return 0x9; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_indx_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v) > +{ > + return (v & 0xff) << 24; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_indx_m(void) > +{ > + return 0xff << 24; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_indx_v(u32 r) > +{ > + return (r >> 24) & 0xff; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v) > +{ > + return (v & 0xff) << 16; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_m(void) > +{ > + return 0xff << 16; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_base_indx_v(u32 r) > +{ > + return (r >> 16) & 0xff; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_offset_s(void) > +{ > + return 16; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v) > +{ > + return (v & 0xffff) << 0; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_offset_m(void) > +{ > + return 0xffff << 0; > +} > +static inline u32 host1x_uclass_wait_syncpt_base_offset_v(u32 r) > +{ > + return (r >> 0) & 0xffff; > +} > +static inline u32 host1x_uclass_load_syncpt_base_r(void) > +{ > + return 0xb; > +} > +static inline u32 host1x_uclass_load_syncpt_base_base_indx_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v) > +{ > + return (v & 0xff) << 24; > +} > +static inline u32 host1x_uclass_load_syncpt_base_base_indx_m(void) > +{ > + return 0xff << 24; > +} > +static inline u32 host1x_uclass_load_syncpt_base_base_indx_v(u32 r) > +{ > + return (r >> 24) & 0xff; > +} > +static inline u32 host1x_uclass_load_syncpt_base_value_s(void) > +{ > + return 24; > +} > +static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v) > +{ > + return (v & 0xffffff) << 0; > +} > +static inline u32 host1x_uclass_load_syncpt_base_value_m(void) > +{ > + return 0xffffff << 0; > +} > +static inline u32 host1x_uclass_load_syncpt_base_value_v(u32 r) > +{ > + return (r >> 0) & 0xffffff; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_r(void) > +{ > + return 0xc; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v) > +{ > + return (v & 0xff) << 24; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_m(void) > +{ > + return 0xff << 24; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_base_indx_v(u32 r) > +{ > + return (r >> 24) & 0xff; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_offset_s(void) > +{ > + return 24; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v) > +{ > + return (v & 0xffffff) << 0; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_offset_m(void) > +{ > + return 0xffffff << 0; > +} > +static inline u32 host1x_uclass_incr_syncpt_base_offset_v(u32 r) > +{ > + return (r >> 0) & 0xffffff; > +} > +static inline u32 host1x_uclass_indoff_r(void) > +{ > + return 0x2d; > +} > +static inline u32 host1x_uclass_indoff_indbe_s(void) > +{ > + return 4; > +} > +static inline u32 host1x_uclass_indoff_indbe_f(u32 v) > +{ > + return (v & 0xf) << 28; > +} > +static inline u32 host1x_uclass_indoff_indbe_m(void) > +{ > + return 0xf << 28; > +} > +static inline u32 host1x_uclass_indoff_indbe_v(u32 r) > +{ > + return (r >> 28) & 0xf; > +} > +static inline u32 host1x_uclass_indoff_autoinc_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_indoff_autoinc_f(u32 v) > +{ > + return (v & 0x1) << 27; > +} > +static inline u32 host1x_uclass_indoff_autoinc_m(void) > +{ > + return 0x1 << 27; > +} > +static inline u32 host1x_uclass_indoff_autoinc_v(u32 r) > +{ > + return (r >> 27) & 0x1; > +} > +static inline u32 host1x_uclass_indoff_spool_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_indoff_spool_f(u32 v) > +{ > + return (v & 0x1) << 26; > +} > +static inline u32 host1x_uclass_indoff_spool_m(void) > +{ > + return 0x1 << 26; > +} > +static inline u32 host1x_uclass_indoff_spool_v(u32 r) > +{ > + return (r >> 26) & 0x1; > +} > +static inline u32 host1x_uclass_indoff_indoffset_s(void) > +{ > + return 24; > +} > +static inline u32 host1x_uclass_indoff_indoffset_f(u32 v) > +{ > + return (v & 0xffffff) << 2; > +} > +static inline u32 host1x_uclass_indoff_indoffset_m(void) > +{ > + return 0xffffff << 2; > +} > +static inline u32 host1x_uclass_indoff_indoffset_v(u32 r) > +{ > + return (r >> 2) & 0xffffff; > +} > +static inline u32 host1x_uclass_indoff_indmodid_s(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_indoff_indmodid_f(u32 v) > +{ > + return (v & 0xff) << 18; > +} > +static inline u32 host1x_uclass_indoff_indmodid_m(void) > +{ > + return 0xff << 18; > +} > +static inline u32 host1x_uclass_indoff_indmodid_v(u32 r) > +{ > + return (r >> 18) & 0xff; > +} > +static inline u32 host1x_uclass_indoff_indmodid_host1x_v(void) > +{ > + return 0; > +} > +static inline u32 host1x_uclass_indoff_indmodid_mpe_v(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_indoff_indmodid_vi_v(void) > +{ > + return 2; > +} > +static inline u32 host1x_uclass_indoff_indmodid_epp_v(void) > +{ > + return 3; > +} > +static inline u32 host1x_uclass_indoff_indmodid_isp_v(void) > +{ > + return 4; > +} > +static inline u32 host1x_uclass_indoff_indmodid_gr2d_v(void) > +{ > + return 5; > +} > +static inline u32 host1x_uclass_indoff_indmodid_gr3d_v(void) > +{ > + return 6; > +} > +static inline u32 host1x_uclass_indoff_indmodid_display_v(void) > +{ > + return 8; > +} > +static inline u32 host1x_uclass_indoff_indmodid_tvo_v(void) > +{ > + return 11; > +} > +static inline u32 host1x_uclass_indoff_indmodid_displayb_v(void) > +{ > + return 9; > +} > +static inline u32 host1x_uclass_indoff_indmodid_dsi_v(void) > +{ > + return 12; > +} > +static inline u32 host1x_uclass_indoff_indmodid_hdmi_v(void) > +{ > + return 10; > +} > +static inline u32 host1x_uclass_indoff_indmodid_dsib_v(void) > +{ > + return 16; > +} > +static inline u32 host1x_uclass_indoff_indroffset_s(void) > +{ > + return 16; > +} > +static inline u32 host1x_uclass_indoff_indroffset_f(u32 v) > +{ > + return (v & 0xffff) << 2; > +} > +static inline u32 host1x_uclass_indoff_indroffset_m(void) > +{ > + return 0xffff << 2; > +} > +static inline u32 host1x_uclass_indoff_indroffset_v(u32 r) > +{ > + return (r >> 2) & 0xffff; > +} > +static inline u32 host1x_uclass_indoff_acctype_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_indoff_acctype_f(u32 v) > +{ > + return (v & 0x1) << 1; > +} > +static inline u32 host1x_uclass_indoff_acctype_m(void) > +{ > + return 0x1 << 1; > +} > +static inline u32 host1x_uclass_indoff_acctype_v(u32 r) > +{ > + return (r >> 1) & 0x1; > +} > +static inline u32 host1x_uclass_indoff_acctype_reg_v(void) > +{ > + return 0; > +} > +static inline u32 host1x_uclass_indoff_acctype_fb_v(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_indoff_rwn_s(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_indoff_rwn_f(u32 v) > +{ > + return (v & 0x1) << 0; > +} > +static inline u32 host1x_uclass_indoff_rwn_m(void) > +{ > + return 0x1 << 0; > +} > +static inline u32 host1x_uclass_indoff_rwn_v(u32 r) > +{ > + return (r >> 0) & 0x1; > +} > +static inline u32 host1x_uclass_indoff_rwn_write_v(void) > +{ > + return 0; > +} > +static inline u32 host1x_uclass_indoff_rwn_read_v(void) > +{ > + return 1; > +} > +static inline u32 host1x_uclass_inddata_r(void) > +{ > + return 0x2e; > +} > + > +#endif /* __hw_host1x_uclass_host1x_h__ */ > diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c > new file mode 100644 > index 0000000..6da816a > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_acm.c > @@ -0,0 +1,532 @@ > +/* > + * drivers/video/tegra/host/nvhost_acm.c > + * > + * Tegra Graphics Host Automatic Clock Management > + * > + * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/stat.h> > +#include <linux/string.h> > +#include <linux/sched.h> > +#include <linux/clk.h> > +#include <linux/err.h> > +#include <linux/device.h> > +#include <linux/delay.h> > +#include <linux/platform_device.h> > +#include <trace/events/nvhost.h> > + > +#include <mach/powergate.h> > +#include <mach/clk.h> > + > +#include "nvhost_acm.h" > +#include "dev.h" > + > +#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ) > +#define POWERGATE_DELAY 10 > +#define MAX_DEVID_LENGTH 16 > + > +static void do_powergate_locked(int id) > +{ > + if (id != -1 && tegra_powergate_is_powered(id)) > + tegra_powergate_power_off(id); > +} > + > +static void do_unpowergate_locked(int id) > +{ > + if (id != -1) > + tegra_powergate_power_on(id); > +} > + > +static void to_state_clockgated_locked(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + if (pdata->powerstate == NVHOST_POWER_STATE_RUNNING) { > + int i, err; > + if (pdata->prepare_clockoff) { > + err = pdata->prepare_clockoff(dev); > + if (err) { > + dev_err(&dev->dev, "error clock gating"); > + return; > + } > + } > + > + for (i = 0; i < pdata->num_clks; i++) > + clk_disable_unprepare(pdata->clk[i]); > + if (dev->dev.parent) > + nvhost_module_idle(to_platform_device(dev->dev.parent)); > + } else if (pdata->powerstate == NVHOST_POWER_STATE_POWERGATED > + && pdata->can_powergate) { > + do_unpowergate_locked(pdata->powergate_ids[0]); > + do_unpowergate_locked(pdata->powergate_ids[1]); > + } > + pdata->powerstate = NVHOST_POWER_STATE_CLOCKGATED; > +} > + > +static void to_state_running_locked(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + int prev_state = pdata->powerstate; > + > + if (pdata->powerstate == NVHOST_POWER_STATE_POWERGATED) > + to_state_clockgated_locked(dev); > + > + if (pdata->powerstate == NVHOST_POWER_STATE_CLOCKGATED) { > + int i; > + > + if (dev->dev.parent) > + nvhost_module_busy(to_platform_device(dev->dev.parent)); > + > + for (i = 0; i < pdata->num_clks; i++) { > + int err = clk_prepare_enable(pdata->clk[i]); > + if (err) { > + dev_err(&dev->dev, "Cannot turn on clock %s", > + pdata->clocks[i].name); > + return; > + } > + } > + > + if (pdata->finalize_clockon) > + pdata->finalize_clockon(dev); > + > + /* Invoke callback after power un-gating. This is used for > + * restoring context. */ > + if (prev_state == NVHOST_POWER_STATE_POWERGATED > + && pdata->finalize_poweron) > + pdata->finalize_poweron(dev); > + } > + pdata->powerstate = NVHOST_POWER_STATE_RUNNING; > +} > + > +/* This gets called from powergate_handler() and from module suspend. > + * Module suspend is done for all modules, runtime power gating only > + * for modules with can_powergate set. > + */ > +static int to_state_powergated_locked(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + int err = 0; > + > + if (pdata->prepare_poweroff && > + pdata->powerstate != NVHOST_POWER_STATE_POWERGATED) { > + /* Clock needs to be on in prepare_poweroff */ > + to_state_running_locked(dev); > + err = pdata->prepare_poweroff(dev); > + if (err) > + return err; > + } > + > + if (pdata->powerstate == NVHOST_POWER_STATE_RUNNING) > + to_state_clockgated_locked(dev); > + > + if (pdata->can_powergate) { > + do_powergate_locked(pdata->powergate_ids[0]); > + do_powergate_locked(pdata->powergate_ids[1]); > + } > + > + pdata->powerstate = NVHOST_POWER_STATE_POWERGATED; > + return 0; > +} > + > +static void schedule_powergating_locked(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + if (pdata->can_powergate) > + schedule_delayed_work(&pdata->powerstate_down, > + msecs_to_jiffies(pdata->powergate_delay)); > +} > + > +static void schedule_clockgating_locked(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + schedule_delayed_work(&pdata->powerstate_down, > + msecs_to_jiffies(pdata->clockgate_delay)); > +} > + > +void nvhost_module_busy(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + if (pdata->busy) > + pdata->busy(dev); > + > + mutex_lock(&pdata->lock); > + cancel_delayed_work(&pdata->powerstate_down); > + > + pdata->refcount++; > + if (pdata->refcount > 0 && !nvhost_module_powered(dev)) > + to_state_running_locked(dev); > + mutex_unlock(&pdata->lock); > +} > + > +static void powerstate_down_handler(struct work_struct *work) > +{ > + struct platform_device *dev; > + struct nvhost_device_data *pdata; > + > + pdata = container_of(to_delayed_work(work), > + struct nvhost_device_data, > + powerstate_down); > + > + dev = pdata->pdev; > + > + mutex_lock(&pdata->lock); > + if (pdata->refcount == 0) { > + switch (pdata->powerstate) { > + case NVHOST_POWER_STATE_RUNNING: > + to_state_clockgated_locked(dev); > + schedule_powergating_locked(dev); > + break; > + case NVHOST_POWER_STATE_CLOCKGATED: > + if (to_state_powergated_locked(dev)) > + schedule_powergating_locked(dev); > + break; > + default: > + break; > + } > + } > + mutex_unlock(&pdata->lock); > +} > + > +void nvhost_module_idle_mult(struct platform_device *dev, int refs) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + bool kick = false; > + > + mutex_lock(&pdata->lock); > + pdata->refcount -= refs; > + if (pdata->refcount == 0) { > + if (nvhost_module_powered(dev)) > + schedule_clockgating_locked(dev); > + kick = true; > + } > + mutex_unlock(&pdata->lock); > + > + if (kick) { > + wake_up(&pdata->idle_wq); > + > + if (pdata->idle) > + pdata->idle(dev); > + } > +} > + > +static ssize_t refcount_show(struct kobject *kobj, > + struct kobj_attribute *attr, char *buf) > +{ > + int ret; > + struct nvhost_device_power_attr *power_attribute = > + container_of(attr, struct nvhost_device_power_attr, > + power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]); > + struct platform_device *dev = power_attribute->ndev; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + mutex_lock(&pdata->lock); > + ret = sprintf(buf, "%d\n", pdata->refcount); > + mutex_unlock(&pdata->lock); > + > + return ret; > +} > + > +static ssize_t powergate_delay_store(struct kobject *kobj, > + struct kobj_attribute *attr, const char *buf, size_t count) > +{ > + int powergate_delay = 0, ret = 0; > + struct nvhost_device_power_attr *power_attribute = > + container_of(attr, struct nvhost_device_power_attr, > + power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]); > + struct platform_device *dev = power_attribute->ndev; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + if (!pdata->can_powergate) { > + dev_info(&dev->dev, "does not support power-gating\n"); > + return count; > + } > + > + mutex_lock(&pdata->lock); > + ret = sscanf(buf, "%d", &powergate_delay); > + if (ret == 1 && powergate_delay >= 0) > + pdata->powergate_delay = powergate_delay; > + else > + dev_err(&dev->dev, "Invalid powergate delay\n"); > + mutex_unlock(&pdata->lock); > + > + return count; > +} > + > +static ssize_t powergate_delay_show(struct kobject *kobj, > + struct kobj_attribute *attr, char *buf) > +{ > + int ret; > + struct nvhost_device_power_attr *power_attribute = > + container_of(attr, struct nvhost_device_power_attr, > + power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]); > + struct platform_device *dev = power_attribute->ndev; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + mutex_lock(&pdata->lock); > + ret = sprintf(buf, "%d\n", pdata->powergate_delay); > + mutex_unlock(&pdata->lock); > + > + return ret; > +} > + > +static ssize_t clockgate_delay_store(struct kobject *kobj, > + struct kobj_attribute *attr, const char *buf, size_t count) > +{ > + int clockgate_delay = 0, ret = 0; > + struct nvhost_device_power_attr *power_attribute = > + container_of(attr, struct nvhost_device_power_attr, > + power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]); > + struct platform_device *dev = power_attribute->ndev; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + mutex_lock(&pdata->lock); > + ret = sscanf(buf, "%d", &clockgate_delay); > + if (ret == 1 && clockgate_delay >= 0) > + pdata->clockgate_delay = clockgate_delay; > + else > + dev_err(&dev->dev, "Invalid clockgate delay\n"); > + mutex_unlock(&pdata->lock); > + > + return count; > +} > + > +static ssize_t clockgate_delay_show(struct kobject *kobj, > + struct kobj_attribute *attr, char *buf) > +{ > + int ret; > + struct nvhost_device_power_attr *power_attribute = > + container_of(attr, struct nvhost_device_power_attr, > + power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]); > + struct platform_device *dev = power_attribute->ndev; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + mutex_lock(&pdata->lock); > + ret = sprintf(buf, "%d\n", pdata->clockgate_delay); > + mutex_unlock(&pdata->lock); > + > + return ret; > +} > + > +int nvhost_module_init(struct platform_device *dev) > +{ > + int i = 0, err = 0; > + struct kobj_attribute *attr = NULL; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + /* initialize clocks to known state */ > + while (pdata->clocks[i].name && i < NVHOST_MODULE_MAX_CLOCKS) { > + char devname[MAX_DEVID_LENGTH]; > + long rate = pdata->clocks[i].default_rate; > + struct clk *c; > + > + snprintf(devname, MAX_DEVID_LENGTH, > + "tegra_%s", dev_name(&dev->dev)); > + c = devm_clk_get(&dev->dev, pdata->clocks[i].name); > + if (IS_ERR_OR_NULL(c)) { > + dev_err(&dev->dev, "Cannot get clock %s\n", > + pdata->clocks[i].name); > + return -ENODEV; > + } > + > + rate = clk_round_rate(c, rate); > + clk_prepare_enable(c); > + clk_set_rate(c, rate); > + clk_disable_unprepare(c); > + pdata->clk[i] = c; > + i++; > + } > + pdata->num_clks = i; > + > + mutex_init(&pdata->lock); > + init_waitqueue_head(&pdata->idle_wq); > + INIT_DELAYED_WORK(&pdata->powerstate_down, powerstate_down_handler); > + > + /* power gate units that we can power gate */ > + if (pdata->can_powergate) { > + do_powergate_locked(pdata->powergate_ids[0]); > + do_powergate_locked(pdata->powergate_ids[1]); > + pdata->powerstate = NVHOST_POWER_STATE_POWERGATED; > + } else { > + do_unpowergate_locked(pdata->powergate_ids[0]); > + do_unpowergate_locked(pdata->powergate_ids[1]); > + pdata->powerstate = NVHOST_POWER_STATE_CLOCKGATED; > + } > + > + /* Init the power sysfs attributes for this device */ > + pdata->power_attrib = devm_kzalloc(&dev->dev, > + sizeof(struct nvhost_device_power_attr), > + GFP_KERNEL); > + if (!pdata->power_attrib) { > + dev_err(&dev->dev, "Unable to allocate sysfs attributes\n"); > + return -ENOMEM; > + } > + pdata->power_attrib->ndev = dev; > + > + pdata->power_kobj = kobject_create_and_add("acm", &dev->dev.kobj); > + if (!pdata->power_kobj) { > + dev_err(&dev->dev, "Could not add dir 'power'\n"); > + err = -EIO; > + goto fail_attrib_alloc; > + } > + > + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]; > + attr->attr.name = "clockgate_delay"; > + attr->attr.mode = S_IWUSR | S_IRUGO; > + attr->show = clockgate_delay_show; > + attr->store = clockgate_delay_store; > + if (sysfs_create_file(pdata->power_kobj, &attr->attr)) { > + dev_err(&dev->dev, "Could not create sysfs attribute clockgate_delay\n"); > + err = -EIO; > + goto fail_clockdelay; > + } > + > + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]; > + attr->attr.name = "powergate_delay"; > + attr->attr.mode = S_IWUSR | S_IRUGO; > + attr->show = powergate_delay_show; > + attr->store = powergate_delay_store; > + if (sysfs_create_file(pdata->power_kobj, &attr->attr)) { > + dev_err(&dev->dev, "Could not create sysfs attribute powergate_delay\n"); > + err = -EIO; > + goto fail_powergatedelay; > + } > + > + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_REFCOUNT]; > + attr->attr.name = "refcount"; > + attr->attr.mode = S_IRUGO; > + attr->show = refcount_show; > + if (sysfs_create_file(pdata->power_kobj, &attr->attr)) { > + dev_err(&dev->dev, "Could not create sysfs attribute refcount\n"); > + err = -EIO; > + goto fail_refcount; > + } > + > + return 0; > + > +fail_refcount: > + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_POWERGATE_DELAY]; > + sysfs_remove_file(pdata->power_kobj, &attr->attr); > + > +fail_powergatedelay: > + attr = &pdata->power_attrib->power_attr[NVHOST_POWER_SYSFS_ATTRIB_CLOCKGATE_DELAY]; > + sysfs_remove_file(pdata->power_kobj, &attr->attr); > + > +fail_clockdelay: > + kobject_put(pdata->power_kobj); > + > +fail_attrib_alloc: > + kfree(pdata->power_attrib); > + > + return err; > +} > + > +static int is_module_idle(struct platform_device *dev) > +{ > + int count; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + mutex_lock(&pdata->lock); > + count = pdata->refcount; > + mutex_unlock(&pdata->lock); > + > + return (count == 0); > +} > + > +int nvhost_module_suspend(struct platform_device *dev) > +{ > + int ret; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + ret = wait_event_timeout(pdata->idle_wq, is_module_idle(dev), > + ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT); > + if (ret == 0) { > + dev_info(&dev->dev, "%s prevented suspend\n", > + dev_name(&dev->dev)); > + return -EBUSY; > + } > + > + mutex_lock(&pdata->lock); > + cancel_delayed_work(&pdata->powerstate_down); > + to_state_powergated_locked(dev); > + mutex_unlock(&pdata->lock); > + > + if (pdata->suspend_ndev) > + pdata->suspend_ndev(dev); > + > + return 0; > +} > + > +void nvhost_module_deinit(struct platform_device *dev) > +{ > + int i; > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + > + if (pdata->deinit) > + pdata->deinit(dev); > + > + nvhost_module_suspend(dev); > + for (i = 0; i < pdata->num_clks; i++) > + clk_put(pdata->clk[i]); > + pdata->powerstate = NVHOST_POWER_STATE_DEINIT; > +} > + > +/* public host1x power management APIs */ > +bool nvhost_module_powered_ext(struct platform_device *dev) > +{ > + bool ret = 0; > + > + /* get the parent */ > + if (dev->dev.parent) { > + struct platform_device *pdev; > + pdev = to_platform_device(dev->dev.parent); > + > + ret = nvhost_module_powered(pdev); > + } else { > + dev_warn(&dev->dev, "Cannot return power state, no parent\n"); > + } > + > + return ret; > +} > + Is this intended not to be exported? > +void nvhost_module_busy_ext(struct platform_device *dev) > +{ > + /* get the parent */ > + if (dev->dev.parent) { > + struct platform_device *pdev; > + pdev = to_platform_device(dev->dev.parent); > + > + nvhost_module_busy(pdev); > + } else { > + dev_warn(&dev->dev, "Cannot turn on, no parent\n"); > + } > +} > +EXPORT_SYMBOL(nvhost_module_busy_ext); > + > +void nvhost_module_idle_ext(struct platform_device *dev) > +{ > + /* get the parent */ > + if (dev->dev.parent) { > + struct platform_device *pdev; > + pdev = to_platform_device(dev->dev.parent); > + > + nvhost_module_idle(pdev); > + } else { > + dev_warn(&dev->dev, "Cannot idle, no parent\n"); > + } > +} Same as above. > diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h > new file mode 100644 > index 0000000..65ab3f0 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_acm.h > @@ -0,0 +1,49 @@ > +/* > + * drivers/video/tegra/host/nvhost_acm.h > + * > + * Tegra Graphics Host Automatic Clock Management > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_ACM_H > +#define __NVHOST_ACM_H > + > +#include <linux/workqueue.h> > +#include <linux/wait.h> > +#include <linux/mutex.h> > +#include <linux/clk.h> > +#include <linux/nvhost.h> > + > +/* Sets clocks and powergating state for a module */ > +int nvhost_module_init(struct platform_device *ndev); > +void nvhost_module_deinit(struct platform_device *dev); > +int nvhost_module_suspend(struct platform_device *dev); > + > +void nvhost_module_busy(struct platform_device *dev); > +void nvhost_module_idle_mult(struct platform_device *dev, int refs); > + > +static inline bool nvhost_module_powered(struct platform_device *dev) > +{ > + struct nvhost_device_data *pdata = platform_get_drvdata(dev); > + return pdata->powerstate == NVHOST_POWER_STATE_RUNNING; > +} > + > +static inline void nvhost_module_idle(struct platform_device *dev) > +{ > + nvhost_module_idle_mult(dev, 1); > +} > + > +#endif > diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c > new file mode 100644 > index 0000000..71f0343 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_cdma.c > @@ -0,0 +1,473 @@ > +/* > + * drivers/video/tegra/host/nvhost_cdma.c > + * > + * Tegra Graphics Host Command DMA > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include "nvhost_cdma.h" > +#include "nvhost_channel.h" > +#include "dev.h" > +#include "debug.h" > +#include "nvhost_memmgr.h" > +#include "chip_support.h" > +#include <asm/cacheflush.h> > + > +#include <linux/slab.h> > +#include <linux/kfifo.h> > +#include <trace/events/nvhost.h> > +#include <linux/interrupt.h> > + > +/* > + * TODO: > + * stats > + * - for figuring out what to optimize further > + * resizable push buffer > + * - some channels hardly need any, some channels (3d) could use more > + */ > + > +/** > + * Add an entry to the sync queue. > + */ > +static void add_to_sync_queue(struct nvhost_cdma *cdma, > + struct nvhost_job *job, > + u32 nr_slots, > + u32 first_get) > +{ > + if (job->syncpt_id == NVSYNCPT_INVALID) { > + dev_warn(&job->ch->dev->dev, "%s: Invalid syncpt\n", > + __func__); > + return; > + } > + > + job->first_get = first_get; > + job->num_slots = nr_slots; > + nvhost_job_get(job); > + list_add_tail(&job->list, &cdma->sync_queue); > +} > + > +/** > + * Return the status of the cdma's sync queue or push buffer for the given event > + * - sq empty: returns 1 for empty, 0 for not empty (as in "1 empty queue" :-) > + * - pb space: returns the number of free slots in the channel's push buffer > + * Must be called with the cdma lock held. > + */ > +static unsigned int cdma_status_locked(struct nvhost_cdma *cdma, > + enum cdma_event event) > +{ > + switch (event) { > + case CDMA_EVENT_SYNC_QUEUE_EMPTY: > + return list_empty(&cdma->sync_queue) ? 1 : 0; > + case CDMA_EVENT_PUSH_BUFFER_SPACE: { > + struct push_buffer *pb = &cdma->push_buffer; > + return cdma_pb_op().space(pb); > + } > + default: > + return 0; > + } > +} > + > +/** > + * Sleep (if necessary) until the requested event happens > + * - CDMA_EVENT_SYNC_QUEUE_EMPTY : sync queue is completely empty. > + * - Returns 1 > + * - CDMA_EVENT_PUSH_BUFFER_SPACE : there is space in the push buffer > + * - Return the amount of space (> 0) > + * Must be called with the cdma lock held. > + */ > +unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, > + enum cdma_event event) > +{ > + for (;;) { > + unsigned int space = cdma_status_locked(cdma, event); > + if (space) > + return space; > + > + trace_nvhost_wait_cdma(cdma_to_channel(cdma)->dev->name, > + event); > + > + /* If somebody has managed to already start waiting, yield */ > + if (cdma->event != CDMA_EVENT_NONE) { > + mutex_unlock(&cdma->lock); > + schedule(); > + mutex_lock(&cdma->lock); > + continue; > + } > + cdma->event = event; > + > + mutex_unlock(&cdma->lock); > + down(&cdma->sem); > + mutex_lock(&cdma->lock); > + } > + return 0; > +} > + > +/** > + * Start timer for a buffer submition that has completed yet. > + * Must be called with the cdma lock held. > + */ > +static void cdma_start_timer_locked(struct nvhost_cdma *cdma, > + struct nvhost_job *job) > +{ > + if (cdma->timeout.clientid) { > + /* timer already started */ > + return; > + } > + > + cdma->timeout.clientid = job->clientid; > + cdma->timeout.syncpt_id = job->syncpt_id; > + cdma->timeout.syncpt_val = job->syncpt_end; > + cdma->timeout.start_ktime = ktime_get(); > + > + schedule_delayed_work(&cdma->timeout.wq, > + msecs_to_jiffies(job->timeout)); > +} > + > +/** > + * Stop timer when a buffer submition completes. > + * Must be called with the cdma lock held. > + */ > +static void stop_cdma_timer_locked(struct nvhost_cdma *cdma) > +{ > + cancel_delayed_work(&cdma->timeout.wq); > + cdma->timeout.clientid = 0; > +} > + > +/** > + * For all sync queue entries that have already finished according to the > + * current sync point registers: > + * - unpin & unref their mems > + * - pop their push buffer slots > + * - remove them from the sync queue > + * This is normally called from the host code's worker thread, but can be > + * called manually if necessary. > + * Must be called with the cdma lock held. > + */ > +static void update_cdma_locked(struct nvhost_cdma *cdma) > +{ > + bool signal = false; > + struct nvhost_master *dev = cdma_to_dev(cdma); > + struct nvhost_syncpt *sp = &dev->syncpt; > + struct nvhost_job *job, *n; > + > + /* If CDMA is stopped, queue is cleared and we can return */ > + if (!cdma->running) > + return; > + > + /* > + * Walk the sync queue, reading the sync point registers as necessary, > + * to consume as many sync queue entries as possible without blocking > + */ > + list_for_each_entry_safe(job, n, &cdma->sync_queue, list) { > + /* Check whether this syncpt has completed, and bail if not */ > + if (!nvhost_syncpt_is_expired(sp, > + job->syncpt_id, job->syncpt_end)) { > + /* Start timer on next pending syncpt */ > + if (job->timeout) > + cdma_start_timer_locked(cdma, job); > + break; > + } > + > + /* Cancel timeout, when a buffer completes */ > + if (cdma->timeout.clientid) > + stop_cdma_timer_locked(cdma); > + > + /* Unpin the memory */ > + nvhost_job_unpin(job); > + > + /* Pop push buffer slots */ > + if (job->num_slots) { > + struct push_buffer *pb = &cdma->push_buffer; > + cdma_pb_op().pop_from(pb, job->num_slots); > + if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE) > + signal = true; > + } > + > + list_del(&job->list); > + nvhost_job_put(job); > + } > + > + if (list_empty(&cdma->sync_queue) && > + cdma->event == CDMA_EVENT_SYNC_QUEUE_EMPTY) > + signal = true; > + > + /* Wake up CdmaWait() if the requested event happened */ > + if (signal) { > + cdma->event = CDMA_EVENT_NONE; > + up(&cdma->sem); > + } > +} > + > +void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, > + struct nvhost_syncpt *syncpt, struct platform_device *dev) > +{ > + u32 get_restart; > + u32 syncpt_incrs; > + struct nvhost_job *job = NULL; > + u32 syncpt_val; > + > + syncpt_val = nvhost_syncpt_update_min(syncpt, cdma->timeout.syncpt_id); > + > + dev_dbg(&dev->dev, > + "%s: starting cleanup (thresh %d)\n", > + __func__, syncpt_val); > + > + /* > + * Move the sync_queue read pointer to the first entry that hasn't > + * completed based on the current HW syncpt value. It's likely there > + * won't be any (i.e. we're still at the head), but covers the case > + * where a syncpt incr happens just prior/during the teardown. > + */ > + > + dev_dbg(&dev->dev, > + "%s: skip completed buffers still in sync_queue\n", > + __func__); > + > + list_for_each_entry(job, &cdma->sync_queue, list) { > + if (syncpt_val < job->syncpt_end) > + break; > + > + nvhost_job_dump(&dev->dev, job); > + } > + > + /* > + * Walk the sync_queue, first incrementing with the CPU syncpts that > + * are partially executed (the first buffer) or fully skipped while > + * still in the current context (slots are also NOP-ed). > + * > + * At the point contexts are interleaved, syncpt increments must be > + * done inline with the pushbuffer from a GATHER buffer to maintain > + * the order (slots are modified to be a GATHER of syncpt incrs). > + * > + * Note: save in get_restart the location where the timed out buffer > + * started in the PB, so we can start the refetch from there (with the > + * modified NOP-ed PB slots). This lets things appear to have completed > + * properly for this buffer and resources are freed. > + */ > + > + dev_dbg(&dev->dev, > + "%s: perform CPU incr on pending same ctx buffers\n", > + __func__); > + > + get_restart = cdma->last_put; > + if (!list_empty(&cdma->sync_queue)) > + get_restart = job->first_get; > + > + /* do CPU increments as long as this context continues */ > + list_for_each_entry_from(job, &cdma->sync_queue, list) { > + /* different context, gets us out of this loop */ > + if (job->clientid != cdma->timeout.clientid) > + break; > + > + /* won't need a timeout when replayed */ > + job->timeout = 0; > + > + syncpt_incrs = job->syncpt_end - syncpt_val; > + dev_dbg(&dev->dev, > + "%s: CPU incr (%d)\n", __func__, syncpt_incrs); > + > + nvhost_job_dump(&dev->dev, job); > + > + /* safe to use CPU to incr syncpts */ > + cdma_op().timeout_cpu_incr(cdma, > + job->first_get, > + syncpt_incrs, > + job->syncpt_end, > + job->num_slots); > + > + syncpt_val += syncpt_incrs; > + } > + > + list_for_each_entry_from(job, &cdma->sync_queue, list) > + if (job->clientid == cdma->timeout.clientid) > + job->timeout = 500; > + > + dev_dbg(&dev->dev, > + "%s: finished sync_queue modification\n", __func__); > + > + /* roll back DMAGET and start up channel again */ > + cdma_op().timeout_teardown_end(cdma, get_restart); > +} > + > +/** > + * Create a cdma > + */ > +int nvhost_cdma_init(struct nvhost_cdma *cdma) > +{ > + int err; > + struct push_buffer *pb = &cdma->push_buffer; > + mutex_init(&cdma->lock); > + sema_init(&cdma->sem, 0); > + > + INIT_LIST_HEAD(&cdma->sync_queue); > + > + cdma->event = CDMA_EVENT_NONE; > + cdma->running = false; > + cdma->torndown = false; > + > + err = cdma_pb_op().init(pb); > + if (err) > + return err; > + return 0; > +} > + > +/** > + * Destroy a cdma > + */ > +void nvhost_cdma_deinit(struct nvhost_cdma *cdma) > +{ > + struct push_buffer *pb = &cdma->push_buffer; > + > + if (cdma->running) { > + pr_warn("%s: CDMA still running\n", > + __func__); > + } else { > + cdma_pb_op().destroy(pb); > + cdma_op().timeout_destroy(cdma); > + } > +} > + > +/** > + * Begin a cdma submit > + */ > +int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job) > +{ > + mutex_lock(&cdma->lock); > + > + if (job->timeout) { > + /* init state on first submit with timeout value */ > + if (!cdma->timeout.initialized) { > + int err; > + err = cdma_op().timeout_init(cdma, > + job->syncpt_id); > + if (err) { > + mutex_unlock(&cdma->lock); > + return err; > + } > + } > + } > + if (!cdma->running) > + cdma_op().start(cdma); > + > + cdma->slots_free = 0; > + cdma->slots_used = 0; > + cdma->first_get = cdma_pb_op().putptr(&cdma->push_buffer); > + return 0; > +} > + > +static void trace_write_gather(struct nvhost_cdma *cdma, > + struct mem_handle *ref, > + u32 offset, u32 words) > +{ > + void *mem = NULL; > + > + if (nvhost_debug_trace_cmdbuf) { > + mem = mem_op().mmap(ref); > + if (IS_ERR_OR_NULL(mem)) > + mem = NULL; > + }; > + > + if (mem) { > + u32 i; > + /* > + * Write in batches of 128 as there seems to be a limit > + * of how much you can output to ftrace at once. > + */ > + for (i = 0; i < words; i += TRACE_MAX_LENGTH) { > + trace_nvhost_cdma_push_gather( > + cdma_to_channel(cdma)->dev->name, > + (u32)ref, > + min(words - i, TRACE_MAX_LENGTH), > + offset + i * sizeof(u32), > + mem); > + } > + mem_op().munmap(ref, mem); > + } > +} > + > +/** > + * Push two words into a push buffer slot > + * Blocks as necessary if the push buffer is full. > + */ > +void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2) > +{ > + if (nvhost_debug_trace_cmdbuf) > + trace_nvhost_cdma_push(cdma_to_channel(cdma)->dev->name, > + op1, op2); > + > + nvhost_cdma_push_gather(cdma, NULL, NULL, 0, op1, op2); > +} > + > +/** > + * Push two words into a push buffer slot > + * Blocks as necessary if the push buffer is full. > + */ > +void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, > + struct mem_mgr *client, struct mem_handle *handle, > + u32 offset, u32 op1, u32 op2) > +{ > + u32 slots_free = cdma->slots_free; > + struct push_buffer *pb = &cdma->push_buffer; > + > + if (handle) > + trace_write_gather(cdma, handle, offset, op1 & 0xffff); > + > + if (slots_free == 0) { > + cdma_op().kick(cdma); > + slots_free = nvhost_cdma_wait_locked(cdma, > + CDMA_EVENT_PUSH_BUFFER_SPACE); > + } > + cdma->slots_free = slots_free - 1; > + cdma->slots_used++; > + cdma_pb_op().push_to(pb, client, handle, op1, op2); > +} > + > +/** > + * End a cdma submit > + * Kick off DMA, add job to the sync queue, and a number of slots to be freed > + * from the pushbuffer. 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 nvhost_cdma *cdma, > + struct nvhost_job *job) > +{ > + bool was_idle = list_empty(&cdma->sync_queue); > + > + cdma_op().kick(cdma); > + > + add_to_sync_queue(cdma, > + job, > + cdma->slots_used, > + cdma->first_get); > + > + /* start timer on idle -> active transitions */ > + if (job->timeout && was_idle) > + cdma_start_timer_locked(cdma, job); > + > + trace_nvhost_cdma_end(job->ch->dev->name); > + > + mutex_unlock(&cdma->lock); > +} > + > +/** > + * Update cdma state according to current sync point values > + */ > +void nvhost_cdma_update(struct nvhost_cdma *cdma) > +{ > + mutex_lock(&cdma->lock); > + update_cdma_locked(cdma); > + mutex_unlock(&cdma->lock); > +} > diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h > new file mode 100644 > index 0000000..2179f29 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_cdma.h > @@ -0,0 +1,116 @@ > +/* > + * drivers/video/tegra/host/nvhost_cdma.h > + * > + * Tegra Graphics Host Command DMA > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_CDMA_H > +#define __NVHOST_CDMA_H > + > +#include <linux/sched.h> > +#include <linux/semaphore.h> > + > +#include <linux/nvhost.h> > +#include <linux/list.h> > + > +struct nvhost_syncpt; > +struct nvhost_userctx_timeout; > +struct nvhost_job; > +struct mem_mgr; > +struct mem_handle; > + > +/* > + * cdma > + * > + * This is in charge of a host command DMA channel. > + * Sends ops to a push buffer, and takes responsibility for unpinning > + * (& possibly freeing) of memory after those ops have completed. > + * Producer: > + * begin > + * push - send ops to the push buffer > + * end - start command DMA and enqueue handles to be unpinned > + * Consumer: > + * update - call to update sync queue and push buffer, unpin memory > + */ > + > +struct mem_mgr_handle { > + struct mem_mgr *client; > + struct mem_handle *handle; > +}; > + > +struct push_buffer { > + u32 *mapped; /* mapped pushbuffer memory */ > + dma_addr_t phys; /* physical address of pushbuffer */ > + u32 fence; /* index we've written */ > + u32 cur; /* index to write to */ > + struct mem_mgr_handle *client_handle; /* handle for each opcode pair */ > +}; > + > +struct buffer_timeout { > + struct delayed_work wq; /* work queue */ > + bool initialized; /* timer one-time setup flag */ > + u32 syncpt_id; /* buffer completion syncpt id */ > + u32 syncpt_val; /* syncpt value when completed */ > + ktime_t start_ktime; /* starting time */ > + /* context timeout information */ > + int clientid; > +}; > + > +enum cdma_event { > + CDMA_EVENT_NONE, /* not waiting for any event */ > + CDMA_EVENT_SYNC_QUEUE_EMPTY, /* wait for empty sync queue */ > + CDMA_EVENT_PUSH_BUFFER_SPACE /* wait for space in push buffer */ > +}; > + > +struct nvhost_cdma { > + struct mutex lock; /* controls access to shared state */ > + struct semaphore sem; /* signalled when event occurs */ > + enum cdma_event event; /* event that sem is waiting for */ > + unsigned int slots_used; /* pb slots used in current submit */ > + unsigned int slots_free; /* pb slots free in current submit */ > + unsigned int first_get; /* DMAGET value, where submit begins */ > + unsigned int last_put; /* last value written to DMAPUT */ > + struct push_buffer push_buffer; /* channel's push buffer */ > + struct list_head sync_queue; /* job queue */ > + struct buffer_timeout timeout; /* channel's timeout state/wq */ > + bool running; > + bool torndown; > +}; > + > +#define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma) > +#define cdma_to_dev(cdma) nvhost_get_host(cdma_to_channel(cdma)->dev) > +#define cdma_to_memmgr(cdma) ((cdma_to_dev(cdma))->memmgr) > +#define pb_to_cdma(pb) container_of(pb, struct nvhost_cdma, push_buffer) > + > +int nvhost_cdma_init(struct nvhost_cdma *cdma); > +void nvhost_cdma_deinit(struct nvhost_cdma *cdma); > +void nvhost_cdma_stop(struct nvhost_cdma *cdma); > +int nvhost_cdma_begin(struct nvhost_cdma *cdma, struct nvhost_job *job); > +void nvhost_cdma_push(struct nvhost_cdma *cdma, u32 op1, u32 op2); > +void nvhost_cdma_push_gather(struct nvhost_cdma *cdma, > + struct mem_mgr *client, > + struct mem_handle *handle, u32 offset, u32 op1, u32 op2); > +void nvhost_cdma_end(struct nvhost_cdma *cdma, > + struct nvhost_job *job); > +void nvhost_cdma_update(struct nvhost_cdma *cdma); > +void nvhost_cdma_peek(struct nvhost_cdma *cdma, > + u32 dmaget, int slot, u32 *out); > +unsigned int nvhost_cdma_wait_locked(struct nvhost_cdma *cdma, > + enum cdma_event event); > +void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma, > + struct nvhost_syncpt *syncpt, struct platform_device *dev); > +#endif > diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c > new file mode 100644 > index 0000000..f95d8f2 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_channel.c > @@ -0,0 +1,129 @@ > +/* > + * drivers/video/tegra/host/nvhost_channel.c > + * > + * Tegra Graphics Host Channel > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include "nvhost_channel.h" > +#include "dev.h" > +#include "nvhost_acm.h" > +#include "chip_support.h" > + > +#include <trace/events/nvhost.h> > +#include <linux/slab.h> > +#include <linux/module.h> > + > +#define NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT 50 > + > +int nvhost_channel_init(struct nvhost_channel *ch, > + struct nvhost_master *dev, int index) > +{ > + int err; > + struct nvhost_device_data *pdata = platform_get_drvdata(ch->dev); > + > + /* Link platform_device to nvhost_channel */ > + err = channel_op().init(ch, dev, index); > + if (err < 0) { > + dev_err(&dev->dev->dev, "failed to init channel %d\n", > + index); > + return err; > + } > + pdata->channel = ch; > + > + return 0; > +} > + > +int nvhost_channel_submit(struct nvhost_job *job) > +{ > + return channel_op().submit(job); > +} > +EXPORT_SYMBOL(nvhost_channel_submit); > + > +struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch) > +{ > + int err = 0; > + struct nvhost_device_data *pdata = platform_get_drvdata(ch->dev); > + > + mutex_lock(&ch->reflock); > + if (ch->refcount == 0) { > + if (pdata->init) > + pdata->init(ch->dev); > + err = nvhost_cdma_init(&ch->cdma); > + } > + if (!err) > + ch->refcount++; > + > + mutex_unlock(&ch->reflock); > + > + return err ? NULL : ch; > +} > +EXPORT_SYMBOL(nvhost_getchannel); > + > +void nvhost_putchannel(struct nvhost_channel *ch) > +{ > + mutex_lock(&ch->reflock); > + if (ch->refcount == 1) { > + channel_cdma_op().stop(&ch->cdma); > + nvhost_cdma_deinit(&ch->cdma); > + nvhost_module_suspend(ch->dev); > + } > + ch->refcount--; > + mutex_unlock(&ch->reflock); > +} > +EXPORT_SYMBOL(nvhost_putchannel); > + > +int nvhost_channel_suspend(struct nvhost_channel *ch) > +{ > + int ret = 0; > + > + mutex_lock(&ch->reflock); > + > + if (ch->refcount) { > + ret = nvhost_module_suspend(ch->dev); > + if (!ret) > + channel_cdma_op().stop(&ch->cdma); > + } > + mutex_unlock(&ch->reflock); > + > + return ret; > +} > + > +struct nvhost_channel *nvhost_alloc_channel_internal(int chindex, > + int max_channels, int *current_channel_count) > +{ > + struct nvhost_channel *ch = NULL; > + > + if (chindex > max_channels || > + (*current_channel_count + 1) > max_channels) > + return NULL; > + else { Nit: redundant else. > + ch = kzalloc(sizeof(*ch), GFP_KERNEL); > + if (ch == NULL) > + return NULL; > + else { Nit: same as above. > + (*current_channel_count)++; > + return ch; > + } > + } > +} > + > +void nvhost_free_channel_internal(struct nvhost_channel *ch, > + int *current_channel_count) > +{ > + kfree(ch); > + (*current_channel_count)--; > +} > diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h > new file mode 100644 > index 0000000..65854b4 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_channel.h > @@ -0,0 +1,65 @@ > +/* > + * drivers/video/tegra/host/nvhost_channel.h > + * > + * Tegra Graphics Host Channel > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_CHANNEL_H > +#define __NVHOST_CHANNEL_H > + > +#include <linux/cdev.h> > +#include <linux/io.h> > +#include "nvhost_cdma.h" > + > +#define NVHOST_MAX_WAIT_CHECKS 256 > +#define NVHOST_MAX_GATHERS 512 > +#define NVHOST_MAX_HANDLES 1280 > +#define NVHOST_MAX_POWERGATE_IDS 2 > + > +struct nvhost_master; > +struct platform_device; > +struct nvhost_channel; > + > +struct nvhost_channel { > + int refcount; > + int chid; > + u32 syncpt_id; > + struct mutex reflock; > + struct mutex submitlock; > + void __iomem *aperture; > + struct device *node; > + struct platform_device *dev; > + struct cdev cdev; > + struct nvhost_cdma cdma; > +}; > + > +int nvhost_channel_init(struct nvhost_channel *ch, > + struct nvhost_master *dev, int index); > + > +struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch); > +void nvhost_putchannel(struct nvhost_channel *ch); > +int nvhost_channel_suspend(struct nvhost_channel *ch); > + > +struct nvhost_channel *nvhost_alloc_channel_internal(int chindex, > + int max_channels, int *current_channel_count); > + > +void nvhost_free_channel_internal(struct nvhost_channel *ch, > + int *current_channel_count); > + > +int nvhost_channel_save_context(struct nvhost_channel *ch); > + > +#endif > diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c > new file mode 100644 > index 0000000..9a0fa3e > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_intr.c > @@ -0,0 +1,391 @@ > +/* > + * drivers/video/tegra/host/nvhost_intr.c > + * > + * Tegra Graphics Host Interrupt Management > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include "nvhost_intr.h" > +#include "dev.h" > +#include "nvhost_acm.h" > +#include <linux/interrupt.h> > +#include <linux/slab.h> > +#include <linux/irq.h> > +#include <trace/events/nvhost.h> > +#include "nvhost_channel.h" > +#include "chip_support.h" > + > +/*** Wait list management ***/ > + > +struct nvhost_waitlist { > + struct list_head list; > + struct kref refcount; > + u32 thresh; > + enum nvhost_intr_action action; > + atomic_t state; > + void *data; > + int count; > +}; > + > +enum waitlist_state { > + WLS_PENDING, > + WLS_REMOVED, > + WLS_CANCELLED, > + WLS_HANDLED > +}; > + > +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 > + */ > +static bool add_waiter_to_queue(struct nvhost_waitlist *waiter, > + struct list_head *queue) > +{ > + struct nvhost_waitlist *pos; > + u32 thresh = waiter->thresh; > + > + list_for_each_entry_reverse(pos, queue, list) > + if ((s32)(pos->thresh - thresh) <= 0) { > + list_add(&waiter->list, &pos->list); > + return false; > + } > + > + list_add(&waiter->list, queue); > + return true; > +} > + > +/** > + * run through a waiter queue for a single sync point ID > + * and gather all completed waiters into lists by actions > + */ > +static void remove_completed_waiters(struct list_head *head, u32 sync, > + struct list_head completed[NVHOST_INTR_ACTION_COUNT]) > +{ > + struct list_head *dest; > + struct nvhost_waitlist *waiter, *next, *prev; > + > + list_for_each_entry_safe(waiter, next, head, list) { > + if ((s32)(waiter->thresh - sync) > 0) > + break; > + > + dest = completed + waiter->action; > + > + /* consolidate submit cleanups */ > + if (waiter->action == NVHOST_INTR_ACTION_SUBMIT_COMPLETE > + && !list_empty(dest)) { > + prev = list_entry(dest->prev, > + struct nvhost_waitlist, list); > + if (prev->data == waiter->data) { > + prev->count++; > + dest = NULL; > + } > + } > + > + /* PENDING->REMOVED or CANCELLED->HANDLED */ > + if (atomic_inc_return(&waiter->state) == WLS_HANDLED || !dest) { > + list_del(&waiter->list); > + kref_put(&waiter->refcount, waiter_release); > + } else { > + list_move_tail(&waiter->list, dest); > + } > + } > +} > + > +void reset_threshold_interrupt(struct nvhost_intr *intr, > + struct list_head *head, > + unsigned int id) > +{ > + u32 thresh = list_first_entry(head, > + struct nvhost_waitlist, list)->thresh; > + > + intr_op().set_syncpt_threshold(intr, id, thresh); > + intr_op().enable_syncpt_intr(intr, id); > +} > + > + > +static void action_submit_complete(struct nvhost_waitlist *waiter) > +{ > + struct nvhost_channel *channel = waiter->data; > + int nr_completed = waiter->count; > + > + nvhost_cdma_update(&channel->cdma); > + nvhost_module_idle_mult(channel->dev, nr_completed); > + > + /* Add nr_completed to trace */ > + trace_nvhost_channel_submit_complete(channel->dev->name, > + nr_completed, waiter->thresh); > + > +} > + > +static void action_wakeup(struct nvhost_waitlist *waiter) > +{ > + wait_queue_head_t *wq = waiter->data; > + > + wake_up(wq); > +} > + > +static void action_wakeup_interruptible(struct nvhost_waitlist *waiter) > +{ > + wait_queue_head_t *wq = waiter->data; > + > + wake_up_interruptible(wq); > +} > + > +typedef void (*action_handler)(struct nvhost_waitlist *waiter); > + > +static action_handler action_handlers[NVHOST_INTR_ACTION_COUNT] = { > + action_submit_complete, > + action_wakeup, > + action_wakeup_interruptible, > +}; > + > +static void run_handlers(struct list_head completed[NVHOST_INTR_ACTION_COUNT]) > +{ > + struct list_head *head = completed; > + int i; > + > + for (i = 0; i < NVHOST_INTR_ACTION_COUNT; ++i, ++head) { > + action_handler handler = action_handlers[i]; > + struct nvhost_waitlist *waiter, *next; > + > + list_for_each_entry_safe(waiter, next, head, list) { > + list_del(&waiter->list); > + handler(waiter); > + WARN_ON(atomic_xchg(&waiter->state, WLS_HANDLED) > + != WLS_REMOVED); > + kref_put(&waiter->refcount, waiter_release); > + } > + } > +} > + > +/** > + * Remove & handle all waiters that have completed for the given syncpt > + */ > +static int process_wait_list(struct nvhost_intr *intr, > + struct nvhost_intr_syncpt *syncpt, > + u32 threshold) > +{ > + struct list_head completed[NVHOST_INTR_ACTION_COUNT]; > + unsigned int i; > + int empty; > + > + for (i = 0; i < NVHOST_INTR_ACTION_COUNT; ++i) > + INIT_LIST_HEAD(completed + i); > + > + spin_lock(&syncpt->lock); > + > + remove_completed_waiters(&syncpt->wait_head, threshold, completed); > + > + empty = list_empty(&syncpt->wait_head); > + if (empty) > + intr_op().disable_syncpt_intr(intr, syncpt->id); > + else > + reset_threshold_interrupt(intr, &syncpt->wait_head, > + syncpt->id); > + > + spin_unlock(&syncpt->lock); > + > + run_handlers(completed); > + > + return empty; > +} > + > +/*** host syncpt interrupt service functions ***/ > +/** > + * Sync point threshold interrupt service thread function > + * Handles sync point threshold triggers, in thread context > + */ > +irqreturn_t nvhost_syncpt_thresh_fn(int irq, void *dev_id) > +{ > + struct nvhost_intr_syncpt *syncpt = dev_id; > + unsigned int id = syncpt->id; > + struct nvhost_intr *intr = intr_syncpt_to_intr(syncpt); > + struct nvhost_master *dev = intr_to_dev(intr); > + > + (void)process_wait_list(intr, syncpt, > + nvhost_syncpt_update_min(&dev->syncpt, id)); > + > + return IRQ_HANDLED; > +} > + > +/*** host general interrupt service functions ***/ > + > + > +/*** Main API ***/ > + > +int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, > + enum nvhost_intr_action action, void *data, > + void *_waiter, > + void **ref) > +{ > + struct nvhost_waitlist *waiter = _waiter; > + struct nvhost_intr_syncpt *syncpt; > + int queue_was_empty; > + > + if (waiter == NULL) { > + pr_warn("%s: NULL waiter\n", __func__); > + return -EINVAL; > + } > + > + /* initialize a new waiter */ > + INIT_LIST_HEAD(&waiter->list); > + kref_init(&waiter->refcount); > + if (ref) > + kref_get(&waiter->refcount); > + waiter->thresh = thresh; > + waiter->action = action; > + atomic_set(&waiter->state, WLS_PENDING); > + waiter->data = data; > + waiter->count = 1; > + > + syncpt = intr->syncpt + id; > + > + spin_lock(&syncpt->lock); > + > + queue_was_empty = list_empty(&syncpt->wait_head); > + > + if (add_waiter_to_queue(waiter, &syncpt->wait_head)) { > + /* added at head of list - new threshold value */ > + intr_op().set_syncpt_threshold(intr, id, thresh); > + > + /* added as first waiter - enable interrupt */ > + if (queue_was_empty) > + intr_op().enable_syncpt_intr(intr, id); > + } > + > + spin_unlock(&syncpt->lock); > + > + if (ref) > + *ref = waiter; > + return 0; > +} > + > +void *nvhost_intr_alloc_waiter() > +{ > + return kzalloc(sizeof(struct nvhost_waitlist), > + GFP_KERNEL|__GFP_REPEAT); > +} > + > +void nvhost_intr_put_ref(struct nvhost_intr *intr, u32 id, void *ref) > +{ > + struct nvhost_waitlist *waiter = ref; > + struct nvhost_intr_syncpt *syncpt; > + struct nvhost_master *host = intr_to_dev(intr); > + > + while (atomic_cmpxchg(&waiter->state, > + WLS_PENDING, WLS_CANCELLED) == WLS_REMOVED) > + schedule(); > + > + syncpt = intr->syncpt + id; > + (void)process_wait_list(intr, syncpt, > + nvhost_syncpt_update_min(&host->syncpt, id)); > + > + kref_put(&waiter->refcount, waiter_release); > +} > + > + > +/*** Init & shutdown ***/ > + > +int nvhost_intr_init(struct nvhost_intr *intr, u32 irq_gen, u32 irq_sync) > +{ > + unsigned int id; > + struct nvhost_intr_syncpt *syncpt; > + struct nvhost_master *host = intr_to_dev(intr); > + u32 nb_pts = nvhost_syncpt_nb_pts(&host->syncpt); > + > + mutex_init(&intr->mutex); > + intr->host_syncpt_irq_base = irq_sync; > + intr->wq = create_workqueue("host_syncpt"); > + intr_op().init_host_sync(intr); > + intr->host_general_irq = irq_gen; > + intr_op().request_host_general_irq(intr); > + > + for (id = 0, syncpt = intr->syncpt; > + id < nb_pts; > + ++id, ++syncpt) { > + syncpt->intr = &host->intr; > + syncpt->id = id; > + syncpt->irq = irq_sync + id; > + spin_lock_init(&syncpt->lock); > + INIT_LIST_HEAD(&syncpt->wait_head); > + snprintf(syncpt->thresh_irq_name, > + sizeof(syncpt->thresh_irq_name), > + "host_sp_%02d", id); > + } > + > + return 0; > +} > + > +void nvhost_intr_deinit(struct nvhost_intr *intr) > +{ > + nvhost_intr_stop(intr); > + destroy_workqueue(intr->wq); > +} > + > +void nvhost_intr_start(struct nvhost_intr *intr, u32 hz) > +{ > + mutex_lock(&intr->mutex); > + > + intr_op().init_host_sync(intr); > + intr_op().set_host_clocks_per_usec(intr, > + (hz + 1000000 - 1)/1000000); > + > + intr_op().request_host_general_irq(intr); > + > + mutex_unlock(&intr->mutex); > +} > + > +void nvhost_intr_stop(struct nvhost_intr *intr) > +{ > + unsigned int id; > + struct nvhost_intr_syncpt *syncpt; > + u32 nb_pts = nvhost_syncpt_nb_pts(&intr_to_dev(intr)->syncpt); > + > + mutex_lock(&intr->mutex); > + > + intr_op().disable_all_syncpt_intrs(intr); > + > + for (id = 0, syncpt = intr->syncpt; > + id < nb_pts; > + ++id, ++syncpt) { > + struct nvhost_waitlist *waiter, *next; > + list_for_each_entry_safe(waiter, next, > + &syncpt->wait_head, list) { > + if (atomic_cmpxchg(&waiter->state, > + WLS_CANCELLED, WLS_HANDLED) > + == WLS_CANCELLED) { > + list_del(&waiter->list); > + kref_put(&waiter->refcount, waiter_release); > + } > + } > + > + if (!list_empty(&syncpt->wait_head)) { /* output diagnostics */ > + pr_warn("%s cannot stop syncpt intr id=%d\n", > + __func__, id); > + return; > + } > + } > + > + intr_op().free_host_general_irq(intr); > + intr_op().free_syncpt_irq(intr); > + > + mutex_unlock(&intr->mutex); > +} > diff --git a/drivers/video/tegra/host/nvhost_intr.h b/drivers/video/tegra/host/nvhost_intr.h > new file mode 100644 > index 0000000..7554864 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_intr.h > @@ -0,0 +1,110 @@ > +/* > + * drivers/video/tegra/host/nvhost_intr.h > + * > + * Tegra Graphics Host Interrupt Management > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#ifndef __NVHOST_INTR_H > +#define __NVHOST_INTR_H > + > +#include <linux/kthread.h> > +#include <linux/semaphore.h> > +#include <linux/interrupt.h> > +#include <linux/workqueue.h> > + > +struct nvhost_channel; > + > +enum nvhost_intr_action { > + /** > + * Perform cleanup after a submit has completed. > + * 'data' points to a channel > + */ > + NVHOST_INTR_ACTION_SUBMIT_COMPLETE = 0, > + > + /** > + * Wake up a task. > + * 'data' points to a wait_queue_head_t > + */ > + NVHOST_INTR_ACTION_WAKEUP, > + > + /** > + * Wake up a interruptible task. > + * 'data' points to a wait_queue_head_t > + */ > + NVHOST_INTR_ACTION_WAKEUP_INTERRUPTIBLE, > + > + NVHOST_INTR_ACTION_COUNT > +}; > + > +struct nvhost_intr; > + > +struct nvhost_intr_syncpt { > + struct nvhost_intr *intr; > + u8 id; > + u16 irq; > + spinlock_t lock; > + struct list_head wait_head; > + char thresh_irq_name[12]; > + struct work_struct work; > +}; > + > +struct nvhost_intr { > + struct nvhost_intr_syncpt *syncpt; > + struct mutex mutex; > + int host_general_irq; > + int host_syncpt_irq_base; > + struct workqueue_struct *wq; > +}; > +#define intr_to_dev(x) container_of(x, struct nvhost_master, intr) > +#define intr_syncpt_to_intr(is) (is->intr) > + > +/** > + * Schedule an action to be taken when a sync point reaches the given threshold. > + * > + * @id the sync point > + * @thresh the threshold > + * @action the action to take > + * @data a pointer to extra data depending on action, see above > + * @waiter waiter allocated with nvhost_intr_alloc_waiter - assumes ownership > + * @ref must be passed if cancellation is possible, else NULL > + * > + * This is a non-blocking api. > + */ > +int nvhost_intr_add_action(struct nvhost_intr *intr, u32 id, u32 thresh, > + enum nvhost_intr_action action, void *data, > + void *waiter, > + void **ref); > + > +/** > + * Allocate a waiter. > + */ > +void *nvhost_intr_alloc_waiter(void); > + > +/** > + * Unreference an action submitted to nvhost_intr_add_action(). > + * You must call this if you passed non-NULL as ref. > + * @ref the ref returned from nvhost_intr_add_action() > + */ > +void nvhost_intr_put_ref(struct nvhost_intr *intr, u32 id, 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_start(struct nvhost_intr *intr, u32 hz); > +void nvhost_intr_stop(struct nvhost_intr *intr); > + > +irqreturn_t nvhost_syncpt_thresh_fn(int irq, void *dev_id); > +#endif > diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c > new file mode 100644 > index 0000000..ebb071d > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_job.c > @@ -0,0 +1,398 @@ > +/* > + * drivers/video/tegra/host/nvhost_job.c > + * > + * Tegra Graphics Host Job > + * > + * 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/module.h> > +#include <linux/slab.h> > +#include <linux/kref.h> > +#include <linux/err.h> > +#include <linux/vmalloc.h> > +#include <linux/scatterlist.h> > +#include <linux/nvhost.h> > +#include <trace/events/nvhost.h> > +#include "nvhost_channel.h" > +#include "nvhost_syncpt.h" > +#include "dev.h" > +#include "nvhost_memmgr.h" > +#include "chip_support.h" > + > +/* Magic to use to fill freed handle slots */ > +#define BAD_MAGIC 0xdeadbeef > + > +static size_t job_size(u32 num_cmdbufs, u32 num_relocs, u32 num_waitchks) > +{ > + u32 num_unpins = num_cmdbufs + num_relocs; > + s64 total; > + > + if (num_relocs < 0 || num_waitchks < 0 || num_cmdbufs < 0) > + return 0; > + > + total = sizeof(struct nvhost_job) > + + num_relocs * sizeof(struct nvhost_reloc) > + + num_unpins * sizeof(struct nvhost_job_unpin_data) > + + num_waitchks * sizeof(struct nvhost_waitchk) > + + num_cmdbufs * sizeof(struct nvhost_job_gather); > + > + if (total > ULONG_MAX) > + return 0; > + return (size_t)total; > +} > + > + > +static void init_fields(struct nvhost_job *job, > + u32 num_cmdbufs, u32 num_relocs, u32 num_waitchks) > +{ > + u32 num_unpins = num_cmdbufs + num_relocs; > + void *mem = job; > + > + /* First init state to zero */ > + > + /* > + * Redistribute memory to the structs. > + * Overflows and negative conditions have > + * already been checked in job_alloc(). > + */ > + mem += sizeof(struct nvhost_job); > + job->relocarray = num_relocs ? mem : NULL; > + mem += num_relocs * sizeof(struct nvhost_reloc); > + job->unpins = num_unpins ? mem : NULL; > + mem += num_unpins * sizeof(struct nvhost_job_unpin_data); > + job->waitchk = num_waitchks ? mem : NULL; > + mem += num_waitchks * sizeof(struct nvhost_waitchk); > + job->gathers = num_cmdbufs ? mem : NULL; > + mem += num_cmdbufs * sizeof(struct nvhost_job_gather); > + job->addr_phys = (num_cmdbufs || num_relocs) ? mem : NULL; > + > + job->reloc_addr_phys = job->addr_phys; > + job->gather_addr_phys = &job->addr_phys[num_relocs]; > +} > + > +struct nvhost_job *nvhost_job_alloc(struct nvhost_channel *ch, > + int num_cmdbufs, int num_relocs, int num_waitchks, > + struct mem_mgr *memmgr) > +{ > + struct nvhost_job *job = NULL; > + size_t size = job_size(num_cmdbufs, num_relocs, num_waitchks); > + > + if (!size) > + return NULL; > + job = vzalloc(size); > + if (!job) > + return NULL; > + > + kref_init(&job->ref); > + job->ch = ch; > + job->memmgr = memmgr ? mem_op().get_mgr(memmgr) : NULL; > + > + init_fields(job, num_cmdbufs, num_relocs, num_waitchks); > + > + return job; > +} > +EXPORT_SYMBOL(nvhost_job_alloc); > + > +void nvhost_job_get(struct nvhost_job *job) > +{ > + kref_get(&job->ref); > +} EXPORT missing? > + > +static void job_free(struct kref *ref) > +{ > + struct nvhost_job *job = container_of(ref, struct nvhost_job, ref); > + > + if (job->memmgr) > + mem_op().put_mgr(job->memmgr); > + vfree(job); > +} > + > +void nvhost_job_put(struct nvhost_job *job) > +{ > + kref_put(&job->ref, job_free); > +} > +EXPORT_SYMBOL(nvhost_job_put); > + > +void nvhost_job_add_gather(struct nvhost_job *job, > + u32 mem_id, u32 words, u32 offset) > +{ > + struct nvhost_job_gather *cur_gather = > + &job->gathers[job->num_gathers]; > + > + cur_gather->words = words; > + cur_gather->mem_id = mem_id; > + cur_gather->offset = offset; > + job->num_gathers += 1; > +} > +EXPORT_SYMBOL(nvhost_job_add_gather); > + > +/* > + * Check driver supplied waitchk structs for syncpt thresholds > + * that have already been satisfied and NULL the comparison (to > + * avoid a wrap condition in the HW). > + */ > +static int do_waitchks(struct nvhost_job *job, struct nvhost_syncpt *sp, > + u32 patch_mem, struct mem_handle *h) > +{ > + int i; > + > + /* compare syncpt vs wait threshold */ > + for (i = 0; i < job->num_waitchk; i++) { > + struct nvhost_waitchk *wait = &job->waitchk[i]; > + > + /* validate syncpt id */ > + if (wait->syncpt_id > nvhost_syncpt_nb_pts(sp)) > + continue; > + > + /* skip all other gathers */ > + if (patch_mem != wait->mem) > + continue; > + > + trace_nvhost_syncpt_wait_check(wait->mem, wait->offset, > + wait->syncpt_id, wait->thresh, > + nvhost_syncpt_read(sp, wait->syncpt_id)); > + if (nvhost_syncpt_is_expired(sp, > + wait->syncpt_id, wait->thresh)) { > + void *patch_addr = NULL; > + > + /* > + * 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().name(sp, wait->syncpt_id), > + wait->thresh, > + nvhost_syncpt_read_min(sp, wait->syncpt_id)); > + > + /* patch the wait */ > + patch_addr = mem_op().kmap(h, > + wait->offset >> PAGE_SHIFT); > + if (patch_addr) { > + nvhost_syncpt_patch_wait(sp, > + (patch_addr + > + (wait->offset & ~PAGE_MASK))); > + mem_op().kunmap(h, > + wait->offset >> PAGE_SHIFT, > + patch_addr); > + } else { > + pr_err("Couldn't map cmdbuf for wait check\n"); > + } > + } > + > + wait->mem = 0; > + } > + return 0; > +} > + > + > +static int pin_job_mem(struct nvhost_job *job) > +{ > + int i; > + int count = 0; > + int result; > + long unsigned *ids = > + kmalloc(sizeof(u32 *) * > + (job->num_relocs + job->num_gathers), > + GFP_KERNEL); > + if (!ids) > + return -ENOMEM; > + > + for (i = 0; i < job->num_relocs; i++) { > + struct nvhost_reloc *reloc = &job->relocarray[i]; > + ids[count] = reloc->target; > + count++; > + } > + > + for (i = 0; i < job->num_gathers; i++) { > + struct nvhost_job_gather *g = &job->gathers[i]; > + ids[count] = g->mem_id; > + count++; > + } > + > + /* validate array and pin unique ids, get refs for unpinning */ > + result = mem_op().pin_array_ids(job->memmgr, job->ch->dev, > + ids, job->addr_phys, > + count, > + job->unpins); > + kfree(ids); > + > + if (result > 0) > + job->num_unpins = result; > + > + return result; > +} > + > +static int do_relocs(struct nvhost_job *job, > + u32 cmdbuf_mem, struct mem_handle *h) > +{ > + int i = 0; > + int last_page = -1; > + void *cmdbuf_page_addr = NULL; > + > + /* pin & patch the relocs for one gather */ > + while (i < job->num_relocs) { > + struct nvhost_reloc *reloc = &job->relocarray[i]; > + > + /* skip all other gathers */ > + if (cmdbuf_mem != reloc->cmdbuf_mem) { > + i++; > + continue; > + } > + > + if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) { > + if (cmdbuf_page_addr) > + mem_op().kunmap(h, last_page, cmdbuf_page_addr); > + > + cmdbuf_page_addr = mem_op().kmap(h, > + reloc->cmdbuf_offset >> PAGE_SHIFT); > + last_page = reloc->cmdbuf_offset >> PAGE_SHIFT; > + > + if (unlikely(!cmdbuf_page_addr)) { > + pr_err("Couldn't map cmdbuf for relocation\n"); > + return -ENOMEM; > + } > + } > + > + __raw_writel( > + (job->reloc_addr_phys[i] + > + reloc->target_offset) >> reloc->shift, > + (cmdbuf_page_addr + > + (reloc->cmdbuf_offset & ~PAGE_MASK))); > + > + /* remove completed reloc from the job */ > + if (i != job->num_relocs - 1) { > + struct nvhost_reloc *reloc_last = > + &job->relocarray[job->num_relocs - 1]; > + reloc->cmdbuf_mem = reloc_last->cmdbuf_mem; > + reloc->cmdbuf_offset = reloc_last->cmdbuf_offset; > + reloc->target = reloc_last->target; > + reloc->target_offset = reloc_last->target_offset; > + reloc->shift = reloc_last->shift; > + job->reloc_addr_phys[i] = > + job->reloc_addr_phys[job->num_relocs - 1]; > + job->num_relocs--; > + } else { > + break; > + } > + } > + > + if (cmdbuf_page_addr) > + mem_op().kunmap(h, last_page, cmdbuf_page_addr); > + > + return 0; > +} > + > + > +int nvhost_job_pin(struct nvhost_job *job, struct platform_device *pdev) > +{ > + int err = 0, i = 0, j = 0; > + struct nvhost_syncpt *sp = &nvhost_get_host(pdev)->syncpt; > + unsigned long waitchk_mask[nvhost_syncpt_nb_pts(sp) / BITS_PER_LONG]; > + > + memset(&waitchk_mask[0], 0, sizeof(waitchk_mask)); > + for (i = 0; i < job->num_waitchk; i++) { > + u32 syncpt_id = job->waitchk[i].syncpt_id; > + if (syncpt_id < nvhost_syncpt_nb_pts(sp)) > + waitchk_mask[BIT_WORD(syncpt_id)] |= > + BIT_MASK(syncpt_id); > + } > + > + /* get current syncpt values for waitchk */ > + for_each_set_bit(i, &waitchk_mask[0], sizeof(waitchk_mask)) > + nvhost_syncpt_update_min(sp, i); > + > + /* pin memory */ > + err = pin_job_mem(job); > + if (err <= 0) > + goto fail; > + > + /* patch gathers */ > + for (i = 0; i < job->num_gathers; i++) { > + struct nvhost_job_gather *g = &job->gathers[i]; > + > + /* process each gather mem only once */ > + if (!g->ref) { > + g->ref = mem_op().get(job->memmgr, > + g->mem_id, job->ch->dev); > + if (IS_ERR(g->ref)) { > + err = PTR_ERR(g->ref); > + g->ref = NULL; > + break; > + } > + > + g->mem_base = job->gather_addr_phys[i]; > + > + for (j = 0; j < job->num_gathers; j++) { > + struct nvhost_job_gather *tmp = > + &job->gathers[j]; > + if (!tmp->ref && tmp->mem_id == g->mem_id) { > + tmp->ref = g->ref; > + tmp->mem_base = g->mem_base; > + } > + } > + err = do_relocs(job, g->mem_id, g->ref); > + if (!err) > + err = do_waitchks(job, sp, > + g->mem_id, g->ref); > + mem_op().put(job->memmgr, g->ref); > + if (err) > + break; > + } > + } > +fail: > + wmb(); > + > + return err; > +} > +EXPORT_SYMBOL(nvhost_job_pin); > + > +void nvhost_job_unpin(struct nvhost_job *job) > +{ > + int i; > + > + for (i = 0; i < job->num_unpins; i++) { > + struct nvhost_job_unpin_data *unpin = &job->unpins[i]; > + mem_op().unpin(job->memmgr, unpin->h, unpin->mem); > + mem_op().put(job->memmgr, unpin->h); > + } > + job->num_unpins = 0; > +} > +EXPORT_SYMBOL(nvhost_job_unpin); > + > +/** > + * Debug routine used to dump job entries > + */ > +void nvhost_job_dump(struct device *dev, struct nvhost_job *job) > +{ > + dev_dbg(dev, " SYNCPT_ID %d\n", > + job->syncpt_id); > + dev_dbg(dev, " SYNCPT_VAL %d\n", > + job->syncpt_end); > + dev_dbg(dev, " FIRST_GET 0x%x\n", > + job->first_get); > + dev_dbg(dev, " TIMEOUT %d\n", > + job->timeout); > + dev_dbg(dev, " NUM_SLOTS %d\n", > + job->num_slots); > + dev_dbg(dev, " NUM_HANDLES %d\n", > + job->num_unpins); > +} > diff --git a/drivers/video/tegra/host/nvhost_memmgr.c b/drivers/video/tegra/host/nvhost_memmgr.c > new file mode 100644 > index 0000000..47e1944 > --- /dev/null > +++ b/drivers/video/tegra/host/nvhost_memmgr.c > @@ -0,0 +1,252 @@ > +/* > + * drivers/video/tegra/host/nvhost_memmgr.c > + * > + * Tegra Graphics Host Memory Management Abstraction > + * > + * Copyright (c) 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/kernel.h> > +#include <linux/err.h> > + > +#include "nvhost_memmgr.h" > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > +#include "dmabuf.h" > +#endif > +#include "chip_support.h" > + > +struct mem_mgr *nvhost_memmgr_alloc_mgr(void) > +{ > + struct mem_mgr *mgr = NULL; > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + mgr = (struct mem_mgr *)1; Perhaps a little explanation would help here (as to why 1 is being returned) > +#endif > + > + return mgr; > +} > + > +void nvhost_memmgr_put_mgr(struct mem_mgr *mgr) > +{ > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + mgr = (struct mem_mgr *)1; Statement with no effect. > +#endif > +} > + > +struct mem_mgr *nvhost_memmgr_get_mgr(struct mem_mgr *_mgr) > +{ > + struct mem_mgr *mgr = NULL; > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + mgr = (struct mem_mgr *)1; > +#endif > + > + return mgr; > +} > + > +struct mem_mgr *nvhost_memmgr_get_mgr_file(int fd) > +{ > + struct mem_mgr *mgr = NULL; > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + mgr = (struct mem_mgr *)1; > +#endif > + > + return mgr; > +} > + > +struct mem_handle *nvhost_memmgr_alloc(struct mem_mgr *mgr, > + size_t size, size_t align, int flags) > +{ > + struct mem_handle *h = NULL; > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + h = nvhost_dmabuf_alloc(mgr, size, align, flags); > +#endif > + > + return h; > +} > + > +struct mem_handle *nvhost_memmgr_get(struct mem_mgr *mgr, > + u32 id, struct platform_device *dev) > +{ > + struct mem_handle *h = NULL; > + > + switch (nvhost_memmgr_type(id)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + h = (struct mem_handle *) nvhost_dmabuf_get(id, dev); > + break; > +#endif > + default: > + break; > + } > + > + return h; > +} > + > +void nvhost_memmgr_put(struct mem_mgr *mgr, struct mem_handle *handle) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + nvhost_dmabuf_put(handle); > + break; > +#endif > + default: > + break; > + } > +} > + > +struct sg_table *nvhost_memmgr_pin(struct mem_mgr *mgr, > + struct mem_handle *handle) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + return nvhost_dmabuf_pin(handle); > + break; > +#endif > + default: > + return 0; > + break; > + } > +} > + > +void nvhost_memmgr_unpin(struct mem_mgr *mgr, > + struct mem_handle *handle, struct sg_table *sgt) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + nvhost_dmabuf_unpin(handle, sgt); > + break; > +#endif > + default: > + break; > + } > +} > + > +void *nvhost_memmgr_mmap(struct mem_handle *handle) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + return nvhost_dmabuf_mmap(handle); > + break; > +#endif > + default: > + return 0; > + break; > + } > +} > + > +void nvhost_memmgr_munmap(struct mem_handle *handle, void *addr) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + nvhost_dmabuf_munmap(handle, addr); > + break; > +#endif > + default: > + break; > + } > +} > + > +void *nvhost_memmgr_kmap(struct mem_handle *handle, unsigned int pagenum) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + return nvhost_dmabuf_kmap(handle, pagenum); > + break; > +#endif > + default: > + return 0; > + break; > + } > +} > + > +void nvhost_memmgr_kunmap(struct mem_handle *handle, unsigned int pagenum, > + void *addr) > +{ > + switch (nvhost_memmgr_type((u32)handle)) { > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + case mem_mgr_type_dmabuf: > + nvhost_dmabuf_kunmap(handle, pagenum, addr); > + break; > +#endif > + default: > + break; > + } > +} > + > +int nvhost_memmgr_pin_array_ids(struct mem_mgr *mgr, > + struct platform_device *dev, > + long unsigned *ids, > + dma_addr_t *phys_addr, > + u32 count, > + struct nvhost_job_unpin_data *unpin_data) > +{ > + int pin_count = 0; > + > +#ifdef CONFIG_TEGRA_GRHOST_USE_DMABUF > + { Redundant brace. -Sivaram -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html