Add host1x, the driver for host1x and its client unit 2D. Signed-off-by: Terje Bergstrom <tbergstrom@xxxxxxxxxx> --- drivers/gpu/Makefile | 1 + drivers/gpu/host1x/Kconfig | 6 + drivers/gpu/host1x/Makefile | 8 ++ drivers/gpu/host1x/dev.c | 161 +++++++++++++++++++++ drivers/gpu/host1x/dev.h | 73 ++++++++++ drivers/gpu/host1x/hw/Makefile | 6 + drivers/gpu/host1x/hw/host1x01.c | 35 +++++ drivers/gpu/host1x/hw/host1x01.h | 25 ++++ drivers/gpu/host1x/hw/host1x01_hardware.h | 26 ++++ drivers/gpu/host1x/hw/hw_host1x01_sync.h | 72 ++++++++++ drivers/gpu/host1x/hw/syncpt_hw.c | 146 +++++++++++++++++++ drivers/gpu/host1x/syncpt.c | 217 +++++++++++++++++++++++++++++ drivers/gpu/host1x/syncpt.h | 153 ++++++++++++++++++++ drivers/video/Kconfig | 2 + include/trace/events/host1x.h | 61 ++++++++ 15 files changed, 992 insertions(+) create mode 100644 drivers/gpu/host1x/Kconfig create mode 100644 drivers/gpu/host1x/Makefile create mode 100644 drivers/gpu/host1x/dev.c create mode 100644 drivers/gpu/host1x/dev.h create mode 100644 drivers/gpu/host1x/hw/Makefile create mode 100644 drivers/gpu/host1x/hw/host1x01.c create mode 100644 drivers/gpu/host1x/hw/host1x01.h create mode 100644 drivers/gpu/host1x/hw/host1x01_hardware.h create mode 100644 drivers/gpu/host1x/hw/hw_host1x01_sync.h create mode 100644 drivers/gpu/host1x/hw/syncpt_hw.c create mode 100644 drivers/gpu/host1x/syncpt.c create mode 100644 drivers/gpu/host1x/syncpt.h create mode 100644 include/trace/events/host1x.h diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index cc92778..7e227097 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1 +1,2 @@ obj-y += drm/ vga/ stub/ +obj-$(CONFIG_TEGRA_HOST1X) += host1x/ diff --git a/drivers/gpu/host1x/Kconfig b/drivers/gpu/host1x/Kconfig new file mode 100644 index 0000000..e89fb2b --- /dev/null +++ b/drivers/gpu/host1x/Kconfig @@ -0,0 +1,6 @@ +config TEGRA_HOST1X + tristate "Tegra host1x driver" + help + Driver for the Tegra host1x hardware. + + Required for enabling tegradrm. diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile new file mode 100644 index 0000000..363e6ab --- /dev/null +++ b/drivers/gpu/host1x/Makefile @@ -0,0 +1,8 @@ +ccflags-y = -Idrivers/gpu/host1x + +host1x-y = \ + syncpt.o \ + dev.o \ + hw/host1x01.o + +obj-$(CONFIG_TEGRA_HOST1X) += host1x.o diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c new file mode 100644 index 0000000..cd2b1ef --- /dev/null +++ b/drivers/gpu/host1x/dev.c @@ -0,0 +1,161 @@ +/* + * Tegra host1x driver + * + * Copyright (c) 2010-2013, 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/list.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include "dev.h" +#include "hw/host1x01.h" + +#define CREATE_TRACE_POINTS +#include <trace/events/host1x.h> + +#define DRIVER_NAME "tegra-host1x" + +void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r) +{ + void __iomem *sync_regs = host1x->regs + host1x->info.sync_offset; + + writel(v, sync_regs + r); +} + +u32 host1x_sync_readl(struct host1x *host1x, u32 r) +{ + void __iomem *sync_regs = host1x->regs + host1x->info.sync_offset; + + return readl(sync_regs + r); +} + +static struct host1x_device_info host1x_info = { + .nb_channels = 8, + .nb_pts = 32, + .nb_mlocks = 16, + .nb_bases = 8, + .init = host1x01_init, + .sync_offset = 0x3000, +}; + +static struct of_device_id host1x_match[] = { + { .compatible = "nvidia,tegra30-host1x", .data = &host1x_info, }, + { .compatible = "nvidia,tegra20-host1x", .data = &host1x_info, }, + { }, +}; + +static int host1x_probe(struct platform_device *dev) +{ + struct host1x *host; + struct resource *regs; + int syncpt_irq; + int err; + const struct of_device_id *devid = + of_match_device(host1x_match, &dev->dev); + + if (!devid) + return -EINVAL; + + regs = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&dev->dev, "missing regs\n"); + return -ENXIO; + } + + syncpt_irq = platform_get_irq(dev, 0); + if (IS_ERR_VALUE(syncpt_irq)) { + dev_err(&dev->dev, "missing irq\n"); + return -ENXIO; + } + + host = devm_kzalloc(&dev->dev, sizeof(*host), GFP_KERNEL); + if (!host) { + dev_err(&dev->dev, "failed to alloc host1x\n"); + return -ENOMEM; + } + + host->dev = dev; + memcpy(&host->info, devid->data, sizeof(struct host1x_device_info)); + + /* set common host1x device data */ + platform_set_drvdata(dev, host); + + host->regs = devm_request_and_ioremap(&dev->dev, regs); + if (!host->regs) { + dev_err(&dev->dev, "failed to remap host registers\n"); + return -ENXIO; + } + + if (host->info.init) { + err = host->info.init(host); + if (err) + return err; + } + + err = host1x_syncpt_init(host); + if (err) + return err; + + host->clk = devm_clk_get(&dev->dev, NULL); + if (IS_ERR(host->clk)) { + dev_err(&dev->dev, "failed to get clock\n"); + err = PTR_ERR(host->clk); + goto fail_deinit_syncpt; + } + + err = clk_prepare_enable(host->clk); + if (err < 0) { + dev_err(&dev->dev, "failed to enable clock\n"); + goto fail_deinit_syncpt; + } + + host1x_syncpt_reset(host); + + dev_info(&dev->dev, "initialized\n"); + + return 0; + +fail_deinit_syncpt: + host1x_syncpt_deinit(host); + return err; +} + +static int __exit host1x_remove(struct platform_device *dev) +{ + struct host1x *host = platform_get_drvdata(dev); + host1x_syncpt_deinit(host); + clk_disable_unprepare(host->clk); + return 0; +} + +static struct platform_driver platform_driver = { + .probe = host1x_probe, + .remove = __exit_p(host1x_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = host1x_match, + }, +}; + +module_platform_driver(platform_driver); + +MODULE_AUTHOR("Terje Bergstrom <tbergstrom@xxxxxxxxxx>"); +MODULE_DESCRIPTION("Host1x driver for Tegra products"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h new file mode 100644 index 0000000..d8f5979 --- /dev/null +++ b/drivers/gpu/host1x/dev.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012-2013, 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 HOST1X_DEV_H +#define HOST1X_DEV_H + +#include "syncpt.h" + +struct host1x; +struct host1x_syncpt; +struct platform_device; + +struct host1x_syncpt_ops { + void (*reset)(struct host1x_syncpt *); + void (*reset_wait_base)(struct host1x_syncpt *); + void (*read_wait_base)(struct host1x_syncpt *); + u32 (*load_min)(struct host1x_syncpt *); + void (*cpu_incr)(struct host1x_syncpt *); + int (*patch_wait)(struct host1x_syncpt *, void *patch_addr); + void (*debug)(struct host1x_syncpt *); + const char * (*name)(struct host1x_syncpt *); +}; + +struct host1x_device_info { + int nb_channels; /* host1x: num channels supported */ + int nb_pts; /* host1x: num syncpoints supported */ + int nb_bases; /* host1x: num syncpoints supported */ + int nb_mlocks; /* host1x: number of mlocks */ + int (*init)(struct host1x *); /* initialize per SoC ops */ + int sync_offset; +}; + +struct host1x { + void __iomem *regs; + struct host1x_syncpt *syncpt; + struct platform_device *dev; + struct host1x_device_info info; + struct clk *clk; + + struct host1x_syncpt_ops syncpt_op; + + struct dentry *debugfs; +}; + +static inline +struct host1x *host1x_get_host(struct platform_device *_dev) +{ + struct platform_device *pdev; + + if (_dev->dev.parent) { + pdev = to_platform_device(_dev->dev.parent); + return platform_get_drvdata(pdev); + } else + return platform_get_drvdata(_dev); +} + +void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v); +u32 host1x_sync_readl(struct host1x *host1x, u32 r); + +#endif diff --git a/drivers/gpu/host1x/hw/Makefile b/drivers/gpu/host1x/hw/Makefile new file mode 100644 index 0000000..9b50863 --- /dev/null +++ b/drivers/gpu/host1x/hw/Makefile @@ -0,0 +1,6 @@ +ccflags-y = -Idrivers/gpu/host1x + +host1x-hw-objs = \ + host1x01.o + +obj-$(CONFIG_TEGRA_HOST1X) += host1x-hw.o diff --git a/drivers/gpu/host1x/hw/host1x01.c b/drivers/gpu/host1x/hw/host1x01.c new file mode 100644 index 0000000..ea6e604 --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x01.c @@ -0,0 +1,35 @@ +/* + * Host1x init for T20 and T30 Architecture Chips + * + * Copyright (c) 2011-2013, 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/init.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +#include "hw/host1x01.h" +#include "dev.h" +#include "hw/host1x01_hardware.h" + +#include "hw/syncpt_hw.c" + +int host1x01_init(struct host1x *host) +{ + host->syncpt_op = host1x_syncpt_ops; + + return 0; +} diff --git a/drivers/gpu/host1x/hw/host1x01.h b/drivers/gpu/host1x/hw/host1x01.h new file mode 100644 index 0000000..6ec30051 --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x01.h @@ -0,0 +1,25 @@ +/* + * Host1x init for T20 and T30 Architecture Chips + * + * Copyright (c) 2011-2013, 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 HOST1X_HOST1X01_H +#define HOST1X_HOST1X01_H + +struct host1x; + +int host1x01_init(struct host1x *); + +#endif /* HOST1X_HOST1X01_H_ */ diff --git a/drivers/gpu/host1x/hw/host1x01_hardware.h b/drivers/gpu/host1x/hw/host1x01_hardware.h new file mode 100644 index 0000000..c1d5324 --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x01_hardware.h @@ -0,0 +1,26 @@ +/* + * Tegra host1x Register Offsets for Tegra20 and Tegra30 + * + * Copyright (c) 2010-2013 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 __HOST1X_HOST1X01_HARDWARE_H +#define __HOST1X_HOST1X01_HARDWARE_H + +#include <linux/types.h> +#include <linux/bitops.h> +#include "hw_host1x01_sync.h" + +#endif diff --git a/drivers/gpu/host1x/hw/hw_host1x01_sync.h b/drivers/gpu/host1x/hw/hw_host1x01_sync.h new file mode 100644 index 0000000..b12c1a4 --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x01_sync.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012-2013, 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_host1x01_sync_h__ +#define __hw_host1x01_sync_h__ + +static inline u32 host1x_sync_syncpt_0_r(void) +{ + return 0x400; +} +#define HOST1X_SYNC_SYNCPT_0 \ + host1x_sync_syncpt_0_r() +static inline u32 host1x_sync_syncpt_base_0_r(void) +{ + return 0x600; +} +#define HOST1X_SYNC_SYNCPT_BASE_0 \ + host1x_sync_syncpt_base_0_r() +static inline u32 host1x_sync_syncpt_cpu_incr_r(void) +{ + return 0x700; +} +#define HOST1X_SYNC_SYNCPT_CPU_INCR \ + host1x_sync_syncpt_cpu_incr_r() +#endif /* __hw_host1x01_sync_h__ */ diff --git a/drivers/gpu/host1x/hw/syncpt_hw.c b/drivers/gpu/host1x/hw/syncpt_hw.c new file mode 100644 index 0000000..16e3ada --- /dev/null +++ b/drivers/gpu/host1x/hw/syncpt_hw.c @@ -0,0 +1,146 @@ +/* + * Tegra host1x Syncpoints + * + * Copyright (c) 2010-2013, 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 "syncpt.h" +#include "dev.h" + +/* + * Write the current syncpoint value back to hw. + */ +static void syncpt_reset(struct host1x_syncpt *sp) +{ + struct host1x *dev = sp->dev; + int min = host1x_syncpt_read_min(sp); + host1x_sync_writel(dev, min, HOST1X_SYNC_SYNCPT_0 + sp->id * 4); +} + +/* + * Write the current waitbase value back to hw. + */ +static void syncpt_reset_wait_base(struct host1x_syncpt *sp) +{ + struct host1x *dev = sp->dev; + host1x_sync_writel(dev, sp->base_val, + HOST1X_SYNC_SYNCPT_BASE_0 + sp->id * 4); +} + +/* + * Read waitbase value from hw. + */ +static void syncpt_read_wait_base(struct host1x_syncpt *sp) +{ + struct host1x *dev = sp->dev; + sp->base_val = host1x_sync_readl(dev, + HOST1X_SYNC_SYNCPT_BASE_0 + sp->id * 4); +} + +/* + * Updates the last value read from hardware. + * (was host1x_syncpt_load_min) + */ +static u32 syncpt_load_min(struct host1x_syncpt *sp) +{ + struct host1x *dev = sp->dev; + u32 old, live; + + do { + old = host1x_syncpt_read_min(sp); + live = host1x_sync_readl(dev, + HOST1X_SYNC_SYNCPT_0 + sp->id * 4); + } while ((u32)atomic_cmpxchg(&sp->min_val, old, live) != old); + + if (!host1x_syncpt_check_max(sp, live)) + dev_err(&dev->dev->dev, + "%s failed: id=%u, min=%d, max=%d\n", + __func__, + sp->id, + host1x_syncpt_read_min(sp), + host1x_syncpt_read_max(sp)); + + return live; +} + +/* + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. Caller is responsible for host being powered. + */ +static void syncpt_cpu_incr(struct host1x_syncpt *sp) +{ + struct host1x *dev = sp->dev; + u32 reg_offset = sp->id / 32; + + if (!host1x_syncpt_client_managed(sp) + && host1x_syncpt_min_eq_max(sp)) { + dev_err(&dev->dev->dev, + "Trying to increment syncpoint id %d beyond max\n", + sp->id); + return; + } + host1x_sync_writel(dev, BIT_MASK(sp->id), + HOST1X_SYNC_SYNCPT_CPU_INCR + reg_offset * 4); + wmb(); +} + +static const char *syncpt_name(struct host1x_syncpt *sp) +{ + struct host1x_device_info *info = &sp->dev->info; + const char *name = NULL; + + if (sp->id < info->nb_pts) + name = sp->name; + + return name ? name : ""; +} + +static void syncpt_debug(struct host1x_syncpt *sp) +{ + u32 i; + for (i = 0; i < host1x_syncpt_nb_pts(sp->dev); i++) { + u32 max = host1x_syncpt_read_max(sp); + u32 min = host1x_syncpt_load_min(sp); + if (!max && !min) + continue; + dev_info(&sp->dev->dev->dev, + "id %d (%s) min %d max %d\n", + i, sp->name, + min, max); + + } + + for (i = 0; i < host1x_syncpt_nb_bases(sp->dev); i++) { + u32 base_val; + host1x_syncpt_read_wait_base(sp); + base_val = sp->base_val; + if (base_val) + dev_info(&sp->dev->dev->dev, + "waitbase id %d val %d\n", + i, base_val); + + } +} + +static const struct host1x_syncpt_ops host1x_syncpt_ops = { + .reset = syncpt_reset, + .reset_wait_base = syncpt_reset_wait_base, + .read_wait_base = syncpt_read_wait_base, + .load_min = syncpt_load_min, + .cpu_incr = syncpt_cpu_incr, + .debug = syncpt_debug, + .name = syncpt_name, +}; diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c new file mode 100644 index 0000000..b45651f --- /dev/null +++ b/drivers/gpu/host1x/syncpt.c @@ -0,0 +1,217 @@ +/* + * Tegra host1x Syncpoints + * + * Copyright (c) 2010-2013, 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/platform_device.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include "syncpt.h" +#include "dev.h" +#include <trace/events/host1x.h> + +#define MAX_SYNCPT_LENGTH 5 + +static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host, + struct platform_device *pdev, + int client_managed); + +u32 host1x_syncpt_id(struct host1x_syncpt *sp) +{ + return sp->id; +} + +/* + * Updates the value sent to hardware. + */ +u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs) +{ + return (u32)atomic_add_return(incrs, &sp->max_val); +} + +/* + * Resets syncpoint and waitbase values to sw shadows + */ +void host1x_syncpt_reset(struct host1x *dev) +{ + struct host1x_syncpt *sp_base = dev->syncpt; + u32 i; + + for (i = 0; i < host1x_syncpt_nb_pts(dev); i++) + dev->syncpt_op.reset(sp_base + i); + for (i = 0; i < host1x_syncpt_nb_bases(dev); i++) + dev->syncpt_op.reset_wait_base(sp_base + i); + wmb(); +} + +/* + * Updates sw shadow state for client managed registers + */ +void host1x_syncpt_save(struct host1x *dev) +{ + struct host1x_syncpt *sp_base = dev->syncpt; + u32 i; + + for (i = 0; i < host1x_syncpt_nb_pts(dev); i++) { + if (host1x_syncpt_client_managed(sp_base + i)) + dev->syncpt_op.load_min(sp_base + i); + else + WARN_ON(!host1x_syncpt_min_eq_max(sp_base + i)); + } + + for (i = 0; i < host1x_syncpt_nb_bases(dev); i++) + dev->syncpt_op.read_wait_base(sp_base + i); +} + +/* + * Updates the last value read from hardware. + */ +u32 host1x_syncpt_load_min(struct host1x_syncpt *sp) +{ + u32 val; + val = sp->dev->syncpt_op.load_min(sp); + trace_host1x_syncpt_load_min(sp->id, val); + + return val; +} + +/* + * Get the current syncpoint base + */ +u32 host1x_syncpt_read_wait_base(struct host1x_syncpt *sp) +{ + u32 val; + sp->dev->syncpt_op.read_wait_base(sp); + val = sp->base_val; + return val; +} + +/* + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. Caller is responsible for host being powered. + */ +void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp) +{ + sp->dev->syncpt_op.cpu_incr(sp); +} + +/* + * Increment syncpoint value from cpu, updating cache + */ +void host1x_syncpt_incr(struct host1x_syncpt *sp) +{ + if (host1x_syncpt_client_managed(sp)) + host1x_syncpt_incr_max(sp, 1); + host1x_syncpt_cpu_incr(sp); +} + +void host1x_syncpt_debug(struct host1x_syncpt *sp) +{ + sp->dev->syncpt_op.debug(sp); +} + +int host1x_syncpt_init(struct host1x *host) +{ + struct host1x_syncpt *syncpt, *sp; + int i; + + syncpt = sp = devm_kzalloc(&host->dev->dev, + sizeof(struct host1x_syncpt) * host->info.nb_pts, + GFP_KERNEL); + if (!syncpt) + return -ENOMEM; + + for (i = 0; i < host->info.nb_pts; ++i, ++sp) { + sp->id = i; + sp->dev = host; + } + + host->syncpt = syncpt; + + return 0; +} + +static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host, + struct platform_device *pdev, + int client_managed) +{ + int i; + struct host1x_syncpt *sp = host->syncpt; + char *name; + + for (i = 0; i < host->info.nb_pts && sp->name; i++, sp++) + ; + if (sp->pdev) + return NULL; + + name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id, + pdev ? dev_name(&pdev->dev) : NULL); + if (!name) + return NULL; + + sp->pdev = pdev; + sp->name = name; + sp->client_managed = client_managed; + + return sp; +} + +struct host1x_syncpt *host1x_syncpt_alloc(struct platform_device *pdev, + int client_managed) +{ + struct host1x *host = host1x_get_host(pdev); + return _host1x_syncpt_alloc(host, pdev, client_managed); +} + +void host1x_syncpt_free(struct host1x_syncpt *sp) +{ + if (!sp) + return; + + kfree(sp->name); + sp->pdev = NULL; + sp->name = NULL; + sp->client_managed = 0; +} + +void host1x_syncpt_deinit(struct host1x *host) +{ + int i; + struct host1x_syncpt *sp = host->syncpt; + for (i = 0; i < host->info.nb_pts; i++, sp++) + kfree(sp->name); +} + +int host1x_syncpt_nb_pts(struct host1x *dev) +{ + return dev->info.nb_pts; +} + +int host1x_syncpt_nb_bases(struct host1x *dev) +{ + return dev->info.nb_bases; +} + +int host1x_syncpt_nb_mlocks(struct host1x *dev) +{ + return dev->info.nb_mlocks; +} + +struct host1x_syncpt *host1x_syncpt_get(struct host1x *dev, u32 id) +{ + return dev->syncpt + id; +} diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h new file mode 100644 index 0000000..d9b9b0a --- /dev/null +++ b/drivers/gpu/host1x/syncpt.h @@ -0,0 +1,153 @@ +/* + * Tegra host1x Syncpoints + * + * Copyright (c) 2010-2013, 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 __HOST1X_SYNCPT_H +#define __HOST1X_SYNCPT_H + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/atomic.h> + +struct host1x; + +#define NVSYNCPT_INVALID (-1) + +struct host1x_syncpt { + int id; + atomic_t min_val; + atomic_t max_val; + u32 base_val; + const char *name; + int client_managed; + struct host1x *dev; + struct platform_device *pdev; +}; + +/* Initialize sync point array */ +int host1x_syncpt_init(struct host1x *); + +/* Free sync point array */ +void host1x_syncpt_deinit(struct host1x *); + +/* + * Read max. It indicates how many operations there are in queue, either in + * channel or in a software thread. + * */ +static inline u32 host1x_syncpt_read_max(struct host1x_syncpt *sp) +{ + smp_rmb(); + return (u32)atomic_read(&sp->max_val); +} + +/* + * Read min, which is a shadow of the current sync point value in hardware. + */ +static inline u32 host1x_syncpt_read_min(struct host1x_syncpt *sp) +{ + smp_rmb(); + return (u32)atomic_read(&sp->min_val); +} + +/* Return number of sync point supported. */ +int host1x_syncpt_nb_pts(struct host1x *dev); + +/* Return number of wait bases supported. */ +int host1x_syncpt_nb_bases(struct host1x *dev); + +/* Return number of mlocks supported. */ +int host1x_syncpt_nb_mlocks(struct host1x *dev); + +/* + * Check sync point sanity. If max is larger than min, there have too many + * sync point increments. + * + * Client managed sync point are not tracked. + * */ +static inline bool host1x_syncpt_check_max(struct host1x_syncpt *sp, u32 real) +{ + u32 max; + if (sp->client_managed) + return true; + max = host1x_syncpt_read_max(sp); + return (s32)(max - real) >= 0; +} + +/* Return true if sync point is client managed. */ +static inline int host1x_syncpt_client_managed(struct host1x_syncpt *sp) +{ + return sp->client_managed; +} + +/* + * Returns true if syncpoint min == max, which means that there are no + * outstanding operations. + */ +static inline bool host1x_syncpt_min_eq_max(struct host1x_syncpt *sp) +{ + int min, max; + smp_rmb(); + min = atomic_read(&sp->min_val); + max = atomic_read(&sp->max_val); + return (min == max); +} + +/* Return pointer to struct denoting sync point id. */ +struct host1x_syncpt *host1x_syncpt_get(struct host1x *dev, u32 id); + +/* Request incrementing a sync point. */ +void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp); + +/* Load current value from hardware to the shadow register. */ +u32 host1x_syncpt_load_min(struct host1x_syncpt *sp); + +/* Save host1x sync point state into shadow registers. */ +void host1x_syncpt_save(struct host1x *dev); + +/* Reset host1x sync point state from shadow registers. */ +void host1x_syncpt_reset(struct host1x *dev); + +/* Read current wait base value into shadow register and return it. */ +u32 host1x_syncpt_read_wait_base(struct host1x_syncpt *sp); + +/* Increment sync point and its max. */ +void host1x_syncpt_incr(struct host1x_syncpt *sp); + +/* Indicate future operations by incrementing the sync point max. */ +u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs); + +/* Do a debug dump of sync point values. */ +void host1x_syncpt_debug(struct host1x_syncpt *sp); + +/* Check if sync point id is valid. */ +static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp) +{ + return sp->id != NVSYNCPT_INVALID && + sp->id < host1x_syncpt_nb_pts(sp->dev); +} + +/* Return id of the sync point */ +u32 host1x_syncpt_id(struct host1x_syncpt *sp); + +/* Allocate a sync point for a device. */ +struct host1x_syncpt *host1x_syncpt_alloc(struct platform_device *pdev, + int client_managed); + +/* Free a sync point. */ +void host1x_syncpt_free(struct host1x_syncpt *sp); + +#endif diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e7068c5..776ddba 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -21,6 +21,8 @@ source "drivers/gpu/vga/Kconfig" source "drivers/gpu/drm/Kconfig" +source "drivers/gpu/host1x/Kconfig" + source "drivers/gpu/stub/Kconfig" config VGASTATE diff --git a/include/trace/events/host1x.h b/include/trace/events/host1x.h new file mode 100644 index 0000000..3c14cac --- /dev/null +++ b/include/trace/events/host1x.h @@ -0,0 +1,61 @@ +/* + * include/trace/events/host1x.h + * + * Nvhost event logging to ftrace. + * + * Copyright (c) 2010-2013, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM host1x + +#if !defined(_TRACE_HOST1X_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HOST1X_H + +#include <linux/ktime.h> +#include <linux/tracepoint.h> + +DECLARE_EVENT_CLASS(host1x, + TP_PROTO(const char *name), + TP_ARGS(name), + TP_STRUCT__entry(__field(const char *, name)), + TP_fast_assign(__entry->name = name;), + TP_printk("name=%s", __entry->name) +); + +TRACE_EVENT(host1x_syncpt_load_min, + TP_PROTO(u32 id, u32 val), + + TP_ARGS(id, val), + + TP_STRUCT__entry( + __field(u32, id) + __field(u32, val) + ), + + TP_fast_assign( + __entry->id = id; + __entry->val = val; + ), + + TP_printk("id=%d, val=%d", __entry->id, __entry->val) +); + +#endif /* _TRACE_HOST1X_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> -- 1.7.9.5 -- 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