drm/tegra: Changes for v3.13-rc1

The biggest part of the changes is the decoupling of the host1x and DRM
 drivers followed by the move of Tegra DRM back to drivers/gpu/drm/tegra
 from whence it came. There is a lot of cleanup as well, and the drivers
 can now be properly unloaded and reloaded.
 
 HDMI support for the Tegra114 SoC was contributed by Mikko Perttunen.
 
 gr2d support was extended to Tegra114 and the gr3d driver that has been
 in the works for quite some time finally made it in. All pieces to run
 an OpenGL driver on top of an upstream kernel are now available.
 
 Support for syncpoint bases was added by Arto Merilainen. This is useful
 for synchronizing between command streams from different engines such as
 gr2d and gr3d.
 
 Erik Faye-Lund and Wei Yongjun contributed various small fixes. Thanks!
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJSch6LAAoJEN0jrNd/PrOhvYwP/ir8/pK0kWLdowfArmZqs1T7
 YhFy3zOCDkf2FKwNshsU8gwc8qeaCkP07LUBtjg+BvPdaHBQShLmwkNLKeV6tfLn
 A6gtCMkJG3l52PxoM9NtRIyq9Xot8Sv1H+PNSYjKgUDrIYNYkMhgG90+Aj409Qkw
 bbons3/CH58ufxY47wpGTFxhsiHX/9CSOlWl2RVfyzeyi5sVG+e+it3jsCl3ma9X
 lmCBJuZlaZsB1xy9kQaYobpL0BiNwhTOVeh0YmUQDBxFtIcY9j2h1dOCsh4o1sPp
 Aht/r8OaBxCu4ykAoGByaO/Bz6PQEaX1nW776edR1dusUBOupRjmyHcz1unq1Qpo
 PUI0zs823WrWX863FQcG+i71wdZDmRReGPduv+94H0IT6NtQEC2fT1eJdn2DtI0L
 GD3d8NR92hGdYVH01tEILlrT4Rrlemb0CWr+Vf6T3LwU44Jns/9X3UnG+GVE7IwT
 GreK0FjCTKeNZeMKGCJ0aKwjwEd7VMiFRiuEZfoemik8X3ib8f8oCeXIX1gD1ksz
 vGSaSjhx//X7/hco+1DSqdPOJnXd/ZUC0AkFQ0lIJ2mQvZfwHx55uE/AFjD1WE4/
 BNsTP+8C97IA/zAK241Ow+f1cLhRp1ZlgOVbFZp+EttKZ2vxRik/jV8MsBfhWJBX
 gIAXabh1MvhxagGaiFmb
 =qc03
 -----END PGP SIGNATURE-----

Merge tag 'drm/for-3.13-rc1' of git://anongit.freedesktop.org/tegra/linux into drm-next

drm/tegra: Changes for v3.13-rc1

The biggest part of the changes is the decoupling of the host1x and DRM
drivers followed by the move of Tegra DRM back to drivers/gpu/drm/tegra
from whence it came. There is a lot of cleanup as well, and the drivers
can now be properly unloaded and reloaded.

HDMI support for the Tegra114 SoC was contributed by Mikko Perttunen.

gr2d support was extended to Tegra114 and the gr3d driver that has been
in the works for quite some time finally made it in. All pieces to run
an OpenGL driver on top of an upstream kernel are now available.

Support for syncpoint bases was added by Arto Merilainen. This is useful
for synchronizing between command streams from different engines such as
gr2d and gr3d.

Erik Faye-Lund and Wei Yongjun contributed various small fixes. Thanks!

* tag 'drm/for-3.13-rc1' of git://anongit.freedesktop.org/tegra/linux: (45 commits)
  drm/tegra: Reserve syncpoint base for gr3d
  drm/tegra: Reserve base for gr2d
  drm/tegra: Deliver syncpoint base to user space
  gpu: host1x: Add syncpoint base support
  gpu: host1x: Add 'flags' field to syncpt request
  drm/tegra: Disable clock on probe failure
  gpu: host1x: Disable clock on probe failure
  drm/tegra: Support bottom-up buffer objects
  drm/tegra: Add support for tiled buffer objects
  drm/tegra: Add 3D support
  drm/tegra: Introduce tegra_drm_submit()
  drm/tegra: Use symbolic names for gr2d registers
  drm/tegra: Start connectors with correct DPMS mode
  drm/tegra: hdmi: Enable VDD earlier for hotplug/DDC
  drm/tegra: hdmi: Fix build warnings
  drm/tegra: hdmi: Detect DVI-only displays
  drm/tegra: Add Tegra114 HDMI support
  drm/tegra: hdmi: Parameterize based on compatible property
  drm/tegra: hdmi: Rename tegra{2,3} to tegra{20,30}
  gpu: host1x: Add support for Tegra114
  ...
This commit is contained in:
Dave Airlie 2013-11-05 16:21:00 +10:00
commit 90c37067b7
55 changed files with 3371 additions and 1268 deletions

View File

@ -2816,7 +2816,9 @@ L: dri-devel@lists.freedesktop.org
L: linux-tegra@vger.kernel.org
T: git git://anongit.freedesktop.org/tegra/linux.git
S: Maintained
F: drivers/gpu/drm/tegra/
F: drivers/gpu/host1x/
F: include/linux/host1x.h
F: include/uapi/drm/tegra_drm.h
F: Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt

View File

@ -189,3 +189,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig"
source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"

View File

@ -57,4 +57,5 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/
obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-y += i2c/

View File

@ -567,6 +567,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
continue;
connector->encoder = NULL;
/*
* drm_helper_disable_unused_functions() ought to be
* doing this, but since we've decoupled the encoder
* from the connector above, the required connection
* between them is henceforth no longer available.
*/
connector->dpms = DRM_MODE_DPMS_OFF;
}
}

View File

@ -396,7 +396,7 @@ long drm_ioctl(struct file *filp,
err_i1:
if (!ioctl)
DRM_DEBUG("invalid iotcl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->device),
file_priv->authenticated, cmd, nr);

View File

@ -1,6 +1,8 @@
config DRM_TEGRA
bool "NVIDIA Tegra DRM"
depends on ARCH_TEGRA || ARCH_MULTIPLATFORM
depends on DRM
select TEGRA_HOST1X
select DRM_KMS_HELPER
select DRM_KMS_FB_HELPER
select FB_SYS_FILLRECT
@ -14,6 +16,11 @@ config DRM_TEGRA
if DRM_TEGRA
config DRM_TEGRA_DEBUG
bool "NVIDIA Tegra DRM debug support"
help
Say yes here to enable debugging support.
config DRM_TEGRA_STAGING
bool "Enable HOST1X interface"
depends on STAGING
@ -22,9 +29,4 @@ config DRM_TEGRA_STAGING
If unsure, choose N.
config DRM_TEGRA_DEBUG
bool "NVIDIA Tegra DRM debug support"
help
Say yes here to enable debugging support.
endif

View File

@ -0,0 +1,15 @@
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
tegra-drm-y := \
bus.o \
drm.o \
gem.o \
fb.o \
dc.o \
output.o \
rgb.o \
hdmi.o \
gr2d.o \
gr3d.o
obj-$(CONFIG_DRM_TEGRA) += tegra-drm.o

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "drm.h"
static int drm_host1x_set_busid(struct drm_device *dev,
struct drm_master *master)
{
const char *device = dev_name(dev->dev);
const char *driver = dev->driver->name;
const char *bus = dev->dev->bus->name;
int length;
master->unique_len = strlen(bus) + 1 + strlen(device);
master->unique_size = master->unique_len;
master->unique = kmalloc(master->unique_len + 1, GFP_KERNEL);
if (!master->unique)
return -ENOMEM;
snprintf(master->unique, master->unique_len + 1, "%s:%s", bus, device);
length = strlen(driver) + 1 + master->unique_len;
dev->devname = kmalloc(length + 1, GFP_KERNEL);
if (!dev->devname)
return -ENOMEM;
snprintf(dev->devname, length + 1, "%s@%s", driver, master->unique);
return 0;
}
static struct drm_bus drm_host1x_bus = {
.bus_type = DRIVER_BUS_HOST1X,
.set_busid = drm_host1x_set_busid,
};
int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device)
{
struct drm_device *drm;
int ret;
INIT_LIST_HEAD(&driver->device_list);
driver->bus = &drm_host1x_bus;
drm = drm_dev_alloc(driver, &device->dev);
if (!drm)
return -ENOMEM;
ret = drm_dev_register(drm, 0);
if (ret)
goto err_free;
DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
driver->major, driver->minor, driver->patchlevel,
driver->date, drm->primary->index);
return 0;
err_free:
drm_dev_free(drm);
return ret;
}
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device)
{
struct tegra_drm *tegra = dev_get_drvdata(&device->dev);
drm_put_dev(tegra->drm);
}

View File

@ -8,13 +8,9 @@
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/clk/tegra.h>
#include <linux/debugfs.h>
#include "host1x_client.h"
#include "dc.h"
#include "drm.h"
#include "gem.h"
@ -51,6 +47,8 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
window.dst.h = crtc_h;
window.format = tegra_dc_format(fb->pixel_format);
window.bits_per_pixel = fb->bits_per_pixel;
window.bottom_up = tegra_fb_is_bottom_up(fb);
window.tiled = tegra_fb_is_tiled(fb);
for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@ -97,8 +95,11 @@ static int tegra_plane_disable(struct drm_plane *plane)
static void tegra_plane_destroy(struct drm_plane *plane)
{
struct tegra_plane *p = to_tegra_plane(plane);
tegra_plane_disable(plane);
drm_plane_cleanup(plane);
kfree(p);
}
static const struct drm_plane_funcs tegra_plane_funcs = {
@ -124,7 +125,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
for (i = 0; i < 2; i++) {
struct tegra_plane *plane;
plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
if (!plane)
return -ENOMEM;
@ -133,8 +134,10 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
err = drm_plane_init(drm, &plane->base, 1 << dc->pipe,
&tegra_plane_funcs, plane_formats,
ARRAY_SIZE(plane_formats), false);
if (err < 0)
if (err < 0) {
kfree(plane);
return err;
}
}
return 0;
@ -145,6 +148,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
{
unsigned int format = tegra_dc_format(fb->pixel_format);
struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
unsigned int h_offset = 0, v_offset = 0;
unsigned long value;
tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@ -156,6 +160,32 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
if (tegra_fb_is_tiled(fb)) {
value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
DC_WIN_BUFFER_ADDR_MODE_TILE;
} else {
value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
DC_WIN_BUFFER_ADDR_MODE_LINEAR;
}
tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
/* make sure bottom-up buffers are properly displayed */
if (tegra_fb_is_bottom_up(fb)) {
value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
value |= INVERT_V;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
v_offset += fb->height - 1;
} else {
value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
value &= ~INVERT_V;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
}
tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
value = GENERAL_UPDATE | WIN_A_UPDATE;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
@ -255,14 +285,26 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
return 0;
}
static void drm_crtc_clear(struct drm_crtc *crtc)
{
memset(crtc, 0, sizeof(*crtc));
}
static void tegra_dc_destroy(struct drm_crtc *crtc)
{
drm_crtc_cleanup(crtc);
drm_crtc_clear(crtc);
}
static const struct drm_crtc_funcs tegra_crtc_funcs = {
.page_flip = tegra_dc_page_flip,
.set_config = drm_crtc_helper_set_config,
.destroy = drm_crtc_cleanup,
.destroy = tegra_dc_destroy,
};
static void tegra_crtc_disable(struct drm_crtc *crtc)
{
struct tegra_dc *dc = to_tegra_dc(crtc);
struct drm_device *drm = crtc->dev;
struct drm_plane *plane;
@ -277,6 +319,8 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
}
}
}
drm_vblank_off(drm, dc->pipe);
}
static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@ -491,9 +535,22 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
}
if (window->bottom_up)
v_offset += window->src.h - 1;
tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
if (window->tiled) {
value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
DC_WIN_BUFFER_ADDR_MODE_TILE;
} else {
value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
DC_WIN_BUFFER_ADDR_MODE_LINEAR;
}
tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
value = WIN_ENABLE;
if (yuv) {
@ -512,6 +569,9 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
value |= COLOR_EXPAND;
}
if (window->bottom_up)
value |= INVERT_V;
tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
/*
@ -1041,30 +1101,30 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
return 0;
}
static int tegra_dc_drm_init(struct host1x_client *client,
struct drm_device *drm)
static int tegra_dc_init(struct host1x_client *client)
{
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct tegra_dc *dc = host1x_client_to_dc(client);
int err;
dc->pipe = drm->mode_config.num_crtc;
dc->pipe = tegra->drm->mode_config.num_crtc;
drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs);
drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
drm_mode_crtc_set_gamma_size(&dc->base, 256);
drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
err = tegra_dc_rgb_init(drm, dc);
err = tegra_dc_rgb_init(tegra->drm, dc);
if (err < 0 && err != -ENODEV) {
dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
return err;
}
err = tegra_dc_add_planes(drm, dc);
err = tegra_dc_add_planes(tegra->drm, dc);
if (err < 0)
return err;
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_dc_debugfs_init(dc, drm->primary);
err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
if (err < 0)
dev_err(dc->dev, "debugfs setup failed: %d\n", err);
}
@ -1080,7 +1140,7 @@ static int tegra_dc_drm_init(struct host1x_client *client,
return 0;
}
static int tegra_dc_drm_exit(struct host1x_client *client)
static int tegra_dc_exit(struct host1x_client *client)
{
struct tegra_dc *dc = host1x_client_to_dc(client);
int err;
@ -1103,13 +1163,12 @@ static int tegra_dc_drm_exit(struct host1x_client *client)
}
static const struct host1x_client_ops dc_client_ops = {
.drm_init = tegra_dc_drm_init,
.drm_exit = tegra_dc_drm_exit,
.init = tegra_dc_init,
.exit = tegra_dc_exit,
};
static int tegra_dc_probe(struct platform_device *pdev)
{
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct resource *regs;
struct tegra_dc *dc;
int err;
@ -1153,7 +1212,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
return err;
}
err = host1x_register_client(host1x, &dc->client);
err = host1x_client_register(&dc->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
@ -1167,17 +1226,22 @@ static int tegra_dc_probe(struct platform_device *pdev)
static int tegra_dc_remove(struct platform_device *pdev)
{
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct tegra_dc *dc = platform_get_drvdata(pdev);
int err;
err = host1x_unregister_client(host1x, &dc->client);
err = host1x_client_unregister(&dc->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
return err;
}
err = tegra_dc_rgb_remove(dc);
if (err < 0) {
dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
return err;
}
clk_disable_unprepare(dc->clk);
return 0;

View File

@ -302,6 +302,7 @@
#define DC_WIN_CSC_KVB 0x618
#define DC_WIN_WIN_OPTIONS 0x700
#define INVERT_V (1 << 2)
#define COLOR_EXPAND (1 << 6)
#define CSC_ENABLE (1 << 18)
#define WIN_ENABLE (1 << 30)
@ -365,6 +366,10 @@
#define DC_WIN_BUF_STRIDE 0x70b
#define DC_WIN_UV_BUF_STRIDE 0x70c
#define DC_WIN_BUFFER_ADDR_MODE 0x70d
#define DC_WIN_BUFFER_ADDR_MODE_LINEAR (0 << 0)
#define DC_WIN_BUFFER_ADDR_MODE_TILE (1 << 0)
#define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV (0 << 16)
#define DC_WIN_BUFFER_ADDR_MODE_TILE_UV (1 << 16)
#define DC_WIN_DV_CONTROL 0x70e
#define DC_WIN_BLEND_NOKEY 0x70f

View File

@ -7,21 +7,10 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/host1x.h>
#include <linux/dma-mapping.h>
#include <asm/dma-iommu.h>
#include <drm/drm.h>
#include <drm/drmP.h>
#include "host1x_client.h"
#include "dev.h"
#include "drm.h"
#include "gem.h"
#include "syncpt.h"
#define DRIVER_NAME "tegra"
#define DRIVER_DESC "NVIDIA Tegra graphics"
@ -30,232 +19,29 @@
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0
struct host1x_drm_client {
struct host1x_client *client;
struct device_node *np;
struct list_head list;
struct tegra_drm_file {
struct list_head contexts;
};
static int host1x_add_drm_client(struct host1x_drm *host1x,
struct device_node *np)
{
struct host1x_drm_client *client;
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client)
return -ENOMEM;
INIT_LIST_HEAD(&client->list);
client->np = of_node_get(np);
list_add_tail(&client->list, &host1x->drm_clients);
return 0;
}
static int host1x_activate_drm_client(struct host1x_drm *host1x,
struct host1x_drm_client *drm,
struct host1x_client *client)
{
mutex_lock(&host1x->drm_clients_lock);
list_del_init(&drm->list);
list_add_tail(&drm->list, &host1x->drm_active);
drm->client = client;
mutex_unlock(&host1x->drm_clients_lock);
return 0;
}
static int host1x_remove_drm_client(struct host1x_drm *host1x,
struct host1x_drm_client *client)
{
mutex_lock(&host1x->drm_clients_lock);
list_del_init(&client->list);
mutex_unlock(&host1x->drm_clients_lock);
of_node_put(client->np);
kfree(client);
return 0;
}
static int host1x_parse_dt(struct host1x_drm *host1x)
{
static const char * const compat[] = {
"nvidia,tegra20-dc",
"nvidia,tegra20-hdmi",
"nvidia,tegra20-gr2d",
"nvidia,tegra30-dc",
"nvidia,tegra30-hdmi",
"nvidia,tegra30-gr2d",
};
unsigned int i;
int err;
for (i = 0; i < ARRAY_SIZE(compat); i++) {
struct device_node *np;
for_each_child_of_node(host1x->dev->of_node, np) {
if (of_device_is_compatible(np, compat[i]) &&
of_device_is_available(np)) {
err = host1x_add_drm_client(host1x, np);
if (err < 0)
return err;
}
}
}
return 0;
}
int host1x_drm_alloc(struct platform_device *pdev)
{
struct host1x_drm *host1x;
int err;
host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
if (!host1x)
return -ENOMEM;
mutex_init(&host1x->drm_clients_lock);
INIT_LIST_HEAD(&host1x->drm_clients);
INIT_LIST_HEAD(&host1x->drm_active);
mutex_init(&host1x->clients_lock);
INIT_LIST_HEAD(&host1x->clients);
host1x->dev = &pdev->dev;
err = host1x_parse_dt(host1x);
if (err < 0) {
dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
return err;
}
host1x_set_drm_data(&pdev->dev, host1x);
return 0;
}
int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
{
struct host1x_client *client;
mutex_lock(&host1x->clients_lock);
list_for_each_entry(client, &host1x->clients, list) {
if (client->ops && client->ops->drm_init) {
int err = client->ops->drm_init(client, drm);
if (err < 0) {
dev_err(host1x->dev,
"DRM setup failed for %s: %d\n",
dev_name(client->dev), err);
mutex_unlock(&host1x->clients_lock);
return err;
}
}
}
mutex_unlock(&host1x->clients_lock);
return 0;
}
int host1x_drm_exit(struct host1x_drm *host1x)
{
struct platform_device *pdev = to_platform_device(host1x->dev);
struct host1x_client *client;
if (!host1x->drm)
return 0;
mutex_lock(&host1x->clients_lock);
list_for_each_entry_reverse(client, &host1x->clients, list) {
if (client->ops && client->ops->drm_exit) {
int err = client->ops->drm_exit(client);
if (err < 0) {
dev_err(host1x->dev,
"DRM cleanup failed for %s: %d\n",
dev_name(client->dev), err);
mutex_unlock(&host1x->clients_lock);
return err;
}
}
}
mutex_unlock(&host1x->clients_lock);
drm_platform_exit(&tegra_drm_driver, pdev);
host1x->drm = NULL;
return 0;
}
int host1x_register_client(struct host1x_drm *host1x,
struct host1x_client *client)
{
struct host1x_drm_client *drm, *tmp;
int err;
mutex_lock(&host1x->clients_lock);
list_add_tail(&client->list, &host1x->clients);
mutex_unlock(&host1x->clients_lock);
list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
if (drm->np == client->dev->of_node)
host1x_activate_drm_client(host1x, drm, client);
if (list_empty(&host1x->drm_clients)) {
struct platform_device *pdev = to_platform_device(host1x->dev);
err = drm_platform_init(&tegra_drm_driver, pdev);
if (err < 0) {
dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
return err;
}
}
return 0;
}
int host1x_unregister_client(struct host1x_drm *host1x,
struct host1x_client *client)
{
struct host1x_drm_client *drm, *tmp;
int err;
list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
if (drm->client == client) {
err = host1x_drm_exit(host1x);
if (err < 0) {
dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
err);
return err;
}
host1x_remove_drm_client(host1x, drm);
break;
}
}
mutex_lock(&host1x->clients_lock);
list_del_init(&client->list);
mutex_unlock(&host1x->clients_lock);
return 0;
}
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
{
struct host1x_drm *host1x;
struct host1x_device *device = to_host1x_device(drm->dev);
struct tegra_drm *tegra;
int err;
host1x = host1x_get_drm_data(drm->dev);
drm->dev_private = host1x;
host1x->drm = drm;
tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
dev_set_drvdata(drm->dev, tegra);
mutex_init(&tegra->clients_lock);
INIT_LIST_HEAD(&tegra->clients);
drm->dev_private = tegra;
tegra->drm = drm;
drm_mode_config_init(drm);
err = host1x_drm_init(host1x, drm);
err = host1x_device_init(device);
if (err < 0)
return err;
@ -281,17 +67,24 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
static int tegra_drm_unload(struct drm_device *drm)
{
struct host1x_device *device = to_host1x_device(drm->dev);
int err;
drm_kms_helper_poll_fini(drm);
tegra_drm_fb_exit(drm);
drm_vblank_cleanup(drm);
drm_mode_config_cleanup(drm);
err = host1x_device_exit(device);
if (err < 0)
return err;
return 0;
}
static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
{
struct host1x_drm_file *fpriv;
struct tegra_drm_file *fpriv;
fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
if (!fpriv)
@ -303,7 +96,7 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
return 0;
}
static void host1x_drm_context_free(struct host1x_drm_context *context)
static void tegra_drm_context_free(struct tegra_drm_context *context)
{
context->client->ops->close_channel(context);
kfree(context);
@ -311,16 +104,150 @@ static void host1x_drm_context_free(struct host1x_drm_context *context)
static void tegra_drm_lastclose(struct drm_device *drm)
{
struct host1x_drm *host1x = drm->dev_private;
struct tegra_drm *tegra = drm->dev_private;
tegra_fbdev_restore_mode(host1x->fbdev);
tegra_fbdev_restore_mode(tegra->fbdev);
}
#ifdef CONFIG_DRM_TEGRA_STAGING
static bool host1x_drm_file_owns_context(struct host1x_drm_file *file,
struct host1x_drm_context *context)
static struct host1x_bo *
host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
{
struct host1x_drm_context *ctx;
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(drm, file, handle);
if (!gem)
return NULL;
mutex_lock(&drm->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&drm->struct_mutex);
bo = to_tegra_bo(gem);
return &bo->base;
}
int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file)
{
unsigned int num_cmdbufs = args->num_cmdbufs;
unsigned int num_relocs = args->num_relocs;
unsigned int num_waitchks = args->num_waitchks;
struct drm_tegra_cmdbuf __user *cmdbufs =
(void * __user)(uintptr_t)args->cmdbufs;
struct drm_tegra_reloc __user *relocs =
(void * __user)(uintptr_t)args->relocs;
struct drm_tegra_waitchk __user *waitchks =
(void * __user)(uintptr_t)args->waitchks;
struct drm_tegra_syncpt syncpt;
struct host1x_job *job;
int err;
/* We don't yet support other than one syncpt_incr struct per submit */
if (args->num_syncpts != 1)
return -EINVAL;
job = host1x_job_alloc(context->channel, args->num_cmdbufs,
args->num_relocs, args->num_waitchks);
if (!job)
return -ENOMEM;
job->num_relocs = args->num_relocs;
job->num_waitchk = args->num_waitchks;
job->client = (u32)args->context;
job->class = context->client->base.class;
job->serialize = true;
while (num_cmdbufs) {
struct drm_tegra_cmdbuf cmdbuf;
struct host1x_bo *bo;
err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
if (err)
goto fail;
bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
if (!bo) {
err = -ENOENT;
goto fail;
}
host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
num_cmdbufs--;
cmdbufs++;
}
err = copy_from_user(job->relocarray, relocs,
sizeof(*relocs) * num_relocs);
if (err)
goto fail;
while (num_relocs--) {
struct host1x_reloc *reloc = &job->relocarray[num_relocs];
struct host1x_bo *cmdbuf, *target;
cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
target = host1x_bo_lookup(drm, file, (u32)reloc->target);
reloc->cmdbuf = cmdbuf;
reloc->target = target;
if (!reloc->target || !reloc->cmdbuf) {
err = -ENOENT;
goto fail;
}
}
err = copy_from_user(job->waitchk, waitchks,
sizeof(*waitchks) * num_waitchks);
if (err)
goto fail;
err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
sizeof(syncpt));
if (err)
goto fail;
job->is_addr_reg = context->client->ops->is_addr_reg;
job->syncpt_incrs = syncpt.incrs;
job->syncpt_id = syncpt.id;
job->timeout = 10000;
if (args->timeout && args->timeout < 10000)
job->timeout = args->timeout;
err = host1x_job_pin(job, context->client->base.dev);
if (err)
goto fail;
err = host1x_job_submit(job);
if (err)
goto fail_submit;
args->fence = job->syncpt_end;
host1x_job_put(job);
return 0;
fail_submit:
host1x_job_unpin(job);
fail:
host1x_job_put(job);
return err;
}
#ifdef CONFIG_DRM_TEGRA_STAGING
static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
{
return (struct tegra_drm_context *)(uintptr_t)context;
}
static bool tegra_drm_file_owns_context(struct tegra_drm_file *file,
struct tegra_drm_context *context)
{
struct tegra_drm_context *ctx;
list_for_each_entry(ctx, &file->contexts, list)
if (ctx == context)
@ -335,7 +262,7 @@ static int tegra_gem_create(struct drm_device *drm, void *data,
struct drm_tegra_gem_create *args = data;
struct tegra_bo *bo;
bo = tegra_bo_create_with_handle(file, drm, args->size,
bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
&args->handle);
if (IS_ERR(bo))
return PTR_ERR(bo);
@ -366,10 +293,11 @@ static int tegra_gem_mmap(struct drm_device *drm, void *data,
static int tegra_syncpt_read(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct host1x *host = dev_get_drvdata(drm->dev->parent);
struct drm_tegra_syncpt_read *args = data;
struct host1x *host = dev_get_drvdata(drm->dev);
struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id);
struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host, args->id);
if (!sp)
return -EINVAL;
@ -380,10 +308,11 @@ static int tegra_syncpt_read(struct drm_device *drm, void *data,
static int tegra_syncpt_incr(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
struct drm_tegra_syncpt_incr *args = data;
struct host1x *host = dev_get_drvdata(drm->dev);
struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id);
struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host1x, args->id);
if (!sp)
return -EINVAL;
@ -393,10 +322,11 @@ static int tegra_syncpt_incr(struct drm_device *drm, void *data,
static int tegra_syncpt_wait(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
struct drm_tegra_syncpt_wait *args = data;
struct host1x *host = dev_get_drvdata(drm->dev);
struct host1x_syncpt *sp = host1x_syncpt_get(host, args->id);
struct host1x_syncpt *sp;
sp = host1x_syncpt_get(host1x, args->id);
if (!sp)
return -EINVAL;
@ -407,26 +337,26 @@ static int tegra_syncpt_wait(struct drm_device *drm, void *data,
static int tegra_open_channel(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_open_channel *args = data;
struct host1x_client *client;
struct host1x_drm_context *context;
struct host1x_drm_file *fpriv = file->driver_priv;
struct host1x_drm *host1x = drm->dev_private;
struct tegra_drm_context *context;
struct tegra_drm_client *client;
int err = -ENODEV;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return -ENOMEM;
list_for_each_entry(client, &host1x->clients, list)
if (client->class == args->client) {
list_for_each_entry(client, &tegra->clients, list)
if (client->base.class == args->client) {
err = client->ops->open_channel(client, context);
if (err)
break;
context->client = client;
list_add(&context->list, &fpriv->contexts);
args->context = (uintptr_t)context;
context->client = client;
return 0;
}
@ -437,16 +367,17 @@ static int tegra_open_channel(struct drm_device *drm, void *data,
static int tegra_close_channel(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_close_channel *args = data;
struct host1x_drm_file *fpriv = file->driver_priv;
struct host1x_drm_context *context =
(struct host1x_drm_context *)(uintptr_t)args->context;
struct tegra_drm_context *context;
if (!host1x_drm_file_owns_context(fpriv, context))
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -EINVAL;
list_del(&context->list);
host1x_drm_context_free(context);
tegra_drm_context_free(context);
return 0;
}
@ -454,19 +385,20 @@ static int tegra_close_channel(struct drm_device *drm, void *data,
static int tegra_get_syncpt(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_get_syncpt *args = data;
struct host1x_drm_file *fpriv = file->driver_priv;
struct host1x_drm_context *context =
(struct host1x_drm_context *)(uintptr_t)args->context;
struct tegra_drm_context *context;
struct host1x_syncpt *syncpt;
if (!host1x_drm_file_owns_context(fpriv, context))
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -ENODEV;
if (args->index >= context->client->num_syncpts)
if (args->index >= context->client->base.num_syncpts)
return -EINVAL;
syncpt = context->client->syncpts[args->index];
syncpt = context->client->base.syncpts[args->index];
args->id = host1x_syncpt_id(syncpt);
return 0;
@ -475,16 +407,45 @@ static int tegra_get_syncpt(struct drm_device *drm, void *data,
static int tegra_submit(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_submit *args = data;
struct host1x_drm_file *fpriv = file->driver_priv;
struct host1x_drm_context *context =
(struct host1x_drm_context *)(uintptr_t)args->context;
struct tegra_drm_context *context;
if (!host1x_drm_file_owns_context(fpriv, context))
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -ENODEV;
return context->client->ops->submit(context, args, drm, file);
}
static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_get_syncpt_base *args = data;
struct tegra_drm_context *context;
struct host1x_syncpt_base *base;
struct host1x_syncpt *syncpt;
context = tegra_drm_get_context(args->context);
if (!tegra_drm_file_owns_context(fpriv, context))
return -ENODEV;
if (args->syncpt >= context->client->base.num_syncpts)
return -EINVAL;
syncpt = context->client->base.syncpts[args->syncpt];
base = host1x_syncpt_get_base(syncpt);
if (!base)
return -ENXIO;
args->id = host1x_syncpt_base_id(base);
return 0;
}
#endif
static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
@ -498,6 +459,7 @@ static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED),
#endif
};
@ -559,15 +521,15 @@ static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
{
struct host1x_drm_file *fpriv = file->driver_priv;
struct host1x_drm_context *context, *tmp;
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm_context *context, *tmp;
struct drm_crtc *crtc;
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
tegra_dc_cancel_page_flip(crtc, file);
list_for_each_entry_safe(context, tmp, &fpriv->contexts, list)
host1x_drm_context_free(context);
tegra_drm_context_free(context);
kfree(fpriv);
}
@ -645,3 +607,108 @@ struct drm_driver tegra_drm_driver = {
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
};
int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client)
{
mutex_lock(&tegra->clients_lock);
list_add_tail(&client->list, &tegra->clients);
mutex_unlock(&tegra->clients_lock);
return 0;
}
int tegra_drm_unregister_client(struct tegra_drm *tegra,
struct tegra_drm_client *client)
{
mutex_lock(&tegra->clients_lock);
list_del_init(&client->list);
mutex_unlock(&tegra->clients_lock);
return 0;
}
static int host1x_drm_probe(struct host1x_device *device)
{
return drm_host1x_init(&tegra_drm_driver, device);
}
static int host1x_drm_remove(struct host1x_device *device)
{
drm_host1x_exit(&tegra_drm_driver, device);
return 0;
}
static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra20-dc", },
{ .compatible = "nvidia,tegra20-hdmi", },
{ .compatible = "nvidia,tegra20-gr2d", },
{ .compatible = "nvidia,tegra20-gr3d", },
{ .compatible = "nvidia,tegra30-dc", },
{ .compatible = "nvidia,tegra30-hdmi", },
{ .compatible = "nvidia,tegra30-gr2d", },
{ .compatible = "nvidia,tegra30-gr3d", },
{ .compatible = "nvidia,tegra114-hdmi", },
{ .compatible = "nvidia,tegra114-gr3d", },
{ /* sentinel */ }
};
static struct host1x_driver host1x_drm_driver = {
.name = "drm",
.probe = host1x_drm_probe,
.remove = host1x_drm_remove,
.subdevs = host1x_drm_subdevs,
};
static int __init host1x_drm_init(void)
{
int err;
err = host1x_driver_register(&host1x_drm_driver);
if (err < 0)
return err;
err = platform_driver_register(&tegra_dc_driver);
if (err < 0)
goto unregister_host1x;
err = platform_driver_register(&tegra_hdmi_driver);
if (err < 0)
goto unregister_dc;
err = platform_driver_register(&tegra_gr2d_driver);
if (err < 0)
goto unregister_hdmi;
err = platform_driver_register(&tegra_gr3d_driver);
if (err < 0)
goto unregister_gr2d;
return 0;
unregister_gr2d:
platform_driver_unregister(&tegra_gr2d_driver);
unregister_hdmi:
platform_driver_unregister(&tegra_hdmi_driver);
unregister_dc:
platform_driver_unregister(&tegra_dc_driver);
unregister_host1x:
host1x_driver_unregister(&host1x_drm_driver);
return err;
}
module_init(host1x_drm_init);
static void __exit host1x_drm_exit(void)
{
platform_driver_unregister(&tegra_gr3d_driver);
platform_driver_unregister(&tegra_gr2d_driver);
platform_driver_unregister(&tegra_hdmi_driver);
platform_driver_unregister(&tegra_dc_driver);
host1x_driver_unregister(&host1x_drm_driver);
}
module_exit(host1x_drm_exit);
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
MODULE_LICENSE("GPL v2");

View File

@ -10,14 +10,14 @@
#ifndef HOST1X_DRM_H
#define HOST1X_DRM_H 1
#include <uapi/drm/tegra_drm.h>
#include <linux/host1x.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fixed.h>
#include <uapi/drm/tegra_drm.h>
#include "host1x.h"
struct tegra_fb {
struct drm_framebuffer base;
@ -30,17 +30,8 @@ struct tegra_fbdev {
struct tegra_fb *fb;
};
struct host1x_drm {
struct tegra_drm {
struct drm_device *drm;
struct device *dev;
void __iomem *regs;
struct clk *clk;
int syncpt;
int irq;
struct mutex drm_clients_lock;
struct list_head drm_clients;
struct list_head drm_active;
struct mutex clients_lock;
struct list_head clients;
@ -48,66 +39,60 @@ struct host1x_drm {
struct tegra_fbdev *fbdev;
};
struct host1x_client;
struct tegra_drm_client;
struct host1x_drm_context {
struct host1x_client *client;
struct tegra_drm_context {
struct tegra_drm_client *client;
struct host1x_channel *channel;
struct list_head list;
};
struct host1x_client_ops {
int (*drm_init)(struct host1x_client *client, struct drm_device *drm);
int (*drm_exit)(struct host1x_client *client);
int (*open_channel)(struct host1x_client *client,
struct host1x_drm_context *context);
void (*close_channel)(struct host1x_drm_context *context);
int (*submit)(struct host1x_drm_context *context,
struct tegra_drm_client_ops {
int (*open_channel)(struct tegra_drm_client *client,
struct tegra_drm_context *context);
void (*close_channel)(struct tegra_drm_context *context);
int (*is_addr_reg)(struct device *dev, u32 class, u32 offset);
int (*submit)(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
};
struct host1x_drm_file {
struct list_head contexts;
};
struct host1x_client {
struct host1x_drm *host1x;
struct device *dev;
const struct host1x_client_ops *ops;
enum host1x_class class;
struct host1x_channel *channel;
struct host1x_syncpt **syncpts;
unsigned int num_syncpts;
int tegra_drm_submit(struct tegra_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file);
struct tegra_drm_client {
struct host1x_client base;
struct list_head list;
const struct tegra_drm_client_ops *ops;
};
extern int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm);
extern int host1x_drm_exit(struct host1x_drm *host1x);
static inline struct tegra_drm_client *
host1x_to_drm_client(struct host1x_client *client)
{
return container_of(client, struct tegra_drm_client, base);
}
extern int host1x_register_client(struct host1x_drm *host1x,
struct host1x_client *client);
extern int host1x_unregister_client(struct host1x_drm *host1x,
struct host1x_client *client);
extern int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
extern int tegra_drm_unregister_client(struct tegra_drm *tegra,
struct tegra_drm_client *client);
extern int tegra_drm_init(struct tegra_drm *tegra, struct drm_device *drm);
extern int tegra_drm_exit(struct tegra_drm *tegra);
struct tegra_output;
struct tegra_dc {
struct host1x_client client;
spinlock_t lock;
struct host1x_drm *host1x;
struct device *dev;
spinlock_t lock;
struct drm_crtc base;
int pipe;
struct clk *clk;
void __iomem *regs;
int irq;
@ -123,7 +108,8 @@ struct tegra_dc {
struct drm_pending_vblank_event *event;
};
static inline struct tegra_dc *host1x_client_to_dc(struct host1x_client *client)
static inline struct tegra_dc *
host1x_client_to_dc(struct host1x_client *client)
{
return container_of(client, struct tegra_dc, client);
}
@ -162,6 +148,8 @@ struct tegra_dc_window {
unsigned int format;
unsigned int stride[2];
unsigned long base[3];
bool bottom_up;
bool tiled;
};
/* from dc.c */
@ -249,23 +237,34 @@ static inline int tegra_output_check_mode(struct tegra_output *output,
return output ? -ENOSYS : -EINVAL;
}
/* from bus.c */
int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device);
void drm_host1x_exit(struct drm_driver *driver, struct host1x_device *device);
/* from rgb.c */
extern int tegra_dc_rgb_probe(struct tegra_dc *dc);
extern int tegra_dc_rgb_remove(struct tegra_dc *dc);
extern int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
extern int tegra_dc_rgb_exit(struct tegra_dc *dc);
/* from output.c */
extern int tegra_output_parse_dt(struct tegra_output *output);
extern int tegra_output_probe(struct tegra_output *output);
extern int tegra_output_remove(struct tegra_output *output);
extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
extern int tegra_output_exit(struct tegra_output *output);
/* from fb.c */
struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
unsigned int index);
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
extern int tegra_drm_fb_init(struct drm_device *drm);
extern void tegra_drm_fb_exit(struct drm_device *drm);
extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
extern struct drm_driver tegra_drm_driver;
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_gr2d_driver;
extern struct platform_driver tegra_gr3d_driver;
#endif /* HOST1X_DRM_H */

View File

@ -10,8 +10,6 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include "drm.h"
#include "gem.h"
@ -36,6 +34,26 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
return fb->planes[index];
}
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP)
return true;
return false;
}
bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
if (fb->planes[0]->flags & TEGRA_BO_TILED)
return true;
return false;
}
static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
{
struct tegra_fb *fb = to_tegra_fb(framebuffer);
@ -190,7 +208,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
size = cmd.pitches[0] * cmd.height;
bo = tegra_bo_create(drm, size);
bo = tegra_bo_create(drm, size, 0);
if (IS_ERR(bo))
return PTR_ERR(bo);
@ -323,10 +341,10 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
static void tegra_fb_output_poll_changed(struct drm_device *drm)
{
struct host1x_drm *host1x = drm->dev_private;
struct tegra_drm *tegra = drm->dev_private;
if (host1x->fbdev)
drm_fb_helper_hotplug_event(&host1x->fbdev->base);
if (tegra->fbdev)
drm_fb_helper_hotplug_event(&tegra->fbdev->base);
}
static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
@ -336,7 +354,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
int tegra_drm_fb_init(struct drm_device *drm)
{
struct host1x_drm *host1x = drm->dev_private;
struct tegra_drm *tegra = drm->dev_private;
struct tegra_fbdev *fbdev;
drm->mode_config.min_width = 0;
@ -352,16 +370,16 @@ int tegra_drm_fb_init(struct drm_device *drm)
if (IS_ERR(fbdev))
return PTR_ERR(fbdev);
host1x->fbdev = fbdev;
tegra->fbdev = fbdev;
return 0;
}
void tegra_drm_fb_exit(struct drm_device *drm)
{
struct host1x_drm *host1x = drm->dev_private;
struct tegra_drm *tegra = drm->dev_private;
tegra_fbdev_free(host1x->fbdev);
tegra_fbdev_free(tegra->fbdev);
}
void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)

View File

@ -18,25 +18,18 @@
* GNU General Public License for more details.
*/
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/export.h>
#include <linux/dma-mapping.h>
#include <drm/drmP.h>
#include <drm/drm.h>
#include <drm/tegra_drm.h>
#include "gem.h"
static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo)
static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo)
{
return container_of(bo, struct tegra_bo, base);
}
static void tegra_bo_put(struct host1x_bo *bo)
{
struct tegra_bo *obj = host1x_to_drm_bo(bo);
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
struct drm_device *drm = obj->gem.dev;
mutex_lock(&drm->struct_mutex);
@ -46,7 +39,7 @@ static void tegra_bo_put(struct host1x_bo *bo)
static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt)
{
struct tegra_bo *obj = host1x_to_drm_bo(bo);
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
return obj->paddr;
}
@ -57,7 +50,7 @@ static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
static void *tegra_bo_mmap(struct host1x_bo *bo)
{
struct tegra_bo *obj = host1x_to_drm_bo(bo);
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
return obj->vaddr;
}
@ -68,7 +61,7 @@ static void tegra_bo_munmap(struct host1x_bo *bo, void *addr)
static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page)
{
struct tegra_bo *obj = host1x_to_drm_bo(bo);
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
return obj->vaddr + page * PAGE_SIZE;
}
@ -80,7 +73,7 @@ static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page,
static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo)
{
struct tegra_bo *obj = host1x_to_drm_bo(bo);
struct tegra_bo *obj = host1x_to_tegra_bo(bo);
struct drm_device *drm = obj->gem.dev;
mutex_lock(&drm->struct_mutex);
@ -106,7 +99,8 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr);
}
struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
unsigned long flags)
{
struct tegra_bo *bo;
int err;
@ -135,6 +129,12 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
if (err)
goto err_mmap;
if (flags & DRM_TEGRA_GEM_CREATE_TILED)
bo->flags |= TEGRA_BO_TILED;
if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP)
bo->flags |= TEGRA_BO_BOTTOM_UP;
return bo;
err_mmap:
@ -149,14 +149,15 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
}
struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
struct drm_device *drm,
unsigned int size,
unsigned int *handle)
struct drm_device *drm,
unsigned int size,
unsigned long flags,
unsigned int *handle)
{
struct tegra_bo *bo;
int ret;
bo = tegra_bo_create(drm, size);
bo = tegra_bo_create(drm, size, flags);
if (IS_ERR(bo))
return bo;
@ -178,7 +179,6 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
struct tegra_bo *bo = to_tegra_bo(gem);
drm_gem_free_mmap_offset(gem);
drm_gem_object_release(gem);
tegra_bo_destroy(gem->dev, bo);
@ -197,8 +197,8 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
if (args->size < args->pitch * args->height)
args->size = args->pitch * args->height;
bo = tegra_bo_create_with_handle(file, drm, args->size,
&args->handle);
bo = tegra_bo_create_with_handle(file, drm, args->size, 0,
&args->handle);
if (IS_ERR(bo))
return PTR_ERR(bo);

View File

@ -19,14 +19,18 @@
#ifndef __HOST1X_GEM_H
#define __HOST1X_GEM_H
#include <linux/host1x.h>
#include <drm/drm.h>
#include <drm/drmP.h>
#include "host1x_bo.h"
#define TEGRA_BO_TILED (1 << 0)
#define TEGRA_BO_BOTTOM_UP (1 << 1)
struct tegra_bo {
struct drm_gem_object gem;
struct host1x_bo base;
unsigned long flags;
dma_addr_t paddr;
void *vaddr;
};
@ -38,11 +42,13 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem)
extern const struct host1x_bo_ops tegra_bo_ops;
struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size);
struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
unsigned long flags);
struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
struct drm_device *drm,
unsigned int size,
unsigned int *handle);
struct drm_device *drm,
unsigned int size,
unsigned long flags,
unsigned int *handle);
void tegra_bo_free_object(struct drm_gem_object *gem);
int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
struct drm_mode_create_dumb *args);

View File

@ -0,0 +1,227 @@
/*
* 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/clk.h>
#include "drm.h"
#include "gem.h"
#include "gr2d.h"
struct gr2d {
struct tegra_drm_client client;
struct host1x_channel *channel;
struct clk *clk;
DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
};
static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
{
return container_of(client, struct gr2d, client);
}
static int gr2d_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
struct gr2d *gr2d = to_gr2d(drm);
gr2d->channel = host1x_channel_request(client->dev);
if (!gr2d->channel)
return -ENOMEM;
client->syncpts[0] = host1x_syncpt_request(client->dev, flags);
if (!client->syncpts[0]) {
host1x_channel_free(gr2d->channel);
return -ENOMEM;
}
return tegra_drm_register_client(tegra, drm);
}
static int gr2d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct gr2d *gr2d = to_gr2d(drm);
int err;
err = tegra_drm_unregister_client(tegra, drm);
if (err < 0)
return err;
host1x_syncpt_free(client->syncpts[0]);
host1x_channel_free(gr2d->channel);
return 0;
}
static const struct host1x_client_ops gr2d_client_ops = {
.init = gr2d_init,
.exit = gr2d_exit,
};
static int gr2d_open_channel(struct tegra_drm_client *client,
struct tegra_drm_context *context)
{
struct gr2d *gr2d = to_gr2d(client);
context->channel = host1x_channel_get(gr2d->channel);
if (!context->channel)
return -ENOMEM;
return 0;
}
static void gr2d_close_channel(struct tegra_drm_context *context)
{
host1x_channel_put(context->channel);
}
static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 offset)
{
struct gr2d *gr2d = dev_get_drvdata(dev);
switch (class) {
case HOST1X_CLASS_HOST1X:
if (offset == 0x2b)
return 1;
break;
case HOST1X_CLASS_GR2D:
case HOST1X_CLASS_GR2D_SB:
if (offset >= GR2D_NUM_REGS)
break;
if (test_bit(offset, gr2d->addr_regs))
return 1;
break;
}
return 0;
}
static const struct tegra_drm_client_ops gr2d_ops = {
.open_channel = gr2d_open_channel,
.close_channel = gr2d_close_channel,
.is_addr_reg = gr2d_is_addr_reg,
.submit = tegra_drm_submit,
};
static const struct of_device_id gr2d_match[] = {
{ .compatible = "nvidia,tegra30-gr2d" },
{ .compatible = "nvidia,tegra20-gr2d" },
{ },
};
static const u32 gr2d_addr_regs[] = {
GR2D_UA_BASE_ADDR,
GR2D_VA_BASE_ADDR,
GR2D_PAT_BASE_ADDR,
GR2D_DSTA_BASE_ADDR,
GR2D_DSTB_BASE_ADDR,
GR2D_DSTC_BASE_ADDR,
GR2D_SRCA_BASE_ADDR,
GR2D_SRCB_BASE_ADDR,
GR2D_SRC_BASE_ADDR_SB,
GR2D_DSTA_BASE_ADDR_SB,
GR2D_DSTB_BASE_ADDR_SB,
GR2D_UA_BASE_ADDR_SB,
GR2D_VA_BASE_ADDR_SB,
};
static int gr2d_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct host1x_syncpt **syncpts;
struct gr2d *gr2d;
unsigned int i;
int err;
gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
if (!gr2d)
return -ENOMEM;
syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
if (!syncpts)
return -ENOMEM;
gr2d->clk = devm_clk_get(dev, NULL);
if (IS_ERR(gr2d->clk)) {
dev_err(dev, "cannot get clock\n");
return PTR_ERR(gr2d->clk);
}
err = clk_prepare_enable(gr2d->clk);
if (err) {
dev_err(dev, "cannot turn on clock\n");
return err;
}
INIT_LIST_HEAD(&gr2d->client.base.list);
gr2d->client.base.ops = &gr2d_client_ops;
gr2d->client.base.dev = dev;
gr2d->client.base.class = HOST1X_CLASS_GR2D;
gr2d->client.base.syncpts = syncpts;
gr2d->client.base.num_syncpts = 1;
INIT_LIST_HEAD(&gr2d->client.list);
gr2d->client.ops = &gr2d_ops;
err = host1x_client_register(&gr2d->client.base);
if (err < 0) {
dev_err(dev, "failed to register host1x client: %d\n", err);
clk_disable_unprepare(gr2d->clk);
return err;
}
/* initialize address register map */
for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
platform_set_drvdata(pdev, gr2d);
return 0;
}
static int gr2d_remove(struct platform_device *pdev)
{
struct gr2d *gr2d = platform_get_drvdata(pdev);
int err;
err = host1x_client_unregister(&gr2d->client.base);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
return err;
}
clk_disable_unprepare(gr2d->clk);
return 0;
}
struct platform_driver tegra_gr2d_driver = {
.driver = {
.name = "tegra-gr2d",
.of_match_table = gr2d_match,
},
.probe = gr2d_probe,
.remove = gr2d_remove,
};

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef TEGRA_GR2D_H
#define TEGRA_GR2D_H
#define GR2D_UA_BASE_ADDR 0x1a
#define GR2D_VA_BASE_ADDR 0x1b
#define GR2D_PAT_BASE_ADDR 0x26
#define GR2D_DSTA_BASE_ADDR 0x2b
#define GR2D_DSTB_BASE_ADDR 0x2c
#define GR2D_DSTC_BASE_ADDR 0x2d
#define GR2D_SRCA_BASE_ADDR 0x31
#define GR2D_SRCB_BASE_ADDR 0x32
#define GR2D_SRC_BASE_ADDR_SB 0x48
#define GR2D_DSTA_BASE_ADDR_SB 0x49
#define GR2D_DSTB_BASE_ADDR_SB 0x4a
#define GR2D_UA_BASE_ADDR_SB 0x4b
#define GR2D_VA_BASE_ADDR_SB 0x4c
#define GR2D_NUM_REGS 0x4d
#endif

View File

@ -0,0 +1,338 @@
/*
* Copyright (C) 2013 Avionic Design GmbH
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/host1x.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/tegra-powergate.h>
#include "drm.h"
#include "gem.h"
#include "gr3d.h"
struct gr3d {
struct tegra_drm_client client;
struct host1x_channel *channel;
struct clk *clk_secondary;
struct clk *clk;
DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
};
static inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
{
return container_of(client, struct gr3d, client);
}
static int gr3d_init(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
unsigned long flags = HOST1X_SYNCPT_HAS_BASE;
struct gr3d *gr3d = to_gr3d(drm);
gr3d->channel = host1x_channel_request(client->dev);
if (!gr3d->channel)
return -ENOMEM;
client->syncpts[0] = host1x_syncpt_request(client->dev, flags);
if (!client->syncpts[0]) {
host1x_channel_free(gr3d->channel);
return -ENOMEM;
}
return tegra_drm_register_client(tegra, drm);
}
static int gr3d_exit(struct host1x_client *client)
{
struct tegra_drm_client *drm = host1x_to_drm_client(client);
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct gr3d *gr3d = to_gr3d(drm);
int err;
err = tegra_drm_unregister_client(tegra, drm);
if (err < 0)
return err;
host1x_syncpt_free(client->syncpts[0]);
host1x_channel_free(gr3d->channel);
return 0;
}
static const struct host1x_client_ops gr3d_client_ops = {
.init = gr3d_init,
.exit = gr3d_exit,
};
static int gr3d_open_channel(struct tegra_drm_client *client,
struct tegra_drm_context *context)
{
struct gr3d *gr3d = to_gr3d(client);
context->channel = host1x_channel_get(gr3d->channel);
if (!context->channel)
return -ENOMEM;
return 0;
}
static void gr3d_close_channel(struct tegra_drm_context *context)
{
host1x_channel_put(context->channel);
}
static int gr3d_is_addr_reg(struct device *dev, u32 class, u32 offset)
{
struct gr3d *gr3d = dev_get_drvdata(dev);
switch (class) {
case HOST1X_CLASS_HOST1X:
if (offset == 0x2b)
return 1;
break;
case HOST1X_CLASS_GR3D:
if (offset >= GR3D_NUM_REGS)
break;
if (test_bit(offset, gr3d->addr_regs))
return 1;
break;
}
return 0;
}
static const struct tegra_drm_client_ops gr3d_ops = {
.open_channel = gr3d_open_channel,
.close_channel = gr3d_close_channel,
.is_addr_reg = gr3d_is_addr_reg,
.submit = tegra_drm_submit,
};
static const struct of_device_id tegra_gr3d_match[] = {
{ .compatible = "nvidia,tegra114-gr3d" },
{ .compatible = "nvidia,tegra30-gr3d" },
{ .compatible = "nvidia,tegra20-gr3d" },
{ }
};
static const u32 gr3d_addr_regs[] = {
GR3D_IDX_ATTRIBUTE( 0),
GR3D_IDX_ATTRIBUTE( 1),
GR3D_IDX_ATTRIBUTE( 2),
GR3D_IDX_ATTRIBUTE( 3),
GR3D_IDX_ATTRIBUTE( 4),
GR3D_IDX_ATTRIBUTE( 5),
GR3D_IDX_ATTRIBUTE( 6),
GR3D_IDX_ATTRIBUTE( 7),
GR3D_IDX_ATTRIBUTE( 8),
GR3D_IDX_ATTRIBUTE( 9),
GR3D_IDX_ATTRIBUTE(10),
GR3D_IDX_ATTRIBUTE(11),
GR3D_IDX_ATTRIBUTE(12),
GR3D_IDX_ATTRIBUTE(13),
GR3D_IDX_ATTRIBUTE(14),
GR3D_IDX_ATTRIBUTE(15),
GR3D_IDX_INDEX_BASE,
GR3D_QR_ZTAG_ADDR,
GR3D_QR_CTAG_ADDR,
GR3D_QR_CZ_ADDR,
GR3D_TEX_TEX_ADDR( 0),
GR3D_TEX_TEX_ADDR( 1),
GR3D_TEX_TEX_ADDR( 2),
GR3D_TEX_TEX_ADDR( 3),
GR3D_TEX_TEX_ADDR( 4),
GR3D_TEX_TEX_ADDR( 5),
GR3D_TEX_TEX_ADDR( 6),
GR3D_TEX_TEX_ADDR( 7),
GR3D_TEX_TEX_ADDR( 8),
GR3D_TEX_TEX_ADDR( 9),
GR3D_TEX_TEX_ADDR(10),
GR3D_TEX_TEX_ADDR(11),
GR3D_TEX_TEX_ADDR(12),
GR3D_TEX_TEX_ADDR(13),
GR3D_TEX_TEX_ADDR(14),
GR3D_TEX_TEX_ADDR(15),
GR3D_DW_MEMORY_OUTPUT_ADDRESS,
GR3D_GLOBAL_SURFADDR( 0),
GR3D_GLOBAL_SURFADDR( 1),
GR3D_GLOBAL_SURFADDR( 2),
GR3D_GLOBAL_SURFADDR( 3),
GR3D_GLOBAL_SURFADDR( 4),
GR3D_GLOBAL_SURFADDR( 5),
GR3D_GLOBAL_SURFADDR( 6),
GR3D_GLOBAL_SURFADDR( 7),
GR3D_GLOBAL_SURFADDR( 8),
GR3D_GLOBAL_SURFADDR( 9),
GR3D_GLOBAL_SURFADDR(10),
GR3D_GLOBAL_SURFADDR(11),
GR3D_GLOBAL_SURFADDR(12),
GR3D_GLOBAL_SURFADDR(13),
GR3D_GLOBAL_SURFADDR(14),
GR3D_GLOBAL_SURFADDR(15),
GR3D_GLOBAL_SPILLSURFADDR,
GR3D_GLOBAL_SURFOVERADDR( 0),
GR3D_GLOBAL_SURFOVERADDR( 1),
GR3D_GLOBAL_SURFOVERADDR( 2),
GR3D_GLOBAL_SURFOVERADDR( 3),
GR3D_GLOBAL_SURFOVERADDR( 4),
GR3D_GLOBAL_SURFOVERADDR( 5),
GR3D_GLOBAL_SURFOVERADDR( 6),
GR3D_GLOBAL_SURFOVERADDR( 7),
GR3D_GLOBAL_SURFOVERADDR( 8),
GR3D_GLOBAL_SURFOVERADDR( 9),
GR3D_GLOBAL_SURFOVERADDR(10),
GR3D_GLOBAL_SURFOVERADDR(11),
GR3D_GLOBAL_SURFOVERADDR(12),
GR3D_GLOBAL_SURFOVERADDR(13),
GR3D_GLOBAL_SURFOVERADDR(14),
GR3D_GLOBAL_SURFOVERADDR(15),
GR3D_GLOBAL_SAMP01SURFADDR( 0),
GR3D_GLOBAL_SAMP01SURFADDR( 1),
GR3D_GLOBAL_SAMP01SURFADDR( 2),
GR3D_GLOBAL_SAMP01SURFADDR( 3),
GR3D_GLOBAL_SAMP01SURFADDR( 4),
GR3D_GLOBAL_SAMP01SURFADDR( 5),
GR3D_GLOBAL_SAMP01SURFADDR( 6),
GR3D_GLOBAL_SAMP01SURFADDR( 7),
GR3D_GLOBAL_SAMP01SURFADDR( 8),
GR3D_GLOBAL_SAMP01SURFADDR( 9),
GR3D_GLOBAL_SAMP01SURFADDR(10),
GR3D_GLOBAL_SAMP01SURFADDR(11),
GR3D_GLOBAL_SAMP01SURFADDR(12),
GR3D_GLOBAL_SAMP01SURFADDR(13),
GR3D_GLOBAL_SAMP01SURFADDR(14),
GR3D_GLOBAL_SAMP01SURFADDR(15),
GR3D_GLOBAL_SAMP23SURFADDR( 0),
GR3D_GLOBAL_SAMP23SURFADDR( 1),
GR3D_GLOBAL_SAMP23SURFADDR( 2),
GR3D_GLOBAL_SAMP23SURFADDR( 3),
GR3D_GLOBAL_SAMP23SURFADDR( 4),
GR3D_GLOBAL_SAMP23SURFADDR( 5),
GR3D_GLOBAL_SAMP23SURFADDR( 6),
GR3D_GLOBAL_SAMP23SURFADDR( 7),
GR3D_GLOBAL_SAMP23SURFADDR( 8),
GR3D_GLOBAL_SAMP23SURFADDR( 9),
GR3D_GLOBAL_SAMP23SURFADDR(10),
GR3D_GLOBAL_SAMP23SURFADDR(11),
GR3D_GLOBAL_SAMP23SURFADDR(12),
GR3D_GLOBAL_SAMP23SURFADDR(13),
GR3D_GLOBAL_SAMP23SURFADDR(14),
GR3D_GLOBAL_SAMP23SURFADDR(15),
};
static int gr3d_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct host1x_syncpt **syncpts;
struct gr3d *gr3d;
unsigned int i;
int err;
gr3d = devm_kzalloc(&pdev->dev, sizeof(*gr3d), GFP_KERNEL);
if (!gr3d)
return -ENOMEM;
syncpts = devm_kzalloc(&pdev->dev, sizeof(*syncpts), GFP_KERNEL);
if (!syncpts)
return -ENOMEM;
gr3d->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(gr3d->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
return PTR_ERR(gr3d->clk);
}
if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) {
gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2");
if (IS_ERR(gr3d->clk)) {
dev_err(&pdev->dev, "cannot get secondary clock\n");
return PTR_ERR(gr3d->clk);
}
}
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D, gr3d->clk);
if (err < 0) {
dev_err(&pdev->dev, "failed to power up 3D unit\n");
return err;
}
if (gr3d->clk_secondary) {
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_3D1,
gr3d->clk_secondary);
if (err < 0) {
dev_err(&pdev->dev,
"failed to power up secondary 3D unit\n");
return err;
}
}
INIT_LIST_HEAD(&gr3d->client.base.list);
gr3d->client.base.ops = &gr3d_client_ops;
gr3d->client.base.dev = &pdev->dev;
gr3d->client.base.class = HOST1X_CLASS_GR3D;
gr3d->client.base.syncpts = syncpts;
gr3d->client.base.num_syncpts = 1;
INIT_LIST_HEAD(&gr3d->client.list);
gr3d->client.ops = &gr3d_ops;
err = host1x_client_register(&gr3d->client.base);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
return err;
}
/* initialize address register map */
for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
platform_set_drvdata(pdev, gr3d);
return 0;
}
static int gr3d_remove(struct platform_device *pdev)
{
struct gr3d *gr3d = platform_get_drvdata(pdev);
int err;
err = host1x_client_unregister(&gr3d->client.base);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
return err;
}
if (gr3d->clk_secondary) {
tegra_powergate_power_off(TEGRA_POWERGATE_3D1);
clk_disable_unprepare(gr3d->clk_secondary);
}
tegra_powergate_power_off(TEGRA_POWERGATE_3D);
clk_disable_unprepare(gr3d->clk);
return 0;
}
struct platform_driver tegra_gr3d_driver = {
.driver = {
.name = "tegra-gr3d",
.of_match_table = tegra_gr3d_match,
},
.probe = gr3d_probe,
.remove = gr3d_remove,
};

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2013 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef TEGRA_GR3D_H
#define TEGRA_GR3D_H
#define GR3D_IDX_ATTRIBUTE(x) (0x100 + (x) * 2)
#define GR3D_IDX_INDEX_BASE 0x121
#define GR3D_QR_ZTAG_ADDR 0x415
#define GR3D_QR_CTAG_ADDR 0x417
#define GR3D_QR_CZ_ADDR 0x419
#define GR3D_TEX_TEX_ADDR(x) (0x710 + (x))
#define GR3D_DW_MEMORY_OUTPUT_ADDRESS 0x904
#define GR3D_GLOBAL_SURFADDR(x) (0xe00 + (x))
#define GR3D_GLOBAL_SPILLSURFADDR 0xe2a
#define GR3D_GLOBAL_SURFOVERADDR(x) (0xe30 + (x))
#define GR3D_GLOBAL_SAMP01SURFADDR(x) (0xe50 + (x))
#define GR3D_GLOBAL_SAMP23SURFADDR(x) (0xe60 + (x))
#define GR3D_NUM_REGS 0xe88
#endif

View File

@ -8,21 +8,33 @@
*/
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/hdmi.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/clk/tegra.h>
#include <drm/drm_edid.h>
#include <linux/debugfs.h>
#include <linux/hdmi.h>
#include <linux/regulator/consumer.h>
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
#include "host1x_client.h"
struct tmds_config {
unsigned int pclk;
u32 pll0;
u32 pll1;
u32 pe_current;
u32 drive_current;
u32 peak_current;
};
struct tegra_hdmi_config {
const struct tmds_config *tmds;
unsigned int num_tmds;
unsigned long fuse_override_offset;
unsigned long fuse_override_value;
bool has_sor_io_peak_current;
};
struct tegra_hdmi {
struct host1x_client client;
@ -38,6 +50,8 @@ struct tegra_hdmi {
struct clk *clk_parent;
struct clk *clk;
const struct tegra_hdmi_config *config;
unsigned int audio_source;
unsigned int audio_freq;
bool stereo;
@ -143,15 +157,7 @@ static const struct tegra_hdmi_audio_config tegra_hdmi_audio_192k[] = {
{ 0, 0, 0, 0 },
};
struct tmds_config {
unsigned int pclk;
u32 pll0;
u32 pll1;
u32 pe_current;
u32 drive_current;
};
static const struct tmds_config tegra2_tmds_config[] = {
static const struct tmds_config tegra20_tmds_config[] = {
{ /* slow pixel clock modes */
.pclk = 27000000,
.pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
@ -184,7 +190,7 @@ static const struct tmds_config tegra2_tmds_config[] = {
},
};
static const struct tmds_config tegra3_tmds_config[] = {
static const struct tmds_config tegra30_tmds_config[] = {
{ /* 480p modes */
.pclk = 27000000,
.pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) |
@ -230,6 +236,85 @@ static const struct tmds_config tegra3_tmds_config[] = {
},
};
static const struct tmds_config tegra114_tmds_config[] = {
{ /* 480p/576p / 25.2MHz/27MHz modes */
.pclk = 27000000,
.pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
SOR_PLL_VCOCAP(0) | SOR_PLL_RESISTORSEL,
.pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(0),
.pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
PE_CURRENT1(PE_CURRENT_0_mA_T114) |
PE_CURRENT2(PE_CURRENT_0_mA_T114) |
PE_CURRENT3(PE_CURRENT_0_mA_T114),
.drive_current =
DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
.peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
}, { /* 720p / 74.25MHz modes */
.pclk = 74250000,
.pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
SOR_PLL_VCOCAP(1) | SOR_PLL_RESISTORSEL,
.pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
SOR_PLL_TMDS_TERMADJ(0),
.pe_current = PE_CURRENT0(PE_CURRENT_15_mA_T114) |
PE_CURRENT1(PE_CURRENT_15_mA_T114) |
PE_CURRENT2(PE_CURRENT_15_mA_T114) |
PE_CURRENT3(PE_CURRENT_15_mA_T114),
.drive_current =
DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_10_400_mA_T114) |
DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_10_400_mA_T114) |
DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_10_400_mA_T114) |
DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_10_400_mA_T114),
.peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
}, { /* 1080p / 148.5MHz modes */
.pclk = 148500000,
.pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
SOR_PLL_VCOCAP(3) | SOR_PLL_RESISTORSEL,
.pll1 = SOR_PLL_PE_EN | SOR_PLL_LOADADJ(3) |
SOR_PLL_TMDS_TERMADJ(0),
.pe_current = PE_CURRENT0(PE_CURRENT_10_mA_T114) |
PE_CURRENT1(PE_CURRENT_10_mA_T114) |
PE_CURRENT2(PE_CURRENT_10_mA_T114) |
PE_CURRENT3(PE_CURRENT_10_mA_T114),
.drive_current =
DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_12_400_mA_T114) |
DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_12_400_mA_T114) |
DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_12_400_mA_T114) |
DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_12_400_mA_T114),
.peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE1(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE2(PEAK_CURRENT_0_000_mA) |
PEAK_CURRENT_LANE3(PEAK_CURRENT_0_000_mA),
}, { /* 225/297MHz modes */
.pclk = UINT_MAX,
.pll0 = SOR_PLL_ICHPMP(1) | SOR_PLL_BG_V17_S(3) |
SOR_PLL_VCOCAP(0xf) | SOR_PLL_RESISTORSEL,
.pll1 = SOR_PLL_LOADADJ(3) | SOR_PLL_TMDS_TERMADJ(7)
| SOR_PLL_TMDS_TERM_ENABLE,
.pe_current = PE_CURRENT0(PE_CURRENT_0_mA_T114) |
PE_CURRENT1(PE_CURRENT_0_mA_T114) |
PE_CURRENT2(PE_CURRENT_0_mA_T114) |
PE_CURRENT3(PE_CURRENT_0_mA_T114),
.drive_current =
DRIVE_CURRENT_LANE0_T114(DRIVE_CURRENT_25_200_mA_T114) |
DRIVE_CURRENT_LANE1_T114(DRIVE_CURRENT_25_200_mA_T114) |
DRIVE_CURRENT_LANE2_T114(DRIVE_CURRENT_25_200_mA_T114) |
DRIVE_CURRENT_LANE3_T114(DRIVE_CURRENT_19_200_mA_T114),
.peak_current = PEAK_CURRENT_LANE0(PEAK_CURRENT_3_000_mA) |
PEAK_CURRENT_LANE1(PEAK_CURRENT_3_000_mA) |
PEAK_CURRENT_LANE2(PEAK_CURRENT_3_000_mA) |
PEAK_CURRENT_LANE3(PEAK_CURRENT_0_800_mA),
},
};
static const struct tegra_hdmi_audio_config *
tegra_hdmi_get_audio_config(unsigned int audio_freq, unsigned int pclk)
{
@ -511,7 +596,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
err = hdmi_audio_infoframe_init(&frame);
if (err < 0) {
dev_err(hdmi->dev, "failed to initialize audio infoframe: %d\n",
dev_err(hdmi->dev, "failed to setup audio infoframe: %zd\n",
err);
return;
}
@ -531,7 +616,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
* contain 7 bytes. Including the 3 byte header only the first 10
* bytes can be programmed.
*/
tegra_hdmi_write_infopack(hdmi, buffer, min(10, err));
tegra_hdmi_write_infopack(hdmi, buffer, min_t(size_t, 10, err));
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
@ -577,8 +662,28 @@ static void tegra_hdmi_setup_tmds(struct tegra_hdmi *hdmi,
tegra_hdmi_writel(hdmi, tmds->pll1, HDMI_NV_PDISP_SOR_PLL1);
tegra_hdmi_writel(hdmi, tmds->pe_current, HDMI_NV_PDISP_PE_CURRENT);
value = tmds->drive_current | DRIVE_CURRENT_FUSE_OVERRIDE;
tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT);
tegra_hdmi_writel(hdmi, tmds->drive_current,
HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT);
value = tegra_hdmi_readl(hdmi, hdmi->config->fuse_override_offset);
value |= hdmi->config->fuse_override_value;
tegra_hdmi_writel(hdmi, value, hdmi->config->fuse_override_offset);
if (hdmi->config->has_sor_io_peak_current)
tegra_hdmi_writel(hdmi, tmds->peak_current,
HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT);
}
static bool tegra_output_is_hdmi(struct tegra_output *output)
{
struct edid *edid;
if (!output->connector.edid_blob_ptr)
return false;
edid = (struct edid *)output->connector.edid_blob_ptr->data;
return drm_detect_hdmi_monitor(edid);
}
static int tegra_output_hdmi_enable(struct tegra_output *output)
@ -589,23 +694,17 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
struct tegra_hdmi *hdmi = to_hdmi(output);
struct device_node *node = hdmi->dev->of_node;
unsigned int pulse_start, div82, pclk;
const struct tmds_config *tmds;
unsigned int num_tmds;
unsigned long value;
int retries = 1000;
int err;
hdmi->dvi = !tegra_output_is_hdmi(output);
pclk = mode->clock * 1000;
h_sync_width = mode->hsync_end - mode->hsync_start;
h_back_porch = mode->htotal - mode->hsync_end;
h_front_porch = mode->hsync_start - mode->hdisplay;
err = regulator_enable(hdmi->vdd);
if (err < 0) {
dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err);
return err;
}
err = regulator_enable(hdmi->pll);
if (err < 0) {
dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err);
@ -710,17 +809,9 @@ static int tegra_output_hdmi_enable(struct tegra_output *output)
tegra_hdmi_setup_stereo_infoframe(hdmi);
/* TMDS CONFIG */
if (of_device_is_compatible(node, "nvidia,tegra30-hdmi")) {
num_tmds = ARRAY_SIZE(tegra3_tmds_config);
tmds = tegra3_tmds_config;
} else {
num_tmds = ARRAY_SIZE(tegra2_tmds_config);
tmds = tegra2_tmds_config;
}
for (i = 0; i < num_tmds; i++) {
if (pclk <= tmds[i].pclk) {
tegra_hdmi_setup_tmds(hdmi, &tmds[i]);
for (i = 0; i < hdmi->config->num_tmds; i++) {
if (pclk <= hdmi->config->tmds[i].pclk) {
tegra_hdmi_setup_tmds(hdmi, &hdmi->config->tmds[i]);
break;
}
}
@ -824,7 +915,6 @@ static int tegra_output_hdmi_disable(struct tegra_output *output)
tegra_periph_reset_assert(hdmi->clk);
clk_disable(hdmi->clk);
regulator_disable(hdmi->pll);
regulator_disable(hdmi->vdd);
return 0;
}
@ -1055,6 +1145,7 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data)
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR);
DUMP_REG(HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE);
DUMP_REG(HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT);
#undef DUMP_REG
@ -1122,24 +1213,31 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi)
return 0;
}
static int tegra_hdmi_drm_init(struct host1x_client *client,
struct drm_device *drm)
static int tegra_hdmi_init(struct host1x_client *client)
{
struct tegra_drm *tegra = dev_get_drvdata(client->parent);
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
err = regulator_enable(hdmi->vdd);
if (err < 0) {
dev_err(client->dev, "failed to enable VDD regulator: %d\n",
err);
return err;
}
hdmi->output.type = TEGRA_OUTPUT_HDMI;
hdmi->output.dev = client->dev;
hdmi->output.ops = &hdmi_ops;
err = tegra_output_init(drm, &hdmi->output);
err = tegra_output_init(tegra->drm, &hdmi->output);
if (err < 0) {
dev_err(client->dev, "output setup failed: %d\n", err);
return err;
}
if (IS_ENABLED(CONFIG_DEBUG_FS)) {
err = tegra_hdmi_debugfs_init(hdmi, drm->primary);
err = tegra_hdmi_debugfs_init(hdmi, tegra->drm->primary);
if (err < 0)
dev_err(client->dev, "debugfs setup failed: %d\n", err);
}
@ -1147,7 +1245,7 @@ static int tegra_hdmi_drm_init(struct host1x_client *client,
return 0;
}
static int tegra_hdmi_drm_exit(struct host1x_client *client)
static int tegra_hdmi_exit(struct host1x_client *client)
{
struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client);
int err;
@ -1171,25 +1269,63 @@ static int tegra_hdmi_drm_exit(struct host1x_client *client)
return err;
}
regulator_disable(hdmi->vdd);
return 0;
}
static const struct host1x_client_ops hdmi_client_ops = {
.drm_init = tegra_hdmi_drm_init,
.drm_exit = tegra_hdmi_drm_exit,
.init = tegra_hdmi_init,
.exit = tegra_hdmi_exit,
};
static const struct tegra_hdmi_config tegra20_hdmi_config = {
.tmds = tegra20_tmds_config,
.num_tmds = ARRAY_SIZE(tegra20_tmds_config),
.fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = false,
};
static const struct tegra_hdmi_config tegra30_hdmi_config = {
.tmds = tegra30_tmds_config,
.num_tmds = ARRAY_SIZE(tegra30_tmds_config),
.fuse_override_offset = HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = false,
};
static const struct tegra_hdmi_config tegra114_hdmi_config = {
.tmds = tegra114_tmds_config,
.num_tmds = ARRAY_SIZE(tegra114_tmds_config),
.fuse_override_offset = HDMI_NV_PDISP_SOR_PAD_CTLS0,
.fuse_override_value = 1 << 31,
.has_sor_io_peak_current = true,
};
static const struct of_device_id tegra_hdmi_of_match[] = {
{ .compatible = "nvidia,tegra114-hdmi", .data = &tegra114_hdmi_config },
{ .compatible = "nvidia,tegra30-hdmi", .data = &tegra30_hdmi_config },
{ .compatible = "nvidia,tegra20-hdmi", .data = &tegra20_hdmi_config },
{ },
};
static int tegra_hdmi_probe(struct platform_device *pdev)
{
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
const struct of_device_id *match;
struct tegra_hdmi *hdmi;
struct resource *regs;
int err;
match = of_match_node(tegra_hdmi_of_match, pdev->dev.of_node);
if (!match)
return -ENODEV;
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
hdmi->config = match->data;
hdmi->dev = &pdev->dev;
hdmi->audio_source = AUTO;
hdmi->audio_freq = 44100;
@ -1234,7 +1370,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
hdmi->output.dev = &pdev->dev;
err = tegra_output_parse_dt(&hdmi->output);
err = tegra_output_probe(&hdmi->output);
if (err < 0)
return err;
@ -1252,11 +1388,11 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
hdmi->irq = err;
hdmi->client.ops = &hdmi_client_ops;
INIT_LIST_HEAD(&hdmi->client.list);
hdmi->client.ops = &hdmi_client_ops;
hdmi->client.dev = &pdev->dev;
err = host1x_register_client(host1x, &hdmi->client);
err = host1x_client_register(&hdmi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
@ -1270,29 +1406,28 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
static int tegra_hdmi_remove(struct platform_device *pdev)
{
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
int err;
err = host1x_unregister_client(host1x, &hdmi->client);
err = host1x_client_unregister(&hdmi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
err);
return err;
}
err = tegra_output_remove(&hdmi->output);
if (err < 0) {
dev_err(&pdev->dev, "failed to remove output: %d\n", err);
return err;
}
clk_unprepare(hdmi->clk_parent);
clk_unprepare(hdmi->clk);
return 0;
}
static struct of_device_id tegra_hdmi_of_match[] = {
{ .compatible = "nvidia,tegra30-hdmi", },
{ .compatible = "nvidia,tegra20-hdmi", },
{ },
};
struct platform_driver tegra_hdmi_driver = {
.driver = {
.name = "tegra-hdmi",

View File

@ -233,7 +233,10 @@
#define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8)
#define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16)
#define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24)
#define DRIVE_CURRENT_FUSE_OVERRIDE (1 << 31)
#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) << 0)
#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) << 8)
#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16)
#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24)
#define DRIVE_CURRENT_1_500_mA 0x00
#define DRIVE_CURRENT_1_875_mA 0x01
@ -299,6 +302,79 @@
#define DRIVE_CURRENT_24_375_mA 0x3d
#define DRIVE_CURRENT_24_750_mA 0x3e
#define DRIVE_CURRENT_0_000_mA_T114 0x00
#define DRIVE_CURRENT_0_400_mA_T114 0x01
#define DRIVE_CURRENT_0_800_mA_T114 0x02
#define DRIVE_CURRENT_1_200_mA_T114 0x03
#define DRIVE_CURRENT_1_600_mA_T114 0x04
#define DRIVE_CURRENT_2_000_mA_T114 0x05
#define DRIVE_CURRENT_2_400_mA_T114 0x06
#define DRIVE_CURRENT_2_800_mA_T114 0x07
#define DRIVE_CURRENT_3_200_mA_T114 0x08
#define DRIVE_CURRENT_3_600_mA_T114 0x09
#define DRIVE_CURRENT_4_000_mA_T114 0x0a
#define DRIVE_CURRENT_4_400_mA_T114 0x0b
#define DRIVE_CURRENT_4_800_mA_T114 0x0c
#define DRIVE_CURRENT_5_200_mA_T114 0x0d
#define DRIVE_CURRENT_5_600_mA_T114 0x0e
#define DRIVE_CURRENT_6_000_mA_T114 0x0f
#define DRIVE_CURRENT_6_400_mA_T114 0x10
#define DRIVE_CURRENT_6_800_mA_T114 0x11
#define DRIVE_CURRENT_7_200_mA_T114 0x12
#define DRIVE_CURRENT_7_600_mA_T114 0x13
#define DRIVE_CURRENT_8_000_mA_T114 0x14
#define DRIVE_CURRENT_8_400_mA_T114 0x15
#define DRIVE_CURRENT_8_800_mA_T114 0x16
#define DRIVE_CURRENT_9_200_mA_T114 0x17
#define DRIVE_CURRENT_9_600_mA_T114 0x18
#define DRIVE_CURRENT_10_000_mA_T114 0x19
#define DRIVE_CURRENT_10_400_mA_T114 0x1a
#define DRIVE_CURRENT_10_800_mA_T114 0x1b
#define DRIVE_CURRENT_11_200_mA_T114 0x1c
#define DRIVE_CURRENT_11_600_mA_T114 0x1d
#define DRIVE_CURRENT_12_000_mA_T114 0x1e
#define DRIVE_CURRENT_12_400_mA_T114 0x1f
#define DRIVE_CURRENT_12_800_mA_T114 0x20
#define DRIVE_CURRENT_13_200_mA_T114 0x21
#define DRIVE_CURRENT_13_600_mA_T114 0x22
#define DRIVE_CURRENT_14_000_mA_T114 0x23
#define DRIVE_CURRENT_14_400_mA_T114 0x24
#define DRIVE_CURRENT_14_800_mA_T114 0x25
#define DRIVE_CURRENT_15_200_mA_T114 0x26
#define DRIVE_CURRENT_15_600_mA_T114 0x27
#define DRIVE_CURRENT_16_000_mA_T114 0x28
#define DRIVE_CURRENT_16_400_mA_T114 0x29
#define DRIVE_CURRENT_16_800_mA_T114 0x2a
#define DRIVE_CURRENT_17_200_mA_T114 0x2b
#define DRIVE_CURRENT_17_600_mA_T114 0x2c
#define DRIVE_CURRENT_18_000_mA_T114 0x2d
#define DRIVE_CURRENT_18_400_mA_T114 0x2e
#define DRIVE_CURRENT_18_800_mA_T114 0x2f
#define DRIVE_CURRENT_19_200_mA_T114 0x30
#define DRIVE_CURRENT_19_600_mA_T114 0x31
#define DRIVE_CURRENT_20_000_mA_T114 0x32
#define DRIVE_CURRENT_20_400_mA_T114 0x33
#define DRIVE_CURRENT_20_800_mA_T114 0x34
#define DRIVE_CURRENT_21_200_mA_T114 0x35
#define DRIVE_CURRENT_21_600_mA_T114 0x36
#define DRIVE_CURRENT_22_000_mA_T114 0x37
#define DRIVE_CURRENT_22_400_mA_T114 0x38
#define DRIVE_CURRENT_22_800_mA_T114 0x39
#define DRIVE_CURRENT_23_200_mA_T114 0x3a
#define DRIVE_CURRENT_23_600_mA_T114 0x3b
#define DRIVE_CURRENT_24_000_mA_T114 0x3c
#define DRIVE_CURRENT_24_400_mA_T114 0x3d
#define DRIVE_CURRENT_24_800_mA_T114 0x3e
#define DRIVE_CURRENT_25_200_mA_T114 0x3f
#define DRIVE_CURRENT_25_400_mA_T114 0x40
#define DRIVE_CURRENT_25_800_mA_T114 0x41
#define DRIVE_CURRENT_26_200_mA_T114 0x42
#define DRIVE_CURRENT_26_600_mA_T114 0x43
#define DRIVE_CURRENT_27_000_mA_T114 0x44
#define DRIVE_CURRENT_27_400_mA_T114 0x45
#define DRIVE_CURRENT_27_800_mA_T114 0x46
#define DRIVE_CURRENT_28_200_mA_T114 0x47
#define HDMI_NV_PDISP_AUDIO_DEBUG0 0x7f
#define HDMI_NV_PDISP_AUDIO_DEBUG1 0x80
#define HDMI_NV_PDISP_AUDIO_DEBUG2 0x81
@ -358,6 +434,23 @@
#define PE_CURRENT_7_0_mA 0xe
#define PE_CURRENT_7_5_mA 0xf
#define PE_CURRENT_0_mA_T114 0x0
#define PE_CURRENT_1_mA_T114 0x1
#define PE_CURRENT_2_mA_T114 0x2
#define PE_CURRENT_3_mA_T114 0x3
#define PE_CURRENT_4_mA_T114 0x4
#define PE_CURRENT_5_mA_T114 0x5
#define PE_CURRENT_6_mA_T114 0x6
#define PE_CURRENT_7_mA_T114 0x7
#define PE_CURRENT_8_mA_T114 0x8
#define PE_CURRENT_9_mA_T114 0x9
#define PE_CURRENT_10_mA_T114 0xa
#define PE_CURRENT_11_mA_T114 0xb
#define PE_CURRENT_12_mA_T114 0xc
#define PE_CURRENT_13_mA_T114 0xd
#define PE_CURRENT_14_mA_T114 0xe
#define PE_CURRENT_15_mA_T114 0xf
#define HDMI_NV_PDISP_KEY_CTRL 0x9a
#define HDMI_NV_PDISP_KEY_DEBUG0 0x9b
#define HDMI_NV_PDISP_KEY_DEBUG1 0x9c
@ -383,4 +476,61 @@
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920 0xc5
#define HDMI_NV_PDISP_SOR_AUDIO_AVAL_DEFAULT 0xc5
#define HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1
#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0)
#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8)
#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16)
#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24)
#define PEAK_CURRENT_0_000_mA 0x00
#define PEAK_CURRENT_0_200_mA 0x01
#define PEAK_CURRENT_0_400_mA 0x02
#define PEAK_CURRENT_0_600_mA 0x03
#define PEAK_CURRENT_0_800_mA 0x04
#define PEAK_CURRENT_1_000_mA 0x05
#define PEAK_CURRENT_1_200_mA 0x06
#define PEAK_CURRENT_1_400_mA 0x07
#define PEAK_CURRENT_1_600_mA 0x08
#define PEAK_CURRENT_1_800_mA 0x09
#define PEAK_CURRENT_2_000_mA 0x0a
#define PEAK_CURRENT_2_200_mA 0x0b
#define PEAK_CURRENT_2_400_mA 0x0c
#define PEAK_CURRENT_2_600_mA 0x0d
#define PEAK_CURRENT_2_800_mA 0x0e
#define PEAK_CURRENT_3_000_mA 0x0f
#define PEAK_CURRENT_3_200_mA 0x10
#define PEAK_CURRENT_3_400_mA 0x11
#define PEAK_CURRENT_3_600_mA 0x12
#define PEAK_CURRENT_3_800_mA 0x13
#define PEAK_CURRENT_4_000_mA 0x14
#define PEAK_CURRENT_4_200_mA 0x15
#define PEAK_CURRENT_4_400_mA 0x16
#define PEAK_CURRENT_4_600_mA 0x17
#define PEAK_CURRENT_4_800_mA 0x18
#define PEAK_CURRENT_5_000_mA 0x19
#define PEAK_CURRENT_5_200_mA 0x1a
#define PEAK_CURRENT_5_400_mA 0x1b
#define PEAK_CURRENT_5_600_mA 0x1c
#define PEAK_CURRENT_5_800_mA 0x1d
#define PEAK_CURRENT_6_000_mA 0x1e
#define PEAK_CURRENT_6_200_mA 0x1f
#define PEAK_CURRENT_6_400_mA 0x20
#define PEAK_CURRENT_6_600_mA 0x21
#define PEAK_CURRENT_6_800_mA 0x22
#define PEAK_CURRENT_7_000_mA 0x23
#define PEAK_CURRENT_7_200_mA 0x24
#define PEAK_CURRENT_7_400_mA 0x25
#define PEAK_CURRENT_7_600_mA 0x26
#define PEAK_CURRENT_7_800_mA 0x27
#define PEAK_CURRENT_8_000_mA 0x28
#define PEAK_CURRENT_8_200_mA 0x29
#define PEAK_CURRENT_8_400_mA 0x2a
#define PEAK_CURRENT_8_600_mA 0x2b
#define PEAK_CURRENT_8_800_mA 0x2c
#define PEAK_CURRENT_9_000_mA 0x2d
#define PEAK_CURRENT_9_200_mA 0x2e
#define PEAK_CURRENT_9_400_mA 0x2f
#define HDMI_NV_PDISP_SOR_PAD_CTLS0 0xd2
#endif /* TEGRA_HDMI_H */

View File

@ -7,9 +7,7 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include "drm.h"
@ -81,10 +79,16 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
return status;
}
static void drm_connector_clear(struct drm_connector *connector)
{
memset(connector, 0, sizeof(*connector));
}
static void tegra_connector_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
drm_connector_clear(connector);
}
static const struct drm_connector_funcs connector_funcs = {
@ -94,9 +98,15 @@ static const struct drm_connector_funcs connector_funcs = {
.destroy = tegra_connector_destroy,
};
static void drm_encoder_clear(struct drm_encoder *encoder)
{
memset(encoder, 0, sizeof(*encoder));
}
static void tegra_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
drm_encoder_clear(encoder);
}
static const struct drm_encoder_funcs encoder_funcs = {
@ -151,7 +161,7 @@ static irqreturn_t hpd_irq(int irq, void *data)
return IRQ_HANDLED;
}
int tegra_output_parse_dt(struct tegra_output *output)
int tegra_output_probe(struct tegra_output *output)
{
enum of_gpio_flags flags;
struct device_node *ddc;
@ -181,14 +191,6 @@ int tegra_output_parse_dt(struct tegra_output *output)
output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
"nvidia,hpd-gpio", 0,
&flags);
return 0;
}
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{
int connector, encoder, err;
if (gpio_is_valid(output->hpd_gpio)) {
unsigned long flags;
@ -202,7 +204,8 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
err = gpio_to_irq(output->hpd_gpio);
if (err < 0) {
dev_err(output->dev, "gpio_to_irq(): %d\n", err);
goto free_hpd;
gpio_free(output->hpd_gpio);
return err;
}
output->hpd_irq = err;
@ -215,12 +218,33 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
if (err < 0) {
dev_err(output->dev, "failed to request IRQ#%u: %d\n",
output->hpd_irq, err);
goto free_hpd;
gpio_free(output->hpd_gpio);
return err;
}
output->connector.polled = DRM_CONNECTOR_POLL_HPD;
}
return 0;
}
int tegra_output_remove(struct tegra_output *output)
{
if (gpio_is_valid(output->hpd_gpio)) {
free_irq(output->hpd_irq, output);
gpio_free(output->hpd_gpio);
}
if (output->ddc)
put_device(&output->ddc->dev);
return 0;
}
int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
{
int connector, encoder;
switch (output->type) {
case TEGRA_OUTPUT_RGB:
connector = DRM_MODE_CONNECTOR_LVDS;
@ -241,6 +265,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
drm_connector_init(drm, &output->connector, &connector_funcs,
connector);
drm_connector_helper_add(&output->connector, &connector_helper_funcs);
output->connector.dpms = DRM_MODE_DPMS_OFF;
drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
@ -251,22 +276,9 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
output->encoder.possible_crtcs = 0x3;
return 0;
free_hpd:
gpio_free(output->hpd_gpio);
return err;
}
int tegra_output_exit(struct tegra_output *output)
{
if (gpio_is_valid(output->hpd_gpio)) {
free_irq(output->hpd_irq, output);
gpio_free(output->hpd_gpio);
}
if (output->ddc)
put_device(&output->ddc->dev);
return 0;
}

View File

@ -8,9 +8,6 @@
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include "drm.h"
#include "dc.h"
@ -150,7 +147,7 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
rgb->output.dev = dc->dev;
rgb->output.of_node = np;
err = tegra_output_parse_dt(&rgb->output);
err = tegra_output_probe(&rgb->output);
if (err < 0)
return err;
@ -177,6 +174,20 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
return 0;
}
int tegra_dc_rgb_remove(struct tegra_dc *dc)
{
int err;
if (!dc->rgb)
return 0;
err = tegra_output_remove(dc->rgb);
if (err < 0)
return err;
return 0;
}
int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
{
struct tegra_rgb *rgb = to_rgb(dc->rgb);

View File

@ -19,6 +19,4 @@ config TEGRA_HOST1X_FIREWALL
If unsure, choose Y.
source "drivers/gpu/host1x/drm/Kconfig"
endif

View File

@ -1,6 +1,5 @@
ccflags-y = -Idrivers/gpu/host1x
host1x-y = \
bus.o \
syncpt.o \
dev.o \
intr.o \
@ -8,13 +7,7 @@ host1x-y = \
channel.o \
job.o \
debug.o \
hw/host1x01.o
hw/host1x01.o \
hw/host1x02.o
ccflags-y += -Iinclude/drm
ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
host1x-$(CONFIG_DRM_TEGRA) += drm/drm.o drm/fb.o drm/dc.o
host1x-$(CONFIG_DRM_TEGRA) += drm/output.o drm/rgb.o drm/hdmi.o
host1x-$(CONFIG_DRM_TEGRA) += drm/gem.o
host1x-$(CONFIG_DRM_TEGRA) += drm/gr2d.o
obj-$(CONFIG_TEGRA_HOST1X) += host1x.o

550
drivers/gpu/host1x/bus.c Normal file
View File

@ -0,0 +1,550 @@
/*
* 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 "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;
}
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;
}
static int host1x_register_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_unregister_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;
}
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, 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_register_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_unregister_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);

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2013, NVIDIA Corporation.
* 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,
@ -14,22 +15,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HOST1X_CLIENT_H
#define HOST1X_CLIENT_H
#ifndef HOST1X_BUS_H
#define HOST1X_BUS_H
struct device;
struct platform_device;
struct host1x;
#ifdef CONFIG_DRM_TEGRA
int host1x_drm_alloc(struct platform_device *pdev);
#else
static inline int host1x_drm_alloc(struct platform_device *pdev)
{
return 0;
}
#endif
int host1x_bus_init(void);
void host1x_bus_exit(void);
void host1x_set_drm_data(struct device *dev, void *data);
void *host1x_get_drm_data(struct device *dev);
int host1x_register(struct host1x *host1x);
int host1x_unregister(struct host1x *host1x);
#endif

View File

@ -20,6 +20,7 @@
#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>
@ -30,7 +31,6 @@
#include "channel.h"
#include "dev.h"
#include "debug.h"
#include "host1x_bo.h"
#include "job.h"
/*

View File

@ -40,12 +40,6 @@ struct host1x_channel {
/* channel list operations */
int host1x_channel_list_init(struct host1x *host);
struct host1x_channel *host1x_channel_request(struct device *dev);
void host1x_channel_free(struct host1x_channel *channel);
struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
void host1x_channel_put(struct host1x_channel *channel);
int host1x_job_submit(struct host1x_job *job);
#define host1x_for_each_channel(host, channel) \
list_for_each_entry(channel, &host->chlist.list, list)

View File

@ -27,24 +27,13 @@
#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 "host1x_client.h"
void host1x_set_drm_data(struct device *dev, void *data)
{
struct host1x *host1x = dev_get_drvdata(dev);
host1x->drm_data = data;
}
void *host1x_get_drm_data(struct device *dev)
{
struct host1x *host1x = dev_get_drvdata(dev);
return host1x ? host1x->drm_data : NULL;
}
#include "hw/host1x02.h"
void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
{
@ -79,7 +68,17 @@ static const struct host1x_info host1x01_info = {
.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 struct of_device_id host1x_of_match[] = {
{ .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },
{ .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, },
{ .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, },
{ },
@ -114,6 +113,9 @@ static int host1x_probe(struct platform_device *pdev)
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;
@ -152,7 +154,7 @@ static int host1x_probe(struct platform_device *pdev)
err = host1x_syncpt_init(host);
if (err) {
dev_err(&pdev->dev, "failed to initialize syncpts\n");
return err;
goto fail_unprepare_disable;
}
err = host1x_intr_init(host, syncpt_irq);
@ -163,19 +165,26 @@ static int host1x_probe(struct platform_device *pdev)
host1x_debug_init(host);
host1x_drm_alloc(pdev);
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 __exit host1x_remove(struct platform_device *pdev)
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);
@ -184,59 +193,36 @@ static int __exit host1x_remove(struct platform_device *pdev)
}
static struct platform_driver tegra_host1x_driver = {
.probe = host1x_probe,
.remove = __exit_p(host1x_remove),
.driver = {
.owner = THIS_MODULE,
.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 = platform_driver_register(&tegra_host1x_driver);
err = host1x_bus_init();
if (err < 0)
return err;
#ifdef CONFIG_DRM_TEGRA
err = platform_driver_register(&tegra_dc_driver);
if (err < 0)
goto unregister_host1x;
err = platform_driver_register(&tegra_hdmi_driver);
if (err < 0)
goto unregister_dc;
err = platform_driver_register(&tegra_gr2d_driver);
if (err < 0)
goto unregister_hdmi;
#endif
err = platform_driver_register(&tegra_host1x_driver);
if (err < 0) {
host1x_bus_exit();
return err;
}
return 0;
#ifdef CONFIG_DRM_TEGRA
unregister_hdmi:
platform_driver_unregister(&tegra_hdmi_driver);
unregister_dc:
platform_driver_unregister(&tegra_dc_driver);
unregister_host1x:
platform_driver_unregister(&tegra_host1x_driver);
return err;
#endif
}
module_init(tegra_host1x_init);
static void __exit tegra_host1x_exit(void)
{
#ifdef CONFIG_DRM_TEGRA
platform_driver_unregister(&tegra_gr2d_driver);
platform_driver_unregister(&tegra_hdmi_driver);
platform_driver_unregister(&tegra_dc_driver);
#endif
platform_driver_unregister(&tegra_host1x_driver);
host1x_bus_exit();
}
module_exit(tegra_host1x_exit);

View File

@ -27,6 +27,7 @@
#include "job.h"
struct host1x_syncpt;
struct host1x_syncpt_base;
struct host1x_channel;
struct host1x_cdma;
struct host1x_job;
@ -102,6 +103,7 @@ struct host1x {
void __iomem *regs;
struct host1x_syncpt *syncpt;
struct host1x_syncpt_base *bases;
struct device *dev;
struct clk *clk;
@ -125,7 +127,10 @@ struct host1x {
struct dentry *debugfs;
void *drm_data;
struct mutex devices_lock;
struct list_head devices;
struct list_head list;
};
void host1x_sync_writel(struct host1x *host1x, u32 r, u32 v);
@ -301,8 +306,4 @@ 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_dc_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_gr2d_driver;
#endif

View File

@ -1,343 +0,0 @@
/*
* drivers/video/tegra/host/gr2d/gr2d.c
*
* Tegra Graphics 2D
*
* 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/export.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include "channel.h"
#include "drm.h"
#include "gem.h"
#include "job.h"
#include "host1x.h"
#include "host1x_bo.h"
#include "host1x_client.h"
#include "syncpt.h"
struct gr2d {
struct host1x_client client;
struct clk *clk;
struct host1x_channel *channel;
unsigned long *addr_regs;
};
static inline struct gr2d *to_gr2d(struct host1x_client *client)
{
return container_of(client, struct gr2d, client);
}
static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg);
static int gr2d_client_init(struct host1x_client *client,
struct drm_device *drm)
{
return 0;
}
static int gr2d_client_exit(struct host1x_client *client)
{
return 0;
}
static int gr2d_open_channel(struct host1x_client *client,
struct host1x_drm_context *context)
{
struct gr2d *gr2d = to_gr2d(client);
context->channel = host1x_channel_get(gr2d->channel);
if (!context->channel)
return -ENOMEM;
return 0;
}
static void gr2d_close_channel(struct host1x_drm_context *context)
{
host1x_channel_put(context->channel);
}
static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm,
struct drm_file *file,
u32 handle)
{
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(drm, file, handle);
if (!gem)
return NULL;
mutex_lock(&drm->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&drm->struct_mutex);
bo = to_tegra_bo(gem);
return &bo->base;
}
static int gr2d_submit(struct host1x_drm_context *context,
struct drm_tegra_submit *args, struct drm_device *drm,
struct drm_file *file)
{
struct host1x_job *job;
unsigned int num_cmdbufs = args->num_cmdbufs;
unsigned int num_relocs = args->num_relocs;
unsigned int num_waitchks = args->num_waitchks;
struct drm_tegra_cmdbuf __user *cmdbufs =
(void * __user)(uintptr_t)args->cmdbufs;
struct drm_tegra_reloc __user *relocs =
(void * __user)(uintptr_t)args->relocs;
struct drm_tegra_waitchk __user *waitchks =
(void * __user)(uintptr_t)args->waitchks;
struct drm_tegra_syncpt syncpt;
int err;
/* We don't yet support other than one syncpt_incr struct per submit */
if (args->num_syncpts != 1)
return -EINVAL;
job = host1x_job_alloc(context->channel, args->num_cmdbufs,
args->num_relocs, args->num_waitchks);
if (!job)
return -ENOMEM;
job->num_relocs = args->num_relocs;
job->num_waitchk = args->num_waitchks;
job->client = (u32)args->context;
job->class = context->client->class;
job->serialize = true;
while (num_cmdbufs) {
struct drm_tegra_cmdbuf cmdbuf;
struct host1x_bo *bo;
err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
if (err)
goto fail;
bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
if (!bo) {
err = -ENOENT;
goto fail;
}
host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
num_cmdbufs--;
cmdbufs++;
}
err = copy_from_user(job->relocarray, relocs,
sizeof(*relocs) * num_relocs);
if (err)
goto fail;
while (num_relocs--) {
struct host1x_reloc *reloc = &job->relocarray[num_relocs];
struct host1x_bo *cmdbuf, *target;
cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
target = host1x_bo_lookup(drm, file, (u32)reloc->target);
reloc->cmdbuf = cmdbuf;
reloc->target = target;
if (!reloc->target || !reloc->cmdbuf) {
err = -ENOENT;
goto fail;
}
}
err = copy_from_user(job->waitchk, waitchks,
sizeof(*waitchks) * num_waitchks);
if (err)
goto fail;
err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
sizeof(syncpt));
if (err)
goto fail;
job->syncpt_id = syncpt.id;
job->syncpt_incrs = syncpt.incrs;
job->timeout = 10000;
job->is_addr_reg = gr2d_is_addr_reg;
if (args->timeout && args->timeout < 10000)
job->timeout = args->timeout;
err = host1x_job_pin(job, context->client->dev);
if (err)
goto fail;
err = host1x_job_submit(job);
if (err)
goto fail_submit;
args->fence = job->syncpt_end;
host1x_job_put(job);
return 0;
fail_submit:
host1x_job_unpin(job);
fail:
host1x_job_put(job);
return err;
}
static struct host1x_client_ops gr2d_client_ops = {
.drm_init = gr2d_client_init,
.drm_exit = gr2d_client_exit,
.open_channel = gr2d_open_channel,
.close_channel = gr2d_close_channel,
.submit = gr2d_submit,
};
static void gr2d_init_addr_reg_map(struct device *dev, struct gr2d *gr2d)
{
const u32 gr2d_addr_regs[] = {0x1a, 0x1b, 0x26, 0x2b, 0x2c, 0x2d, 0x31,
0x32, 0x48, 0x49, 0x4a, 0x4b, 0x4c};
unsigned long *bitmap;
int i;
bitmap = devm_kzalloc(dev, DIV_ROUND_UP(256, BITS_PER_BYTE),
GFP_KERNEL);
for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); ++i) {
u32 reg = gr2d_addr_regs[i];
bitmap[BIT_WORD(reg)] |= BIT_MASK(reg);
}
gr2d->addr_regs = bitmap;
}
static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg)
{
struct gr2d *gr2d = dev_get_drvdata(dev);
switch (class) {
case HOST1X_CLASS_HOST1X:
return reg == 0x2b;
case HOST1X_CLASS_GR2D:
case HOST1X_CLASS_GR2D_SB:
reg &= 0xff;
if (gr2d->addr_regs[BIT_WORD(reg)] & BIT_MASK(reg))
return 1;
default:
return 0;
}
}
static const struct of_device_id gr2d_match[] = {
{ .compatible = "nvidia,tegra30-gr2d" },
{ .compatible = "nvidia,tegra20-gr2d" },
{ },
};
static int gr2d_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct host1x_drm *host1x = host1x_get_drm_data(dev->parent);
int err;
struct gr2d *gr2d = NULL;
struct host1x_syncpt **syncpts;
gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
if (!gr2d)
return -ENOMEM;
syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
if (!syncpts)
return -ENOMEM;
gr2d->clk = devm_clk_get(dev, NULL);
if (IS_ERR(gr2d->clk)) {
dev_err(dev, "cannot get clock\n");
return PTR_ERR(gr2d->clk);
}
err = clk_prepare_enable(gr2d->clk);
if (err) {
dev_err(dev, "cannot turn on clock\n");
return err;
}
gr2d->channel = host1x_channel_request(dev);
if (!gr2d->channel)
return -ENOMEM;
*syncpts = host1x_syncpt_request(dev, false);
if (!(*syncpts)) {
host1x_channel_free(gr2d->channel);
return -ENOMEM;
}
gr2d->client.ops = &gr2d_client_ops;
gr2d->client.dev = dev;
gr2d->client.class = HOST1X_CLASS_GR2D;
gr2d->client.syncpts = syncpts;
gr2d->client.num_syncpts = 1;
err = host1x_register_client(host1x, &gr2d->client);
if (err < 0) {
dev_err(dev, "failed to register host1x client: %d\n", err);
return err;
}
gr2d_init_addr_reg_map(dev, gr2d);
platform_set_drvdata(pdev, gr2d);
return 0;
}
static int __exit gr2d_remove(struct platform_device *pdev)
{
struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
struct gr2d *gr2d = platform_get_drvdata(pdev);
unsigned int i;
int err;
err = host1x_unregister_client(host1x, &gr2d->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister client: %d\n", err);
return err;
}
for (i = 0; i < gr2d->client.num_syncpts; i++)
host1x_syncpt_free(gr2d->client.syncpts[i]);
host1x_channel_free(gr2d->channel);
clk_disable_unprepare(gr2d->clk);
return 0;
}
struct platform_driver tegra_gr2d_driver = {
.probe = gr2d_probe,
.remove = __exit_p(gr2d_remove),
.driver = {
.owner = THIS_MODULE,
.name = "gr2d",
.of_match_table = gr2d_match,
}
};

View File

@ -1,30 +0,0 @@
/*
* Tegra host1x driver
*
* Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __LINUX_HOST1X_H
#define __LINUX_HOST1X_H
enum host1x_class {
HOST1X_CLASS_HOST1X = 0x1,
HOST1X_CLASS_GR2D = 0x51,
HOST1X_CLASS_GR2D_SB = 0x52
};
#endif

View File

@ -1,87 +0,0 @@
/*
* Tegra host1x Memory Management Abstraction header
*
* 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_BO_H
#define _HOST1X_BO_H
struct host1x_bo;
struct host1x_bo_ops {
struct host1x_bo *(*get)(struct host1x_bo *bo);
void (*put)(struct host1x_bo *bo);
dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
void *(*mmap)(struct host1x_bo *bo);
void (*munmap)(struct host1x_bo *bo, void *addr);
void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
};
struct host1x_bo {
const struct host1x_bo_ops *ops;
};
static inline void host1x_bo_init(struct host1x_bo *bo,
const struct host1x_bo_ops *ops)
{
bo->ops = ops;
}
static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
{
return bo->ops->get(bo);
}
static inline void host1x_bo_put(struct host1x_bo *bo)
{
bo->ops->put(bo);
}
static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
struct sg_table **sgt)
{
return bo->ops->pin(bo, sgt);
}
static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
{
bo->ops->unpin(bo, sgt);
}
static inline void *host1x_bo_mmap(struct host1x_bo *bo)
{
return bo->ops->mmap(bo);
}
static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
{
bo->ops->munmap(bo, addr);
}
static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
{
return bo->ops->kmap(bo, pagenum);
}
static inline void host1x_bo_kunmap(struct host1x_bo *bo,
unsigned int pagenum, void *addr)
{
bo->ops->kunmap(bo, pagenum, addr);
}
#endif

View File

@ -1,6 +0,0 @@
ccflags-y = -Idrivers/gpu/host1x
host1x-hw-objs = \
host1x01.o
obj-$(CONFIG_TEGRA_HOST1X) += host1x-hw.o

View File

@ -20,10 +20,10 @@
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include "cdma.h"
#include "channel.h"
#include "dev.h"
#include "debug.h"
#include "../cdma.h"
#include "../channel.h"
#include "../dev.h"
#include "../debug.h"
/*
* Put the restart at the end of pushbuffer memor

View File

@ -16,15 +16,15 @@
* 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 "host1x.h"
#include "host1x_bo.h"
#include "channel.h"
#include "dev.h"
#include "intr.h"
#include "job.h"
#include "../channel.h"
#include "../dev.h"
#include "../intr.h"
#include "../job.h"
#define HOST1X_CHANNEL_SIZE 16384
#define TRACE_MAX_LENGTH 128U
@ -67,6 +67,22 @@ static void submit_gathers(struct host1x_job *job)
}
}
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;
@ -118,6 +134,10 @@ static int channel_submit(struct host1x_job *job)
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;

View File

@ -15,18 +15,10 @@
*
*/
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/io.h>
#include "dev.h"
#include "debug.h"
#include "cdma.h"
#include "channel.h"
#include "host1x_bo.h"
#include "../dev.h"
#include "../debug.h"
#include "../cdma.h"
#include "../channel.h"
#define HOST1X_DEBUG_MAX_PAGE_OFFSET 102400

View File

@ -17,17 +17,17 @@
*/
/* include hw specification */
#include "hw/host1x01.h"
#include "hw/host1x01_hardware.h"
#include "host1x01.h"
#include "host1x01_hardware.h"
/* include code */
#include "hw/cdma_hw.c"
#include "hw/channel_hw.c"
#include "hw/debug_hw.c"
#include "hw/intr_hw.c"
#include "hw/syncpt_hw.c"
#include "cdma_hw.c"
#include "channel_hw.c"
#include "debug_hw.c"
#include "intr_hw.c"
#include "syncpt_hw.c"
#include "dev.h"
#include "../dev.h"
int host1x01_init(struct host1x *host)
{

View 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 "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 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;
}

View 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

View File

@ -111,6 +111,12 @@ static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
}
#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;

View 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

View 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

View File

@ -0,0 +1,175 @@
/*
* 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_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

View File

@ -22,8 +22,8 @@
#include <linux/io.h>
#include <asm/mach/irq.h>
#include "intr.h"
#include "dev.h"
#include "../intr.h"
#include "../dev.h"
/*
* Sync point threshold interrupt service function

View File

@ -18,8 +18,8 @@
#include <linux/io.h>
#include "dev.h"
#include "syncpt.h"
#include "../dev.h"
#include "../syncpt.h"
/*
* Write the current syncpoint value back to hw.

View File

@ -18,6 +18,7 @@
#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>
@ -27,7 +28,6 @@
#include "channel.h"
#include "dev.h"
#include "host1x_bo.h"
#include "job.h"
#include "syncpt.h"
@ -264,7 +264,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
}
static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
unsigned int offset)
unsigned int offset)
{
offset *= sizeof(u32);
@ -281,7 +281,7 @@ struct host1x_firewall {
unsigned int num_relocs;
struct host1x_reloc *reloc;
struct host1x_bo *cmdbuf_id;
struct host1x_bo *cmdbuf;
unsigned int offset;
u32 words;
@ -291,25 +291,37 @@ struct host1x_firewall {
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) {
if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
if (!fw->num_relocs)
return -EINVAL;
if (!check_reloc(fw->reloc, fw->cmdbuf_id,
fw->offset))
return -EINVAL;
fw->reloc++;
fw->num_relocs--;
}
ret = check_register(fw, reg);
if (ret < 0)
return ret;
fw->words--;
fw->offset++;
}
@ -324,19 +336,16 @@ 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;
if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
if (!fw->num_relocs)
return -EINVAL;
if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
return -EINVAL;
fw->reloc++;
fw->num_relocs--;
}
ret = check_register(fw, reg);
if (ret < 0)
return ret;
reg++;
fw->words--;
fw->offset++;
@ -348,21 +357,17 @@ static int check_incr(struct host1x_firewall *fw)
static int check_nonincr(struct host1x_firewall *fw)
{
int is_addr_reg = fw->job->is_addr_reg(fw->dev, fw->class, fw->reg);
u32 count = fw->count;
int ret;
while (count) {
if (fw->words == 0)
return -EINVAL;
if (is_addr_reg) {
if (!fw->num_relocs)
return -EINVAL;
if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
return -EINVAL;
fw->reloc++;
fw->num_relocs--;
}
ret = check_register(fw, fw->reg);
if (ret < 0)
return ret;
fw->words--;
fw->offset++;
count--;
@ -381,7 +386,7 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
return 0;
fw->words = g->words;
fw->cmdbuf_id = g->bo;
fw->cmdbuf = g->bo;
fw->offset = 0;
while (fw->words && !err) {
@ -436,10 +441,6 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
}
}
/* No relocs should remain at this point */
if (fw->num_relocs)
err = -EINVAL;
out:
return err;
}
@ -493,6 +494,10 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
offset += g->words * sizeof(u32);
}
/* No relocs should remain at this point */
if (fw.num_relocs)
return -EINVAL;
return 0;
}

View File

@ -34,15 +34,6 @@ struct host1x_cmdbuf {
u32 pad;
};
struct host1x_reloc {
struct host1x_bo *cmdbuf;
u32 cmdbuf_offset;
struct host1x_bo *target;
u32 target_offset;
u32 shift;
u32 pad;
};
struct host1x_waitchk {
struct host1x_bo *bo;
u32 offset;
@ -55,105 +46,6 @@ struct host1x_job_unpin_data {
struct sg_table *sgt;
};
/*
* Each submit is tracked as a host1x_job.
*/
struct host1x_job {
/* When refcount goes to zero, job can be freed */
struct kref ref;
/* List entry */
struct list_head list;
/* Channel where job is submitted to */
struct host1x_channel *channel;
u32 client;
/* Gathers and their memory */
struct host1x_job_gather *gathers;
unsigned int num_gathers;
/* Wait checks to be processed at submit time */
struct host1x_waitchk *waitchk;
unsigned int num_waitchk;
u32 waitchk_mask;
/* Array of handles to be pinned & unpinned */
struct host1x_reloc *relocarray;
unsigned int num_relocs;
struct host1x_job_unpin_data *unpins;
unsigned int num_unpins;
dma_addr_t *addr_phys;
dma_addr_t *gather_addr_phys;
dma_addr_t *reloc_addr_phys;
/* Sync point id, number of increments and end related to the submit */
u32 syncpt_id;
u32 syncpt_incrs;
u32 syncpt_end;
/* Maximum time to wait for this job */
unsigned int timeout;
/* Index and number of slots used in the push buffer */
unsigned int first_get;
unsigned int num_slots;
/* Copy of gathers */
size_t gather_copy_size;
dma_addr_t gather_copy;
u8 *gather_copy_mapped;
/* Check if register is marked as an address reg */
int (*is_addr_reg)(struct device *dev, u32 reg, u32 class);
/* Request a SETCLASS to this class */
u32 class;
/* Add a channel wait for previous ops to complete */
bool serialize;
};
/*
* Allocate memory for a job. Just enough memory will be allocated to
* accomodate the submit.
*/
struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
u32 num_cmdbufs, u32 num_relocs,
u32 num_waitchks);
/*
* Add a gather to a job.
*/
void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id,
u32 words, u32 offset);
/*
* Increment reference going to host1x_job.
*/
struct host1x_job *host1x_job_get(struct host1x_job *job);
/*
* Decrement reference job, free if goes to zero.
*/
void host1x_job_put(struct host1x_job *job);
/*
* Pin memory related to job. This handles relocation of addresses to the
* host1x address space. Handles both the gather memory and any other memory
* referred to from the gather buffers.
*
* Handles also patching out host waits that would wait for an expired sync
* point value.
*/
int host1x_job_pin(struct host1x_job *job, struct device *dev);
/*
* Unpin memory related to job.
*/
void host1x_job_unpin(struct host1x_job *job);
/*
* Dump contents of job to debug output.
*/

View File

@ -30,9 +30,32 @@
#define SYNCPT_CHECK_PERIOD (2 * HZ)
#define MAX_STUCK_CHECK_COUNT 15
static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
struct device *dev,
bool client_managed)
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;
@ -44,6 +67,12 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
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)
@ -51,7 +80,11 @@ static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
sp->dev = dev;
sp->name = name;
sp->client_managed = client_managed;
if (flags & HOST1X_SYNCPT_CLIENT_MANAGED)
sp->client_managed = true;
else
sp->client_managed = false;
return sp;
}
@ -303,25 +336,35 @@ int host1x_syncpt_patch_wait(struct host1x_syncpt *sp, void *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);
GFP_KERNEL);
if (!syncpt)
return -ENOMEM;
for (i = 0; i < host->info->nb_pts; ++i) {
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, false);
host->nop_sp = host1x_syncpt_alloc(host, NULL, 0);
if (!host->nop_sp)
return -ENOMEM;
@ -329,10 +372,10 @@ int host1x_syncpt_init(struct host1x *host)
}
struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
bool client_managed)
unsigned long flags)
{
struct host1x *host = dev_get_drvdata(dev->parent);
return _host1x_syncpt_alloc(host, dev, client_managed);
return host1x_syncpt_alloc(host, dev, flags);
}
void host1x_syncpt_free(struct host1x_syncpt *sp)
@ -340,7 +383,9 @@ 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;
@ -354,6 +399,25 @@ void host1x_syncpt_deinit(struct host1x *host)
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);
}
/*
* 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);
}
int host1x_syncpt_nb_pts(struct host1x *host)
{
return host->info->nb_pts;
@ -375,3 +439,13 @@ struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id)
return NULL;
return host->syncpt + id;
}
struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp)
{
return sp ? sp->base : NULL;
}
u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base)
{
return base->id;
}

View File

@ -20,6 +20,7 @@
#define __HOST1X_SYNCPT_H
#include <linux/atomic.h>
#include <linux/host1x.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@ -30,6 +31,11 @@ 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;
@ -39,6 +45,7 @@ struct host1x_syncpt {
bool client_managed;
struct host1x *host;
struct device *dev;
struct host1x_syncpt_base *base;
/* interrupt data */
struct host1x_syncpt_intr intr;
@ -50,25 +57,6 @@ int host1x_syncpt_init(struct host1x *host);
/* Free sync point array */
void host1x_syncpt_deinit(struct host1x *host);
/*
* Read max. It indicates how many operations there are in queue, either in
* channel or in a software thread.
* */
static inline u32 host1x_syncpt_read_max(struct host1x_syncpt *sp)
{
smp_rmb();
return (u32)atomic_read(&sp->max_val);
}
/*
* Read min, which is a shadow of the current sync point value in hardware.
*/
static inline u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
{
smp_rmb();
return (u32)atomic_read(&sp->min_val);
}
/* Return number of sync point supported. */
int host1x_syncpt_nb_pts(struct host1x *host);
@ -112,9 +100,6 @@ static inline bool host1x_syncpt_idle(struct host1x_syncpt *sp)
return (min == max);
}
/* Return pointer to struct denoting sync point id. */
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id);
/* Load current value from hardware to the shadow register. */
u32 host1x_syncpt_load(struct host1x_syncpt *sp);
@ -130,16 +115,9 @@ 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);
/* Request incrementing a sync point. */
int host1x_syncpt_incr(struct host1x_syncpt *sp);
/* Indicate future operations by incrementing the sync point max. */
u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs);
/* Wait until sync point reaches a threshold value, or a timeout. */
int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh,
long timeout, u32 *value);
/* Check if sync point id is valid. */
static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
{
@ -149,14 +127,4 @@ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
/* 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);
/* Return id of the sync point */
u32 host1x_syncpt_id(struct host1x_syncpt *sp);
/* Allocate a sync point for a device. */
struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
bool client_managed);
/* Free a sync point. */
void host1x_syncpt_free(struct host1x_syncpt *sp);
#endif

View File

@ -19,10 +19,10 @@ source "drivers/char/agp/Kconfig"
source "drivers/gpu/vga/Kconfig"
source "drivers/gpu/drm/Kconfig"
source "drivers/gpu/host1x/Kconfig"
source "drivers/gpu/drm/Kconfig"
config VGASTATE
tristate
default n

View File

@ -150,6 +150,7 @@ int drm_err(const char *func, const char *format, ...);
#define DRIVER_BUS_PCI 0x1
#define DRIVER_BUS_PLATFORM 0x2
#define DRIVER_BUS_USB 0x3
#define DRIVER_BUS_HOST1X 0x4
/***********************************************************************/
/** \name Begin the DRM... */

284
include/linux/host1x.h Normal file
View File

@ -0,0 +1,284 @@
/*
* Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __LINUX_HOST1X_H
#define __LINUX_HOST1X_H
#include <linux/device.h>
#include <linux/types.h>
enum host1x_class {
HOST1X_CLASS_HOST1X = 0x1,
HOST1X_CLASS_GR2D = 0x51,
HOST1X_CLASS_GR2D_SB = 0x52,
HOST1X_CLASS_GR3D = 0x60,
};
struct host1x_client;
struct host1x_client_ops {
int (*init)(struct host1x_client *client);
int (*exit)(struct host1x_client *client);
};
struct host1x_client {
struct list_head list;
struct device *parent;
struct device *dev;
const struct host1x_client_ops *ops;
enum host1x_class class;
struct host1x_channel *channel;
struct host1x_syncpt **syncpts;
unsigned int num_syncpts;
};
/*
* host1x buffer objects
*/
struct host1x_bo;
struct sg_table;
struct host1x_bo_ops {
struct host1x_bo *(*get)(struct host1x_bo *bo);
void (*put)(struct host1x_bo *bo);
dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
void *(*mmap)(struct host1x_bo *bo);
void (*munmap)(struct host1x_bo *bo, void *addr);
void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
};
struct host1x_bo {
const struct host1x_bo_ops *ops;
};
static inline void host1x_bo_init(struct host1x_bo *bo,
const struct host1x_bo_ops *ops)
{
bo->ops = ops;
}
static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
{
return bo->ops->get(bo);
}
static inline void host1x_bo_put(struct host1x_bo *bo)
{
bo->ops->put(bo);
}
static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
struct sg_table **sgt)
{
return bo->ops->pin(bo, sgt);
}
static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
{
bo->ops->unpin(bo, sgt);
}
static inline void *host1x_bo_mmap(struct host1x_bo *bo)
{
return bo->ops->mmap(bo);
}
static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
{
bo->ops->munmap(bo, addr);
}
static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
{
return bo->ops->kmap(bo, pagenum);
}
static inline void host1x_bo_kunmap(struct host1x_bo *bo,
unsigned int pagenum, void *addr)
{
bo->ops->kunmap(bo, pagenum, addr);
}
/*
* host1x syncpoints
*/
#define HOST1X_SYNCPT_CLIENT_MANAGED (1 << 0)
#define HOST1X_SYNCPT_HAS_BASE (1 << 1)
struct host1x_syncpt_base;
struct host1x_syncpt;
struct host1x;
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id);
u32 host1x_syncpt_id(struct host1x_syncpt *sp);
u32 host1x_syncpt_read_min(struct host1x_syncpt *sp);
u32 host1x_syncpt_read_max(struct host1x_syncpt *sp);
int host1x_syncpt_incr(struct host1x_syncpt *sp);
int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
u32 *value);
struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
unsigned long flags);
void host1x_syncpt_free(struct host1x_syncpt *sp);
struct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp);
u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base);
/*
* host1x channel
*/
struct host1x_channel;
struct host1x_job;
struct host1x_channel *host1x_channel_request(struct device *dev);
void host1x_channel_free(struct host1x_channel *channel);
struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
void host1x_channel_put(struct host1x_channel *channel);
int host1x_job_submit(struct host1x_job *job);
/*
* host1x job
*/
struct host1x_reloc {
struct host1x_bo *cmdbuf;
u32 cmdbuf_offset;
struct host1x_bo *target;
u32 target_offset;
u32 shift;
u32 pad;
};
struct host1x_job {
/* When refcount goes to zero, job can be freed */
struct kref ref;
/* List entry */
struct list_head list;
/* Channel where job is submitted to */
struct host1x_channel *channel;
u32 client;
/* Gathers and their memory */
struct host1x_job_gather *gathers;
unsigned int num_gathers;
/* Wait checks to be processed at submit time */
struct host1x_waitchk *waitchk;
unsigned int num_waitchk;
u32 waitchk_mask;
/* Array of handles to be pinned & unpinned */
struct host1x_reloc *relocarray;
unsigned int num_relocs;
struct host1x_job_unpin_data *unpins;
unsigned int num_unpins;
dma_addr_t *addr_phys;
dma_addr_t *gather_addr_phys;
dma_addr_t *reloc_addr_phys;
/* Sync point id, number of increments and end related to the submit */
u32 syncpt_id;
u32 syncpt_incrs;
u32 syncpt_end;
/* Maximum time to wait for this job */
unsigned int timeout;
/* Index and number of slots used in the push buffer */
unsigned int first_get;
unsigned int num_slots;
/* Copy of gathers */
size_t gather_copy_size;
dma_addr_t gather_copy;
u8 *gather_copy_mapped;
/* Check if register is marked as an address reg */
int (*is_addr_reg)(struct device *dev, u32 reg, u32 class);
/* Request a SETCLASS to this class */
u32 class;
/* Add a channel wait for previous ops to complete */
bool serialize;
};
struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
u32 num_cmdbufs, u32 num_relocs,
u32 num_waitchks);
void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id,
u32 words, u32 offset);
struct host1x_job *host1x_job_get(struct host1x_job *job);
void host1x_job_put(struct host1x_job *job);
int host1x_job_pin(struct host1x_job *job, struct device *dev);
void host1x_job_unpin(struct host1x_job *job);
/*
* subdevice probe infrastructure
*/
struct host1x_device;
struct host1x_driver {
const struct of_device_id *subdevs;
struct list_head list;
const char *name;
int (*probe)(struct host1x_device *device);
int (*remove)(struct host1x_device *device);
};
int host1x_driver_register(struct host1x_driver *driver);
void host1x_driver_unregister(struct host1x_driver *driver);
struct host1x_device {
struct host1x_driver *driver;
struct list_head list;
struct device dev;
struct mutex subdevs_lock;
struct list_head subdevs;
struct list_head active;
struct mutex clients_lock;
struct list_head clients;
};
static inline struct host1x_device *to_host1x_device(struct device *dev)
{
return container_of(dev, struct host1x_device, dev);
}
int host1x_device_init(struct host1x_device *device);
int host1x_device_exit(struct host1x_device *device);
int host1x_client_register(struct host1x_client *client);
int host1x_client_unregister(struct host1x_client *client);
#endif

View File

@ -19,6 +19,9 @@
#include <drm/drm.h>
#define DRM_TEGRA_GEM_CREATE_TILED (1 << 0)
#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
struct drm_tegra_gem_create {
__u64 size;
__u32 flags;
@ -65,6 +68,12 @@ struct drm_tegra_get_syncpt {
__u32 id;
};
struct drm_tegra_get_syncpt_base {
__u64 context;
__u32 syncpt;
__u32 id;
};
struct drm_tegra_syncpt {
__u32 id;
__u32 incrs;
@ -115,15 +124,16 @@ struct drm_tegra_submit {
__u32 reserved[5]; /* future expansion */
};
#define DRM_TEGRA_GEM_CREATE 0x00
#define DRM_TEGRA_GEM_MMAP 0x01
#define DRM_TEGRA_SYNCPT_READ 0x02
#define DRM_TEGRA_SYNCPT_INCR 0x03
#define DRM_TEGRA_SYNCPT_WAIT 0x04
#define DRM_TEGRA_OPEN_CHANNEL 0x05
#define DRM_TEGRA_CLOSE_CHANNEL 0x06
#define DRM_TEGRA_GET_SYNCPT 0x07
#define DRM_TEGRA_SUBMIT 0x08
#define DRM_TEGRA_GEM_CREATE 0x00
#define DRM_TEGRA_GEM_MMAP 0x01
#define DRM_TEGRA_SYNCPT_READ 0x02
#define DRM_TEGRA_SYNCPT_INCR 0x03
#define DRM_TEGRA_SYNCPT_WAIT 0x04
#define DRM_TEGRA_OPEN_CHANNEL 0x05
#define DRM_TEGRA_CLOSE_CHANNEL 0x06
#define DRM_TEGRA_GET_SYNCPT 0x07
#define DRM_TEGRA_SUBMIT 0x08
#define DRM_TEGRA_GET_SYNCPT_BASE 0x09
#define DRM_IOCTL_TEGRA_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_CREATE, struct drm_tegra_gem_create)
#define DRM_IOCTL_TEGRA_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GEM_MMAP, struct drm_tegra_gem_mmap)
@ -134,5 +144,6 @@ struct drm_tegra_submit {
#define DRM_IOCTL_TEGRA_CLOSE_CHANNEL DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_CLOSE_CHANNEL, struct drm_tegra_open_channel)
#define DRM_IOCTL_TEGRA_GET_SYNCPT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT, struct drm_tegra_get_syncpt)
#define DRM_IOCTL_TEGRA_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_SUBMIT, struct drm_tegra_submit)
#define DRM_IOCTL_TEGRA_GET_SYNCPT_BASE DRM_IOWR(DRM_COMMAND_BASE + DRM_TEGRA_GET_SYNCPT_BASE, struct drm_tegra_get_syncpt_base)
#endif