diff --git a/MAINTAINERS b/MAINTAINERS index 284969fa2896..4907a747f3e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index e41a2d0311f8..f86427591167 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -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" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 5266e9fae216..cc08b845f965 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -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/ diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index dbcd68709ab7..305b4cdcfb29 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -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; } } diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index b55f138bd990..138de14134f0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -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); diff --git a/drivers/gpu/host1x/drm/Kconfig b/drivers/gpu/drm/tegra/Kconfig similarity index 90% rename from drivers/gpu/host1x/drm/Kconfig rename to drivers/gpu/drm/tegra/Kconfig index 0f36ddd74e87..8961ba6a34b8 100644 --- a/drivers/gpu/host1x/drm/Kconfig +++ b/drivers/gpu/drm/tegra/Kconfig @@ -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 diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile new file mode 100644 index 000000000000..edc76abd58bb --- /dev/null +++ b/drivers/gpu/drm/tegra/Makefile @@ -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 diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c new file mode 100644 index 000000000000..565f8f7b9a47 --- /dev/null +++ b/drivers/gpu/drm/tegra/bus.c @@ -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); +} diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/drm/tegra/dc.c similarity index 93% rename from drivers/gpu/host1x/drm/dc.c rename to drivers/gpu/drm/tegra/dc.c index b1a05ad901c3..ae1cb31ead7e 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -8,13 +8,9 @@ */ #include -#include -#include -#include -#include #include +#include -#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; diff --git a/drivers/gpu/host1x/drm/dc.h b/drivers/gpu/drm/tegra/dc.h similarity index 98% rename from drivers/gpu/host1x/drm/dc.h rename to drivers/gpu/drm/tegra/dc.h index 79eaec9aac77..91bbda291470 100644 --- a/drivers/gpu/host1x/drm/dc.h +++ b/drivers/gpu/drm/tegra/dc.h @@ -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 diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/drm/tegra/drm.c similarity index 50% rename from drivers/gpu/host1x/drm/drm.c rename to drivers/gpu/drm/tegra/drm.c index df7d90a3a4fa..28e178137718 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -7,21 +7,10 @@ * published by the Free Software Foundation. */ -#include -#include -#include +#include -#include -#include - -#include -#include - -#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 "); +MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/host1x/drm/drm.h b/drivers/gpu/drm/tegra/drm.h similarity index 72% rename from drivers/gpu/host1x/drm/drm.h rename to drivers/gpu/drm/tegra/drm.h index 02ce020f2575..fdfe259ed7f8 100644 --- a/drivers/gpu/host1x/drm/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -10,14 +10,14 @@ #ifndef HOST1X_DRM_H #define HOST1X_DRM_H 1 +#include +#include + #include #include #include #include #include -#include - -#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 */ diff --git a/drivers/gpu/host1x/drm/fb.c b/drivers/gpu/drm/tegra/fb.c similarity index 92% rename from drivers/gpu/host1x/drm/fb.c rename to drivers/gpu/drm/tegra/fb.c index 979a3e32b78b..490f7719e317 100644 --- a/drivers/gpu/host1x/drm/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -10,8 +10,6 @@ * published by the Free Software Foundation. */ -#include - #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) diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/drm/tegra/gem.c similarity index 86% rename from drivers/gpu/host1x/drm/gem.c rename to drivers/gpu/drm/tegra/gem.c index 59623de4ee15..28a9cbc07ab9 100644 --- a/drivers/gpu/host1x/drm/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -18,25 +18,18 @@ * GNU General Public License for more details. */ -#include -#include -#include -#include -#include - -#include -#include +#include #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); diff --git a/drivers/gpu/host1x/drm/gem.h b/drivers/gpu/drm/tegra/gem.h similarity index 84% rename from drivers/gpu/host1x/drm/gem.h rename to drivers/gpu/drm/tegra/gem.h index 492533a2dacb..7674000bf47d 100644 --- a/drivers/gpu/host1x/drm/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -19,14 +19,18 @@ #ifndef __HOST1X_GEM_H #define __HOST1X_GEM_H +#include + #include #include -#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); diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c new file mode 100644 index 000000000000..7ec4259ffded --- /dev/null +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -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 . + */ + +#include + +#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, +}; diff --git a/drivers/gpu/drm/tegra/gr2d.h b/drivers/gpu/drm/tegra/gr2d.h new file mode 100644 index 000000000000..4d7304fb015e --- /dev/null +++ b/drivers/gpu/drm/tegra/gr2d.h @@ -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 diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c new file mode 100644 index 000000000000..4cec8f526af7 --- /dev/null +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -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 +#include +#include +#include +#include + +#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, +}; diff --git a/drivers/gpu/drm/tegra/gr3d.h b/drivers/gpu/drm/tegra/gr3d.h new file mode 100644 index 000000000000..0c30a1351c83 --- /dev/null +++ b/drivers/gpu/drm/tegra/gr3d.h @@ -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 diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c similarity index 83% rename from drivers/gpu/host1x/drm/hdmi.c rename to drivers/gpu/drm/tegra/hdmi.c index 644d95c7d489..0cd9bc2056e8 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -8,21 +8,33 @@ */ #include -#include -#include -#include -#include -#include -#include -#include #include - -#include +#include +#include +#include #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", diff --git a/drivers/gpu/host1x/drm/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h similarity index 72% rename from drivers/gpu/host1x/drm/hdmi.h rename to drivers/gpu/drm/tegra/hdmi.h index 52ac36e08ccb..0aebc485f7fa 100644 --- a/drivers/gpu/host1x/drm/hdmi.h +++ b/drivers/gpu/drm/tegra/hdmi.h @@ -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 */ diff --git a/drivers/gpu/host1x/drm/output.c b/drivers/gpu/drm/tegra/output.c similarity index 91% rename from drivers/gpu/host1x/drm/output.c rename to drivers/gpu/drm/tegra/output.c index 137ae81ab80e..2cb0065e0578 100644 --- a/drivers/gpu/host1x/drm/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -7,9 +7,7 @@ * published by the Free Software Foundation. */ -#include #include -#include #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; } diff --git a/drivers/gpu/host1x/drm/rgb.c b/drivers/gpu/drm/tegra/rgb.c similarity index 96% rename from drivers/gpu/host1x/drm/rgb.c rename to drivers/gpu/drm/tegra/rgb.c index 5aa66ef7a946..ba47ca4fb880 100644 --- a/drivers/gpu/host1x/drm/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -8,9 +8,6 @@ */ #include -#include -#include -#include #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); diff --git a/drivers/gpu/host1x/Kconfig b/drivers/gpu/host1x/Kconfig index ccfd42b23606..7d6bed222542 100644 --- a/drivers/gpu/host1x/Kconfig +++ b/drivers/gpu/host1x/Kconfig @@ -19,6 +19,4 @@ config TEGRA_HOST1X_FIREWALL If unsure, choose Y. -source "drivers/gpu/host1x/drm/Kconfig" - endif diff --git a/drivers/gpu/host1x/Makefile b/drivers/gpu/host1x/Makefile index 3b037b6e0298..afa1e9e4e512 100644 --- a/drivers/gpu/host1x/Makefile +++ b/drivers/gpu/host1x/Makefile @@ -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 diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c new file mode 100644 index 000000000000..509383f8be03 --- /dev/null +++ b/drivers/gpu/host1x/bus.c @@ -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 . + */ + +#include +#include +#include + +#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); diff --git a/drivers/gpu/host1x/host1x_client.h b/drivers/gpu/host1x/bus.h similarity index 60% rename from drivers/gpu/host1x/host1x_client.h rename to drivers/gpu/host1x/bus.h index 9b85f10f4a44..4099e99212c8 100644 --- a/drivers/gpu/host1x/host1x_client.h +++ b/drivers/gpu/host1x/bus.h @@ -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 . */ -#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 diff --git a/drivers/gpu/host1x/cdma.c b/drivers/gpu/host1x/cdma.c index de72172d3b5f..3995255b16c7 100644 --- a/drivers/gpu/host1x/cdma.c +++ b/drivers/gpu/host1x/cdma.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include "channel.h" #include "dev.h" #include "debug.h" -#include "host1x_bo.h" #include "job.h" /* diff --git a/drivers/gpu/host1x/channel.h b/drivers/gpu/host1x/channel.h index 48723b8eea42..df767cf90d51 100644 --- a/drivers/gpu/host1x/channel.h +++ b/drivers/gpu/host1x/channel.h @@ -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) diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 471630299878..80da003d63de 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -27,24 +27,13 @@ #define CREATE_TRACE_POINTS #include +#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); diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index bed90a8131be..a61a976e7a42 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -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 diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c deleted file mode 100644 index 27ffcf15a4b4..000000000000 --- a/drivers/gpu/host1x/drm/gr2d.c +++ /dev/null @@ -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 . - */ - -#include -#include -#include -#include - -#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, - } -}; diff --git a/drivers/gpu/host1x/host1x.h b/drivers/gpu/host1x/host1x.h deleted file mode 100644 index a2bc1e65e972..000000000000 --- a/drivers/gpu/host1x/host1x.h +++ /dev/null @@ -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 diff --git a/drivers/gpu/host1x/host1x_bo.h b/drivers/gpu/host1x/host1x_bo.h deleted file mode 100644 index 4c1f10bd773d..000000000000 --- a/drivers/gpu/host1x/host1x_bo.h +++ /dev/null @@ -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 . - */ - -#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 diff --git a/drivers/gpu/host1x/hw/Makefile b/drivers/gpu/host1x/hw/Makefile deleted file mode 100644 index 9b50863a2236..000000000000 --- a/drivers/gpu/host1x/hw/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -ccflags-y = -Idrivers/gpu/host1x - -host1x-hw-objs = \ - host1x01.o - -obj-$(CONFIG_TEGRA_HOST1X) += host1x-hw.o diff --git a/drivers/gpu/host1x/hw/cdma_hw.c b/drivers/gpu/host1x/hw/cdma_hw.c index 2ee4ad55c4db..37e2a63241a9 100644 --- a/drivers/gpu/host1x/hw/cdma_hw.c +++ b/drivers/gpu/host1x/hw/cdma_hw.c @@ -20,10 +20,10 @@ #include #include -#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 diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c index ee199623e365..4608257ab656 100644 --- a/drivers/gpu/host1x/hw/channel_hw.c +++ b/drivers/gpu/host1x/hw/channel_hw.c @@ -16,15 +16,15 @@ * along with this program. If not, see . */ +#include #include + #include -#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; diff --git a/drivers/gpu/host1x/hw/debug_hw.c b/drivers/gpu/host1x/hw/debug_hw.c index 334c038052f5..640c75ca5a8b 100644 --- a/drivers/gpu/host1x/hw/debug_hw.c +++ b/drivers/gpu/host1x/hw/debug_hw.c @@ -15,18 +15,10 @@ * */ -#include -#include -#include -#include - -#include - -#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 diff --git a/drivers/gpu/host1x/hw/host1x01.c b/drivers/gpu/host1x/hw/host1x01.c index a14e91cd1e58..859b73beb4d0 100644 --- a/drivers/gpu/host1x/hw/host1x01.c +++ b/drivers/gpu/host1x/hw/host1x01.c @@ -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) { diff --git a/drivers/gpu/host1x/hw/host1x02.c b/drivers/gpu/host1x/hw/host1x02.c new file mode 100644 index 000000000000..e98caca0ca42 --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x02.c @@ -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 . + */ + +/* 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; +} diff --git a/drivers/gpu/host1x/hw/host1x02.h b/drivers/gpu/host1x/hw/host1x02.h new file mode 100644 index 000000000000..f7486609a90e --- /dev/null +++ b/drivers/gpu/host1x/hw/host1x02.h @@ -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 . + */ + +#ifndef HOST1X_HOST1X02_H +#define HOST1X_HOST1X02_H + +struct host1x; + +int host1x02_init(struct host1x *host); + +#endif diff --git a/drivers/gpu/host1x/hw/hw_host1x01_uclass.h b/drivers/gpu/host1x/hw/hw_host1x01_uclass.h index 42f3ce19ca32..f7553599ee27 100644 --- a/drivers/gpu/host1x/hw/hw_host1x01_uclass.h +++ b/drivers/gpu/host1x/hw/hw_host1x01_uclass.h @@ -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; diff --git a/drivers/gpu/host1x/hw/hw_host1x02_channel.h b/drivers/gpu/host1x/hw/hw_host1x02_channel.h new file mode 100644 index 000000000000..e490bcde33fe --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x02_channel.h @@ -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 . + * + */ + + /* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * 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 of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ + +#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 diff --git a/drivers/gpu/host1x/hw/hw_host1x02_sync.h b/drivers/gpu/host1x/hw/hw_host1x02_sync.h new file mode 100644 index 000000000000..4495401525e8 --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x02_sync.h @@ -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 . + * + */ + + /* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * 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 of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ + +#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 diff --git a/drivers/gpu/host1x/hw/hw_host1x02_uclass.h b/drivers/gpu/host1x/hw/hw_host1x02_uclass.h new file mode 100644 index 000000000000..a3b3c9874413 --- /dev/null +++ b/drivers/gpu/host1x/hw/hw_host1x02_uclass.h @@ -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 . + * + */ + + /* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * 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 of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ + +#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 diff --git a/drivers/gpu/host1x/hw/intr_hw.c b/drivers/gpu/host1x/hw/intr_hw.c index b592eef1efcb..b26dcc83bc1b 100644 --- a/drivers/gpu/host1x/hw/intr_hw.c +++ b/drivers/gpu/host1x/hw/intr_hw.c @@ -22,8 +22,8 @@ #include #include -#include "intr.h" -#include "dev.h" +#include "../intr.h" +#include "../dev.h" /* * Sync point threshold interrupt service function diff --git a/drivers/gpu/host1x/hw/syncpt_hw.c b/drivers/gpu/host1x/hw/syncpt_hw.c index 0cf6095d3367..56e85395ac24 100644 --- a/drivers/gpu/host1x/hw/syncpt_hw.c +++ b/drivers/gpu/host1x/hw/syncpt_hw.c @@ -18,8 +18,8 @@ #include -#include "dev.h" -#include "syncpt.h" +#include "../dev.h" +#include "../syncpt.h" /* * Write the current syncpoint value back to hw. diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index c4e1050f2252..de5ec333ce1a 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -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; } diff --git a/drivers/gpu/host1x/job.h b/drivers/gpu/host1x/job.h index fba45f20458e..33a697d6dcef 100644 --- a/drivers/gpu/host1x/job.h +++ b/drivers/gpu/host1x/job.h @@ -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. */ diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 409745b949db..159c479829c9 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -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; +} diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h index 267c0b9d3647..9056465ecd3f 100644 --- a/drivers/gpu/host1x/syncpt.h +++ b/drivers/gpu/host1x/syncpt.h @@ -20,6 +20,7 @@ #define __HOST1X_SYNCPT_H #include +#include #include #include @@ -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 diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 84b685f7ab6e..a312f048656f 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -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 diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 7062307a4a2d..e6d0cd9f518e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -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... */ diff --git a/include/linux/host1x.h b/include/linux/host1x.h new file mode 100644 index 000000000000..f5b9b87ac9a9 --- /dev/null +++ b/include/linux/host1x.h @@ -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 +#include + +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 diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h index 73bde4eaf16c..5e1ab552cbed 100644 --- a/include/uapi/drm/tegra_drm.h +++ b/include/uapi/drm/tegra_drm.h @@ -19,6 +19,9 @@ #include +#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