mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
22
drivers/gpu/host1x/Kconfig
Normal file
22
drivers/gpu/host1x/Kconfig
Normal file
|
@ -0,0 +1,22 @@
|
|||
config TEGRA_HOST1X
|
||||
tristate "NVIDIA Tegra host1x driver"
|
||||
depends on ARCH_TEGRA || (ARM && COMPILE_TEST)
|
||||
help
|
||||
Driver for the NVIDIA Tegra host1x hardware.
|
||||
|
||||
The Tegra host1x module is the DMA engine for register access to
|
||||
Tegra's graphics- and multimedia-related modules. The modules served
|
||||
by host1x are referred to as clients. host1x includes some other
|
||||
functionality, such as synchronization.
|
||||
|
||||
if TEGRA_HOST1X
|
||||
|
||||
config TEGRA_HOST1X_FIREWALL
|
||||
bool "Enable HOST1X security firewall"
|
||||
default y
|
||||
help
|
||||
Say yes if kernel should protect command streams from tampering.
|
||||
|
||||
If unsure, choose Y.
|
||||
|
||||
endif
|
15
drivers/gpu/host1x/Makefile
Normal file
15
drivers/gpu/host1x/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
host1x-y = \
|
||||
bus.o \
|
||||
syncpt.o \
|
||||
dev.o \
|
||||
intr.o \
|
||||
cdma.o \
|
||||
channel.o \
|
||||
job.o \
|
||||
debug.o \
|
||||
mipi.o \
|
||||
hw/host1x01.o \
|
||||
hw/host1x02.o \
|
||||
hw/host1x04.o
|
||||
|
||||
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o
|
553
drivers/gpu/host1x/bus.c
Normal file
553
drivers/gpu/host1x/bus.c
Normal file
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Avionic Design GmbH
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "bus.h"
|
||||
#include "dev.h"
|
||||
|
||||
static DEFINE_MUTEX(clients_lock);
|
||||
static LIST_HEAD(clients);
|
||||
|
||||
static DEFINE_MUTEX(drivers_lock);
|
||||
static LIST_HEAD(drivers);
|
||||
|
||||
static DEFINE_MUTEX(devices_lock);
|
||||
static LIST_HEAD(devices);
|
||||
|
||||
struct host1x_subdev {
|
||||
struct host1x_client *client;
|
||||
struct device_node *np;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/**
|
||||
* host1x_subdev_add() - add a new subdevice with an associated device node
|
||||
*/
|
||||
static int host1x_subdev_add(struct host1x_device *device,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct host1x_subdev *subdev;
|
||||
|
||||
subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
|
||||
if (!subdev)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&subdev->list);
|
||||
subdev->np = of_node_get(np);
|
||||
|
||||
mutex_lock(&device->subdevs_lock);
|
||||
list_add_tail(&subdev->list, &device->subdevs);
|
||||
mutex_unlock(&device->subdevs_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* host1x_subdev_del() - remove subdevice
|
||||
*/
|
||||
static void host1x_subdev_del(struct host1x_subdev *subdev)
|
||||
{
|
||||
list_del(&subdev->list);
|
||||
of_node_put(subdev->np);
|
||||
kfree(subdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* host1x_device_parse_dt() - scan device tree and add matching subdevices
|
||||
*/
|
||||
static int host1x_device_parse_dt(struct host1x_device *device)
|
||||
{
|
||||
struct device_node *np;
|
||||
int err;
|
||||
|
||||
for_each_child_of_node(device->dev.parent->of_node, np) {
|
||||
if (of_match_node(device->driver->subdevs, np) &&
|
||||
of_device_is_available(np)) {
|
||||
err = host1x_subdev_add(device, np);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void host1x_subdev_register(struct host1x_device *device,
|
||||
struct host1x_subdev *subdev,
|
||||
struct host1x_client *client)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Move the subdevice to the list of active (registered) subdevices
|
||||
* and associate it with a client. At the same time, associate the
|
||||
* client with its parent device.
|
||||
*/
|
||||
mutex_lock(&device->subdevs_lock);
|
||||
mutex_lock(&device->clients_lock);
|
||||
list_move_tail(&client->list, &device->clients);
|
||||
list_move_tail(&subdev->list, &device->active);
|
||||
client->parent = &device->dev;
|
||||
subdev->client = client;
|
||||
mutex_unlock(&device->clients_lock);
|
||||
mutex_unlock(&device->subdevs_lock);
|
||||
|
||||
/*
|
||||
* When all subdevices have been registered, the composite device is
|
||||
* ready to be probed.
|
||||
*/
|
||||
if (list_empty(&device->subdevs)) {
|
||||
err = device->driver->probe(device);
|
||||
if (err < 0)
|
||||
dev_err(&device->dev, "probe failed: %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void __host1x_subdev_unregister(struct host1x_device *device,
|
||||
struct host1x_subdev *subdev)
|
||||
{
|
||||
struct host1x_client *client = subdev->client;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If all subdevices have been activated, we're about to remove the
|
||||
* first active subdevice, so unload the driver first.
|
||||
*/
|
||||
if (list_empty(&device->subdevs)) {
|
||||
err = device->driver->remove(device);
|
||||
if (err < 0)
|
||||
dev_err(&device->dev, "remove failed: %d\n", err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the subdevice back to the list of idle subdevices and remove
|
||||
* it from list of clients.
|
||||
*/
|
||||
mutex_lock(&device->clients_lock);
|
||||
subdev->client = NULL;
|
||||
client->parent = NULL;
|
||||
list_move_tail(&subdev->list, &device->subdevs);
|
||||
/*
|
||||
* XXX: Perhaps don't do this here, but rather explicitly remove it
|
||||
* when the device is about to be deleted.
|
||||
*
|
||||
* This is somewhat complicated by the fact that this function is
|
||||
* used to remove the subdevice when a client is unregistered but
|
||||
* also when the composite device is about to be removed.
|
||||
*/
|
||||
list_del_init(&client->list);
|
||||
mutex_unlock(&device->clients_lock);
|
||||
}
|
||||
|
||||
static void host1x_subdev_unregister(struct host1x_device *device,
|
||||
struct host1x_subdev *subdev)
|
||||
{
|
||||
mutex_lock(&device->subdevs_lock);
|
||||
__host1x_subdev_unregister(device, subdev);
|
||||
mutex_unlock(&device->subdevs_lock);
|
||||
}
|
||||
|
||||
int host1x_device_init(struct host1x_device *device)
|
||||
{
|
||||
struct host1x_client *client;
|
||||
int err;
|
||||
|
||||
mutex_lock(&device->clients_lock);
|
||||
|
||||
list_for_each_entry(client, &device->clients, list) {
|
||||
if (client->ops && client->ops->init) {
|
||||
err = client->ops->init(client);
|
||||
if (err < 0) {
|
||||
dev_err(&device->dev,
|
||||
"failed to initialize %s: %d\n",
|
||||
dev_name(client->dev), err);
|
||||
mutex_unlock(&device->clients_lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&device->clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_device_init);
|
||||
|
||||
int host1x_device_exit(struct host1x_device *device)
|
||||
{
|
||||
struct host1x_client *client;
|
||||
int err;
|
||||
|
||||
mutex_lock(&device->clients_lock);
|
||||
|
||||
list_for_each_entry_reverse(client, &device->clients, list) {
|
||||
if (client->ops && client->ops->exit) {
|
||||
err = client->ops->exit(client);
|
||||
if (err < 0) {
|
||||
dev_err(&device->dev,
|
||||
"failed to cleanup %s: %d\n",
|
||||
dev_name(client->dev), err);
|
||||
mutex_unlock(&device->clients_lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&device->clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_device_exit);
|
||||
|
||||
static int host1x_add_client(struct host1x *host1x,
|
||||
struct host1x_client *client)
|
||||
{
|
||||
struct host1x_device *device;
|
||||
struct host1x_subdev *subdev;
|
||||
|
||||
mutex_lock(&host1x->devices_lock);
|
||||
|
||||
list_for_each_entry(device, &host1x->devices, list) {
|
||||
list_for_each_entry(subdev, &device->subdevs, list) {
|
||||
if (subdev->np == client->dev->of_node) {
|
||||
host1x_subdev_register(device, subdev, client);
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int host1x_del_client(struct host1x *host1x,
|
||||
struct host1x_client *client)
|
||||
{
|
||||
struct host1x_device *device, *dt;
|
||||
struct host1x_subdev *subdev;
|
||||
|
||||
mutex_lock(&host1x->devices_lock);
|
||||
|
||||
list_for_each_entry_safe(device, dt, &host1x->devices, list) {
|
||||
list_for_each_entry(subdev, &device->active, list) {
|
||||
if (subdev->client == client) {
|
||||
host1x_subdev_unregister(device, subdev);
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct bus_type host1x_bus_type = {
|
||||
.name = "host1x",
|
||||
};
|
||||
|
||||
int host1x_bus_init(void)
|
||||
{
|
||||
return bus_register(&host1x_bus_type);
|
||||
}
|
||||
|
||||
void host1x_bus_exit(void)
|
||||
{
|
||||
bus_unregister(&host1x_bus_type);
|
||||
}
|
||||
|
||||
static void host1x_device_release(struct device *dev)
|
||||
{
|
||||
struct host1x_device *device = to_host1x_device(dev);
|
||||
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static int host1x_device_add(struct host1x *host1x,
|
||||
struct host1x_driver *driver)
|
||||
{
|
||||
struct host1x_client *client, *tmp;
|
||||
struct host1x_subdev *subdev;
|
||||
struct host1x_device *device;
|
||||
int err;
|
||||
|
||||
device = kzalloc(sizeof(*device), GFP_KERNEL);
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&device->subdevs_lock);
|
||||
INIT_LIST_HEAD(&device->subdevs);
|
||||
INIT_LIST_HEAD(&device->active);
|
||||
mutex_init(&device->clients_lock);
|
||||
INIT_LIST_HEAD(&device->clients);
|
||||
INIT_LIST_HEAD(&device->list);
|
||||
device->driver = driver;
|
||||
|
||||
device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
|
||||
device->dev.dma_mask = &device->dev.coherent_dma_mask;
|
||||
device->dev.release = host1x_device_release;
|
||||
dev_set_name(&device->dev, "%s", driver->name);
|
||||
device->dev.bus = &host1x_bus_type;
|
||||
device->dev.parent = host1x->dev;
|
||||
|
||||
err = device_register(&device->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = host1x_device_parse_dt(device);
|
||||
if (err < 0) {
|
||||
device_unregister(&device->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
mutex_lock(&host1x->devices_lock);
|
||||
list_add_tail(&device->list, &host1x->devices);
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
|
||||
mutex_lock(&clients_lock);
|
||||
|
||||
list_for_each_entry_safe(client, tmp, &clients, list) {
|
||||
list_for_each_entry(subdev, &device->subdevs, list) {
|
||||
if (subdev->np == client->dev->of_node) {
|
||||
host1x_subdev_register(device, subdev, client);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes a device by first unregistering any subdevices and then removing
|
||||
* itself from the list of devices.
|
||||
*
|
||||
* This function must be called with the host1x->devices_lock held.
|
||||
*/
|
||||
static void host1x_device_del(struct host1x *host1x,
|
||||
struct host1x_device *device)
|
||||
{
|
||||
struct host1x_subdev *subdev, *sd;
|
||||
struct host1x_client *client, *cl;
|
||||
|
||||
mutex_lock(&device->subdevs_lock);
|
||||
|
||||
/* unregister subdevices */
|
||||
list_for_each_entry_safe(subdev, sd, &device->active, list) {
|
||||
/*
|
||||
* host1x_subdev_unregister() will remove the client from
|
||||
* any lists, so we'll need to manually add it back to the
|
||||
* list of idle clients.
|
||||
*
|
||||
* XXX: Alternatively, perhaps don't remove the client from
|
||||
* any lists in host1x_subdev_unregister() and instead do
|
||||
* that explicitly from host1x_unregister_client()?
|
||||
*/
|
||||
client = subdev->client;
|
||||
|
||||
__host1x_subdev_unregister(device, subdev);
|
||||
|
||||
/* add the client to the list of idle clients */
|
||||
mutex_lock(&clients_lock);
|
||||
list_add_tail(&client->list, &clients);
|
||||
mutex_unlock(&clients_lock);
|
||||
}
|
||||
|
||||
/* remove subdevices */
|
||||
list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
|
||||
host1x_subdev_del(subdev);
|
||||
|
||||
mutex_unlock(&device->subdevs_lock);
|
||||
|
||||
/* move clients to idle list */
|
||||
mutex_lock(&clients_lock);
|
||||
mutex_lock(&device->clients_lock);
|
||||
|
||||
list_for_each_entry_safe(client, cl, &device->clients, list)
|
||||
list_move_tail(&client->list, &clients);
|
||||
|
||||
mutex_unlock(&device->clients_lock);
|
||||
mutex_unlock(&clients_lock);
|
||||
|
||||
/* finally remove the device */
|
||||
list_del_init(&device->list);
|
||||
device_unregister(&device->dev);
|
||||
}
|
||||
|
||||
static void host1x_attach_driver(struct host1x *host1x,
|
||||
struct host1x_driver *driver)
|
||||
{
|
||||
struct host1x_device *device;
|
||||
int err;
|
||||
|
||||
mutex_lock(&host1x->devices_lock);
|
||||
|
||||
list_for_each_entry(device, &host1x->devices, list) {
|
||||
if (device->driver == driver) {
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
|
||||
err = host1x_device_add(host1x, driver);
|
||||
if (err < 0)
|
||||
dev_err(host1x->dev, "failed to allocate device: %d\n", err);
|
||||
}
|
||||
|
||||
static void host1x_detach_driver(struct host1x *host1x,
|
||||
struct host1x_driver *driver)
|
||||
{
|
||||
struct host1x_device *device, *tmp;
|
||||
|
||||
mutex_lock(&host1x->devices_lock);
|
||||
|
||||
list_for_each_entry_safe(device, tmp, &host1x->devices, list)
|
||||
if (device->driver == driver)
|
||||
host1x_device_del(host1x, device);
|
||||
|
||||
mutex_unlock(&host1x->devices_lock);
|
||||
}
|
||||
|
||||
int host1x_register(struct host1x *host1x)
|
||||
{
|
||||
struct host1x_driver *driver;
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
list_add_tail(&host1x->list, &devices);
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
mutex_lock(&drivers_lock);
|
||||
|
||||
list_for_each_entry(driver, &drivers, list)
|
||||
host1x_attach_driver(host1x, driver);
|
||||
|
||||
mutex_unlock(&drivers_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_unregister(struct host1x *host1x)
|
||||
{
|
||||
struct host1x_driver *driver;
|
||||
|
||||
mutex_lock(&drivers_lock);
|
||||
|
||||
list_for_each_entry(driver, &drivers, list)
|
||||
host1x_detach_driver(host1x, driver);
|
||||
|
||||
mutex_unlock(&drivers_lock);
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
list_del_init(&host1x->list);
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_driver_register(struct host1x_driver *driver)
|
||||
{
|
||||
struct host1x *host1x;
|
||||
|
||||
INIT_LIST_HEAD(&driver->list);
|
||||
|
||||
mutex_lock(&drivers_lock);
|
||||
list_add_tail(&driver->list, &drivers);
|
||||
mutex_unlock(&drivers_lock);
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
|
||||
list_for_each_entry(host1x, &devices, list)
|
||||
host1x_attach_driver(host1x, driver);
|
||||
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_driver_register);
|
||||
|
||||
void host1x_driver_unregister(struct host1x_driver *driver)
|
||||
{
|
||||
mutex_lock(&drivers_lock);
|
||||
list_del_init(&driver->list);
|
||||
mutex_unlock(&drivers_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_driver_unregister);
|
||||
|
||||
int host1x_client_register(struct host1x_client *client)
|
||||
{
|
||||
struct host1x *host1x;
|
||||
int err;
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
|
||||
list_for_each_entry(host1x, &devices, list) {
|
||||
err = host1x_add_client(host1x, client);
|
||||
if (!err) {
|
||||
mutex_unlock(&devices_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&devices_lock);
|
||||
|
||||
mutex_lock(&clients_lock);
|
||||
list_add_tail(&client->list, &clients);
|
||||
mutex_unlock(&clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_client_register);
|
||||
|
||||
int host1x_client_unregister(struct host1x_client *client)
|
||||
{
|
||||
struct host1x_client *c;
|
||||
struct host1x *host1x;
|
||||
int err;
|
||||
|
||||
mutex_lock(&devices_lock);
|
||||
|
||||
list_for_each_entry(host1x, &devices, list) {
|
||||
err = host1x_del_client(host1x, client);
|
||||
if (!err) {
|
||||
mutex_unlock(&devices_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&devices_lock);
|
||||
mutex_lock(&clients_lock);
|
||||
|
||||
list_for_each_entry(c, &clients, list) {
|
||||
if (c == client) {
|
||||
list_del_init(&c->list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&clients_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_client_unregister);
|
29
drivers/gpu/host1x/bus.h
Normal file
29
drivers/gpu/host1x/bus.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Avionic Design GmbH
|
||||
* 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_BUS_H
|
||||
#define HOST1X_BUS_H
|
||||
|
||||
struct host1x;
|
||||
|
||||
int host1x_bus_init(void);
|
||||
void host1x_bus_exit(void);
|
||||
|
||||
int host1x_register(struct host1x *host1x);
|
||||
int host1x_unregister(struct host1x *host1x);
|
||||
|
||||
#endif
|
491
drivers/gpu/host1x/cdma.c
Normal file
491
drivers/gpu/host1x/cdma.c
Normal file
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* Tegra host1x Command DMA
|
||||
*
|
||||
* 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 <asm/cacheflush.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/slab.h>
|
||||
#include <trace/events/host1x.h>
|
||||
|
||||
#include "cdma.h"
|
||||
#include "channel.h"
|
||||
#include "dev.h"
|
||||
#include "debug.h"
|
||||
#include "job.h"
|
||||
|
||||
/*
|
||||
* 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 == pos
|
||||
* means that the push buffer is full, not empty.
|
||||
*/
|
||||
|
||||
#define HOST1X_PUSHBUFFER_SLOTS 512
|
||||
|
||||
/*
|
||||
* Clean up push buffer resources
|
||||
*/
|
||||
static void host1x_pushbuffer_destroy(struct push_buffer *pb)
|
||||
{
|
||||
struct host1x_cdma *cdma = pb_to_cdma(pb);
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
|
||||
if (pb->phys != 0)
|
||||
dma_free_writecombine(host1x->dev, pb->size_bytes + 4,
|
||||
pb->mapped, pb->phys);
|
||||
|
||||
pb->mapped = NULL;
|
||||
pb->phys = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init push buffer resources
|
||||
*/
|
||||
static int host1x_pushbuffer_init(struct push_buffer *pb)
|
||||
{
|
||||
struct host1x_cdma *cdma = pb_to_cdma(pb);
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
|
||||
pb->mapped = NULL;
|
||||
pb->phys = 0;
|
||||
pb->size_bytes = HOST1X_PUSHBUFFER_SLOTS * 8;
|
||||
|
||||
/* initialize buffer pointers */
|
||||
pb->fence = pb->size_bytes - 8;
|
||||
pb->pos = 0;
|
||||
|
||||
/* allocate and map pushbuffer memory */
|
||||
pb->mapped = dma_alloc_writecombine(host1x->dev, pb->size_bytes + 4,
|
||||
&pb->phys, GFP_KERNEL);
|
||||
if (!pb->mapped)
|
||||
goto fail;
|
||||
|
||||
host1x_hw_pushbuffer_init(host1x, pb);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
host1x_pushbuffer_destroy(pb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push two words to the push buffer
|
||||
* Caller must ensure push buffer is not full
|
||||
*/
|
||||
static void host1x_pushbuffer_push(struct push_buffer *pb, u32 op1, u32 op2)
|
||||
{
|
||||
u32 pos = pb->pos;
|
||||
u32 *p = (u32 *)((u32)pb->mapped + pos);
|
||||
WARN_ON(pos == pb->fence);
|
||||
*(p++) = op1;
|
||||
*(p++) = op2;
|
||||
pb->pos = (pos + 8) & (pb->size_bytes - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pop a number of two word slots from the push buffer
|
||||
* Caller must ensure push buffer is not empty
|
||||
*/
|
||||
static void host1x_pushbuffer_pop(struct push_buffer *pb, unsigned int slots)
|
||||
{
|
||||
/* Advance the next write position */
|
||||
pb->fence = (pb->fence + slots * 8) & (pb->size_bytes - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of two word slots free in the push buffer
|
||||
*/
|
||||
static u32 host1x_pushbuffer_space(struct push_buffer *pb)
|
||||
{
|
||||
return ((pb->fence - pb->pos) & (pb->size_bytes - 1)) / 8;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 host1x_cdma_wait_locked(struct host1x_cdma *cdma,
|
||||
enum cdma_event event)
|
||||
{
|
||||
for (;;) {
|
||||
unsigned int space;
|
||||
|
||||
if (event == CDMA_EVENT_SYNC_QUEUE_EMPTY)
|
||||
space = list_empty(&cdma->sync_queue) ? 1 : 0;
|
||||
else if (event == CDMA_EVENT_PUSH_BUFFER_SPACE) {
|
||||
struct push_buffer *pb = &cdma->push_buffer;
|
||||
space = host1x_pushbuffer_space(pb);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (space)
|
||||
return space;
|
||||
|
||||
trace_host1x_wait_cdma(dev_name(cdma_to_channel(cdma)->dev),
|
||||
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 that tracks the time spent by the job.
|
||||
* Must be called with the cdma lock held.
|
||||
*/
|
||||
static void cdma_start_timer_locked(struct host1x_cdma *cdma,
|
||||
struct host1x_job *job)
|
||||
{
|
||||
struct host1x *host = cdma_to_host1x(cdma);
|
||||
|
||||
if (cdma->timeout.client) {
|
||||
/* timer already started */
|
||||
return;
|
||||
}
|
||||
|
||||
cdma->timeout.client = job->client;
|
||||
cdma->timeout.syncpt = host1x_syncpt_get(host, 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 submission completes.
|
||||
* Must be called with the cdma lock held.
|
||||
*/
|
||||
static void stop_cdma_timer_locked(struct host1x_cdma *cdma)
|
||||
{
|
||||
cancel_delayed_work(&cdma->timeout.wq);
|
||||
cdma->timeout.client = 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 host1x_cdma *cdma)
|
||||
{
|
||||
bool signal = false;
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
struct host1x_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) {
|
||||
struct host1x_syncpt *sp =
|
||||
host1x_syncpt_get(host1x, job->syncpt_id);
|
||||
|
||||
/* Check whether this syncpt has completed, and bail if not */
|
||||
if (!host1x_syncpt_is_expired(sp, 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.client)
|
||||
stop_cdma_timer_locked(cdma);
|
||||
|
||||
/* Unpin the memory */
|
||||
host1x_job_unpin(job);
|
||||
|
||||
/* Pop push buffer slots */
|
||||
if (job->num_slots) {
|
||||
struct push_buffer *pb = &cdma->push_buffer;
|
||||
host1x_pushbuffer_pop(pb, job->num_slots);
|
||||
if (cdma->event == CDMA_EVENT_PUSH_BUFFER_SPACE)
|
||||
signal = true;
|
||||
}
|
||||
|
||||
list_del(&job->list);
|
||||
host1x_job_put(job);
|
||||
}
|
||||
|
||||
if (cdma->event == CDMA_EVENT_SYNC_QUEUE_EMPTY &&
|
||||
list_empty(&cdma->sync_queue))
|
||||
signal = true;
|
||||
|
||||
if (signal) {
|
||||
cdma->event = CDMA_EVENT_NONE;
|
||||
up(&cdma->sem);
|
||||
}
|
||||
}
|
||||
|
||||
void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
|
||||
struct device *dev)
|
||||
{
|
||||
u32 restart_addr;
|
||||
u32 syncpt_incrs;
|
||||
struct host1x_job *job = NULL;
|
||||
u32 syncpt_val;
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
|
||||
syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
|
||||
|
||||
dev_dbg(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, "%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;
|
||||
|
||||
host1x_job_dump(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 restart_addr 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, "%s: perform CPU incr on pending same ctx buffers\n",
|
||||
__func__);
|
||||
|
||||
if (!list_empty(&cdma->sync_queue))
|
||||
restart_addr = job->first_get;
|
||||
else
|
||||
restart_addr = cdma->last_pos;
|
||||
|
||||
/* 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->client != cdma->timeout.client)
|
||||
break;
|
||||
|
||||
/* won't need a timeout when replayed */
|
||||
job->timeout = 0;
|
||||
|
||||
syncpt_incrs = job->syncpt_end - syncpt_val;
|
||||
dev_dbg(dev, "%s: CPU incr (%d)\n", __func__, syncpt_incrs);
|
||||
|
||||
host1x_job_dump(dev, job);
|
||||
|
||||
/* safe to use CPU to incr syncpts */
|
||||
host1x_hw_cdma_timeout_cpu_incr(host1x, cdma, job->first_get,
|
||||
syncpt_incrs, job->syncpt_end,
|
||||
job->num_slots);
|
||||
|
||||
syncpt_val += syncpt_incrs;
|
||||
}
|
||||
|
||||
/* The following sumbits from the same client may be dependent on the
|
||||
* failed submit and therefore they may fail. Force a small timeout
|
||||
* to make the queue cleanup faster */
|
||||
|
||||
list_for_each_entry_from(job, &cdma->sync_queue, list)
|
||||
if (job->client == cdma->timeout.client)
|
||||
job->timeout = min_t(unsigned int, job->timeout, 500);
|
||||
|
||||
dev_dbg(dev, "%s: finished sync_queue modification\n", __func__);
|
||||
|
||||
/* roll back DMAGET and start up channel again */
|
||||
host1x_hw_cdma_resume(host1x, cdma, restart_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a cdma
|
||||
*/
|
||||
int host1x_cdma_init(struct host1x_cdma *cdma)
|
||||
{
|
||||
int err;
|
||||
|
||||
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 = host1x_pushbuffer_init(&cdma->push_buffer);
|
||||
if (err)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a cdma
|
||||
*/
|
||||
int host1x_cdma_deinit(struct host1x_cdma *cdma)
|
||||
{
|
||||
struct push_buffer *pb = &cdma->push_buffer;
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
|
||||
if (cdma->running) {
|
||||
pr_warn("%s: CDMA still running\n", __func__);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
host1x_pushbuffer_destroy(pb);
|
||||
host1x_hw_cdma_timeout_destroy(host1x, cdma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin a cdma submit
|
||||
*/
|
||||
int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
|
||||
{
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
|
||||
mutex_lock(&cdma->lock);
|
||||
|
||||
if (job->timeout) {
|
||||
/* init state on first submit with timeout value */
|
||||
if (!cdma->timeout.initialized) {
|
||||
int err;
|
||||
err = host1x_hw_cdma_timeout_init(host1x, cdma,
|
||||
job->syncpt_id);
|
||||
if (err) {
|
||||
mutex_unlock(&cdma->lock);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cdma->running)
|
||||
host1x_hw_cdma_start(host1x, cdma);
|
||||
|
||||
cdma->slots_free = 0;
|
||||
cdma->slots_used = 0;
|
||||
cdma->first_get = cdma->push_buffer.pos;
|
||||
|
||||
trace_host1x_cdma_begin(dev_name(job->channel->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push two words into a push buffer slot
|
||||
* Blocks as necessary if the push buffer is full.
|
||||
*/
|
||||
void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2)
|
||||
{
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
struct push_buffer *pb = &cdma->push_buffer;
|
||||
u32 slots_free = cdma->slots_free;
|
||||
|
||||
if (host1x_debug_trace_cmdbuf)
|
||||
trace_host1x_cdma_push(dev_name(cdma_to_channel(cdma)->dev),
|
||||
op1, op2);
|
||||
|
||||
if (slots_free == 0) {
|
||||
host1x_hw_cdma_flush(host1x, cdma);
|
||||
slots_free = host1x_cdma_wait_locked(cdma,
|
||||
CDMA_EVENT_PUSH_BUFFER_SPACE);
|
||||
}
|
||||
cdma->slots_free = slots_free - 1;
|
||||
cdma->slots_used++;
|
||||
host1x_pushbuffer_push(pb, 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 host1x_cdma_end(struct host1x_cdma *cdma,
|
||||
struct host1x_job *job)
|
||||
{
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
bool idle = list_empty(&cdma->sync_queue);
|
||||
|
||||
host1x_hw_cdma_flush(host1x, cdma);
|
||||
|
||||
job->first_get = cdma->first_get;
|
||||
job->num_slots = cdma->slots_used;
|
||||
host1x_job_get(job);
|
||||
list_add_tail(&job->list, &cdma->sync_queue);
|
||||
|
||||
/* start timer on idle -> active transitions */
|
||||
if (job->timeout && idle)
|
||||
cdma_start_timer_locked(cdma, job);
|
||||
|
||||
trace_host1x_cdma_end(dev_name(job->channel->dev));
|
||||
mutex_unlock(&cdma->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update cdma state according to current sync point values
|
||||
*/
|
||||
void host1x_cdma_update(struct host1x_cdma *cdma)
|
||||
{
|
||||
mutex_lock(&cdma->lock);
|
||||
update_cdma_locked(cdma);
|
||||
mutex_unlock(&cdma->lock);
|
||||
}
|
100
drivers/gpu/host1x/cdma.h
Normal file
100
drivers/gpu/host1x/cdma.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Tegra host1x Command DMA
|
||||
*
|
||||
* 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_CDMA_H
|
||||
#define __HOST1X_CDMA_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
struct host1x_syncpt;
|
||||
struct host1x_userctx_timeout;
|
||||
struct host1x_job;
|
||||
|
||||
/*
|
||||
* 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 push_buffer {
|
||||
u32 *mapped; /* mapped pushbuffer memory */
|
||||
dma_addr_t phys; /* physical address of pushbuffer */
|
||||
u32 fence; /* index we've written */
|
||||
u32 pos; /* index to write to */
|
||||
u32 size_bytes;
|
||||
};
|
||||
|
||||
struct buffer_timeout {
|
||||
struct delayed_work wq; /* work queue */
|
||||
bool initialized; /* timer one-time setup flag */
|
||||
struct host1x_syncpt *syncpt; /* buffer completion syncpt */
|
||||
u32 syncpt_val; /* syncpt value when completed */
|
||||
ktime_t start_ktime; /* starting time */
|
||||
/* context timeout information */
|
||||
int client;
|
||||
};
|
||||
|
||||
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 host1x_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_pos; /* 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 host1x_channel, cdma)
|
||||
#define cdma_to_host1x(cdma) dev_get_drvdata(cdma_to_channel(cdma)->dev->parent)
|
||||
#define pb_to_cdma(pb) container_of(pb, struct host1x_cdma, push_buffer)
|
||||
|
||||
int host1x_cdma_init(struct host1x_cdma *cdma);
|
||||
int host1x_cdma_deinit(struct host1x_cdma *cdma);
|
||||
void host1x_cdma_stop(struct host1x_cdma *cdma);
|
||||
int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job);
|
||||
void host1x_cdma_push(struct host1x_cdma *cdma, u32 op1, u32 op2);
|
||||
void host1x_cdma_end(struct host1x_cdma *cdma, struct host1x_job *job);
|
||||
void host1x_cdma_update(struct host1x_cdma *cdma);
|
||||
void host1x_cdma_peek(struct host1x_cdma *cdma, u32 dmaget, int slot,
|
||||
u32 *out);
|
||||
unsigned int host1x_cdma_wait_locked(struct host1x_cdma *cdma,
|
||||
enum cdma_event event);
|
||||
void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
|
||||
struct device *dev);
|
||||
#endif
|
131
drivers/gpu/host1x/channel.c
Normal file
131
drivers/gpu/host1x/channel.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Tegra host1x Channel
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "channel.h"
|
||||
#include "dev.h"
|
||||
#include "job.h"
|
||||
|
||||
/* Constructor for the host1x device list */
|
||||
int host1x_channel_list_init(struct host1x *host)
|
||||
{
|
||||
INIT_LIST_HEAD(&host->chlist.list);
|
||||
mutex_init(&host->chlist_mutex);
|
||||
|
||||
if (host->info->nb_channels > BITS_PER_LONG) {
|
||||
WARN(1, "host1x hardware has more channels than supported by the driver\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_job_submit(struct host1x_job *job)
|
||||
{
|
||||
struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
|
||||
|
||||
return host1x_hw_channel_submit(host, job);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_submit);
|
||||
|
||||
struct host1x_channel *host1x_channel_get(struct host1x_channel *channel)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&channel->reflock);
|
||||
|
||||
if (channel->refcount == 0)
|
||||
err = host1x_cdma_init(&channel->cdma);
|
||||
|
||||
if (!err)
|
||||
channel->refcount++;
|
||||
|
||||
mutex_unlock(&channel->reflock);
|
||||
|
||||
return err ? NULL : channel;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_channel_get);
|
||||
|
||||
void host1x_channel_put(struct host1x_channel *channel)
|
||||
{
|
||||
mutex_lock(&channel->reflock);
|
||||
|
||||
if (channel->refcount == 1) {
|
||||
struct host1x *host = dev_get_drvdata(channel->dev->parent);
|
||||
|
||||
host1x_hw_cdma_stop(host, &channel->cdma);
|
||||
host1x_cdma_deinit(&channel->cdma);
|
||||
}
|
||||
|
||||
channel->refcount--;
|
||||
|
||||
mutex_unlock(&channel->reflock);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_channel_put);
|
||||
|
||||
struct host1x_channel *host1x_channel_request(struct device *dev)
|
||||
{
|
||||
struct host1x *host = dev_get_drvdata(dev->parent);
|
||||
int max_channels = host->info->nb_channels;
|
||||
struct host1x_channel *channel = NULL;
|
||||
int index, err;
|
||||
|
||||
mutex_lock(&host->chlist_mutex);
|
||||
|
||||
index = find_first_zero_bit(&host->allocated_channels, max_channels);
|
||||
if (index >= max_channels)
|
||||
goto fail;
|
||||
|
||||
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||
if (!channel)
|
||||
goto fail;
|
||||
|
||||
err = host1x_hw_channel_init(host, channel, index);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
/* Link device to host1x_channel */
|
||||
channel->dev = dev;
|
||||
|
||||
/* Add to channel list */
|
||||
list_add_tail(&channel->list, &host->chlist.list);
|
||||
|
||||
host->allocated_channels |= BIT(index);
|
||||
|
||||
mutex_unlock(&host->chlist_mutex);
|
||||
return channel;
|
||||
|
||||
fail:
|
||||
dev_err(dev, "failed to init channel\n");
|
||||
kfree(channel);
|
||||
mutex_unlock(&host->chlist_mutex);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_channel_request);
|
||||
|
||||
void host1x_channel_free(struct host1x_channel *channel)
|
||||
{
|
||||
struct host1x *host = dev_get_drvdata(channel->dev->parent);
|
||||
|
||||
host->allocated_channels &= ~BIT(channel->id);
|
||||
list_del(&channel->list);
|
||||
kfree(channel);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_channel_free);
|
46
drivers/gpu/host1x/channel.h
Normal file
46
drivers/gpu/host1x/channel.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Tegra host1x Channel
|
||||
*
|
||||
* 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_CHANNEL_H
|
||||
#define __HOST1X_CHANNEL_H
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "cdma.h"
|
||||
|
||||
struct host1x;
|
||||
|
||||
struct host1x_channel {
|
||||
struct list_head list;
|
||||
|
||||
unsigned int refcount;
|
||||
unsigned int id;
|
||||
struct mutex reflock;
|
||||
struct mutex submitlock;
|
||||
void __iomem *regs;
|
||||
struct device *dev;
|
||||
struct host1x_cdma cdma;
|
||||
};
|
||||
|
||||
/* channel list operations */
|
||||
int host1x_channel_list_init(struct host1x *host);
|
||||
|
||||
#define host1x_for_each_channel(host, channel) \
|
||||
list_for_each_entry(channel, &host->chlist.list, list)
|
||||
|
||||
#endif
|
213
drivers/gpu/host1x/debug.c
Normal file
213
drivers/gpu/host1x/debug.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* Copyright (C) 2011-2013 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 "channel.h"
|
||||
|
||||
unsigned int host1x_debug_trace_cmdbuf;
|
||||
|
||||
static pid_t host1x_debug_force_timeout_pid;
|
||||
static u32 host1x_debug_force_timeout_val;
|
||||
static u32 host1x_debug_force_timeout_channel;
|
||||
|
||||
void host1x_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 host1x_channel *ch, void *data, bool show_fifo)
|
||||
{
|
||||
struct host1x *m = dev_get_drvdata(ch->dev->parent);
|
||||
struct output *o = data;
|
||||
|
||||
mutex_lock(&ch->reflock);
|
||||
if (ch->refcount) {
|
||||
mutex_lock(&ch->cdma.lock);
|
||||
if (show_fifo)
|
||||
host1x_hw_show_channel_fifo(m, ch, o);
|
||||
host1x_hw_show_channel_cdma(m, ch, o);
|
||||
mutex_unlock(&ch->cdma.lock);
|
||||
}
|
||||
mutex_unlock(&ch->reflock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void show_syncpts(struct host1x *m, struct output *o)
|
||||
{
|
||||
int i;
|
||||
host1x_debug_output(o, "---- syncpts ----\n");
|
||||
for (i = 0; i < host1x_syncpt_nb_pts(m); i++) {
|
||||
u32 max = host1x_syncpt_read_max(m->syncpt + i);
|
||||
u32 min = host1x_syncpt_load(m->syncpt + i);
|
||||
if (!min && !max)
|
||||
continue;
|
||||
host1x_debug_output(o, "id %d (%s) min %d max %d\n",
|
||||
i, m->syncpt[i].name, min, max);
|
||||
}
|
||||
|
||||
for (i = 0; i < host1x_syncpt_nb_bases(m); i++) {
|
||||
u32 base_val;
|
||||
base_val = host1x_syncpt_load_wait_base(m->syncpt + i);
|
||||
if (base_val)
|
||||
host1x_debug_output(o, "waitbase id %d val %d\n", i,
|
||||
base_val);
|
||||
}
|
||||
|
||||
host1x_debug_output(o, "\n");
|
||||
}
|
||||
|
||||
static void show_all(struct host1x *m, struct output *o)
|
||||
{
|
||||
struct host1x_channel *ch;
|
||||
|
||||
host1x_hw_show_mlocks(m, o);
|
||||
show_syncpts(m, o);
|
||||
host1x_debug_output(o, "---- channels ----\n");
|
||||
|
||||
host1x_for_each_channel(m, ch)
|
||||
show_channels(ch, o, true);
|
||||
}
|
||||
|
||||
static void show_all_no_fifo(struct host1x *host1x, struct output *o)
|
||||
{
|
||||
struct host1x_channel *ch;
|
||||
|
||||
host1x_hw_show_mlocks(host1x, o);
|
||||
show_syncpts(host1x, o);
|
||||
host1x_debug_output(o, "---- channels ----\n");
|
||||
|
||||
host1x_for_each_channel(host1x, ch)
|
||||
show_channels(ch, o, false);
|
||||
}
|
||||
|
||||
static int host1x_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 host1x_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 host1x_debug_open_all(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, host1x_debug_show_all, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations host1x_debug_all_fops = {
|
||||
.open = host1x_debug_open_all,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int host1x_debug_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, host1x_debug_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations host1x_debug_fops = {
|
||||
.open = host1x_debug_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void host1x_debugfs_init(struct host1x *host1x)
|
||||
{
|
||||
struct dentry *de = debugfs_create_dir("tegra-host1x", NULL);
|
||||
|
||||
if (!de)
|
||||
return;
|
||||
|
||||
/* Store the created entry */
|
||||
host1x->debugfs = de;
|
||||
|
||||
debugfs_create_file("status", S_IRUGO, de, host1x, &host1x_debug_fops);
|
||||
debugfs_create_file("status_all", S_IRUGO, de, host1x,
|
||||
&host1x_debug_all_fops);
|
||||
|
||||
debugfs_create_u32("trace_cmdbuf", S_IRUGO|S_IWUSR, de,
|
||||
&host1x_debug_trace_cmdbuf);
|
||||
|
||||
host1x_hw_debug_init(host1x, de);
|
||||
|
||||
debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de,
|
||||
&host1x_debug_force_timeout_pid);
|
||||
debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de,
|
||||
&host1x_debug_force_timeout_val);
|
||||
debugfs_create_u32("force_timeout_channel", S_IRUGO|S_IWUSR, de,
|
||||
&host1x_debug_force_timeout_channel);
|
||||
}
|
||||
|
||||
static void host1x_debugfs_exit(struct host1x *host1x)
|
||||
{
|
||||
debugfs_remove_recursive(host1x->debugfs);
|
||||
}
|
||||
|
||||
void host1x_debug_init(struct host1x *host1x)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
host1x_debugfs_init(host1x);
|
||||
}
|
||||
|
||||
void host1x_debug_deinit(struct host1x *host1x)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
host1x_debugfs_exit(host1x);
|
||||
}
|
||||
|
||||
void host1x_debug_dump(struct host1x *host1x)
|
||||
{
|
||||
struct output o = {
|
||||
.fn = write_to_printk
|
||||
};
|
||||
show_all(host1x, &o);
|
||||
}
|
||||
|
||||
void host1x_debug_dump_syncpts(struct host1x *host1x)
|
||||
{
|
||||
struct output o = {
|
||||
.fn = write_to_printk
|
||||
};
|
||||
show_syncpts(host1x, &o);
|
||||
}
|
51
drivers/gpu/host1x/debug.h
Normal file
51
drivers/gpu/host1x/debug.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Tegra host1x Debug
|
||||
*
|
||||
* 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_DEBUG_H
|
||||
#define __HOST1X_DEBUG_H
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
struct host1x;
|
||||
|
||||
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 __printf(2, 3) host1x_debug_output(struct output *o, const char *fmt, ...);
|
||||
|
||||
extern unsigned int host1x_debug_trace_cmdbuf;
|
||||
|
||||
void host1x_debug_init(struct host1x *host1x);
|
||||
void host1x_debug_deinit(struct host1x *host1x);
|
||||
void host1x_debug_dump(struct host1x *host1x);
|
||||
void host1x_debug_dump_syncpts(struct host1x *host1x);
|
||||
|
||||
#endif
|
252
drivers/gpu/host1x/dev.c
Normal file
252
drivers/gpu/host1x/dev.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/host1x.h>
|
||||
|
||||
#include "bus.h"
|
||||
#include "dev.h"
|
||||
#include "intr.h"
|
||||
#include "channel.h"
|
||||
#include "debug.h"
|
||||
#include "hw/host1x01.h"
|
||||
#include "hw/host1x02.h"
|
||||
#include "hw/host1x04.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r)
|
||||
{
|
||||
writel(v, ch->regs + r);
|
||||
}
|
||||
|
||||
u32 host1x_ch_readl(struct host1x_channel *ch, u32 r)
|
||||
{
|
||||
return readl(ch->regs + r);
|
||||
}
|
||||
|
||||
static const struct host1x_info host1x01_info = {
|
||||
.nb_channels = 8,
|
||||
.nb_pts = 32,
|
||||
.nb_mlocks = 16,
|
||||
.nb_bases = 8,
|
||||
.init = host1x01_init,
|
||||
.sync_offset = 0x3000,
|
||||
};
|
||||
|
||||
static const struct host1x_info host1x02_info = {
|
||||
.nb_channels = 9,
|
||||
.nb_pts = 32,
|
||||
.nb_mlocks = 16,
|
||||
.nb_bases = 12,
|
||||
.init = host1x02_init,
|
||||
.sync_offset = 0x3000,
|
||||
};
|
||||
|
||||
static const struct host1x_info host1x04_info = {
|
||||
.nb_channels = 12,
|
||||
.nb_pts = 192,
|
||||
.nb_mlocks = 16,
|
||||
.nb_bases = 64,
|
||||
.init = host1x04_init,
|
||||
.sync_offset = 0x2100,
|
||||
};
|
||||
|
||||
static struct of_device_id host1x_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-host1x", .data = &host1x04_info, },
|
||||
{ .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },
|
||||
{ .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, },
|
||||
{ .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, host1x_of_match);
|
||||
|
||||
static int host1x_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *id;
|
||||
struct host1x *host;
|
||||
struct resource *regs;
|
||||
int syncpt_irq;
|
||||
int err;
|
||||
|
||||
id = of_match_device(host1x_of_match, &pdev->dev);
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs) {
|
||||
dev_err(&pdev->dev, "failed to get registers\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
syncpt_irq = platform_get_irq(pdev, 0);
|
||||
if (syncpt_irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get IRQ\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&host->devices_lock);
|
||||
INIT_LIST_HEAD(&host->devices);
|
||||
INIT_LIST_HEAD(&host->list);
|
||||
host->dev = &pdev->dev;
|
||||
host->info = id->data;
|
||||
|
||||
/* set common host1x device data */
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
host->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
if (IS_ERR(host->regs))
|
||||
return PTR_ERR(host->regs);
|
||||
|
||||
if (host->info->init) {
|
||||
err = host->info->init(host);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
host->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(host->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
err = PTR_ERR(host->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = host1x_channel_list_init(host);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to initialize channel list\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(host->clk);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = host1x_syncpt_init(host);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to initialize syncpts\n");
|
||||
goto fail_unprepare_disable;
|
||||
}
|
||||
|
||||
err = host1x_intr_init(host, syncpt_irq);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to initialize interrupts\n");
|
||||
goto fail_deinit_syncpt;
|
||||
}
|
||||
|
||||
host1x_debug_init(host);
|
||||
|
||||
err = host1x_register(host);
|
||||
if (err < 0)
|
||||
goto fail_deinit_intr;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_deinit_intr:
|
||||
host1x_intr_deinit(host);
|
||||
fail_deinit_syncpt:
|
||||
host1x_syncpt_deinit(host);
|
||||
fail_unprepare_disable:
|
||||
clk_disable_unprepare(host->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int host1x_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct host1x *host = platform_get_drvdata(pdev);
|
||||
|
||||
host1x_unregister(host);
|
||||
host1x_intr_deinit(host);
|
||||
host1x_syncpt_deinit(host);
|
||||
clk_disable_unprepare(host->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tegra_host1x_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-host1x",
|
||||
.of_match_table = host1x_of_match,
|
||||
},
|
||||
.probe = host1x_probe,
|
||||
.remove = host1x_remove,
|
||||
};
|
||||
|
||||
static int __init tegra_host1x_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = host1x_bus_init();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = platform_driver_register(&tegra_host1x_driver);
|
||||
if (err < 0)
|
||||
goto unregister_bus;
|
||||
|
||||
err = platform_driver_register(&tegra_mipi_driver);
|
||||
if (err < 0)
|
||||
goto unregister_host1x;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_host1x:
|
||||
platform_driver_unregister(&tegra_host1x_driver);
|
||||
unregister_bus:
|
||||
host1x_bus_exit();
|
||||
return err;
|
||||
}
|
||||
module_init(tegra_host1x_init);
|
||||
|
||||
static void __exit tegra_host1x_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tegra_mipi_driver);
|
||||
platform_driver_unregister(&tegra_host1x_driver);
|
||||
host1x_bus_exit();
|
||||
}
|
||||
module_exit(tegra_host1x_exit);
|
||||
|
||||
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
|
||||
MODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Host1x driver for Tegra products");
|
||||
MODULE_LICENSE("GPL");
|
311
drivers/gpu/host1x/dev.h
Normal file
311
drivers/gpu/host1x/dev.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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 <linux/platform_device.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "channel.h"
|
||||
#include "syncpt.h"
|
||||
#include "intr.h"
|
||||
#include "cdma.h"
|
||||
#include "job.h"
|
||||
|
||||
struct host1x_syncpt;
|
||||
struct host1x_syncpt_base;
|
||||
struct host1x_channel;
|
||||
struct host1x_cdma;
|
||||
struct host1x_job;
|
||||
struct push_buffer;
|
||||
struct output;
|
||||
struct dentry;
|
||||
|
||||
struct host1x_channel_ops {
|
||||
int (*init)(struct host1x_channel *channel, struct host1x *host,
|
||||
unsigned int id);
|
||||
int (*submit)(struct host1x_job *job);
|
||||
};
|
||||
|
||||
struct host1x_cdma_ops {
|
||||
void (*start)(struct host1x_cdma *cdma);
|
||||
void (*stop)(struct host1x_cdma *cdma);
|
||||
void (*flush)(struct host1x_cdma *cdma);
|
||||
int (*timeout_init)(struct host1x_cdma *cdma, u32 syncpt_id);
|
||||
void (*timeout_destroy)(struct host1x_cdma *cdma);
|
||||
void (*freeze)(struct host1x_cdma *cdma);
|
||||
void (*resume)(struct host1x_cdma *cdma, u32 getptr);
|
||||
void (*timeout_cpu_incr)(struct host1x_cdma *cdma, u32 getptr,
|
||||
u32 syncpt_incrs, u32 syncval, u32 nr_slots);
|
||||
};
|
||||
|
||||
struct host1x_pushbuffer_ops {
|
||||
void (*init)(struct push_buffer *pb);
|
||||
};
|
||||
|
||||
struct host1x_debug_ops {
|
||||
void (*debug_init)(struct dentry *de);
|
||||
void (*show_channel_cdma)(struct host1x *host,
|
||||
struct host1x_channel *ch,
|
||||
struct output *o);
|
||||
void (*show_channel_fifo)(struct host1x *host,
|
||||
struct host1x_channel *ch,
|
||||
struct output *o);
|
||||
void (*show_mlocks)(struct host1x *host, struct output *output);
|
||||
|
||||
};
|
||||
|
||||
struct host1x_syncpt_ops {
|
||||
void (*restore)(struct host1x_syncpt *syncpt);
|
||||
void (*restore_wait_base)(struct host1x_syncpt *syncpt);
|
||||
void (*load_wait_base)(struct host1x_syncpt *syncpt);
|
||||
u32 (*load)(struct host1x_syncpt *syncpt);
|
||||
int (*cpu_incr)(struct host1x_syncpt *syncpt);
|
||||
int (*patch_wait)(struct host1x_syncpt *syncpt, void *patch_addr);
|
||||
};
|
||||
|
||||
struct host1x_intr_ops {
|
||||
int (*init_host_sync)(struct host1x *host, u32 cpm,
|
||||
void (*syncpt_thresh_work)(struct work_struct *work));
|
||||
void (*set_syncpt_threshold)(
|
||||
struct host1x *host, u32 id, u32 thresh);
|
||||
void (*enable_syncpt_intr)(struct host1x *host, u32 id);
|
||||
void (*disable_syncpt_intr)(struct host1x *host, u32 id);
|
||||
void (*disable_all_syncpt_intrs)(struct host1x *host);
|
||||
int (*free_syncpt_irq)(struct host1x *host);
|
||||
};
|
||||
|
||||
struct host1x_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 {
|
||||
const struct host1x_info *info;
|
||||
|
||||
void __iomem *regs;
|
||||
struct host1x_syncpt *syncpt;
|
||||
struct host1x_syncpt_base *bases;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
|
||||
struct mutex intr_mutex;
|
||||
struct workqueue_struct *intr_wq;
|
||||
int intr_syncpt_irq;
|
||||
|
||||
const struct host1x_syncpt_ops *syncpt_op;
|
||||
const struct host1x_intr_ops *intr_op;
|
||||
const struct host1x_channel_ops *channel_op;
|
||||
const struct host1x_cdma_ops *cdma_op;
|
||||
const struct host1x_pushbuffer_ops *cdma_pb_op;
|
||||
const struct host1x_debug_ops *debug_op;
|
||||
|
||||
struct host1x_syncpt *nop_sp;
|
||||
|
||||
struct mutex chlist_mutex;
|
||||
struct host1x_channel chlist;
|
||||
unsigned long allocated_channels;
|
||||
unsigned int num_allocated_channels;
|
||||
|
||||
struct dentry *debugfs;
|
||||
|
||||
struct mutex devices_lock;
|
||||
struct list_head devices;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
|
||||
u32 host1x_sync_readl(struct host1x *host1x, u32 r);
|
||||
void host1x_ch_writel(struct host1x_channel *ch, u32 r, u32 v);
|
||||
u32 host1x_ch_readl(struct host1x_channel *ch, u32 r);
|
||||
|
||||
static inline void host1x_hw_syncpt_restore(struct host1x *host,
|
||||
struct host1x_syncpt *sp)
|
||||
{
|
||||
host->syncpt_op->restore(sp);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_syncpt_restore_wait_base(struct host1x *host,
|
||||
struct host1x_syncpt *sp)
|
||||
{
|
||||
host->syncpt_op->restore_wait_base(sp);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_syncpt_load_wait_base(struct host1x *host,
|
||||
struct host1x_syncpt *sp)
|
||||
{
|
||||
host->syncpt_op->load_wait_base(sp);
|
||||
}
|
||||
|
||||
static inline u32 host1x_hw_syncpt_load(struct host1x *host,
|
||||
struct host1x_syncpt *sp)
|
||||
{
|
||||
return host->syncpt_op->load(sp);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_syncpt_cpu_incr(struct host1x *host,
|
||||
struct host1x_syncpt *sp)
|
||||
{
|
||||
return host->syncpt_op->cpu_incr(sp);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_syncpt_patch_wait(struct host1x *host,
|
||||
struct host1x_syncpt *sp,
|
||||
void *patch_addr)
|
||||
{
|
||||
return host->syncpt_op->patch_wait(sp, patch_addr);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm,
|
||||
void (*syncpt_thresh_work)(struct work_struct *))
|
||||
{
|
||||
return host->intr_op->init_host_sync(host, cpm, syncpt_thresh_work);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_intr_set_syncpt_threshold(struct host1x *host,
|
||||
u32 id, u32 thresh)
|
||||
{
|
||||
host->intr_op->set_syncpt_threshold(host, id, thresh);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_intr_enable_syncpt_intr(struct host1x *host,
|
||||
u32 id)
|
||||
{
|
||||
host->intr_op->enable_syncpt_intr(host, id);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_intr_disable_syncpt_intr(struct host1x *host,
|
||||
u32 id)
|
||||
{
|
||||
host->intr_op->disable_syncpt_intr(host, id);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_intr_disable_all_syncpt_intrs(struct host1x *host)
|
||||
{
|
||||
host->intr_op->disable_all_syncpt_intrs(host);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_intr_free_syncpt_irq(struct host1x *host)
|
||||
{
|
||||
return host->intr_op->free_syncpt_irq(host);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_channel_init(struct host1x *host,
|
||||
struct host1x_channel *channel,
|
||||
int chid)
|
||||
{
|
||||
return host->channel_op->init(channel, host, chid);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_channel_submit(struct host1x *host,
|
||||
struct host1x_job *job)
|
||||
{
|
||||
return host->channel_op->submit(job);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_start(struct host1x *host,
|
||||
struct host1x_cdma *cdma)
|
||||
{
|
||||
host->cdma_op->start(cdma);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_stop(struct host1x *host,
|
||||
struct host1x_cdma *cdma)
|
||||
{
|
||||
host->cdma_op->stop(cdma);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_flush(struct host1x *host,
|
||||
struct host1x_cdma *cdma)
|
||||
{
|
||||
host->cdma_op->flush(cdma);
|
||||
}
|
||||
|
||||
static inline int host1x_hw_cdma_timeout_init(struct host1x *host,
|
||||
struct host1x_cdma *cdma,
|
||||
u32 syncpt_id)
|
||||
{
|
||||
return host->cdma_op->timeout_init(cdma, syncpt_id);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_timeout_destroy(struct host1x *host,
|
||||
struct host1x_cdma *cdma)
|
||||
{
|
||||
host->cdma_op->timeout_destroy(cdma);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_freeze(struct host1x *host,
|
||||
struct host1x_cdma *cdma)
|
||||
{
|
||||
host->cdma_op->freeze(cdma);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_resume(struct host1x *host,
|
||||
struct host1x_cdma *cdma, u32 getptr)
|
||||
{
|
||||
host->cdma_op->resume(cdma, getptr);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_cdma_timeout_cpu_incr(struct host1x *host,
|
||||
struct host1x_cdma *cdma,
|
||||
u32 getptr,
|
||||
u32 syncpt_incrs,
|
||||
u32 syncval, u32 nr_slots)
|
||||
{
|
||||
host->cdma_op->timeout_cpu_incr(cdma, getptr, syncpt_incrs, syncval,
|
||||
nr_slots);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_pushbuffer_init(struct host1x *host,
|
||||
struct push_buffer *pb)
|
||||
{
|
||||
host->cdma_pb_op->init(pb);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_debug_init(struct host1x *host, struct dentry *de)
|
||||
{
|
||||
if (host->debug_op && host->debug_op->debug_init)
|
||||
host->debug_op->debug_init(de);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_show_channel_cdma(struct host1x *host,
|
||||
struct host1x_channel *channel,
|
||||
struct output *o)
|
||||
{
|
||||
host->debug_op->show_channel_cdma(host, channel, o);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_show_channel_fifo(struct host1x *host,
|
||||
struct host1x_channel *channel,
|
||||
struct output *o)
|
||||
{
|
||||
host->debug_op->show_channel_fifo(host, channel, o);
|
||||
}
|
||||
|
||||
static inline void host1x_hw_show_mlocks(struct host1x *host, struct output *o)
|
||||
{
|
||||
host->debug_op->show_mlocks(host, o);
|
||||
}
|
||||
|
||||
extern struct platform_driver tegra_mipi_driver;
|
||||
|
||||
#endif
|
326
drivers/gpu/host1x/hw/cdma_hw.c
Normal file
326
drivers/gpu/host1x/hw/cdma_hw.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Tegra host1x Command DMA
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include "../cdma.h"
|
||||
#include "../channel.h"
|
||||
#include "../dev.h"
|
||||
#include "../debug.h"
|
||||
|
||||
/*
|
||||
* Put the restart at the end of pushbuffer memor
|
||||
*/
|
||||
static void push_buffer_init(struct push_buffer *pb)
|
||||
{
|
||||
*(pb->mapped + (pb->size_bytes >> 2)) = host1x_opcode_restart(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment timedout buffer's syncpt via CPU.
|
||||
*/
|
||||
static void cdma_timeout_cpu_incr(struct host1x_cdma *cdma, u32 getptr,
|
||||
u32 syncpt_incrs, u32 syncval, u32 nr_slots)
|
||||
{
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
struct push_buffer *pb = &cdma->push_buffer;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < syncpt_incrs; i++)
|
||||
host1x_syncpt_incr(cdma->timeout.syncpt);
|
||||
|
||||
/* after CPU incr, ensure shadow is up to date */
|
||||
host1x_syncpt_load(cdma->timeout.syncpt);
|
||||
|
||||
/* NOP all the PB slots */
|
||||
while (nr_slots--) {
|
||||
u32 *p = (u32 *)((u32)pb->mapped + getptr);
|
||||
*(p++) = HOST1X_OPCODE_NOP;
|
||||
*(p++) = HOST1X_OPCODE_NOP;
|
||||
dev_dbg(host1x->dev, "%s: NOP at %#llx\n", __func__,
|
||||
(u64)pb->phys + getptr);
|
||||
getptr = (getptr + 8) & (pb->size_bytes - 1);
|
||||
}
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Start channel DMA
|
||||
*/
|
||||
static void cdma_start(struct host1x_cdma *cdma)
|
||||
{
|
||||
struct host1x_channel *ch = cdma_to_channel(cdma);
|
||||
|
||||
if (cdma->running)
|
||||
return;
|
||||
|
||||
cdma->last_pos = cdma->push_buffer.pos;
|
||||
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
/* set base, put and end pointer */
|
||||
host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART);
|
||||
host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT);
|
||||
host1x_ch_writel(ch, cdma->push_buffer.phys +
|
||||
cdma->push_buffer.size_bytes + 4,
|
||||
HOST1X_CHANNEL_DMAEND);
|
||||
|
||||
/* reset GET */
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP |
|
||||
HOST1X_CHANNEL_DMACTRL_DMAGETRST |
|
||||
HOST1X_CHANNEL_DMACTRL_DMAINITGET,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
/* start the command DMA */
|
||||
host1x_ch_writel(ch, 0, HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
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 host1x_cdma *cdma, u32 getptr)
|
||||
{
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
struct host1x_channel *ch = cdma_to_channel(cdma);
|
||||
|
||||
if (cdma->running)
|
||||
return;
|
||||
|
||||
cdma->last_pos = cdma->push_buffer.pos;
|
||||
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
/* set base, end pointer (all of memory) */
|
||||
host1x_ch_writel(ch, cdma->push_buffer.phys, HOST1X_CHANNEL_DMASTART);
|
||||
host1x_ch_writel(ch, cdma->push_buffer.phys +
|
||||
cdma->push_buffer.size_bytes,
|
||||
HOST1X_CHANNEL_DMAEND);
|
||||
|
||||
/* set GET, by loading the value in PUT (then reset GET) */
|
||||
host1x_ch_writel(ch, getptr, HOST1X_CHANNEL_DMAPUT);
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP |
|
||||
HOST1X_CHANNEL_DMACTRL_DMAGETRST |
|
||||
HOST1X_CHANNEL_DMACTRL_DMAINITGET,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
dev_dbg(host1x->dev,
|
||||
"%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n", __func__,
|
||||
host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET),
|
||||
host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT),
|
||||
cdma->last_pos);
|
||||
|
||||
/* deassert GET reset and set PUT */
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
host1x_ch_writel(ch, cdma->push_buffer.pos, HOST1X_CHANNEL_DMAPUT);
|
||||
|
||||
/* start the command DMA */
|
||||
host1x_ch_writel(ch, 0, HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
cdma->running = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kick channel DMA into action by writing its PUT offset (if it has changed)
|
||||
*/
|
||||
static void cdma_flush(struct host1x_cdma *cdma)
|
||||
{
|
||||
struct host1x_channel *ch = cdma_to_channel(cdma);
|
||||
|
||||
if (cdma->push_buffer.pos != cdma->last_pos) {
|
||||
host1x_ch_writel(ch, cdma->push_buffer.pos,
|
||||
HOST1X_CHANNEL_DMAPUT);
|
||||
cdma->last_pos = cdma->push_buffer.pos;
|
||||
}
|
||||
}
|
||||
|
||||
static void cdma_stop(struct host1x_cdma *cdma)
|
||||
{
|
||||
struct host1x_channel *ch = cdma_to_channel(cdma);
|
||||
|
||||
mutex_lock(&cdma->lock);
|
||||
if (cdma->running) {
|
||||
host1x_cdma_wait_locked(cdma, CDMA_EVENT_SYNC_QUEUE_EMPTY);
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
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_freeze(struct host1x_cdma *cdma)
|
||||
{
|
||||
struct host1x *host = cdma_to_host1x(cdma);
|
||||
struct host1x_channel *ch = cdma_to_channel(cdma);
|
||||
u32 cmdproc_stop;
|
||||
|
||||
if (cdma->torndown && !cdma->running) {
|
||||
dev_warn(host->dev, "Already torn down\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(host->dev, "freezing channel (id %d)\n", ch->id);
|
||||
|
||||
cmdproc_stop = host1x_sync_readl(host, HOST1X_SYNC_CMDPROC_STOP);
|
||||
cmdproc_stop |= BIT(ch->id);
|
||||
host1x_sync_writel(host, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
|
||||
|
||||
dev_dbg(host->dev, "%s: DMA GET 0x%x, PUT HW 0x%x / shadow 0x%x\n",
|
||||
__func__, host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET),
|
||||
host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT),
|
||||
cdma->last_pos);
|
||||
|
||||
host1x_ch_writel(ch, HOST1X_CHANNEL_DMACTRL_DMASTOP,
|
||||
HOST1X_CHANNEL_DMACTRL);
|
||||
|
||||
host1x_sync_writel(host, BIT(ch->id), HOST1X_SYNC_CH_TEARDOWN);
|
||||
|
||||
cdma->running = false;
|
||||
cdma->torndown = true;
|
||||
}
|
||||
|
||||
static void cdma_resume(struct host1x_cdma *cdma, u32 getptr)
|
||||
{
|
||||
struct host1x *host1x = cdma_to_host1x(cdma);
|
||||
struct host1x_channel *ch = cdma_to_channel(cdma);
|
||||
u32 cmdproc_stop;
|
||||
|
||||
dev_dbg(host1x->dev,
|
||||
"resuming channel (id %d, DMAGET restart = 0x%x)\n",
|
||||
ch->id, getptr);
|
||||
|
||||
cmdproc_stop = host1x_sync_readl(host1x, HOST1X_SYNC_CMDPROC_STOP);
|
||||
cmdproc_stop &= ~(BIT(ch->id));
|
||||
host1x_sync_writel(host1x, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
|
||||
|
||||
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 host1x_cdma *cdma;
|
||||
struct host1x *host1x;
|
||||
struct host1x_channel *ch;
|
||||
|
||||
u32 syncpt_val;
|
||||
|
||||
u32 prev_cmdproc, cmdproc_stop;
|
||||
|
||||
cdma = container_of(to_delayed_work(work), struct host1x_cdma,
|
||||
timeout.wq);
|
||||
host1x = cdma_to_host1x(cdma);
|
||||
ch = cdma_to_channel(cdma);
|
||||
|
||||
host1x_debug_dump(cdma_to_host1x(cdma));
|
||||
|
||||
mutex_lock(&cdma->lock);
|
||||
|
||||
if (!cdma->timeout.client) {
|
||||
dev_dbg(host1x->dev,
|
||||
"cdma_timeout: expired, but has no clientid\n");
|
||||
mutex_unlock(&cdma->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop processing to get a clean snapshot */
|
||||
prev_cmdproc = host1x_sync_readl(host1x, HOST1X_SYNC_CMDPROC_STOP);
|
||||
cmdproc_stop = prev_cmdproc | BIT(ch->id);
|
||||
host1x_sync_writel(host1x, cmdproc_stop, HOST1X_SYNC_CMDPROC_STOP);
|
||||
|
||||
dev_dbg(host1x->dev, "cdma_timeout: cmdproc was 0x%x is 0x%x\n",
|
||||
prev_cmdproc, cmdproc_stop);
|
||||
|
||||
syncpt_val = host1x_syncpt_load(cdma->timeout.syncpt);
|
||||
|
||||
/* has buffer actually completed? */
|
||||
if ((s32)(syncpt_val - cdma->timeout.syncpt_val) >= 0) {
|
||||
dev_dbg(host1x->dev,
|
||||
"cdma_timeout: expired, but buffer had completed\n");
|
||||
/* restore */
|
||||
cmdproc_stop = prev_cmdproc & ~(BIT(ch->id));
|
||||
host1x_sync_writel(host1x, cmdproc_stop,
|
||||
HOST1X_SYNC_CMDPROC_STOP);
|
||||
mutex_unlock(&cdma->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_warn(host1x->dev, "%s: timeout: %d (%s), HW thresh %d, done %d\n",
|
||||
__func__, cdma->timeout.syncpt->id, cdma->timeout.syncpt->name,
|
||||
syncpt_val, cdma->timeout.syncpt_val);
|
||||
|
||||
/* stop HW, resetting channel/module */
|
||||
host1x_hw_cdma_freeze(host1x, cdma);
|
||||
|
||||
host1x_cdma_update_sync_queue(cdma, ch->dev);
|
||||
mutex_unlock(&cdma->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init timeout resources
|
||||
*/
|
||||
static int cdma_timeout_init(struct host1x_cdma *cdma, u32 syncpt_id)
|
||||
{
|
||||
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 host1x_cdma *cdma)
|
||||
{
|
||||
if (cdma->timeout.initialized)
|
||||
cancel_delayed_work(&cdma->timeout.wq);
|
||||
cdma->timeout.initialized = false;
|
||||
}
|
||||
|
||||
static const struct host1x_cdma_ops host1x_cdma_ops = {
|
||||
.start = cdma_start,
|
||||
.stop = cdma_stop,
|
||||
.flush = cdma_flush,
|
||||
|
||||
.timeout_init = cdma_timeout_init,
|
||||
.timeout_destroy = cdma_timeout_destroy,
|
||||
.freeze = cdma_freeze,
|
||||
.resume = cdma_resume,
|
||||
.timeout_cpu_incr = cdma_timeout_cpu_incr,
|
||||
};
|
||||
|
||||
static const struct host1x_pushbuffer_ops host1x_pushbuffer_ops = {
|
||||
.init = push_buffer_init,
|
||||
};
|
188
drivers/gpu/host1x/hw/channel_hw.c
Normal file
188
drivers/gpu/host1x/hw/channel_hw.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Tegra host1x Channel
|
||||
*
|
||||
* 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/host1x.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <trace/events/host1x.h>
|
||||
|
||||
#include "../channel.h"
|
||||
#include "../dev.h"
|
||||
#include "../intr.h"
|
||||
#include "../job.h"
|
||||
|
||||
#define HOST1X_CHANNEL_SIZE 16384
|
||||
#define TRACE_MAX_LENGTH 128U
|
||||
|
||||
static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
|
||||
u32 offset, u32 words)
|
||||
{
|
||||
void *mem = NULL;
|
||||
|
||||
if (host1x_debug_trace_cmdbuf)
|
||||
mem = host1x_bo_mmap(bo);
|
||||
|
||||
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_host1x_cdma_push_gather(
|
||||
dev_name(cdma_to_channel(cdma)->dev),
|
||||
(u32)bo, min(words - i, TRACE_MAX_LENGTH),
|
||||
offset + i * sizeof(u32), mem);
|
||||
}
|
||||
host1x_bo_munmap(bo, mem);
|
||||
}
|
||||
}
|
||||
|
||||
static void submit_gathers(struct host1x_job *job)
|
||||
{
|
||||
struct host1x_cdma *cdma = &job->channel->cdma;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < job->num_gathers; i++) {
|
||||
struct host1x_job_gather *g = &job->gathers[i];
|
||||
u32 op1 = host1x_opcode_gather(g->words);
|
||||
u32 op2 = g->base + g->offset;
|
||||
trace_write_gather(cdma, g->bo, g->offset, op1 & 0xffff);
|
||||
host1x_cdma_push(cdma, op1, op2);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void synchronize_syncpt_base(struct host1x_job *job)
|
||||
{
|
||||
struct host1x *host = dev_get_drvdata(job->channel->dev->parent);
|
||||
struct host1x_syncpt *sp = host->syncpt + job->syncpt_id;
|
||||
u32 id, value;
|
||||
|
||||
value = host1x_syncpt_read_max(sp);
|
||||
id = sp->base->id;
|
||||
|
||||
host1x_cdma_push(&job->channel->cdma,
|
||||
host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
|
||||
HOST1X_UCLASS_LOAD_SYNCPT_BASE, 1),
|
||||
HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(id) |
|
||||
HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(value));
|
||||
}
|
||||
|
||||
static int channel_submit(struct host1x_job *job)
|
||||
{
|
||||
struct host1x_channel *ch = job->channel;
|
||||
struct host1x_syncpt *sp;
|
||||
u32 user_syncpt_incrs = job->syncpt_incrs;
|
||||
u32 prev_max = 0;
|
||||
u32 syncval;
|
||||
int err;
|
||||
struct host1x_waitlist *completed_waiter = NULL;
|
||||
struct host1x *host = dev_get_drvdata(ch->dev->parent);
|
||||
|
||||
sp = host->syncpt + job->syncpt_id;
|
||||
trace_host1x_channel_submit(dev_name(ch->dev),
|
||||
job->num_gathers, job->num_relocs,
|
||||
job->num_waitchk, job->syncpt_id,
|
||||
job->syncpt_incrs);
|
||||
|
||||
/* before error checks, return current max */
|
||||
prev_max = job->syncpt_end = host1x_syncpt_read_max(sp);
|
||||
|
||||
/* get submit lock */
|
||||
err = mutex_lock_interruptible(&ch->submitlock);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
completed_waiter = kzalloc(sizeof(*completed_waiter), GFP_KERNEL);
|
||||
if (!completed_waiter) {
|
||||
mutex_unlock(&ch->submitlock);
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* begin a CDMA submit */
|
||||
err = host1x_cdma_begin(&ch->cdma, job);
|
||||
if (err) {
|
||||
mutex_unlock(&ch->submitlock);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (job->serialize) {
|
||||
/*
|
||||
* Force serialization by inserting a host wait for the
|
||||
* previous job to finish before this one can commence.
|
||||
*/
|
||||
host1x_cdma_push(&ch->cdma,
|
||||
host1x_opcode_setclass(HOST1X_CLASS_HOST1X,
|
||||
host1x_uclass_wait_syncpt_r(), 1),
|
||||
host1x_class_host_wait_syncpt(job->syncpt_id,
|
||||
host1x_syncpt_read_max(sp)));
|
||||
}
|
||||
|
||||
/* Synchronize base register to allow using it for relative waiting */
|
||||
if (sp->base)
|
||||
synchronize_syncpt_base(job);
|
||||
|
||||
syncval = host1x_syncpt_incr_max(sp, user_syncpt_incrs);
|
||||
|
||||
job->syncpt_end = syncval;
|
||||
|
||||
/* add a setclass for modules that require it */
|
||||
if (job->class)
|
||||
host1x_cdma_push(&ch->cdma,
|
||||
host1x_opcode_setclass(job->class, 0, 0),
|
||||
HOST1X_OPCODE_NOP);
|
||||
|
||||
submit_gathers(job);
|
||||
|
||||
/* end CDMA submit & stash pinned hMems into sync queue */
|
||||
host1x_cdma_end(&ch->cdma, job);
|
||||
|
||||
trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval);
|
||||
|
||||
/* schedule a submit complete interrupt */
|
||||
err = host1x_intr_add_action(host, job->syncpt_id, syncval,
|
||||
HOST1X_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 int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
|
||||
unsigned int index)
|
||||
{
|
||||
ch->id = index;
|
||||
mutex_init(&ch->reflock);
|
||||
mutex_init(&ch->submitlock);
|
||||
|
||||
ch->regs = dev->regs + index * HOST1X_CHANNEL_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct host1x_channel_ops host1x_channel_ops = {
|
||||
.init = host1x_channel_init,
|
||||
.submit = channel_submit,
|
||||
};
|
314
drivers/gpu/host1x/hw/debug_hw.c
Normal file
314
drivers/gpu/host1x/hw/debug_hw.c
Normal file
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Erik Gilling <konkers@android.com>
|
||||
*
|
||||
* Copyright (C) 2011-2013 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 "../dev.h"
|
||||
#include "../debug.h"
|
||||
#include "../cdma.h"
|
||||
#include "../channel.h"
|
||||
|
||||
#define HOST1X_DEBUG_MAX_PAGE_OFFSET 102400
|
||||
|
||||
enum {
|
||||
HOST1X_OPCODE_SETCLASS = 0x00,
|
||||
HOST1X_OPCODE_INCR = 0x01,
|
||||
HOST1X_OPCODE_NONINCR = 0x02,
|
||||
HOST1X_OPCODE_MASK = 0x03,
|
||||
HOST1X_OPCODE_IMM = 0x04,
|
||||
HOST1X_OPCODE_RESTART = 0x05,
|
||||
HOST1X_OPCODE_GATHER = 0x06,
|
||||
HOST1X_OPCODE_EXTEND = 0x0e,
|
||||
};
|
||||
|
||||
enum {
|
||||
HOST1X_OPCODE_EXTEND_ACQUIRE_MLOCK = 0x00,
|
||||
HOST1X_OPCODE_EXTEND_RELEASE_MLOCK = 0x01,
|
||||
};
|
||||
|
||||
static unsigned int show_channel_command(struct output *o, u32 val)
|
||||
{
|
||||
unsigned mask;
|
||||
unsigned subop;
|
||||
|
||||
switch (val >> 28) {
|
||||
case HOST1X_OPCODE_SETCLASS:
|
||||
mask = val & 0x3f;
|
||||
if (mask) {
|
||||
host1x_debug_output(o, "SETCL(class=%03x, offset=%03x, mask=%02x, [",
|
||||
val >> 6 & 0x3ff,
|
||||
val >> 16 & 0xfff, mask);
|
||||
return hweight8(mask);
|
||||
} else {
|
||||
host1x_debug_output(o, "SETCL(class=%03x)\n",
|
||||
val >> 6 & 0x3ff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case HOST1X_OPCODE_INCR:
|
||||
host1x_debug_output(o, "INCR(offset=%03x, [",
|
||||
val >> 16 & 0xfff);
|
||||
return val & 0xffff;
|
||||
|
||||
case HOST1X_OPCODE_NONINCR:
|
||||
host1x_debug_output(o, "NONINCR(offset=%03x, [",
|
||||
val >> 16 & 0xfff);
|
||||
return val & 0xffff;
|
||||
|
||||
case HOST1X_OPCODE_MASK:
|
||||
mask = val & 0xffff;
|
||||
host1x_debug_output(o, "MASK(offset=%03x, mask=%03x, [",
|
||||
val >> 16 & 0xfff, mask);
|
||||
return hweight16(mask);
|
||||
|
||||
case HOST1X_OPCODE_IMM:
|
||||
host1x_debug_output(o, "IMM(offset=%03x, data=%03x)\n",
|
||||
val >> 16 & 0xfff, val & 0xffff);
|
||||
return 0;
|
||||
|
||||
case HOST1X_OPCODE_RESTART:
|
||||
host1x_debug_output(o, "RESTART(offset=%08x)\n", val << 4);
|
||||
return 0;
|
||||
|
||||
case HOST1X_OPCODE_GATHER:
|
||||
host1x_debug_output(o, "GATHER(offset=%03x, insert=%d, type=%d, count=%04x, addr=[",
|
||||
val >> 16 & 0xfff, val >> 15 & 0x1,
|
||||
val >> 14 & 0x1, val & 0x3fff);
|
||||
return 1;
|
||||
|
||||
case HOST1X_OPCODE_EXTEND:
|
||||
subop = val >> 24 & 0xf;
|
||||
if (subop == HOST1X_OPCODE_EXTEND_ACQUIRE_MLOCK)
|
||||
host1x_debug_output(o, "ACQUIRE_MLOCK(index=%d)\n",
|
||||
val & 0xff);
|
||||
else if (subop == HOST1X_OPCODE_EXTEND_RELEASE_MLOCK)
|
||||
host1x_debug_output(o, "RELEASE_MLOCK(index=%d)\n",
|
||||
val & 0xff);
|
||||
else
|
||||
host1x_debug_output(o, "EXTEND_UNKNOWN(%08x)\n", val);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void show_gather(struct output *o, phys_addr_t phys_addr,
|
||||
unsigned int words, struct host1x_cdma *cdma,
|
||||
phys_addr_t pin_addr, u32 *map_addr)
|
||||
{
|
||||
/* Map dmaget cursor to corresponding mem handle */
|
||||
u32 offset = phys_addr - pin_addr;
|
||||
unsigned int data_count = 0, i;
|
||||
|
||||
/*
|
||||
* 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 > HOST1X_DEBUG_MAX_PAGE_OFFSET) {
|
||||
host1x_debug_output(o, "[address mismatch]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < words; i++) {
|
||||
u32 addr = phys_addr + i * 4;
|
||||
u32 val = *(map_addr + offset / 4 + i);
|
||||
|
||||
if (!data_count) {
|
||||
host1x_debug_output(o, "%08x: %08x:", addr, val);
|
||||
data_count = show_channel_command(o, val);
|
||||
} else {
|
||||
host1x_debug_output(o, "%08x%s", val,
|
||||
data_count > 0 ? ", " : "])\n");
|
||||
data_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma)
|
||||
{
|
||||
struct host1x_job *job;
|
||||
|
||||
list_for_each_entry(job, &cdma->sync_queue, list) {
|
||||
int i;
|
||||
host1x_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 host1x_job_gather *g = &job->gathers[i];
|
||||
u32 *mapped;
|
||||
|
||||
if (job->gather_copy_mapped)
|
||||
mapped = (u32 *)job->gather_copy_mapped;
|
||||
else
|
||||
mapped = host1x_bo_mmap(g->bo);
|
||||
|
||||
if (!mapped) {
|
||||
host1x_debug_output(o, "[could not mmap]\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
host1x_debug_output(o, " GATHER at %#llx+%04x, %d words\n",
|
||||
(u64)g->base, g->offset, g->words);
|
||||
|
||||
show_gather(o, g->base + g->offset, g->words, cdma,
|
||||
g->base, mapped);
|
||||
|
||||
if (!job->gather_copy_mapped)
|
||||
host1x_bo_munmap(g->bo, mapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void host1x_debug_show_channel_cdma(struct host1x *host,
|
||||
struct host1x_channel *ch,
|
||||
struct output *o)
|
||||
{
|
||||
struct host1x_cdma *cdma = &ch->cdma;
|
||||
u32 dmaput, dmaget, dmactrl;
|
||||
u32 cbstat, cbread;
|
||||
u32 val, base, baseval;
|
||||
|
||||
dmaput = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT);
|
||||
dmaget = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET);
|
||||
dmactrl = host1x_ch_readl(ch, HOST1X_CHANNEL_DMACTRL);
|
||||
cbread = host1x_sync_readl(host, HOST1X_SYNC_CBREAD(ch->id));
|
||||
cbstat = host1x_sync_readl(host, HOST1X_SYNC_CBSTAT(ch->id));
|
||||
|
||||
host1x_debug_output(o, "%d-%s: ", ch->id, dev_name(ch->dev));
|
||||
|
||||
if (HOST1X_CHANNEL_DMACTRL_DMASTOP_V(dmactrl) ||
|
||||
!ch->cdma.push_buffer.mapped) {
|
||||
host1x_debug_output(o, "inactive\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (HOST1X_SYNC_CBSTAT_CBCLASS_V(cbstat) == HOST1X_CLASS_HOST1X &&
|
||||
HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat) ==
|
||||
HOST1X_UCLASS_WAIT_SYNCPT)
|
||||
host1x_debug_output(o, "waiting on syncpt %d val %d\n",
|
||||
cbread >> 24, cbread & 0xffffff);
|
||||
else if (HOST1X_SYNC_CBSTAT_CBCLASS_V(cbstat) ==
|
||||
HOST1X_CLASS_HOST1X &&
|
||||
HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat) ==
|
||||
HOST1X_UCLASS_WAIT_SYNCPT_BASE) {
|
||||
|
||||
base = (cbread >> 16) & 0xff;
|
||||
baseval =
|
||||
host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_BASE(base));
|
||||
val = cbread & 0xffff;
|
||||
host1x_debug_output(o, "waiting on syncpt %d val %d (base %d = %d; offset = %d)\n",
|
||||
cbread >> 24, baseval + val, base,
|
||||
baseval, val);
|
||||
} else
|
||||
host1x_debug_output(o, "active class %02x, offset %04x, val %08x\n",
|
||||
HOST1X_SYNC_CBSTAT_CBCLASS_V(cbstat),
|
||||
HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat),
|
||||
cbread);
|
||||
|
||||
host1x_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n",
|
||||
dmaput, dmaget, dmactrl);
|
||||
host1x_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat);
|
||||
|
||||
show_channel_gathers(o, cdma);
|
||||
host1x_debug_output(o, "\n");
|
||||
}
|
||||
|
||||
static void host1x_debug_show_channel_fifo(struct host1x *host,
|
||||
struct host1x_channel *ch,
|
||||
struct output *o)
|
||||
{
|
||||
u32 val, rd_ptr, wr_ptr, start, end;
|
||||
unsigned int data_count = 0;
|
||||
|
||||
host1x_debug_output(o, "%d: fifo:\n", ch->id);
|
||||
|
||||
val = host1x_ch_readl(ch, HOST1X_CHANNEL_FIFOSTAT);
|
||||
host1x_debug_output(o, "FIFOSTAT %08x\n", val);
|
||||
if (HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(val)) {
|
||||
host1x_debug_output(o, "[empty]\n");
|
||||
return;
|
||||
}
|
||||
|
||||
host1x_sync_writel(host, 0x0, HOST1X_SYNC_CFPEEK_CTRL);
|
||||
host1x_sync_writel(host, HOST1X_SYNC_CFPEEK_CTRL_ENA_F(1) |
|
||||
HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(ch->id),
|
||||
HOST1X_SYNC_CFPEEK_CTRL);
|
||||
|
||||
val = host1x_sync_readl(host, HOST1X_SYNC_CFPEEK_PTRS);
|
||||
rd_ptr = HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(val);
|
||||
wr_ptr = HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(val);
|
||||
|
||||
val = host1x_sync_readl(host, HOST1X_SYNC_CF_SETUP(ch->id));
|
||||
start = HOST1X_SYNC_CF_SETUP_BASE_V(val);
|
||||
end = HOST1X_SYNC_CF_SETUP_LIMIT_V(val);
|
||||
|
||||
do {
|
||||
host1x_sync_writel(host, 0x0, HOST1X_SYNC_CFPEEK_CTRL);
|
||||
host1x_sync_writel(host, HOST1X_SYNC_CFPEEK_CTRL_ENA_F(1) |
|
||||
HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(ch->id) |
|
||||
HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(rd_ptr),
|
||||
HOST1X_SYNC_CFPEEK_CTRL);
|
||||
val = host1x_sync_readl(host, HOST1X_SYNC_CFPEEK_READ);
|
||||
|
||||
if (!data_count) {
|
||||
host1x_debug_output(o, "%08x:", val);
|
||||
data_count = show_channel_command(o, val);
|
||||
} else {
|
||||
host1x_debug_output(o, "%08x%s", val,
|
||||
data_count > 0 ? ", " : "])\n");
|
||||
data_count--;
|
||||
}
|
||||
|
||||
if (rd_ptr == end)
|
||||
rd_ptr = start;
|
||||
else
|
||||
rd_ptr++;
|
||||
} while (rd_ptr != wr_ptr);
|
||||
|
||||
if (data_count)
|
||||
host1x_debug_output(o, ", ...])\n");
|
||||
host1x_debug_output(o, "\n");
|
||||
|
||||
host1x_sync_writel(host, 0x0, HOST1X_SYNC_CFPEEK_CTRL);
|
||||
}
|
||||
|
||||
static void host1x_debug_show_mlocks(struct host1x *host, struct output *o)
|
||||
{
|
||||
int i;
|
||||
|
||||
host1x_debug_output(o, "---- mlocks ----\n");
|
||||
for (i = 0; i < host1x_syncpt_nb_mlocks(host); i++) {
|
||||
u32 owner =
|
||||
host1x_sync_readl(host, HOST1X_SYNC_MLOCK_OWNER(i));
|
||||
if (HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(owner))
|
||||
host1x_debug_output(o, "%d: locked by channel %d\n",
|
||||
i, HOST1X_SYNC_MLOCK_OWNER_CHID_F(owner));
|
||||
else if (HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(owner))
|
||||
host1x_debug_output(o, "%d: locked by cpu\n", i);
|
||||
else
|
||||
host1x_debug_output(o, "%d: unlocked\n", i);
|
||||
}
|
||||
host1x_debug_output(o, "\n");
|
||||
}
|
||||
|
||||
static const struct host1x_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,
|
||||
};
|
42
drivers/gpu/host1x/hw/host1x01.c
Normal file
42
drivers/gpu/host1x/hw/host1x01.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 hw specification */
|
||||
#include "host1x01.h"
|
||||
#include "host1x01_hardware.h"
|
||||
|
||||
/* include code */
|
||||
#include "cdma_hw.c"
|
||||
#include "channel_hw.c"
|
||||
#include "debug_hw.c"
|
||||
#include "intr_hw.c"
|
||||
#include "syncpt_hw.c"
|
||||
|
||||
#include "../dev.h"
|
||||
|
||||
int host1x01_init(struct host1x *host)
|
||||
{
|
||||
host->channel_op = &host1x_channel_ops;
|
||||
host->cdma_op = &host1x_cdma_ops;
|
||||
host->cdma_pb_op = &host1x_pushbuffer_ops;
|
||||
host->syncpt_op = &host1x_syncpt_ops;
|
||||
host->intr_op = &host1x_intr_ops;
|
||||
host->debug_op = &host1x_debug_ops;
|
||||
|
||||
return 0;
|
||||
}
|
25
drivers/gpu/host1x/hw/host1x01.h
Normal file
25
drivers/gpu/host1x/hw/host1x01.h
Normal file
|
@ -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 *host);
|
||||
|
||||
#endif /* HOST1X_HOST1X01_H_ */
|
143
drivers/gpu/host1x/hw/host1x01_hardware.h
Normal file
143
drivers/gpu/host1x/hw/host1x01_hardware.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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_channel.h"
|
||||
#include "hw_host1x01_sync.h"
|
||||
#include "hw_host1x01_uclass.h"
|
||||
|
||||
static inline u32 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_opcode_setclass(
|
||||
unsigned class_id, unsigned offset, unsigned mask)
|
||||
{
|
||||
return (0 << 28) | (offset << 16) | (class_id << 6) | mask;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_incr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (1 << 28) | (offset << 16) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (2 << 28) | (offset << 16) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask)
|
||||
{
|
||||
return (3 << 28) | (offset << 16) | mask;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_imm(unsigned offset, unsigned value)
|
||||
{
|
||||
return (4 << 28) | (offset << 16) | value;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx)
|
||||
{
|
||||
return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(),
|
||||
host1x_class_host_incr_syncpt(cond, indx));
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_restart(unsigned address)
|
||||
{
|
||||
return (5 << 28) | (address >> 4);
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather(unsigned count)
|
||||
{
|
||||
return (6 << 28) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (6 << 28) | (offset << 16) | BIT(15) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
|
||||
}
|
||||
|
||||
#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
|
||||
|
||||
#endif
|
42
drivers/gpu/host1x/hw/host1x02.c
Normal file
42
drivers/gpu/host1x/hw/host1x02.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Host1x init for Tegra114 SoCs
|
||||
*
|
||||
* Copyright (c) 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 hw specification */
|
||||
#include "host1x02.h"
|
||||
#include "host1x02_hardware.h"
|
||||
|
||||
/* include code */
|
||||
#include "cdma_hw.c"
|
||||
#include "channel_hw.c"
|
||||
#include "debug_hw.c"
|
||||
#include "intr_hw.c"
|
||||
#include "syncpt_hw.c"
|
||||
|
||||
#include "../dev.h"
|
||||
|
||||
int host1x02_init(struct host1x *host)
|
||||
{
|
||||
host->channel_op = &host1x_channel_ops;
|
||||
host->cdma_op = &host1x_cdma_ops;
|
||||
host->cdma_pb_op = &host1x_pushbuffer_ops;
|
||||
host->syncpt_op = &host1x_syncpt_ops;
|
||||
host->intr_op = &host1x_intr_ops;
|
||||
host->debug_op = &host1x_debug_ops;
|
||||
|
||||
return 0;
|
||||
}
|
26
drivers/gpu/host1x/hw/host1x02.h
Normal file
26
drivers/gpu/host1x/hw/host1x02.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Host1x init for Tegra114 SoCs
|
||||
*
|
||||
* Copyright (c) 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_HOST1X02_H
|
||||
#define HOST1X_HOST1X02_H
|
||||
|
||||
struct host1x;
|
||||
|
||||
int host1x02_init(struct host1x *host);
|
||||
|
||||
#endif
|
142
drivers/gpu/host1x/hw/host1x02_hardware.h
Normal file
142
drivers/gpu/host1x/hw/host1x02_hardware.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Tegra host1x Register Offsets for Tegra114
|
||||
*
|
||||
* 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_HOST1X02_HARDWARE_H
|
||||
#define __HOST1X_HOST1X02_HARDWARE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "hw_host1x02_channel.h"
|
||||
#include "hw_host1x02_sync.h"
|
||||
#include "hw_host1x02_uclass.h"
|
||||
|
||||
static inline u32 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_opcode_setclass(
|
||||
unsigned class_id, unsigned offset, unsigned mask)
|
||||
{
|
||||
return (0 << 28) | (offset << 16) | (class_id << 6) | mask;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_incr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (1 << 28) | (offset << 16) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (2 << 28) | (offset << 16) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask)
|
||||
{
|
||||
return (3 << 28) | (offset << 16) | mask;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_imm(unsigned offset, unsigned value)
|
||||
{
|
||||
return (4 << 28) | (offset << 16) | value;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx)
|
||||
{
|
||||
return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(),
|
||||
host1x_class_host_incr_syncpt(cond, indx));
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_restart(unsigned address)
|
||||
{
|
||||
return (5 << 28) | (address >> 4);
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather(unsigned count)
|
||||
{
|
||||
return (6 << 28) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (6 << 28) | (offset << 16) | BIT(15) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
|
||||
}
|
||||
|
||||
#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
|
||||
|
||||
#endif
|
42
drivers/gpu/host1x/hw/host1x04.c
Normal file
42
drivers/gpu/host1x/hw/host1x04.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Host1x init for Tegra124 SoCs
|
||||
*
|
||||
* Copyright (c) 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 hw specification */
|
||||
#include "host1x04.h"
|
||||
#include "host1x04_hardware.h"
|
||||
|
||||
/* include code */
|
||||
#include "cdma_hw.c"
|
||||
#include "channel_hw.c"
|
||||
#include "debug_hw.c"
|
||||
#include "intr_hw.c"
|
||||
#include "syncpt_hw.c"
|
||||
|
||||
#include "../dev.h"
|
||||
|
||||
int host1x04_init(struct host1x *host)
|
||||
{
|
||||
host->channel_op = &host1x_channel_ops;
|
||||
host->cdma_op = &host1x_cdma_ops;
|
||||
host->cdma_pb_op = &host1x_pushbuffer_ops;
|
||||
host->syncpt_op = &host1x_syncpt_ops;
|
||||
host->intr_op = &host1x_intr_ops;
|
||||
host->debug_op = &host1x_debug_ops;
|
||||
|
||||
return 0;
|
||||
}
|
26
drivers/gpu/host1x/hw/host1x04.h
Normal file
26
drivers/gpu/host1x/hw/host1x04.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Host1x init for Tegra124 SoCs
|
||||
*
|
||||
* Copyright (c) 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_HOST1X04_H
|
||||
#define HOST1X_HOST1X04_H
|
||||
|
||||
struct host1x;
|
||||
|
||||
int host1x04_init(struct host1x *host);
|
||||
|
||||
#endif
|
142
drivers/gpu/host1x/hw/host1x04_hardware.h
Normal file
142
drivers/gpu/host1x/hw/host1x04_hardware.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Tegra host1x Register Offsets for Tegra124
|
||||
*
|
||||
* 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_HOST1X04_HARDWARE_H
|
||||
#define __HOST1X_HOST1X04_HARDWARE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include "hw_host1x04_channel.h"
|
||||
#include "hw_host1x04_sync.h"
|
||||
#include "hw_host1x04_uclass.h"
|
||||
|
||||
static inline u32 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_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 host1x_opcode_setclass(
|
||||
unsigned class_id, unsigned offset, unsigned mask)
|
||||
{
|
||||
return (0 << 28) | (offset << 16) | (class_id << 6) | mask;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_incr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (1 << 28) | (offset << 16) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_nonincr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (2 << 28) | (offset << 16) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_mask(unsigned offset, unsigned mask)
|
||||
{
|
||||
return (3 << 28) | (offset << 16) | mask;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_imm(unsigned offset, unsigned value)
|
||||
{
|
||||
return (4 << 28) | (offset << 16) | value;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_imm_incr_syncpt(unsigned cond, unsigned indx)
|
||||
{
|
||||
return host1x_opcode_imm(host1x_uclass_incr_syncpt_r(),
|
||||
host1x_class_host_incr_syncpt(cond, indx));
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_restart(unsigned address)
|
||||
{
|
||||
return (5 << 28) | (address >> 4);
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather(unsigned count)
|
||||
{
|
||||
return (6 << 28) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather_nonincr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (6 << 28) | (offset << 16) | BIT(15) | count;
|
||||
}
|
||||
|
||||
static inline u32 host1x_opcode_gather_incr(unsigned offset, unsigned count)
|
||||
{
|
||||
return (6 << 28) | (offset << 16) | BIT(15) | BIT(14) | count;
|
||||
}
|
||||
|
||||
#define HOST1X_OPCODE_NOP host1x_opcode_nonincr(0, 0)
|
||||
|
||||
#endif
|
120
drivers/gpu/host1x/hw/hw_host1x01_channel.h
Normal file
120
drivers/gpu/host1x/hw/hw_host1x01_channel.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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_host1x_channel_host1x_h__
|
||||
#define __hw_host1x_channel_host1x_h__
|
||||
|
||||
static inline u32 host1x_channel_fifostat_r(void)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
#define HOST1X_CHANNEL_FIFOSTAT \
|
||||
host1x_channel_fifostat_r()
|
||||
static inline u32 host1x_channel_fifostat_cfempty_v(u32 r)
|
||||
{
|
||||
return (r >> 10) & 0x1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(r) \
|
||||
host1x_channel_fifostat_cfempty_v(r)
|
||||
static inline u32 host1x_channel_dmastart_r(void)
|
||||
{
|
||||
return 0x14;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMASTART \
|
||||
host1x_channel_dmastart_r()
|
||||
static inline u32 host1x_channel_dmaput_r(void)
|
||||
{
|
||||
return 0x18;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAPUT \
|
||||
host1x_channel_dmaput_r()
|
||||
static inline u32 host1x_channel_dmaget_r(void)
|
||||
{
|
||||
return 0x1c;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAGET \
|
||||
host1x_channel_dmaget_r()
|
||||
static inline u32 host1x_channel_dmaend_r(void)
|
||||
{
|
||||
return 0x20;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAEND \
|
||||
host1x_channel_dmaend_r()
|
||||
static inline u32 host1x_channel_dmactrl_r(void)
|
||||
{
|
||||
return 0x24;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL \
|
||||
host1x_channel_dmactrl_r()
|
||||
static inline u32 host1x_channel_dmactrl_dmastop(void)
|
||||
{
|
||||
return 1 << 0;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMASTOP \
|
||||
host1x_channel_dmactrl_dmastop()
|
||||
static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMASTOP_V(r) \
|
||||
host1x_channel_dmactrl_dmastop_v(r)
|
||||
static inline u32 host1x_channel_dmactrl_dmagetrst(void)
|
||||
{
|
||||
return 1 << 1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \
|
||||
host1x_channel_dmactrl_dmagetrst()
|
||||
static inline u32 host1x_channel_dmactrl_dmainitget(void)
|
||||
{
|
||||
return 1 << 2;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \
|
||||
host1x_channel_dmactrl_dmainitget()
|
||||
#endif
|
243
drivers/gpu/host1x/hw/hw_host1x01_sync.h
Normal file
243
drivers/gpu/host1x/hw/hw_host1x01_sync.h
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* 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__
|
||||
|
||||
#define REGISTER_STRIDE 4
|
||||
|
||||
static inline u32 host1x_sync_syncpt_r(unsigned int id)
|
||||
{
|
||||
return 0x400 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT(id) \
|
||||
host1x_sync_syncpt_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(unsigned int id)
|
||||
{
|
||||
return 0x40 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id) \
|
||||
host1x_sync_syncpt_thresh_cpu0_int_status_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_int_disable_r(unsigned int id)
|
||||
{
|
||||
return 0x60 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id) \
|
||||
host1x_sync_syncpt_thresh_int_disable_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id)
|
||||
{
|
||||
return 0x68 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \
|
||||
host1x_sync_syncpt_thresh_int_enable_cpu0_r(id)
|
||||
static inline u32 host1x_sync_cf_setup_r(unsigned int channel)
|
||||
{
|
||||
return 0x80 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP(channel) \
|
||||
host1x_sync_cf_setup_r(channel)
|
||||
static inline u32 host1x_sync_cf_setup_base_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP_BASE_V(r) \
|
||||
host1x_sync_cf_setup_base_v(r)
|
||||
static inline u32 host1x_sync_cf_setup_limit_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x1ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP_LIMIT_V(r) \
|
||||
host1x_sync_cf_setup_limit_v(r)
|
||||
static inline u32 host1x_sync_cmdproc_stop_r(void)
|
||||
{
|
||||
return 0xac;
|
||||
}
|
||||
#define HOST1X_SYNC_CMDPROC_STOP \
|
||||
host1x_sync_cmdproc_stop_r()
|
||||
static inline u32 host1x_sync_ch_teardown_r(void)
|
||||
{
|
||||
return 0xb0;
|
||||
}
|
||||
#define HOST1X_SYNC_CH_TEARDOWN \
|
||||
host1x_sync_ch_teardown_r()
|
||||
static inline u32 host1x_sync_usec_clk_r(void)
|
||||
{
|
||||
return 0x1a4;
|
||||
}
|
||||
#define HOST1X_SYNC_USEC_CLK \
|
||||
host1x_sync_usec_clk_r()
|
||||
static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void)
|
||||
{
|
||||
return 0x1a8;
|
||||
}
|
||||
#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG \
|
||||
host1x_sync_ctxsw_timeout_cfg_r()
|
||||
static inline u32 host1x_sync_ip_busy_timeout_r(void)
|
||||
{
|
||||
return 0x1bc;
|
||||
}
|
||||
#define HOST1X_SYNC_IP_BUSY_TIMEOUT \
|
||||
host1x_sync_ip_busy_timeout_r()
|
||||
static inline u32 host1x_sync_mlock_owner_r(unsigned int id)
|
||||
{
|
||||
return 0x340 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER(id) \
|
||||
host1x_sync_mlock_owner_r(id)
|
||||
static inline u32 host1x_sync_mlock_owner_chid_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 8;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CHID_F(v) \
|
||||
host1x_sync_mlock_owner_chid_f(v)
|
||||
static inline u32 host1x_sync_mlock_owner_cpu_owns_v(u32 r)
|
||||
{
|
||||
return (r >> 1) & 0x1;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(r) \
|
||||
host1x_sync_mlock_owner_cpu_owns_v(r)
|
||||
static inline u32 host1x_sync_mlock_owner_ch_owns_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(r) \
|
||||
host1x_sync_mlock_owner_ch_owns_v(r)
|
||||
static inline u32 host1x_sync_syncpt_int_thresh_r(unsigned int id)
|
||||
{
|
||||
return 0x500 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_INT_THRESH(id) \
|
||||
host1x_sync_syncpt_int_thresh_r(id)
|
||||
static inline u32 host1x_sync_syncpt_base_r(unsigned int id)
|
||||
{
|
||||
return 0x600 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_BASE(id) \
|
||||
host1x_sync_syncpt_base_r(id)
|
||||
static inline u32 host1x_sync_syncpt_cpu_incr_r(unsigned int id)
|
||||
{
|
||||
return 0x700 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_CPU_INCR(id) \
|
||||
host1x_sync_syncpt_cpu_incr_r(id)
|
||||
static inline u32 host1x_sync_cbread_r(unsigned int channel)
|
||||
{
|
||||
return 0x720 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CBREAD(channel) \
|
||||
host1x_sync_cbread_r(channel)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_r(void)
|
||||
{
|
||||
return 0x74c;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL \
|
||||
host1x_sync_cfpeek_ctrl_r()
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_addr_f(u32 v)
|
||||
{
|
||||
return (v & 0x1ff) << 0;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_addr_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_channr_f(u32 v)
|
||||
{
|
||||
return (v & 0x7) << 16;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_channr_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_ena_f(u32 v)
|
||||
{
|
||||
return (v & 0x1) << 31;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_ENA_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_ena_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_read_r(void)
|
||||
{
|
||||
return 0x750;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_READ \
|
||||
host1x_sync_cfpeek_read_r()
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_r(void)
|
||||
{
|
||||
return 0x754;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS \
|
||||
host1x_sync_cfpeek_ptrs_r()
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(r) \
|
||||
host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(r)
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x1ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(r) \
|
||||
host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(r)
|
||||
static inline u32 host1x_sync_cbstat_r(unsigned int channel)
|
||||
{
|
||||
return 0x758 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT(channel) \
|
||||
host1x_sync_cbstat_r(channel)
|
||||
static inline u32 host1x_sync_cbstat_cboffset_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0xffff;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT_CBOFFSET_V(r) \
|
||||
host1x_sync_cbstat_cboffset_v(r)
|
||||
static inline u32 host1x_sync_cbstat_cbclass_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT_CBCLASS_V(r) \
|
||||
host1x_sync_cbstat_cbclass_v(r)
|
||||
|
||||
#endif /* __hw_host1x01_sync_h__ */
|
180
drivers/gpu/host1x/hw/hw_host1x01_uclass.h
Normal file
180
drivers/gpu/host1x/hw/hw_host1x01_uclass.h
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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_host1x_uclass_host1x_h__
|
||||
#define __hw_host1x_uclass_host1x_h__
|
||||
|
||||
static inline u32 host1x_uclass_incr_syncpt_r(void)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT \
|
||||
host1x_uclass_incr_syncpt_r()
|
||||
static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 8;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
|
||||
host1x_uclass_incr_syncpt_cond_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
|
||||
host1x_uclass_incr_syncpt_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_r(void)
|
||||
{
|
||||
return 0x8;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT \
|
||||
host1x_uclass_wait_syncpt_r()
|
||||
static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
|
||||
host1x_uclass_wait_syncpt_thresh_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_r(void)
|
||||
{
|
||||
return 0x9;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
|
||||
host1x_uclass_wait_syncpt_base_r()
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 16;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_offset_f(v)
|
||||
static inline u32 host1x_uclass_load_syncpt_base_r(void)
|
||||
{
|
||||
return 0xb;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \
|
||||
host1x_uclass_load_syncpt_base_r()
|
||||
static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_load_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
|
||||
host1x_uclass_load_syncpt_base_value_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_incr_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
|
||||
host1x_uclass_incr_syncpt_base_offset_f(v)
|
||||
static inline u32 host1x_uclass_indoff_r(void)
|
||||
{
|
||||
return 0x2d;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF \
|
||||
host1x_uclass_indoff_r()
|
||||
static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 28;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
|
||||
host1x_uclass_indoff_indbe_f(v)
|
||||
static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
|
||||
{
|
||||
return (v & 0x1) << 27;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
|
||||
host1x_uclass_indoff_autoinc_f(v)
|
||||
static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 18;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
|
||||
host1x_uclass_indoff_indmodid_f(v)
|
||||
static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffff) << 2;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
|
||||
host1x_uclass_indoff_indroffset_f(v)
|
||||
static inline u32 host1x_uclass_indoff_rwn_read_v(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
|
||||
host1x_uclass_indoff_indroffset_f(v)
|
||||
#endif
|
121
drivers/gpu/host1x/hw/hw_host1x02_channel.h
Normal file
121
drivers/gpu/host1x/hw/hw_host1x02_channel.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 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 HOST1X_HW_HOST1X02_CHANNEL_H
|
||||
#define HOST1X_HW_HOST1X02_CHANNEL_H
|
||||
|
||||
static inline u32 host1x_channel_fifostat_r(void)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
#define HOST1X_CHANNEL_FIFOSTAT \
|
||||
host1x_channel_fifostat_r()
|
||||
static inline u32 host1x_channel_fifostat_cfempty_v(u32 r)
|
||||
{
|
||||
return (r >> 11) & 0x1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(r) \
|
||||
host1x_channel_fifostat_cfempty_v(r)
|
||||
static inline u32 host1x_channel_dmastart_r(void)
|
||||
{
|
||||
return 0x14;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMASTART \
|
||||
host1x_channel_dmastart_r()
|
||||
static inline u32 host1x_channel_dmaput_r(void)
|
||||
{
|
||||
return 0x18;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAPUT \
|
||||
host1x_channel_dmaput_r()
|
||||
static inline u32 host1x_channel_dmaget_r(void)
|
||||
{
|
||||
return 0x1c;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAGET \
|
||||
host1x_channel_dmaget_r()
|
||||
static inline u32 host1x_channel_dmaend_r(void)
|
||||
{
|
||||
return 0x20;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAEND \
|
||||
host1x_channel_dmaend_r()
|
||||
static inline u32 host1x_channel_dmactrl_r(void)
|
||||
{
|
||||
return 0x24;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL \
|
||||
host1x_channel_dmactrl_r()
|
||||
static inline u32 host1x_channel_dmactrl_dmastop(void)
|
||||
{
|
||||
return 1 << 0;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMASTOP \
|
||||
host1x_channel_dmactrl_dmastop()
|
||||
static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMASTOP_V(r) \
|
||||
host1x_channel_dmactrl_dmastop_v(r)
|
||||
static inline u32 host1x_channel_dmactrl_dmagetrst(void)
|
||||
{
|
||||
return 1 << 1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \
|
||||
host1x_channel_dmactrl_dmagetrst()
|
||||
static inline u32 host1x_channel_dmactrl_dmainitget(void)
|
||||
{
|
||||
return 1 << 2;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \
|
||||
host1x_channel_dmactrl_dmainitget()
|
||||
|
||||
#endif
|
243
drivers/gpu/host1x/hw/hw_host1x02_sync.h
Normal file
243
drivers/gpu/host1x/hw/hw_host1x02_sync.h
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright (c) 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 HOST1X_HW_HOST1X02_SYNC_H
|
||||
#define HOST1X_HW_HOST1X02_SYNC_H
|
||||
|
||||
#define REGISTER_STRIDE 4
|
||||
|
||||
static inline u32 host1x_sync_syncpt_r(unsigned int id)
|
||||
{
|
||||
return 0x400 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT(id) \
|
||||
host1x_sync_syncpt_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(unsigned int id)
|
||||
{
|
||||
return 0x40 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id) \
|
||||
host1x_sync_syncpt_thresh_cpu0_int_status_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_int_disable_r(unsigned int id)
|
||||
{
|
||||
return 0x60 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id) \
|
||||
host1x_sync_syncpt_thresh_int_disable_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id)
|
||||
{
|
||||
return 0x68 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \
|
||||
host1x_sync_syncpt_thresh_int_enable_cpu0_r(id)
|
||||
static inline u32 host1x_sync_cf_setup_r(unsigned int channel)
|
||||
{
|
||||
return 0x80 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP(channel) \
|
||||
host1x_sync_cf_setup_r(channel)
|
||||
static inline u32 host1x_sync_cf_setup_base_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP_BASE_V(r) \
|
||||
host1x_sync_cf_setup_base_v(r)
|
||||
static inline u32 host1x_sync_cf_setup_limit_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP_LIMIT_V(r) \
|
||||
host1x_sync_cf_setup_limit_v(r)
|
||||
static inline u32 host1x_sync_cmdproc_stop_r(void)
|
||||
{
|
||||
return 0xac;
|
||||
}
|
||||
#define HOST1X_SYNC_CMDPROC_STOP \
|
||||
host1x_sync_cmdproc_stop_r()
|
||||
static inline u32 host1x_sync_ch_teardown_r(void)
|
||||
{
|
||||
return 0xb0;
|
||||
}
|
||||
#define HOST1X_SYNC_CH_TEARDOWN \
|
||||
host1x_sync_ch_teardown_r()
|
||||
static inline u32 host1x_sync_usec_clk_r(void)
|
||||
{
|
||||
return 0x1a4;
|
||||
}
|
||||
#define HOST1X_SYNC_USEC_CLK \
|
||||
host1x_sync_usec_clk_r()
|
||||
static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void)
|
||||
{
|
||||
return 0x1a8;
|
||||
}
|
||||
#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG \
|
||||
host1x_sync_ctxsw_timeout_cfg_r()
|
||||
static inline u32 host1x_sync_ip_busy_timeout_r(void)
|
||||
{
|
||||
return 0x1bc;
|
||||
}
|
||||
#define HOST1X_SYNC_IP_BUSY_TIMEOUT \
|
||||
host1x_sync_ip_busy_timeout_r()
|
||||
static inline u32 host1x_sync_mlock_owner_r(unsigned int id)
|
||||
{
|
||||
return 0x340 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER(id) \
|
||||
host1x_sync_mlock_owner_r(id)
|
||||
static inline u32 host1x_sync_mlock_owner_chid_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 8;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CHID_F(v) \
|
||||
host1x_sync_mlock_owner_chid_f(v)
|
||||
static inline u32 host1x_sync_mlock_owner_cpu_owns_v(u32 r)
|
||||
{
|
||||
return (r >> 1) & 0x1;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(r) \
|
||||
host1x_sync_mlock_owner_cpu_owns_v(r)
|
||||
static inline u32 host1x_sync_mlock_owner_ch_owns_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(r) \
|
||||
host1x_sync_mlock_owner_ch_owns_v(r)
|
||||
static inline u32 host1x_sync_syncpt_int_thresh_r(unsigned int id)
|
||||
{
|
||||
return 0x500 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_INT_THRESH(id) \
|
||||
host1x_sync_syncpt_int_thresh_r(id)
|
||||
static inline u32 host1x_sync_syncpt_base_r(unsigned int id)
|
||||
{
|
||||
return 0x600 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_BASE(id) \
|
||||
host1x_sync_syncpt_base_r(id)
|
||||
static inline u32 host1x_sync_syncpt_cpu_incr_r(unsigned int id)
|
||||
{
|
||||
return 0x700 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_CPU_INCR(id) \
|
||||
host1x_sync_syncpt_cpu_incr_r(id)
|
||||
static inline u32 host1x_sync_cbread_r(unsigned int channel)
|
||||
{
|
||||
return 0x720 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CBREAD(channel) \
|
||||
host1x_sync_cbread_r(channel)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_r(void)
|
||||
{
|
||||
return 0x74c;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL \
|
||||
host1x_sync_cfpeek_ctrl_r()
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_addr_f(u32 v)
|
||||
{
|
||||
return (v & 0x3ff) << 0;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_addr_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_channr_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 16;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_channr_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_ena_f(u32 v)
|
||||
{
|
||||
return (v & 0x1) << 31;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_ENA_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_ena_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_read_r(void)
|
||||
{
|
||||
return 0x750;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_READ \
|
||||
host1x_sync_cfpeek_read_r()
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_r(void)
|
||||
{
|
||||
return 0x754;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS \
|
||||
host1x_sync_cfpeek_ptrs_r()
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(r) \
|
||||
host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(r)
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(r) \
|
||||
host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(r)
|
||||
static inline u32 host1x_sync_cbstat_r(unsigned int channel)
|
||||
{
|
||||
return 0x758 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT(channel) \
|
||||
host1x_sync_cbstat_r(channel)
|
||||
static inline u32 host1x_sync_cbstat_cboffset_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0xffff;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT_CBOFFSET_V(r) \
|
||||
host1x_sync_cbstat_cboffset_v(r)
|
||||
static inline u32 host1x_sync_cbstat_cbclass_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT_CBCLASS_V(r) \
|
||||
host1x_sync_cbstat_cbclass_v(r)
|
||||
|
||||
#endif
|
181
drivers/gpu/host1x/hw/hw_host1x02_uclass.h
Normal file
181
drivers/gpu/host1x/hw/hw_host1x02_uclass.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (c) 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 HOST1X_HW_HOST1X02_UCLASS_H
|
||||
#define HOST1X_HW_HOST1X02_UCLASS_H
|
||||
|
||||
static inline u32 host1x_uclass_incr_syncpt_r(void)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT \
|
||||
host1x_uclass_incr_syncpt_r()
|
||||
static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 8;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
|
||||
host1x_uclass_incr_syncpt_cond_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
|
||||
host1x_uclass_incr_syncpt_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_r(void)
|
||||
{
|
||||
return 0x8;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT \
|
||||
host1x_uclass_wait_syncpt_r()
|
||||
static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
|
||||
host1x_uclass_wait_syncpt_thresh_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_r(void)
|
||||
{
|
||||
return 0x9;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
|
||||
host1x_uclass_wait_syncpt_base_r()
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 16;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_offset_f(v)
|
||||
static inline u32 host1x_uclass_load_syncpt_base_r(void)
|
||||
{
|
||||
return 0xb;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \
|
||||
host1x_uclass_load_syncpt_base_r()
|
||||
static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_load_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
|
||||
host1x_uclass_load_syncpt_base_value_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_incr_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
|
||||
host1x_uclass_incr_syncpt_base_offset_f(v)
|
||||
static inline u32 host1x_uclass_indoff_r(void)
|
||||
{
|
||||
return 0x2d;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF \
|
||||
host1x_uclass_indoff_r()
|
||||
static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 28;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
|
||||
host1x_uclass_indoff_indbe_f(v)
|
||||
static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
|
||||
{
|
||||
return (v & 0x1) << 27;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
|
||||
host1x_uclass_indoff_autoinc_f(v)
|
||||
static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 18;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
|
||||
host1x_uclass_indoff_indmodid_f(v)
|
||||
static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffff) << 2;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
|
||||
host1x_uclass_indoff_indroffset_f(v)
|
||||
static inline u32 host1x_uclass_indoff_rwn_read_v(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
|
||||
host1x_uclass_indoff_indroffset_f(v)
|
||||
|
||||
#endif
|
121
drivers/gpu/host1x/hw/hw_host1x04_channel.h
Normal file
121
drivers/gpu/host1x/hw/hw_host1x04_channel.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 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 HOST1X_HW_HOST1X04_CHANNEL_H
|
||||
#define HOST1X_HW_HOST1X04_CHANNEL_H
|
||||
|
||||
static inline u32 host1x_channel_fifostat_r(void)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
#define HOST1X_CHANNEL_FIFOSTAT \
|
||||
host1x_channel_fifostat_r()
|
||||
static inline u32 host1x_channel_fifostat_cfempty_v(u32 r)
|
||||
{
|
||||
return (r >> 11) & 0x1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(r) \
|
||||
host1x_channel_fifostat_cfempty_v(r)
|
||||
static inline u32 host1x_channel_dmastart_r(void)
|
||||
{
|
||||
return 0x14;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMASTART \
|
||||
host1x_channel_dmastart_r()
|
||||
static inline u32 host1x_channel_dmaput_r(void)
|
||||
{
|
||||
return 0x18;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAPUT \
|
||||
host1x_channel_dmaput_r()
|
||||
static inline u32 host1x_channel_dmaget_r(void)
|
||||
{
|
||||
return 0x1c;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAGET \
|
||||
host1x_channel_dmaget_r()
|
||||
static inline u32 host1x_channel_dmaend_r(void)
|
||||
{
|
||||
return 0x20;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMAEND \
|
||||
host1x_channel_dmaend_r()
|
||||
static inline u32 host1x_channel_dmactrl_r(void)
|
||||
{
|
||||
return 0x24;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL \
|
||||
host1x_channel_dmactrl_r()
|
||||
static inline u32 host1x_channel_dmactrl_dmastop(void)
|
||||
{
|
||||
return 1 << 0;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMASTOP \
|
||||
host1x_channel_dmactrl_dmastop()
|
||||
static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMASTOP_V(r) \
|
||||
host1x_channel_dmactrl_dmastop_v(r)
|
||||
static inline u32 host1x_channel_dmactrl_dmagetrst(void)
|
||||
{
|
||||
return 1 << 1;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \
|
||||
host1x_channel_dmactrl_dmagetrst()
|
||||
static inline u32 host1x_channel_dmactrl_dmainitget(void)
|
||||
{
|
||||
return 1 << 2;
|
||||
}
|
||||
#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \
|
||||
host1x_channel_dmactrl_dmainitget()
|
||||
|
||||
#endif
|
243
drivers/gpu/host1x/hw/hw_host1x04_sync.h
Normal file
243
drivers/gpu/host1x/hw/hw_host1x04_sync.h
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* Copyright (c) 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 HOST1X_HW_HOST1X04_SYNC_H
|
||||
#define HOST1X_HW_HOST1X04_SYNC_H
|
||||
|
||||
#define REGISTER_STRIDE 4
|
||||
|
||||
static inline u32 host1x_sync_syncpt_r(unsigned int id)
|
||||
{
|
||||
return 0xf80 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT(id) \
|
||||
host1x_sync_syncpt_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(unsigned int id)
|
||||
{
|
||||
return 0xe80 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id) \
|
||||
host1x_sync_syncpt_thresh_cpu0_int_status_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_int_disable_r(unsigned int id)
|
||||
{
|
||||
return 0xf00 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id) \
|
||||
host1x_sync_syncpt_thresh_int_disable_r(id)
|
||||
static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id)
|
||||
{
|
||||
return 0xf20 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \
|
||||
host1x_sync_syncpt_thresh_int_enable_cpu0_r(id)
|
||||
static inline u32 host1x_sync_cf_setup_r(unsigned int channel)
|
||||
{
|
||||
return 0xc00 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP(channel) \
|
||||
host1x_sync_cf_setup_r(channel)
|
||||
static inline u32 host1x_sync_cf_setup_base_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP_BASE_V(r) \
|
||||
host1x_sync_cf_setup_base_v(r)
|
||||
static inline u32 host1x_sync_cf_setup_limit_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CF_SETUP_LIMIT_V(r) \
|
||||
host1x_sync_cf_setup_limit_v(r)
|
||||
static inline u32 host1x_sync_cmdproc_stop_r(void)
|
||||
{
|
||||
return 0xac;
|
||||
}
|
||||
#define HOST1X_SYNC_CMDPROC_STOP \
|
||||
host1x_sync_cmdproc_stop_r()
|
||||
static inline u32 host1x_sync_ch_teardown_r(void)
|
||||
{
|
||||
return 0xb0;
|
||||
}
|
||||
#define HOST1X_SYNC_CH_TEARDOWN \
|
||||
host1x_sync_ch_teardown_r()
|
||||
static inline u32 host1x_sync_usec_clk_r(void)
|
||||
{
|
||||
return 0x1a4;
|
||||
}
|
||||
#define HOST1X_SYNC_USEC_CLK \
|
||||
host1x_sync_usec_clk_r()
|
||||
static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void)
|
||||
{
|
||||
return 0x1a8;
|
||||
}
|
||||
#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG \
|
||||
host1x_sync_ctxsw_timeout_cfg_r()
|
||||
static inline u32 host1x_sync_ip_busy_timeout_r(void)
|
||||
{
|
||||
return 0x1bc;
|
||||
}
|
||||
#define HOST1X_SYNC_IP_BUSY_TIMEOUT \
|
||||
host1x_sync_ip_busy_timeout_r()
|
||||
static inline u32 host1x_sync_mlock_owner_r(unsigned int id)
|
||||
{
|
||||
return 0x340 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER(id) \
|
||||
host1x_sync_mlock_owner_r(id)
|
||||
static inline u32 host1x_sync_mlock_owner_chid_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 8;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CHID_F(v) \
|
||||
host1x_sync_mlock_owner_chid_f(v)
|
||||
static inline u32 host1x_sync_mlock_owner_cpu_owns_v(u32 r)
|
||||
{
|
||||
return (r >> 1) & 0x1;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(r) \
|
||||
host1x_sync_mlock_owner_cpu_owns_v(r)
|
||||
static inline u32 host1x_sync_mlock_owner_ch_owns_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x1;
|
||||
}
|
||||
#define HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(r) \
|
||||
host1x_sync_mlock_owner_ch_owns_v(r)
|
||||
static inline u32 host1x_sync_syncpt_int_thresh_r(unsigned int id)
|
||||
{
|
||||
return 0x1380 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_INT_THRESH(id) \
|
||||
host1x_sync_syncpt_int_thresh_r(id)
|
||||
static inline u32 host1x_sync_syncpt_base_r(unsigned int id)
|
||||
{
|
||||
return 0x600 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_BASE(id) \
|
||||
host1x_sync_syncpt_base_r(id)
|
||||
static inline u32 host1x_sync_syncpt_cpu_incr_r(unsigned int id)
|
||||
{
|
||||
return 0xf60 + id * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_SYNCPT_CPU_INCR(id) \
|
||||
host1x_sync_syncpt_cpu_incr_r(id)
|
||||
static inline u32 host1x_sync_cbread_r(unsigned int channel)
|
||||
{
|
||||
return 0xc80 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CBREAD(channel) \
|
||||
host1x_sync_cbread_r(channel)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_r(void)
|
||||
{
|
||||
return 0x74c;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL \
|
||||
host1x_sync_cfpeek_ctrl_r()
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_addr_f(u32 v)
|
||||
{
|
||||
return (v & 0x3ff) << 0;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_addr_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_channr_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 16;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_channr_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_ctrl_ena_f(u32 v)
|
||||
{
|
||||
return (v & 0x1) << 31;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_CTRL_ENA_F(v) \
|
||||
host1x_sync_cfpeek_ctrl_ena_f(v)
|
||||
static inline u32 host1x_sync_cfpeek_read_r(void)
|
||||
{
|
||||
return 0x750;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_READ \
|
||||
host1x_sync_cfpeek_read_r()
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_r(void)
|
||||
{
|
||||
return 0x754;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS \
|
||||
host1x_sync_cfpeek_ptrs_r()
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(r) \
|
||||
host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(r)
|
||||
static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(r) \
|
||||
host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(r)
|
||||
static inline u32 host1x_sync_cbstat_r(unsigned int channel)
|
||||
{
|
||||
return 0xcc0 + channel * REGISTER_STRIDE;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT(channel) \
|
||||
host1x_sync_cbstat_r(channel)
|
||||
static inline u32 host1x_sync_cbstat_cboffset_v(u32 r)
|
||||
{
|
||||
return (r >> 0) & 0xffff;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT_CBOFFSET_V(r) \
|
||||
host1x_sync_cbstat_cboffset_v(r)
|
||||
static inline u32 host1x_sync_cbstat_cbclass_v(u32 r)
|
||||
{
|
||||
return (r >> 16) & 0x3ff;
|
||||
}
|
||||
#define HOST1X_SYNC_CBSTAT_CBCLASS_V(r) \
|
||||
host1x_sync_cbstat_cbclass_v(r)
|
||||
|
||||
#endif
|
181
drivers/gpu/host1x/hw/hw_host1x04_uclass.h
Normal file
181
drivers/gpu/host1x/hw/hw_host1x04_uclass.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (c) 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 HOST1X_HW_HOST1X04_UCLASS_H
|
||||
#define HOST1X_HW_HOST1X04_UCLASS_H
|
||||
|
||||
static inline u32 host1x_uclass_incr_syncpt_r(void)
|
||||
{
|
||||
return 0x0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT \
|
||||
host1x_uclass_incr_syncpt_r()
|
||||
static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 8;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
|
||||
host1x_uclass_incr_syncpt_cond_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
|
||||
host1x_uclass_incr_syncpt_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_r(void)
|
||||
{
|
||||
return 0x8;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT \
|
||||
host1x_uclass_wait_syncpt_r()
|
||||
static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
|
||||
host1x_uclass_wait_syncpt_thresh_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_r(void)
|
||||
{
|
||||
return 0x9;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
|
||||
host1x_uclass_wait_syncpt_base_r()
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 16;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
|
||||
host1x_uclass_wait_syncpt_base_offset_f(v)
|
||||
static inline u32 host1x_uclass_load_syncpt_base_r(void)
|
||||
{
|
||||
return 0xb;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE \
|
||||
host1x_uclass_load_syncpt_base_r()
|
||||
static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_load_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
|
||||
host1x_uclass_load_syncpt_base_value_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 24;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
|
||||
host1x_uclass_incr_syncpt_base_base_indx_f(v)
|
||||
static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffffff) << 0;
|
||||
}
|
||||
#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
|
||||
host1x_uclass_incr_syncpt_base_offset_f(v)
|
||||
static inline u32 host1x_uclass_indoff_r(void)
|
||||
{
|
||||
return 0x2d;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF \
|
||||
host1x_uclass_indoff_r()
|
||||
static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
|
||||
{
|
||||
return (v & 0xf) << 28;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
|
||||
host1x_uclass_indoff_indbe_f(v)
|
||||
static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
|
||||
{
|
||||
return (v & 0x1) << 27;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
|
||||
host1x_uclass_indoff_autoinc_f(v)
|
||||
static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
|
||||
{
|
||||
return (v & 0xff) << 18;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
|
||||
host1x_uclass_indoff_indmodid_f(v)
|
||||
static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
|
||||
{
|
||||
return (v & 0xffff) << 2;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
|
||||
host1x_uclass_indoff_indroffset_f(v)
|
||||
static inline u32 host1x_uclass_indoff_rwn_read_v(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
|
||||
host1x_uclass_indoff_indroffset_f(v)
|
||||
|
||||
#endif
|
142
drivers/gpu/host1x/hw/intr_hw.c
Normal file
142
drivers/gpu/host1x/hw/intr_hw.c
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Tegra host1x Interrupt Management
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* 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/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "../intr.h"
|
||||
#include "../dev.h"
|
||||
|
||||
/*
|
||||
* Sync point threshold interrupt service function
|
||||
* Handles sync point threshold triggers, in interrupt context
|
||||
*/
|
||||
static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt)
|
||||
{
|
||||
unsigned int id = syncpt->id;
|
||||
struct host1x *host = syncpt->host;
|
||||
|
||||
host1x_sync_writel(host, BIT_MASK(id),
|
||||
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(BIT_WORD(id)));
|
||||
host1x_sync_writel(host, BIT_MASK(id),
|
||||
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(BIT_WORD(id)));
|
||||
|
||||
queue_work(host->intr_wq, &syncpt->intr.work);
|
||||
}
|
||||
|
||||
static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct host1x *host = dev_id;
|
||||
unsigned long reg;
|
||||
int i, id;
|
||||
|
||||
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) {
|
||||
reg = host1x_sync_readl(host,
|
||||
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
|
||||
for_each_set_bit(id, ®, BITS_PER_LONG) {
|
||||
struct host1x_syncpt *syncpt =
|
||||
host->syncpt + (i * BITS_PER_LONG + id);
|
||||
host1x_intr_syncpt_handle(syncpt);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); ++i) {
|
||||
host1x_sync_writel(host, 0xffffffffu,
|
||||
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
|
||||
host1x_sync_writel(host, 0xffffffffu,
|
||||
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
|
||||
}
|
||||
}
|
||||
|
||||
static int _host1x_intr_init_host_sync(struct host1x *host, u32 cpm,
|
||||
void (*syncpt_thresh_work)(struct work_struct *))
|
||||
{
|
||||
int i, err;
|
||||
|
||||
host1x_hw_intr_disable_all_syncpt_intrs(host);
|
||||
|
||||
for (i = 0; i < host->info->nb_pts; i++)
|
||||
INIT_WORK(&host->syncpt[i].intr.work, syncpt_thresh_work);
|
||||
|
||||
err = devm_request_irq(host->dev, host->intr_syncpt_irq,
|
||||
syncpt_thresh_isr, IRQF_SHARED,
|
||||
"host1x_syncpt", host);
|
||||
if (IS_ERR_VALUE(err)) {
|
||||
WARN_ON(1);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* disable the ip_busy_timeout. this prevents write drops */
|
||||
host1x_sync_writel(host, 0, HOST1X_SYNC_IP_BUSY_TIMEOUT);
|
||||
|
||||
/*
|
||||
* increase the auto-ack timout to the maximum value. 2d will hang
|
||||
* otherwise on Tegra2.
|
||||
*/
|
||||
host1x_sync_writel(host, 0xff, HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
|
||||
|
||||
/* update host clocks per usec */
|
||||
host1x_sync_writel(host, cpm, HOST1X_SYNC_USEC_CLK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _host1x_intr_set_syncpt_threshold(struct host1x *host,
|
||||
u32 id, u32 thresh)
|
||||
{
|
||||
host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id));
|
||||
}
|
||||
|
||||
static void _host1x_intr_enable_syncpt_intr(struct host1x *host, u32 id)
|
||||
{
|
||||
host1x_sync_writel(host, BIT_MASK(id),
|
||||
HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(BIT_WORD(id)));
|
||||
}
|
||||
|
||||
static void _host1x_intr_disable_syncpt_intr(struct host1x *host, u32 id)
|
||||
{
|
||||
host1x_sync_writel(host, BIT_MASK(id),
|
||||
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(BIT_WORD(id)));
|
||||
host1x_sync_writel(host, BIT_MASK(id),
|
||||
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(BIT_WORD(id)));
|
||||
}
|
||||
|
||||
static int _host1x_free_syncpt_irq(struct host1x *host)
|
||||
{
|
||||
devm_free_irq(host->dev, host->intr_syncpt_irq, host);
|
||||
flush_workqueue(host->intr_wq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct host1x_intr_ops host1x_intr_ops = {
|
||||
.init_host_sync = _host1x_intr_init_host_sync,
|
||||
.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,
|
||||
.free_syncpt_irq = _host1x_free_syncpt_irq,
|
||||
};
|
112
drivers/gpu/host1x/hw/syncpt_hw.c
Normal file
112
drivers/gpu/host1x/hw/syncpt_hw.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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 "../dev.h"
|
||||
#include "../syncpt.h"
|
||||
|
||||
/*
|
||||
* Write the current syncpoint value back to hw.
|
||||
*/
|
||||
static void syncpt_restore(struct host1x_syncpt *sp)
|
||||
{
|
||||
struct host1x *host = sp->host;
|
||||
int min = host1x_syncpt_read_min(sp);
|
||||
host1x_sync_writel(host, min, HOST1X_SYNC_SYNCPT(sp->id));
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the current waitbase value back to hw.
|
||||
*/
|
||||
static void syncpt_restore_wait_base(struct host1x_syncpt *sp)
|
||||
{
|
||||
struct host1x *host = sp->host;
|
||||
host1x_sync_writel(host, sp->base_val,
|
||||
HOST1X_SYNC_SYNCPT_BASE(sp->id));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read waitbase value from hw.
|
||||
*/
|
||||
static void syncpt_read_wait_base(struct host1x_syncpt *sp)
|
||||
{
|
||||
struct host1x *host = sp->host;
|
||||
sp->base_val =
|
||||
host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_BASE(sp->id));
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the last value read from hardware.
|
||||
*/
|
||||
static u32 syncpt_load(struct host1x_syncpt *sp)
|
||||
{
|
||||
struct host1x *host = sp->host;
|
||||
u32 old, live;
|
||||
|
||||
/* Loop in case there's a race writing to min_val */
|
||||
do {
|
||||
old = host1x_syncpt_read_min(sp);
|
||||
live = host1x_sync_readl(host, HOST1X_SYNC_SYNCPT(sp->id));
|
||||
} while ((u32)atomic_cmpxchg(&sp->min_val, old, live) != old);
|
||||
|
||||
if (!host1x_syncpt_check_max(sp, live))
|
||||
dev_err(host->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.
|
||||
*/
|
||||
static int syncpt_cpu_incr(struct host1x_syncpt *sp)
|
||||
{
|
||||
struct host1x *host = sp->host;
|
||||
u32 reg_offset = sp->id / 32;
|
||||
|
||||
if (!host1x_syncpt_client_managed(sp) &&
|
||||
host1x_syncpt_idle(sp))
|
||||
return -EINVAL;
|
||||
host1x_sync_writel(host, BIT_MASK(sp->id),
|
||||
HOST1X_SYNC_SYNCPT_CPU_INCR(reg_offset));
|
||||
wmb();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove a wait pointed to by patch_addr */
|
||||
static int syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr)
|
||||
{
|
||||
u32 override = host1x_class_host_wait_syncpt(
|
||||
HOST1X_SYNCPT_RESERVED, 0);
|
||||
|
||||
*((u32 *)patch_addr) = override;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct host1x_syncpt_ops host1x_syncpt_ops = {
|
||||
.restore = syncpt_restore,
|
||||
.restore_wait_base = syncpt_restore_wait_base,
|
||||
.load_wait_base = syncpt_read_wait_base,
|
||||
.load = syncpt_load,
|
||||
.cpu_incr = syncpt_cpu_incr,
|
||||
.patch_wait = syncpt_patch_wait,
|
||||
};
|
354
drivers/gpu/host1x/intr.c
Normal file
354
drivers/gpu/host1x/intr.c
Normal file
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Tegra host1x Interrupt Management
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <trace/events/host1x.h>
|
||||
#include "channel.h"
|
||||
#include "dev.h"
|
||||
#include "intr.h"
|
||||
|
||||
/* Wait list management */
|
||||
|
||||
enum waitlist_state {
|
||||
WLS_PENDING,
|
||||
WLS_REMOVED,
|
||||
WLS_CANCELLED,
|
||||
WLS_HANDLED
|
||||
};
|
||||
|
||||
static void waiter_release(struct kref *kref)
|
||||
{
|
||||
kfree(container_of(kref, struct host1x_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 host1x_waitlist *waiter,
|
||||
struct list_head *queue)
|
||||
{
|
||||
struct host1x_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[HOST1X_INTR_ACTION_COUNT])
|
||||
{
|
||||
struct list_head *dest;
|
||||
struct host1x_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 == HOST1X_INTR_ACTION_SUBMIT_COMPLETE &&
|
||||
!list_empty(dest)) {
|
||||
prev = list_entry(dest->prev,
|
||||
struct host1x_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);
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_threshold_interrupt(struct host1x *host,
|
||||
struct list_head *head,
|
||||
unsigned int id)
|
||||
{
|
||||
u32 thresh =
|
||||
list_first_entry(head, struct host1x_waitlist, list)->thresh;
|
||||
|
||||
host1x_hw_intr_set_syncpt_threshold(host, id, thresh);
|
||||
host1x_hw_intr_enable_syncpt_intr(host, id);
|
||||
}
|
||||
|
||||
static void action_submit_complete(struct host1x_waitlist *waiter)
|
||||
{
|
||||
struct host1x_channel *channel = waiter->data;
|
||||
|
||||
host1x_cdma_update(&channel->cdma);
|
||||
|
||||
/* Add nr_completed to trace */
|
||||
trace_host1x_channel_submit_complete(dev_name(channel->dev),
|
||||
waiter->count, waiter->thresh);
|
||||
|
||||
}
|
||||
|
||||
static void action_wakeup(struct host1x_waitlist *waiter)
|
||||
{
|
||||
wait_queue_head_t *wq = waiter->data;
|
||||
wake_up(wq);
|
||||
}
|
||||
|
||||
static void action_wakeup_interruptible(struct host1x_waitlist *waiter)
|
||||
{
|
||||
wait_queue_head_t *wq = waiter->data;
|
||||
wake_up_interruptible(wq);
|
||||
}
|
||||
|
||||
typedef void (*action_handler)(struct host1x_waitlist *waiter);
|
||||
|
||||
static action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = {
|
||||
action_submit_complete,
|
||||
action_wakeup,
|
||||
action_wakeup_interruptible,
|
||||
};
|
||||
|
||||
static void run_handlers(struct list_head completed[HOST1X_INTR_ACTION_COUNT])
|
||||
{
|
||||
struct list_head *head = completed;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HOST1X_INTR_ACTION_COUNT; ++i, ++head) {
|
||||
action_handler handler = action_handlers[i];
|
||||
struct host1x_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 host1x *host,
|
||||
struct host1x_syncpt *syncpt,
|
||||
u32 threshold)
|
||||
{
|
||||
struct list_head completed[HOST1X_INTR_ACTION_COUNT];
|
||||
unsigned int i;
|
||||
int empty;
|
||||
|
||||
for (i = 0; i < HOST1X_INTR_ACTION_COUNT; ++i)
|
||||
INIT_LIST_HEAD(completed + i);
|
||||
|
||||
spin_lock(&syncpt->intr.lock);
|
||||
|
||||
remove_completed_waiters(&syncpt->intr.wait_head, threshold,
|
||||
completed);
|
||||
|
||||
empty = list_empty(&syncpt->intr.wait_head);
|
||||
if (empty)
|
||||
host1x_hw_intr_disable_syncpt_intr(host, syncpt->id);
|
||||
else
|
||||
reset_threshold_interrupt(host, &syncpt->intr.wait_head,
|
||||
syncpt->id);
|
||||
|
||||
spin_unlock(&syncpt->intr.lock);
|
||||
|
||||
run_handlers(completed);
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync point threshold interrupt service thread function
|
||||
* Handles sync point threshold triggers, in thread context
|
||||
*/
|
||||
|
||||
static void syncpt_thresh_work(struct work_struct *work)
|
||||
{
|
||||
struct host1x_syncpt_intr *syncpt_intr =
|
||||
container_of(work, struct host1x_syncpt_intr, work);
|
||||
struct host1x_syncpt *syncpt =
|
||||
container_of(syncpt_intr, struct host1x_syncpt, intr);
|
||||
unsigned int id = syncpt->id;
|
||||
struct host1x *host = syncpt->host;
|
||||
|
||||
(void)process_wait_list(host, syncpt,
|
||||
host1x_syncpt_load(host->syncpt + id));
|
||||
}
|
||||
|
||||
int host1x_intr_add_action(struct host1x *host, u32 id, u32 thresh,
|
||||
enum host1x_intr_action action, void *data,
|
||||
struct host1x_waitlist *waiter, void **ref)
|
||||
{
|
||||
struct host1x_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 = host->syncpt + id;
|
||||
|
||||
spin_lock(&syncpt->intr.lock);
|
||||
|
||||
queue_was_empty = list_empty(&syncpt->intr.wait_head);
|
||||
|
||||
if (add_waiter_to_queue(waiter, &syncpt->intr.wait_head)) {
|
||||
/* added at head of list - new threshold value */
|
||||
host1x_hw_intr_set_syncpt_threshold(host, id, thresh);
|
||||
|
||||
/* added as first waiter - enable interrupt */
|
||||
if (queue_was_empty)
|
||||
host1x_hw_intr_enable_syncpt_intr(host, id);
|
||||
}
|
||||
|
||||
spin_unlock(&syncpt->intr.lock);
|
||||
|
||||
if (ref)
|
||||
*ref = waiter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void host1x_intr_put_ref(struct host1x *host, u32 id, void *ref)
|
||||
{
|
||||
struct host1x_waitlist *waiter = ref;
|
||||
struct host1x_syncpt *syncpt;
|
||||
|
||||
while (atomic_cmpxchg(&waiter->state, WLS_PENDING, WLS_CANCELLED) ==
|
||||
WLS_REMOVED)
|
||||
schedule();
|
||||
|
||||
syncpt = host->syncpt + id;
|
||||
(void)process_wait_list(host, syncpt,
|
||||
host1x_syncpt_load(host->syncpt + id));
|
||||
|
||||
kref_put(&waiter->refcount, waiter_release);
|
||||
}
|
||||
|
||||
int host1x_intr_init(struct host1x *host, unsigned int irq_sync)
|
||||
{
|
||||
unsigned int id;
|
||||
u32 nb_pts = host1x_syncpt_nb_pts(host);
|
||||
|
||||
mutex_init(&host->intr_mutex);
|
||||
host->intr_syncpt_irq = irq_sync;
|
||||
host->intr_wq = create_workqueue("host_syncpt");
|
||||
if (!host->intr_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
for (id = 0; id < nb_pts; ++id) {
|
||||
struct host1x_syncpt *syncpt = host->syncpt + id;
|
||||
|
||||
spin_lock_init(&syncpt->intr.lock);
|
||||
INIT_LIST_HEAD(&syncpt->intr.wait_head);
|
||||
snprintf(syncpt->intr.thresh_irq_name,
|
||||
sizeof(syncpt->intr.thresh_irq_name),
|
||||
"host1x_sp_%02d", id);
|
||||
}
|
||||
|
||||
host1x_intr_start(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void host1x_intr_deinit(struct host1x *host)
|
||||
{
|
||||
host1x_intr_stop(host);
|
||||
destroy_workqueue(host->intr_wq);
|
||||
}
|
||||
|
||||
void host1x_intr_start(struct host1x *host)
|
||||
{
|
||||
u32 hz = clk_get_rate(host->clk);
|
||||
int err;
|
||||
|
||||
mutex_lock(&host->intr_mutex);
|
||||
err = host1x_hw_intr_init_host_sync(host, DIV_ROUND_UP(hz, 1000000),
|
||||
syncpt_thresh_work);
|
||||
if (err) {
|
||||
mutex_unlock(&host->intr_mutex);
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&host->intr_mutex);
|
||||
}
|
||||
|
||||
void host1x_intr_stop(struct host1x *host)
|
||||
{
|
||||
unsigned int id;
|
||||
struct host1x_syncpt *syncpt = host->syncpt;
|
||||
u32 nb_pts = host1x_syncpt_nb_pts(host);
|
||||
|
||||
mutex_lock(&host->intr_mutex);
|
||||
|
||||
host1x_hw_intr_disable_all_syncpt_intrs(host);
|
||||
|
||||
for (id = 0; id < nb_pts; ++id) {
|
||||
struct host1x_waitlist *waiter, *next;
|
||||
|
||||
list_for_each_entry_safe(waiter, next,
|
||||
&syncpt[id].intr.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[id].intr.wait_head)) {
|
||||
/* output diagnostics */
|
||||
mutex_unlock(&host->intr_mutex);
|
||||
pr_warn("%s cannot stop syncpt intr id=%d\n",
|
||||
__func__, id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
host1x_hw_intr_free_syncpt_irq(host);
|
||||
|
||||
mutex_unlock(&host->intr_mutex);
|
||||
}
|
102
drivers/gpu/host1x/intr.h
Normal file
102
drivers/gpu/host1x/intr.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Tegra host1x Interrupt Management
|
||||
*
|
||||
* 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_INTR_H
|
||||
#define __HOST1X_INTR_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct host1x;
|
||||
|
||||
enum host1x_intr_action {
|
||||
/*
|
||||
* Perform cleanup after a submit has completed.
|
||||
* 'data' points to a channel
|
||||
*/
|
||||
HOST1X_INTR_ACTION_SUBMIT_COMPLETE = 0,
|
||||
|
||||
/*
|
||||
* Wake up a task.
|
||||
* 'data' points to a wait_queue_head_t
|
||||
*/
|
||||
HOST1X_INTR_ACTION_WAKEUP,
|
||||
|
||||
/*
|
||||
* Wake up a interruptible task.
|
||||
* 'data' points to a wait_queue_head_t
|
||||
*/
|
||||
HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
|
||||
|
||||
HOST1X_INTR_ACTION_COUNT
|
||||
};
|
||||
|
||||
struct host1x_syncpt_intr {
|
||||
spinlock_t lock;
|
||||
struct list_head wait_head;
|
||||
char thresh_irq_name[12];
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
struct host1x_waitlist {
|
||||
struct list_head list;
|
||||
struct kref refcount;
|
||||
u32 thresh;
|
||||
enum host1x_intr_action action;
|
||||
atomic_t state;
|
||||
void *data;
|
||||
int count;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 structure - assumes ownership
|
||||
* @ref must be passed if cancellation is possible, else NULL
|
||||
*
|
||||
* This is a non-blocking api.
|
||||
*/
|
||||
int host1x_intr_add_action(struct host1x *host, u32 id, u32 thresh,
|
||||
enum host1x_intr_action action, void *data,
|
||||
struct host1x_waitlist *waiter, void **ref);
|
||||
|
||||
/*
|
||||
* Unreference an action submitted to host1x_intr_add_action().
|
||||
* You must call this if you passed non-NULL as ref.
|
||||
* @ref the ref returned from host1x_intr_add_action()
|
||||
*/
|
||||
void host1x_intr_put_ref(struct host1x *host, u32 id, void *ref);
|
||||
|
||||
/* Initialize host1x sync point interrupt */
|
||||
int host1x_intr_init(struct host1x *host, unsigned int irq_sync);
|
||||
|
||||
/* Deinitialize host1x sync point interrupt */
|
||||
void host1x_intr_deinit(struct host1x *host);
|
||||
|
||||
/* Enable host1x sync point interrupt */
|
||||
void host1x_intr_start(struct host1x *host);
|
||||
|
||||
/* Disable host1x sync point interrupt */
|
||||
void host1x_intr_stop(struct host1x *host);
|
||||
|
||||
irqreturn_t host1x_syncpt_thresh_fn(void *dev_id);
|
||||
#endif
|
598
drivers/gpu/host1x/job.c
Normal file
598
drivers/gpu/host1x/job.c
Normal file
|
@ -0,0 +1,598 @@
|
|||
/*
|
||||
* Tegra host1x Job
|
||||
*
|
||||
* 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/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <trace/events/host1x.h>
|
||||
|
||||
#include "channel.h"
|
||||
#include "dev.h"
|
||||
#include "job.h"
|
||||
#include "syncpt.h"
|
||||
|
||||
struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
|
||||
u32 num_cmdbufs, u32 num_relocs,
|
||||
u32 num_waitchks)
|
||||
{
|
||||
struct host1x_job *job = NULL;
|
||||
unsigned int num_unpins = num_cmdbufs + num_relocs;
|
||||
u64 total;
|
||||
void *mem;
|
||||
|
||||
/* Check that we're not going to overflow */
|
||||
total = sizeof(struct host1x_job) +
|
||||
(u64)num_relocs * sizeof(struct host1x_reloc) +
|
||||
(u64)num_unpins * sizeof(struct host1x_job_unpin_data) +
|
||||
(u64)num_waitchks * sizeof(struct host1x_waitchk) +
|
||||
(u64)num_cmdbufs * sizeof(struct host1x_job_gather) +
|
||||
(u64)num_unpins * sizeof(dma_addr_t) +
|
||||
(u64)num_unpins * sizeof(u32 *);
|
||||
if (total > ULONG_MAX)
|
||||
return NULL;
|
||||
|
||||
mem = job = kzalloc(total, GFP_KERNEL);
|
||||
if (!job)
|
||||
return NULL;
|
||||
|
||||
kref_init(&job->ref);
|
||||
job->channel = ch;
|
||||
|
||||
/* Redistribute memory to the structs */
|
||||
mem += sizeof(struct host1x_job);
|
||||
job->relocarray = num_relocs ? mem : NULL;
|
||||
mem += num_relocs * sizeof(struct host1x_reloc);
|
||||
job->unpins = num_unpins ? mem : NULL;
|
||||
mem += num_unpins * sizeof(struct host1x_job_unpin_data);
|
||||
job->waitchk = num_waitchks ? mem : NULL;
|
||||
mem += num_waitchks * sizeof(struct host1x_waitchk);
|
||||
job->gathers = num_cmdbufs ? mem : NULL;
|
||||
mem += num_cmdbufs * sizeof(struct host1x_job_gather);
|
||||
job->addr_phys = num_unpins ? mem : NULL;
|
||||
|
||||
job->reloc_addr_phys = job->addr_phys;
|
||||
job->gather_addr_phys = &job->addr_phys[num_relocs];
|
||||
|
||||
return job;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_alloc);
|
||||
|
||||
struct host1x_job *host1x_job_get(struct host1x_job *job)
|
||||
{
|
||||
kref_get(&job->ref);
|
||||
return job;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_get);
|
||||
|
||||
static void job_free(struct kref *ref)
|
||||
{
|
||||
struct host1x_job *job = container_of(ref, struct host1x_job, ref);
|
||||
|
||||
kfree(job);
|
||||
}
|
||||
|
||||
void host1x_job_put(struct host1x_job *job)
|
||||
{
|
||||
kref_put(&job->ref, job_free);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_put);
|
||||
|
||||
void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo,
|
||||
u32 words, u32 offset)
|
||||
{
|
||||
struct host1x_job_gather *cur_gather = &job->gathers[job->num_gathers];
|
||||
|
||||
cur_gather->words = words;
|
||||
cur_gather->bo = bo;
|
||||
cur_gather->offset = offset;
|
||||
job->num_gathers++;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_add_gather);
|
||||
|
||||
/*
|
||||
* 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) HOST1X_SYNCPT_RESERVED syncpt
|
||||
* with a matching threshold value of 0, so is guaranteed to be popped
|
||||
* by the host HW.
|
||||
*/
|
||||
static void host1x_syncpt_patch_offset(struct host1x_syncpt *sp,
|
||||
struct host1x_bo *h, u32 offset)
|
||||
{
|
||||
void *patch_addr = NULL;
|
||||
|
||||
/* patch the wait */
|
||||
patch_addr = host1x_bo_kmap(h, offset >> PAGE_SHIFT);
|
||||
if (patch_addr) {
|
||||
host1x_syncpt_patch_wait(sp,
|
||||
patch_addr + (offset & ~PAGE_MASK));
|
||||
host1x_bo_kunmap(h, offset >> PAGE_SHIFT, patch_addr);
|
||||
} else
|
||||
pr_err("Could not map cmdbuf for wait check\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 host1x_job *job, struct host1x *host,
|
||||
struct host1x_bo *patch)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* compare syncpt vs wait threshold */
|
||||
for (i = 0; i < job->num_waitchk; i++) {
|
||||
struct host1x_waitchk *wait = &job->waitchk[i];
|
||||
struct host1x_syncpt *sp =
|
||||
host1x_syncpt_get(host, wait->syncpt_id);
|
||||
|
||||
/* validate syncpt id */
|
||||
if (wait->syncpt_id > host1x_syncpt_nb_pts(host))
|
||||
continue;
|
||||
|
||||
/* skip all other gathers */
|
||||
if (patch != wait->bo)
|
||||
continue;
|
||||
|
||||
trace_host1x_syncpt_wait_check(wait->bo, wait->offset,
|
||||
wait->syncpt_id, wait->thresh,
|
||||
host1x_syncpt_read_min(sp));
|
||||
|
||||
if (host1x_syncpt_is_expired(sp, wait->thresh)) {
|
||||
dev_dbg(host->dev,
|
||||
"drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n",
|
||||
wait->syncpt_id, sp->name, wait->thresh,
|
||||
host1x_syncpt_read_min(sp));
|
||||
|
||||
host1x_syncpt_patch_offset(sp, patch, wait->offset);
|
||||
}
|
||||
|
||||
wait->bo = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int pin_job(struct host1x_job *job)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
job->num_unpins = 0;
|
||||
|
||||
for (i = 0; i < job->num_relocs; i++) {
|
||||
struct host1x_reloc *reloc = &job->relocarray[i];
|
||||
struct sg_table *sgt;
|
||||
dma_addr_t phys_addr;
|
||||
|
||||
reloc->target.bo = host1x_bo_get(reloc->target.bo);
|
||||
if (!reloc->target.bo)
|
||||
goto unpin;
|
||||
|
||||
phys_addr = host1x_bo_pin(reloc->target.bo, &sgt);
|
||||
if (!phys_addr)
|
||||
goto unpin;
|
||||
|
||||
job->addr_phys[job->num_unpins] = phys_addr;
|
||||
job->unpins[job->num_unpins].bo = reloc->target.bo;
|
||||
job->unpins[job->num_unpins].sgt = sgt;
|
||||
job->num_unpins++;
|
||||
}
|
||||
|
||||
for (i = 0; i < job->num_gathers; i++) {
|
||||
struct host1x_job_gather *g = &job->gathers[i];
|
||||
struct sg_table *sgt;
|
||||
dma_addr_t phys_addr;
|
||||
|
||||
g->bo = host1x_bo_get(g->bo);
|
||||
if (!g->bo)
|
||||
goto unpin;
|
||||
|
||||
phys_addr = host1x_bo_pin(g->bo, &sgt);
|
||||
if (!phys_addr)
|
||||
goto unpin;
|
||||
|
||||
job->addr_phys[job->num_unpins] = phys_addr;
|
||||
job->unpins[job->num_unpins].bo = g->bo;
|
||||
job->unpins[job->num_unpins].sgt = sgt;
|
||||
job->num_unpins++;
|
||||
}
|
||||
|
||||
return job->num_unpins;
|
||||
|
||||
unpin:
|
||||
host1x_job_unpin(job);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
|
||||
{
|
||||
int i = 0;
|
||||
u32 last_page = ~0;
|
||||
void *cmdbuf_page_addr = NULL;
|
||||
|
||||
/* pin & patch the relocs for one gather */
|
||||
for (i = 0; i < job->num_relocs; i++) {
|
||||
struct host1x_reloc *reloc = &job->relocarray[i];
|
||||
u32 reloc_addr = (job->reloc_addr_phys[i] +
|
||||
reloc->target.offset) >> reloc->shift;
|
||||
u32 *target;
|
||||
|
||||
/* skip all other gathers */
|
||||
if (cmdbuf != reloc->cmdbuf.bo)
|
||||
continue;
|
||||
|
||||
if (last_page != reloc->cmdbuf.offset >> PAGE_SHIFT) {
|
||||
if (cmdbuf_page_addr)
|
||||
host1x_bo_kunmap(cmdbuf, last_page,
|
||||
cmdbuf_page_addr);
|
||||
|
||||
cmdbuf_page_addr = host1x_bo_kmap(cmdbuf,
|
||||
reloc->cmdbuf.offset >> PAGE_SHIFT);
|
||||
last_page = reloc->cmdbuf.offset >> PAGE_SHIFT;
|
||||
|
||||
if (unlikely(!cmdbuf_page_addr)) {
|
||||
pr_err("Could not map cmdbuf for relocation\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
target = cmdbuf_page_addr + (reloc->cmdbuf.offset & ~PAGE_MASK);
|
||||
*target = reloc_addr;
|
||||
}
|
||||
|
||||
if (cmdbuf_page_addr)
|
||||
host1x_bo_kunmap(cmdbuf, last_page, cmdbuf_page_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
|
||||
unsigned int offset)
|
||||
{
|
||||
offset *= sizeof(u32);
|
||||
|
||||
if (reloc->cmdbuf.bo != cmdbuf || reloc->cmdbuf.offset != offset)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct host1x_firewall {
|
||||
struct host1x_job *job;
|
||||
struct device *dev;
|
||||
|
||||
unsigned int num_relocs;
|
||||
struct host1x_reloc *reloc;
|
||||
|
||||
struct host1x_bo *cmdbuf;
|
||||
unsigned int offset;
|
||||
|
||||
u32 words;
|
||||
u32 class;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
static int check_register(struct host1x_firewall *fw, unsigned long offset)
|
||||
{
|
||||
if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) {
|
||||
if (!fw->num_relocs)
|
||||
return -EINVAL;
|
||||
|
||||
if (!check_reloc(fw->reloc, fw->cmdbuf, fw->offset))
|
||||
return -EINVAL;
|
||||
|
||||
fw->num_relocs--;
|
||||
fw->reloc++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_mask(struct host1x_firewall *fw)
|
||||
{
|
||||
u32 mask = fw->mask;
|
||||
u32 reg = fw->reg;
|
||||
int ret;
|
||||
|
||||
while (mask) {
|
||||
if (fw->words == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (mask & 1) {
|
||||
ret = check_register(fw, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fw->words--;
|
||||
fw->offset++;
|
||||
}
|
||||
mask >>= 1;
|
||||
reg++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_incr(struct host1x_firewall *fw)
|
||||
{
|
||||
u32 count = fw->count;
|
||||
u32 reg = fw->reg;
|
||||
int ret;
|
||||
|
||||
while (count) {
|
||||
if (fw->words == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = check_register(fw, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
reg++;
|
||||
fw->words--;
|
||||
fw->offset++;
|
||||
count--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_nonincr(struct host1x_firewall *fw)
|
||||
{
|
||||
u32 count = fw->count;
|
||||
int ret;
|
||||
|
||||
while (count) {
|
||||
if (fw->words == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = check_register(fw, fw->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fw->words--;
|
||||
fw->offset++;
|
||||
count--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
|
||||
{
|
||||
u32 *cmdbuf_base = (u32 *)fw->job->gather_copy_mapped +
|
||||
(g->offset / sizeof(u32));
|
||||
int err = 0;
|
||||
|
||||
if (!fw->job->is_addr_reg)
|
||||
return 0;
|
||||
|
||||
fw->words = g->words;
|
||||
fw->cmdbuf = g->bo;
|
||||
fw->offset = 0;
|
||||
|
||||
while (fw->words && !err) {
|
||||
u32 word = cmdbuf_base[fw->offset];
|
||||
u32 opcode = (word & 0xf0000000) >> 28;
|
||||
|
||||
fw->mask = 0;
|
||||
fw->reg = 0;
|
||||
fw->count = 0;
|
||||
fw->words--;
|
||||
fw->offset++;
|
||||
|
||||
switch (opcode) {
|
||||
case 0:
|
||||
fw->class = word >> 6 & 0x3ff;
|
||||
fw->mask = word & 0x3f;
|
||||
fw->reg = word >> 16 & 0xfff;
|
||||
err = check_mask(fw);
|
||||
if (err)
|
||||
goto out;
|
||||
break;
|
||||
case 1:
|
||||
fw->reg = word >> 16 & 0xfff;
|
||||
fw->count = word & 0xffff;
|
||||
err = check_incr(fw);
|
||||
if (err)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
fw->reg = word >> 16 & 0xfff;
|
||||
fw->count = word & 0xffff;
|
||||
err = check_nonincr(fw);
|
||||
if (err)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
fw->mask = word & 0xffff;
|
||||
fw->reg = word >> 16 & 0xfff;
|
||||
err = check_mask(fw);
|
||||
if (err)
|
||||
goto out;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 14:
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int copy_gathers(struct host1x_job *job, struct device *dev)
|
||||
{
|
||||
struct host1x_firewall fw;
|
||||
size_t size = 0;
|
||||
size_t offset = 0;
|
||||
int i;
|
||||
|
||||
fw.job = job;
|
||||
fw.dev = dev;
|
||||
fw.reloc = job->relocarray;
|
||||
fw.num_relocs = job->num_relocs;
|
||||
fw.class = 0;
|
||||
|
||||
for (i = 0; i < job->num_gathers; i++) {
|
||||
struct host1x_job_gather *g = &job->gathers[i];
|
||||
size += g->words * sizeof(u32);
|
||||
}
|
||||
|
||||
job->gather_copy_mapped = dma_alloc_writecombine(dev, size,
|
||||
&job->gather_copy,
|
||||
GFP_KERNEL);
|
||||
if (!job->gather_copy_mapped) {
|
||||
job->gather_copy_mapped = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
job->gather_copy_size = size;
|
||||
|
||||
for (i = 0; i < job->num_gathers; i++) {
|
||||
struct host1x_job_gather *g = &job->gathers[i];
|
||||
void *gather;
|
||||
|
||||
/* Copy the gather */
|
||||
gather = host1x_bo_mmap(g->bo);
|
||||
memcpy(job->gather_copy_mapped + offset, gather + g->offset,
|
||||
g->words * sizeof(u32));
|
||||
host1x_bo_munmap(g->bo, gather);
|
||||
|
||||
/* Store the location in the buffer */
|
||||
g->base = job->gather_copy;
|
||||
g->offset = offset;
|
||||
|
||||
/* Validate the job */
|
||||
if (validate(&fw, g))
|
||||
return -EINVAL;
|
||||
|
||||
offset += g->words * sizeof(u32);
|
||||
}
|
||||
|
||||
/* No relocs should remain at this point */
|
||||
if (fw.num_relocs)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int host1x_job_pin(struct host1x_job *job, struct device *dev)
|
||||
{
|
||||
int err;
|
||||
unsigned int i, j;
|
||||
struct host1x *host = dev_get_drvdata(dev->parent);
|
||||
DECLARE_BITMAP(waitchk_mask, host1x_syncpt_nb_pts(host));
|
||||
|
||||
bitmap_zero(waitchk_mask, host1x_syncpt_nb_pts(host));
|
||||
for (i = 0; i < job->num_waitchk; i++) {
|
||||
u32 syncpt_id = job->waitchk[i].syncpt_id;
|
||||
if (syncpt_id < host1x_syncpt_nb_pts(host))
|
||||
set_bit(syncpt_id, waitchk_mask);
|
||||
}
|
||||
|
||||
/* get current syncpt values for waitchk */
|
||||
for_each_set_bit(i, waitchk_mask, host1x_syncpt_nb_pts(host))
|
||||
host1x_syncpt_load(host->syncpt + i);
|
||||
|
||||
/* pin memory */
|
||||
err = pin_job(job);
|
||||
if (!err)
|
||||
goto out;
|
||||
|
||||
/* patch gathers */
|
||||
for (i = 0; i < job->num_gathers; i++) {
|
||||
struct host1x_job_gather *g = &job->gathers[i];
|
||||
|
||||
/* process each gather mem only once */
|
||||
if (g->handled)
|
||||
continue;
|
||||
|
||||
g->base = job->gather_addr_phys[i];
|
||||
|
||||
for (j = i + 1; j < job->num_gathers; j++)
|
||||
if (job->gathers[j].bo == g->bo)
|
||||
job->gathers[j].handled = true;
|
||||
|
||||
err = do_relocs(job, g->bo);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
err = do_waitchks(job, host, g->bo);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && !err) {
|
||||
err = copy_gathers(job, dev);
|
||||
if (err) {
|
||||
host1x_job_unpin(job);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
wmb();
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_pin);
|
||||
|
||||
void host1x_job_unpin(struct host1x_job *job)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < job->num_unpins; i++) {
|
||||
struct host1x_job_unpin_data *unpin = &job->unpins[i];
|
||||
host1x_bo_unpin(unpin->bo, unpin->sgt);
|
||||
host1x_bo_put(unpin->bo);
|
||||
}
|
||||
job->num_unpins = 0;
|
||||
|
||||
if (job->gather_copy_size)
|
||||
dma_free_writecombine(job->channel->dev, job->gather_copy_size,
|
||||
job->gather_copy_mapped,
|
||||
job->gather_copy);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_job_unpin);
|
||||
|
||||
/*
|
||||
* Debug routine used to dump job entries
|
||||
*/
|
||||
void host1x_job_dump(struct device *dev, struct host1x_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);
|
||||
}
|
54
drivers/gpu/host1x/job.h
Normal file
54
drivers/gpu/host1x/job.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Tegra host1x Job
|
||||
*
|
||||
* 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_JOB_H
|
||||
#define __HOST1X_JOB_H
|
||||
|
||||
struct host1x_job_gather {
|
||||
u32 words;
|
||||
dma_addr_t base;
|
||||
struct host1x_bo *bo;
|
||||
int offset;
|
||||
bool handled;
|
||||
};
|
||||
|
||||
struct host1x_cmdbuf {
|
||||
u32 handle;
|
||||
u32 offset;
|
||||
u32 words;
|
||||
u32 pad;
|
||||
};
|
||||
|
||||
struct host1x_waitchk {
|
||||
struct host1x_bo *bo;
|
||||
u32 offset;
|
||||
u32 syncpt_id;
|
||||
u32 thresh;
|
||||
};
|
||||
|
||||
struct host1x_job_unpin_data {
|
||||
struct host1x_bo *bo;
|
||||
struct sg_table *sgt;
|
||||
};
|
||||
|
||||
/*
|
||||
* Dump contents of job to debug output.
|
||||
*/
|
||||
void host1x_job_dump(struct device *dev, struct host1x_job *job);
|
||||
|
||||
#endif
|
275
drivers/gpu/host1x/mipi.c
Normal file
275
drivers/gpu/host1x/mipi.c
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* Copyright (C) 2013 NVIDIA Corporation
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that copyright
|
||||
* notice and this permission notice appear in supporting documentation, and
|
||||
* that the name of the copyright holders not be used in advertising or
|
||||
* publicity pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no representations
|
||||
* about the suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
* OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dev.h"
|
||||
|
||||
#define MIPI_CAL_CTRL 0x00
|
||||
#define MIPI_CAL_CTRL_START (1 << 0)
|
||||
|
||||
#define MIPI_CAL_AUTOCAL_CTRL 0x01
|
||||
|
||||
#define MIPI_CAL_STATUS 0x02
|
||||
#define MIPI_CAL_STATUS_DONE (1 << 16)
|
||||
#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
|
||||
|
||||
#define MIPI_CAL_CONFIG_CSIA 0x05
|
||||
#define MIPI_CAL_CONFIG_CSIB 0x06
|
||||
#define MIPI_CAL_CONFIG_CSIC 0x07
|
||||
#define MIPI_CAL_CONFIG_CSID 0x08
|
||||
#define MIPI_CAL_CONFIG_CSIE 0x09
|
||||
#define MIPI_CAL_CONFIG_DSIA 0x0e
|
||||
#define MIPI_CAL_CONFIG_DSIB 0x0f
|
||||
#define MIPI_CAL_CONFIG_DSIC 0x10
|
||||
#define MIPI_CAL_CONFIG_DSID 0x11
|
||||
|
||||
#define MIPI_CAL_CONFIG_SELECT (1 << 21)
|
||||
#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
|
||||
#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
|
||||
#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
|
||||
|
||||
#define MIPI_CAL_BIAS_PAD_CFG0 0x16
|
||||
#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
|
||||
#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
|
||||
|
||||
#define MIPI_CAL_BIAS_PAD_CFG1 0x17
|
||||
|
||||
#define MIPI_CAL_BIAS_PAD_CFG2 0x18
|
||||
#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
|
||||
|
||||
static const struct module {
|
||||
unsigned long reg;
|
||||
} modules[] = {
|
||||
{ .reg = MIPI_CAL_CONFIG_CSIA },
|
||||
{ .reg = MIPI_CAL_CONFIG_CSIB },
|
||||
{ .reg = MIPI_CAL_CONFIG_CSIC },
|
||||
{ .reg = MIPI_CAL_CONFIG_CSID },
|
||||
{ .reg = MIPI_CAL_CONFIG_CSIE },
|
||||
{ .reg = MIPI_CAL_CONFIG_DSIA },
|
||||
{ .reg = MIPI_CAL_CONFIG_DSIB },
|
||||
{ .reg = MIPI_CAL_CONFIG_DSIC },
|
||||
{ .reg = MIPI_CAL_CONFIG_DSID },
|
||||
};
|
||||
|
||||
struct tegra_mipi {
|
||||
void __iomem *regs;
|
||||
struct mutex lock;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
struct tegra_mipi_device {
|
||||
struct platform_device *pdev;
|
||||
struct tegra_mipi *mipi;
|
||||
struct device *device;
|
||||
unsigned long pads;
|
||||
};
|
||||
|
||||
static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi,
|
||||
unsigned long reg)
|
||||
{
|
||||
return readl(mipi->regs + (reg << 2));
|
||||
}
|
||||
|
||||
static inline void tegra_mipi_writel(struct tegra_mipi *mipi,
|
||||
unsigned long value, unsigned long reg)
|
||||
{
|
||||
writel(value, mipi->regs + (reg << 2));
|
||||
}
|
||||
|
||||
struct tegra_mipi_device *tegra_mipi_request(struct device *device)
|
||||
{
|
||||
struct device_node *np = device->of_node;
|
||||
struct tegra_mipi_device *dev;
|
||||
struct of_phandle_args args;
|
||||
int err;
|
||||
|
||||
err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",
|
||||
"#nvidia,mipi-calibrate-cells", 0,
|
||||
&args);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
of_node_put(args.np);
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->pdev = of_find_device_by_node(args.np);
|
||||
if (!dev->pdev) {
|
||||
of_node_put(args.np);
|
||||
err = -ENODEV;
|
||||
goto free;
|
||||
}
|
||||
|
||||
of_node_put(args.np);
|
||||
|
||||
dev->mipi = platform_get_drvdata(dev->pdev);
|
||||
if (!dev->mipi) {
|
||||
err = -EPROBE_DEFER;
|
||||
goto pdev_put;
|
||||
}
|
||||
|
||||
dev->pads = args.args[0];
|
||||
dev->device = device;
|
||||
|
||||
return dev;
|
||||
|
||||
pdev_put:
|
||||
platform_device_put(dev->pdev);
|
||||
free:
|
||||
kfree(dev);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_mipi_request);
|
||||
|
||||
void tegra_mipi_free(struct tegra_mipi_device *device)
|
||||
{
|
||||
platform_device_put(device->pdev);
|
||||
kfree(device);
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_mipi_free);
|
||||
|
||||
static int tegra_mipi_wait(struct tegra_mipi *mipi)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(250);
|
||||
unsigned long value;
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
|
||||
if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
|
||||
(value & MIPI_CAL_STATUS_DONE) != 0)
|
||||
return 0;
|
||||
|
||||
usleep_range(10, 50);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int tegra_mipi_calibrate(struct tegra_mipi_device *device)
|
||||
{
|
||||
unsigned long value;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = clk_enable(device->mipi->clk);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&device->mipi->lock);
|
||||
|
||||
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
|
||||
value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
|
||||
value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
|
||||
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
|
||||
|
||||
value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
|
||||
value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
|
||||
tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(modules); i++) {
|
||||
if (device->pads & BIT(i))
|
||||
value = MIPI_CAL_CONFIG_SELECT |
|
||||
MIPI_CAL_CONFIG_HSPDOS(0) |
|
||||
MIPI_CAL_CONFIG_HSPUOS(4) |
|
||||
MIPI_CAL_CONFIG_TERMOS(5);
|
||||
else
|
||||
value = 0;
|
||||
|
||||
tegra_mipi_writel(device->mipi, value, modules[i].reg);
|
||||
}
|
||||
|
||||
tegra_mipi_writel(device->mipi, MIPI_CAL_CTRL_START, MIPI_CAL_CTRL);
|
||||
|
||||
err = tegra_mipi_wait(device->mipi);
|
||||
|
||||
mutex_unlock(&device->mipi->lock);
|
||||
clk_disable(device->mipi->clk);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_mipi_calibrate);
|
||||
|
||||
static int tegra_mipi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_mipi *mipi;
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
|
||||
if (!mipi)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mipi->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(mipi->regs))
|
||||
return PTR_ERR(mipi->regs);
|
||||
|
||||
mutex_init(&mipi->lock);
|
||||
|
||||
mipi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mipi->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock\n");
|
||||
return PTR_ERR(mipi->clk);
|
||||
}
|
||||
|
||||
err = clk_prepare(mipi->clk);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
platform_set_drvdata(pdev, mipi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_mipi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_mipi *mipi = platform_get_drvdata(pdev);
|
||||
|
||||
clk_unprepare(mipi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id tegra_mipi_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra114-mipi", },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct platform_driver tegra_mipi_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-mipi",
|
||||
.of_match_table = tegra_mipi_of_match,
|
||||
},
|
||||
.probe = tegra_mipi_probe,
|
||||
.remove = tegra_mipi_remove,
|
||||
};
|
461
drivers/gpu/host1x/syncpt.c
Normal file
461
drivers/gpu/host1x/syncpt.c
Normal file
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <trace/events/host1x.h>
|
||||
|
||||
#include "syncpt.h"
|
||||
#include "dev.h"
|
||||
#include "intr.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define SYNCPT_CHECK_PERIOD (2 * HZ)
|
||||
#define MAX_STUCK_CHECK_COUNT 15
|
||||
|
||||
static struct host1x_syncpt_base *
|
||||
host1x_syncpt_base_request(struct host1x *host)
|
||||
{
|
||||
struct host1x_syncpt_base *bases = host->bases;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < host->info->nb_bases; i++)
|
||||
if (!bases[i].requested)
|
||||
break;
|
||||
|
||||
if (i >= host->info->nb_bases)
|
||||
return NULL;
|
||||
|
||||
bases[i].requested = true;
|
||||
return &bases[i];
|
||||
}
|
||||
|
||||
static void host1x_syncpt_base_free(struct host1x_syncpt_base *base)
|
||||
{
|
||||
if (base)
|
||||
base->requested = false;
|
||||
}
|
||||
|
||||
static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host,
|
||||
struct device *dev,
|
||||
unsigned long flags)
|
||||
{
|
||||
int i;
|
||||
struct host1x_syncpt *sp = host->syncpt;
|
||||
char *name;
|
||||
|
||||
for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++)
|
||||
;
|
||||
|
||||
if (i >= host->info->nb_pts)
|
||||
return NULL;
|
||||
|
||||
if (flags & HOST1X_SYNCPT_HAS_BASE) {
|
||||
sp->base = host1x_syncpt_base_request(host);
|
||||
if (!sp->base)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id,
|
||||
dev ? dev_name(dev) : NULL);
|
||||
if (!name)
|
||||
return NULL;
|
||||
|
||||
sp->dev = dev;
|
||||
sp->name = name;
|
||||
|
||||
if (flags & HOST1X_SYNCPT_CLIENT_MANAGED)
|
||||
sp->client_managed = true;
|
||||
else
|
||||
sp->client_managed = false;
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
u32 host1x_syncpt_id(struct host1x_syncpt *sp)
|
||||
{
|
||||
return sp->id;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_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);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_incr_max);
|
||||
|
||||
/*
|
||||
* Write cached syncpoint and waitbase values to hardware.
|
||||
*/
|
||||
void host1x_syncpt_restore(struct host1x *host)
|
||||
{
|
||||
struct host1x_syncpt *sp_base = host->syncpt;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
|
||||
host1x_hw_syncpt_restore(host, sp_base + i);
|
||||
for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
|
||||
host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the cached syncpoint and waitbase values by reading them
|
||||
* from the registers.
|
||||
*/
|
||||
void host1x_syncpt_save(struct host1x *host)
|
||||
{
|
||||
struct host1x_syncpt *sp_base = host->syncpt;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
|
||||
if (host1x_syncpt_client_managed(sp_base + i))
|
||||
host1x_hw_syncpt_load(host, sp_base + i);
|
||||
else
|
||||
WARN_ON(!host1x_syncpt_idle(sp_base + i));
|
||||
}
|
||||
|
||||
for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
|
||||
host1x_hw_syncpt_load_wait_base(host, sp_base + i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the cached syncpoint value by reading a new value from the hardware
|
||||
* register
|
||||
*/
|
||||
u32 host1x_syncpt_load(struct host1x_syncpt *sp)
|
||||
{
|
||||
u32 val;
|
||||
val = host1x_hw_syncpt_load(sp->host, sp);
|
||||
trace_host1x_syncpt_load_min(sp->id, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current syncpoint base
|
||||
*/
|
||||
u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp)
|
||||
{
|
||||
u32 val;
|
||||
host1x_hw_syncpt_load_wait_base(sp->host, sp);
|
||||
val = sp->base_val;
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment syncpoint value from cpu, updating cache
|
||||
*/
|
||||
int host1x_syncpt_incr(struct host1x_syncpt *sp)
|
||||
{
|
||||
return host1x_hw_syncpt_cpu_incr(sp->host, sp);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_incr);
|
||||
|
||||
/*
|
||||
* Updated sync point form hardware, and returns true if syncpoint is expired,
|
||||
* false if we may need to wait
|
||||
*/
|
||||
static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh)
|
||||
{
|
||||
host1x_hw_syncpt_load(sp->host, sp);
|
||||
return host1x_syncpt_is_expired(sp, thresh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main entrypoint for syncpoint value waits.
|
||||
*/
|
||||
int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
|
||||
u32 *value)
|
||||
{
|
||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
||||
void *ref;
|
||||
struct host1x_waitlist *waiter;
|
||||
int err = 0, check_count = 0;
|
||||
u32 val;
|
||||
|
||||
if (value)
|
||||
*value = 0;
|
||||
|
||||
/* first check cache */
|
||||
if (host1x_syncpt_is_expired(sp, thresh)) {
|
||||
if (value)
|
||||
*value = host1x_syncpt_load(sp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to read from register */
|
||||
val = host1x_hw_syncpt_load(sp->host, sp);
|
||||
if (host1x_syncpt_is_expired(sp, thresh)) {
|
||||
if (value)
|
||||
*value = val;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!timeout) {
|
||||
err = -EAGAIN;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* allocate a waiter */
|
||||
waiter = kzalloc(sizeof(*waiter), GFP_KERNEL);
|
||||
if (!waiter) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* schedule a wakeup when the syncpoint value is reached */
|
||||
err = host1x_intr_add_action(sp->host, sp->id, thresh,
|
||||
HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
|
||||
&wq, waiter, &ref);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = -EAGAIN;
|
||||
/* Caller-specified timeout may be impractically low */
|
||||
if (timeout < 0)
|
||||
timeout = LONG_MAX;
|
||||
|
||||
/* wait for the syncpoint, or timeout, or signal */
|
||||
while (timeout) {
|
||||
long check = min_t(long, SYNCPT_CHECK_PERIOD, timeout);
|
||||
int remain = wait_event_interruptible_timeout(wq,
|
||||
syncpt_load_min_is_expired(sp, thresh),
|
||||
check);
|
||||
if (remain > 0 || host1x_syncpt_is_expired(sp, thresh)) {
|
||||
if (value)
|
||||
*value = host1x_syncpt_load(sp);
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
if (remain < 0) {
|
||||
err = remain;
|
||||
break;
|
||||
}
|
||||
timeout -= check;
|
||||
if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) {
|
||||
dev_warn(sp->host->dev,
|
||||
"%s: syncpoint id %d (%s) stuck waiting %d, timeout=%ld\n",
|
||||
current->comm, sp->id, sp->name,
|
||||
thresh, timeout);
|
||||
|
||||
host1x_debug_dump_syncpts(sp->host);
|
||||
if (check_count == MAX_STUCK_CHECK_COUNT)
|
||||
host1x_debug_dump(sp->host);
|
||||
check_count++;
|
||||
}
|
||||
}
|
||||
host1x_intr_put_ref(sp->host, sp->id, ref);
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_wait);
|
||||
|
||||
/*
|
||||
* Returns true if syncpoint is expired, false if we may need to wait
|
||||
*/
|
||||
bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh)
|
||||
{
|
||||
u32 current_val;
|
||||
u32 future_val;
|
||||
smp_rmb();
|
||||
current_val = (u32)atomic_read(&sp->min_val);
|
||||
future_val = (u32)atomic_read(&sp->max_val);
|
||||
|
||||
/* Note the use of unsigned arithmetic here (mod 1<<32).
|
||||
*
|
||||
* c = current_val = min_val = the current value of the syncpoint.
|
||||
* t = thresh = the value we are checking
|
||||
* f = future_val = max_val = the value c will reach when all
|
||||
* outstanding increments have completed.
|
||||
*
|
||||
* Note that c always chases f until it reaches f.
|
||||
*
|
||||
* Dtf = (f - t)
|
||||
* Dtc = (c - t)
|
||||
*
|
||||
* Consider all cases:
|
||||
*
|
||||
* A) .....c..t..f..... Dtf < Dtc need to wait
|
||||
* B) .....c.....f..t.. Dtf > Dtc expired
|
||||
* C) ..t..c.....f..... Dtf > Dtc expired (Dct very large)
|
||||
*
|
||||
* Any case where f==c: always expired (for any t). Dtf == Dcf
|
||||
* Any case where t==c: always expired (for any f). Dtf >= Dtc (because Dtc==0)
|
||||
* Any case where t==f!=c: always wait. Dtf < Dtc (because Dtf==0,
|
||||
* Dtc!=0)
|
||||
*
|
||||
* Other cases:
|
||||
*
|
||||
* A) .....t..f..c..... Dtf < Dtc need to wait
|
||||
* A) .....f..c..t..... Dtf < Dtc need to wait
|
||||
* A) .....f..t..c..... Dtf > Dtc expired
|
||||
*
|
||||
* So:
|
||||
* Dtf >= Dtc implies EXPIRED (return true)
|
||||
* Dtf < Dtc implies WAIT (return false)
|
||||
*
|
||||
* Note: If t is expired then we *cannot* wait on it. We would wait
|
||||
* forever (hang the system).
|
||||
*
|
||||
* Note: do NOT get clever and remove the -thresh from both sides. It
|
||||
* is NOT the same.
|
||||
*
|
||||
* If future valueis zero, we have a client managed sync point. In that
|
||||
* case we do a direct comparison.
|
||||
*/
|
||||
if (!host1x_syncpt_client_managed(sp))
|
||||
return future_val - thresh >= current_val - thresh;
|
||||
else
|
||||
return (s32)(current_val - thresh) >= 0;
|
||||
}
|
||||
|
||||
/* remove a wait pointed to by patch_addr */
|
||||
int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr)
|
||||
{
|
||||
return host1x_hw_syncpt_patch_wait(sp->host, sp, patch_addr);
|
||||
}
|
||||
|
||||
int host1x_syncpt_init(struct host1x *host)
|
||||
{
|
||||
struct host1x_syncpt_base *bases;
|
||||
struct host1x_syncpt *syncpt;
|
||||
int i;
|
||||
|
||||
syncpt = devm_kzalloc(host->dev, sizeof(*syncpt) * host->info->nb_pts,
|
||||
GFP_KERNEL);
|
||||
if (!syncpt)
|
||||
return -ENOMEM;
|
||||
|
||||
bases = devm_kzalloc(host->dev, sizeof(*bases) * host->info->nb_bases,
|
||||
GFP_KERNEL);
|
||||
if (!bases)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < host->info->nb_pts; i++) {
|
||||
syncpt[i].id = i;
|
||||
syncpt[i].host = host;
|
||||
}
|
||||
|
||||
for (i = 0; i < host->info->nb_bases; i++)
|
||||
bases[i].id = i;
|
||||
|
||||
host->syncpt = syncpt;
|
||||
host->bases = bases;
|
||||
|
||||
host1x_syncpt_restore(host);
|
||||
|
||||
/* Allocate sync point to use for clearing waits for expired fences */
|
||||
host->nop_sp = host1x_syncpt_alloc(host, NULL, 0);
|
||||
if (!host->nop_sp)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct host1x *host = dev_get_drvdata(dev->parent);
|
||||
return host1x_syncpt_alloc(host, dev, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_request);
|
||||
|
||||
void host1x_syncpt_free(struct host1x_syncpt *sp)
|
||||
{
|
||||
if (!sp)
|
||||
return;
|
||||
|
||||
host1x_syncpt_base_free(sp->base);
|
||||
kfree(sp->name);
|
||||
sp->base = NULL;
|
||||
sp->dev = NULL;
|
||||
sp->name = NULL;
|
||||
sp->client_managed = false;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_free);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read max. It indicates how many operations there are in queue, either in
|
||||
* channel or in a software thread.
|
||||
* */
|
||||
u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
|
||||
{
|
||||
smp_rmb();
|
||||
return (u32)atomic_read(&sp->max_val);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_read_max);
|
||||
|
||||
/*
|
||||
* Read min, which is a shadow of the current sync point value in hardware.
|
||||
*/
|
||||
u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
|
||||
{
|
||||
smp_rmb();
|
||||
return (u32)atomic_read(&sp->min_val);
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_read_min);
|
||||
|
||||
int host1x_syncpt_nb_pts(struct host1x *host)
|
||||
{
|
||||
return host->info->nb_pts;
|
||||
}
|
||||
|
||||
int host1x_syncpt_nb_bases(struct host1x *host)
|
||||
{
|
||||
return host->info->nb_bases;
|
||||
}
|
||||
|
||||
int host1x_syncpt_nb_mlocks(struct host1x *host)
|
||||
{
|
||||
return host->info->nb_mlocks;
|
||||
}
|
||||
|
||||
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id)
|
||||
{
|
||||
if (host->info->nb_pts < id)
|
||||
return NULL;
|
||||
return host->syncpt + id;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_get);
|
||||
|
||||
struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp)
|
||||
{
|
||||
return sp ? sp->base : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_get_base);
|
||||
|
||||
u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)
|
||||
{
|
||||
return base->id;
|
||||
}
|
||||
EXPORT_SYMBOL(host1x_syncpt_base_id);
|
130
drivers/gpu/host1x/syncpt.h
Normal file
130
drivers/gpu/host1x/syncpt.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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/atomic.h>
|
||||
#include <linux/host1x.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "intr.h"
|
||||
|
||||
struct host1x;
|
||||
|
||||
/* Reserved for replacing an expired wait with a NOP */
|
||||
#define HOST1X_SYNCPT_RESERVED 0
|
||||
|
||||
struct host1x_syncpt_base {
|
||||
unsigned int id;
|
||||
bool requested;
|
||||
};
|
||||
|
||||
struct host1x_syncpt {
|
||||
int id;
|
||||
atomic_t min_val;
|
||||
atomic_t max_val;
|
||||
u32 base_val;
|
||||
const char *name;
|
||||
bool client_managed;
|
||||
struct host1x *host;
|
||||
struct device *dev;
|
||||
struct host1x_syncpt_base *base;
|
||||
|
||||
/* interrupt data */
|
||||
struct host1x_syncpt_intr intr;
|
||||
};
|
||||
|
||||
/* Initialize sync point array */
|
||||
int host1x_syncpt_init(struct host1x *host);
|
||||
|
||||
/* Free sync point array */
|
||||
void host1x_syncpt_deinit(struct host1x *host);
|
||||
|
||||
/* Return number of sync point supported. */
|
||||
int host1x_syncpt_nb_pts(struct host1x *host);
|
||||
|
||||
/* Return number of wait bases supported. */
|
||||
int host1x_syncpt_nb_bases(struct host1x *host);
|
||||
|
||||
/* Return number of mlocks supported. */
|
||||
int host1x_syncpt_nb_mlocks(struct host1x *host);
|
||||
|
||||
/*
|
||||
* 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 bool 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_idle(struct host1x_syncpt *sp)
|
||||
{
|
||||
int min, max;
|
||||
smp_rmb();
|
||||
min = atomic_read(&sp->min_val);
|
||||
max = atomic_read(&sp->max_val);
|
||||
return (min == max);
|
||||
}
|
||||
|
||||
/* Load current value from hardware to the shadow register. */
|
||||
u32 host1x_syncpt_load(struct host1x_syncpt *sp);
|
||||
|
||||
/* Check if the given syncpoint value has already passed */
|
||||
bool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh);
|
||||
|
||||
/* Save host1x sync point state into shadow registers. */
|
||||
void host1x_syncpt_save(struct host1x *host);
|
||||
|
||||
/* Reset host1x sync point state from shadow registers. */
|
||||
void host1x_syncpt_restore(struct host1x *host);
|
||||
|
||||
/* Read current wait base value into shadow register and return it. */
|
||||
u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp);
|
||||
|
||||
/* Indicate future operations by incrementing the sync point max. */
|
||||
u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs);
|
||||
|
||||
/* Check if sync point id is valid. */
|
||||
static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
|
||||
{
|
||||
return sp->id < host1x_syncpt_nb_pts(sp->host);
|
||||
}
|
||||
|
||||
/* Patch a wait by replacing it with a wait for syncpt 0 value 0 */
|
||||
int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *patch_addr);
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue