From 4b9e10f4c1181d535ef236692fe7fce736e6b269 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Mon, 23 Aug 2010 21:58:20 -0700 Subject: [PATCH] video: tegra: add skeleton host bus support The host (or host1x) bus sits between the cpu core and the 3d, 2d, camera, display, and mpeg encoder functions. It contains provides DMA channels, hardware mutexes, and synchronization points. Signed-off-by: Erik Gilling --- drivers/video/tegra/Kconfig | 8 + drivers/video/tegra/Makefile | 1 + drivers/video/tegra/host/Makefile | 5 + drivers/video/tegra/host/bus.c | 570 ++++++++++++++++++++++++++++++ drivers/video/tegra/host/dev.c | 90 +++++ drivers/video/tegra/host/dev.h | 30 ++ include/linux/nvhost.h | 69 ++++ 7 files changed, 773 insertions(+) create mode 100644 drivers/video/tegra/host/Makefile create mode 100644 drivers/video/tegra/host/bus.c create mode 100644 drivers/video/tegra/host/dev.c create mode 100644 drivers/video/tegra/host/dev.h create mode 100644 include/linux/nvhost.h diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index b01dc9534060..2610d137ffb8 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -1,6 +1,14 @@ +config TEGRA_GRHOST + tristate "Tegra graphics host driver" + depends on TEGRA_IOVMM + default n + help + Driver for the Tegra graphics host hardware. + config TEGRA_DC tristate "Tegra Display Contoller" depends on ARCH_TEGRA + select FB_MODE_HELPERS help Tegra display controller support. diff --git a/drivers/video/tegra/Makefile b/drivers/video/tegra/Makefile index 8f9d0e213acf..177ec484c10a 100644 --- a/drivers/video/tegra/Makefile +++ b/drivers/video/tegra/Makefile @@ -1,2 +1,3 @@ +obj-$(CONFIG_TEGRA_GRHOST) += host/ obj-$(CONFIG_TEGRA_DC) += dc/ obj-$(CONFIG_FB_TEGRA) += fb.o diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile new file mode 100644 index 000000000000..c9833592425c --- /dev/null +++ b/drivers/video/tegra/host/Makefile @@ -0,0 +1,5 @@ +nvhost-objs = \ + dev.o \ + bus.o + +obj-$(CONFIG_TEGRA_GRHOST) += nvhost.o diff --git a/drivers/video/tegra/host/bus.c b/drivers/video/tegra/host/bus.c new file mode 100644 index 000000000000..b8caf337ede4 --- /dev/null +++ b/drivers/video/tegra/host/bus.c @@ -0,0 +1,570 @@ +/* + * drivers/video/tegra/host/bus.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Erik Gilling + * + * based heavily on drivers/base/platform.c + * + * 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 +#include + +#include "dev.h" + +struct nvhost_master *nvhost; +struct device nvhost_bus = { + .init_name = "nvhost", +}; + +struct resource *nvhost_get_resource(struct nvhost_device *dev, + unsigned int type, unsigned int num) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + if (type == resource_type(r) && num-- == 0) + return r; + } + return NULL; +} +EXPORT_SYMBOL_GPL(nvhost_get_resource); + +int nvhost_get_irq(struct nvhost_device *dev, unsigned int num) +{ + struct resource *r = nvhost_get_resource(dev, IORESOURCE_IRQ, num); + + return r ? r->start : -ENXIO; +} +EXPORT_SYMBOL_GPL(nvhost_get_irq); + +struct resource *nvhost_get_resource_byname(struct nvhost_device *dev, + unsigned int type, + const char *name) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + if (type == resource_type(r) && !strcmp(r->name, name)) + return r; + } + return NULL; +} +EXPORT_SYMBOL_GPL(nvhost_get_resource_byname); + +int nvhost_get_irq_byname(struct nvhost_device *dev, const char *name) +{ + struct resource *r = nvhost_get_resource_byname(dev, IORESOURCE_IRQ, + name); + + return r ? r->start : -ENXIO; +} +EXPORT_SYMBOL_GPL(nvhost_get_irq_byname); + +static int nvhost_drv_probe(struct device *_dev) +{ + struct nvhost_driver *drv = to_nvhost_driver(_dev->driver); + struct nvhost_device *dev = to_nvhost_device(_dev); + + dev->host = nvhost; + + return drv->probe(dev); +} + +static int nvhost_drv_remove(struct device *_dev) +{ + struct nvhost_driver *drv = to_nvhost_driver(_dev->driver); + struct nvhost_device *dev = to_nvhost_device(_dev); + + return drv->remove(dev); +} + +static void nvhost_drv_shutdown(struct device *_dev) +{ + struct nvhost_driver *drv = to_nvhost_driver(_dev->driver); + struct nvhost_device *dev = to_nvhost_device(_dev); + + drv->shutdown(dev); +} + +int nvhost_driver_register(struct nvhost_driver *drv) +{ + drv->driver.bus = &nvhost_bus_type; + if (drv->probe) + drv->driver.probe = nvhost_drv_probe; + if (drv->remove) + drv->driver.remove = nvhost_drv_remove; + if (drv->shutdown) + drv->driver.shutdown = nvhost_drv_shutdown; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(nvhost_driver_register); + +void nvhost_driver_unregister(struct nvhost_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(nvhost_driver_unregister); + +int nvhost_device_register(struct nvhost_device *dev) +{ + int i, ret = 0; + + if (!dev) + return -EINVAL; + + device_initialize(&dev->dev); + + if (!dev->dev.parent) + dev->dev.parent = &nvhost_bus; + + dev->dev.bus = &nvhost_bus_type; + + if (dev->id != -1) + dev_set_name(&dev->dev, "%s.%d", dev->name, dev->id); + else + dev_set_name(&dev->dev, "%s", dev->name); + + for (i = 0; i < dev->num_resources; i++) { + struct resource *p, *r = &dev->resource[i]; + + if (r->name == NULL) + r->name = dev_name(&dev->dev); + + p = r->parent; + if (!p) { + if (resource_type(r) == IORESOURCE_MEM) + p = &iomem_resource; + else if (resource_type(r) == IORESOURCE_IO) + p = &ioport_resource; + } + + if (p && insert_resource(p, r)) { + pr_err("%s: failed to claim resource %d\n", + dev_name(&dev->dev), i); + ret = -EBUSY; + goto failed; + } + } + + ret = device_add(&dev->dev); + if (ret == 0) + return ret; + +failed: + while (--i >= 0) { + struct resource *r = &dev->resource[i]; + unsigned long type = resource_type(r); + + if (type == IORESOURCE_MEM || type == IORESOURCE_IO) + release_resource(r); + } + + return ret; +} +EXPORT_SYMBOL_GPL(nvhost_device_register); + +void nvhost_device_unregister(struct nvhost_device *dev) +{ + int i; + if (dev) { + device_del(&dev->dev); + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + unsigned long type = resource_type(r); + + if (type == IORESOURCE_MEM || type == IORESOURCE_IO) + release_resource(r); + } + + put_device(&dev->dev); + } +} +EXPORT_SYMBOL_GPL(nvhost_device_unregister); + + +static int nvhost_bus_match(struct device *_dev, struct device_driver *drv) +{ + struct nvhost_device *dev = to_nvhost_device(_dev); + + pr_info("host1x: %s %s\n", dev->name, drv->name); + return !strncmp(dev->name, drv->name, strlen(drv->name)); +} + +#ifdef CONFIG_PM_SLEEP + +static int nvhost_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct nvhost_driver *pdrv = to_nvhost_driver(dev->driver); + struct nvhost_device *pdev = to_nvhost_device(dev); + int ret = 0; + + if (dev->driver && pdrv->suspend) + ret = pdrv->suspend(pdev, mesg); + + return ret; +} + +static int nvhost_legacy_resume(struct device *dev) +{ + struct nvhost_driver *pdrv = to_nvhost_driver(dev->driver); + struct nvhost_device *pdev = to_nvhost_device(dev); + int ret = 0; + + if (dev->driver && pdrv->resume) + ret = pdrv->resume(pdev); + + return ret; +} + +static int nvhost_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm && drv->pm->prepare) + ret = drv->pm->prepare(dev); + + return ret; +} + +static void nvhost_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#else /* !CONFIG_PM_SLEEP */ + +#define nvhost_pm_prepare NULL +#define nvhost_pm_complete NULL + +#endif /* !CONFIG_PM_SLEEP */ + +#ifdef CONFIG_SUSPEND + +int __weak nvhost_pm_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend) + ret = drv->pm->suspend(dev); + } else { + ret = nvhost_legacy_suspend(dev, PMSG_SUSPEND); + } + + return ret; +} + +int __weak nvhost_pm_suspend_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->suspend_noirq) + ret = drv->pm->suspend_noirq(dev); + } + + return ret; +} + +int __weak nvhost_pm_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume) + ret = drv->pm->resume(dev); + } else { + ret = nvhost_legacy_resume(dev); + } + + return ret; +} + +int __weak nvhost_pm_resume_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->resume_noirq) + ret = drv->pm->resume_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_SUSPEND */ + +#define nvhost_pm_suspend NULL +#define nvhost_pm_resume NULL +#define nvhost_pm_suspend_noirq NULL +#define nvhost_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int nvhost_pm_freeze(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze) + ret = drv->pm->freeze(dev); + } else { + ret = nvhost_legacy_suspend(dev, PMSG_FREEZE); + } + + return ret; +} + +static int nvhost_pm_freeze_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze_noirq) + ret = drv->pm->freeze_noirq(dev); + } + + return ret; +} + +static int nvhost_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw) + ret = drv->pm->thaw(dev); + } else { + ret = nvhost_legacy_resume(dev); + } + + return ret; +} + +static int nvhost_pm_thaw_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->thaw_noirq) + ret = drv->pm->thaw_noirq(dev); + } + + return ret; +} + +static int nvhost_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff) + ret = drv->pm->poweroff(dev); + } else { + ret = nvhost_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int nvhost_pm_poweroff_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->poweroff_noirq) + ret = drv->pm->poweroff_noirq(dev); + } + + return ret; +} + +static int nvhost_pm_restore(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore) + ret = drv->pm->restore(dev); + } else { + ret = nvhost_legacy_resume(dev); + } + + return ret; +} + +static int nvhost_pm_restore_noirq(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->restore_noirq) + ret = drv->pm->restore_noirq(dev); + } + + return ret; +} + +#else /* !CONFIG_HIBERNATION */ + +#define nvhost_pm_freeze NULL +#define nvhost_pm_thaw NULL +#define nvhost_pm_poweroff NULL +#define nvhost_pm_restore NULL +#define nvhost_pm_freeze_noirq NULL +#define nvhost_pm_thaw_noirq NULL +#define nvhost_pm_poweroff_noirq NULL +#define nvhost_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +#ifdef CONFIG_PM_RUNTIME + +int __weak nvhost_pm_runtime_suspend(struct device *dev) +{ + return pm_generic_runtime_suspend(dev); +}; + +int __weak nvhost_pm_runtime_resume(struct device *dev) +{ + return pm_generic_runtime_resume(dev); +}; + +int __weak nvhost_pm_runtime_idle(struct device *dev) +{ + return pm_generic_runtime_idle(dev); +}; + +#else /* !CONFIG_PM_RUNTIME */ + +#define nvhost_pm_runtime_suspend NULL +#define nvhost_pm_runtime_resume NULL +#define nvhost_pm_runtime_idle NULL + +#endif /* !CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops nvhost_dev_pm_ops = { + .prepare = nvhost_pm_prepare, + .complete = nvhost_pm_complete, + .suspend = nvhost_pm_suspend, + .resume = nvhost_pm_resume, + .freeze = nvhost_pm_freeze, + .thaw = nvhost_pm_thaw, + .poweroff = nvhost_pm_poweroff, + .restore = nvhost_pm_restore, + .suspend_noirq = nvhost_pm_suspend_noirq, + .resume_noirq = nvhost_pm_resume_noirq, + .freeze_noirq = nvhost_pm_freeze_noirq, + .thaw_noirq = nvhost_pm_thaw_noirq, + .poweroff_noirq = nvhost_pm_poweroff_noirq, + .restore_noirq = nvhost_pm_restore_noirq, + .runtime_suspend = nvhost_pm_runtime_suspend, + .runtime_resume = nvhost_pm_runtime_resume, + .runtime_idle = nvhost_pm_runtime_idle, +}; + +struct bus_type nvhost_bus_type = { + .name = "nvhost", + .match = nvhost_bus_match, + .pm = &nvhost_dev_pm_ops, +}; +EXPORT_SYMBOL(nvhost_bus_type); + +int nvhost_bus_register(struct nvhost_master *host) +{ + nvhost = host; + + return 0; +} + + +int nvhost_bus_init(void) +{ + int err; + + pr_info("host1x bus init\n"); + err = device_register(&nvhost_bus); + if (err) + return err; + + err = bus_register(&nvhost_bus_type); + if (err) + device_unregister(&nvhost_bus); + + return err; +} +postcore_initcall(nvhost_bus_init); + diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c new file mode 100644 index 000000000000..254036271d37 --- /dev/null +++ b/drivers/video/tegra/host/dev.c @@ -0,0 +1,90 @@ +/* + * drivers/video/tegra/host/dev.c + * + * Tegra Graphics Host Driver Entrypoint + * + * Copyright (c) 2010, 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. + */ + +#include "dev.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "tegra_grhost" +#define IFACE_NAME "nvhost" + +static int __init nvhost_probe(struct platform_device *pdev) +{ + struct nvhost_master *host; + + host = kzalloc(sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->pdev = pdev; + + platform_set_drvdata(pdev, host); + + nvhost_bus_register(host); + + dev_info(&pdev->dev, "initialized\n"); + return 0; +} + +static int __exit nvhost_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver nvhost_driver = { + .probe = nvhost_probe, + .remove = __exit_p(nvhost_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME + } +}; + +static int __init nvhost_mod_init(void) +{ + return platform_driver_register(&nvhost_driver); +} + +static void __exit nvhost_mod_exit(void) +{ + platform_driver_unregister(&nvhost_driver); +} + +module_init(nvhost_mod_init); +module_exit(nvhost_mod_exit); + +MODULE_AUTHOR("NVIDIA"); +MODULE_DESCRIPTION("Graphics host driver for Tegra products"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("Dual BSD/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 000000000000..b6edab2b58c8 --- /dev/null +++ b/drivers/video/tegra/host/dev.h @@ -0,0 +1,30 @@ +/* + * drivers/video/tegra/host/dev.h + * + * Tegra Graphics Host Driver Entrypoint + * + * Copyright (c) 2010, 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. + */ + +#ifndef __NVHOST_DEV_H +#define __NVHOST_DEV_H + +struct nvhost_master { + struct platform_device *pdev; +}; + +#endif diff --git a/include/linux/nvhost.h b/include/linux/nvhost.h new file mode 100644 index 000000000000..c7db9d2ec30b --- /dev/null +++ b/include/linux/nvhost.h @@ -0,0 +1,69 @@ +/* + * include/linux/nvhost.h + * + * Tegra graphics host driver + * + * Copyright (c) 2009-2010, 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. + */ + +#ifndef __LINUX_NVHOST_H +#define __LINUX_NVHOST_H + +#include + +struct nvhost_master; + +struct nvhost_device { + const char *name; + struct device dev; + int id; + u32 num_resources; + struct resource *resource; + + struct nvhost_master *host; +}; + +extern int nvhost_device_register(struct nvhost_device *); +extern void nvhost_device_unregister(struct nvhost_device *); + +extern struct bus_type nvhost_bus_type; + +struct nvhost_driver { + int (*probe)(struct nvhost_device *); + int (*remove)(struct nvhost_device *); + void (*shutdown)(struct nvhost_device *); + int (*suspend)(struct nvhost_device *, pm_message_t state); + int (*resume)(struct nvhost_device *); + struct device_driver driver; +}; + +extern int nvhost_driver_register(struct nvhost_driver *); +extern void nvhost_driver_unregister(struct nvhost_driver *); +extern struct resource *nvhost_get_resource(struct nvhost_device *, unsigned int, unsigned int); +extern int nvhost_get_irq(struct nvhost_device *, unsigned int); +extern struct resource *nvhost_get_resource_byname(struct nvhost_device *, unsigned int, const char *); +extern int nvhost_get_irq_byname(struct nvhost_device *, const char *); + +#define to_nvhost_device(x) container_of((x), struct nvhost_device, dev) +#define to_nvhost_driver(drv) (container_of((drv), struct nvhost_driver, \ + driver)) + +#define nvhost_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev) +#define nvhost_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data)) + +int nvhost_bus_register(struct nvhost_master *host); +#endif -- 2.34.1