]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'drm-tegra/drm/for-next'
authorThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:36:01 +0000 (14:36 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:36:01 +0000 (14:36 +0200)
Conflicts:
drivers/gpu/drm/tegra/Kconfig

67 files changed:
Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
Documentation/devicetree/bindings/misc/nvidia,tegra114-mipi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/auo,b101aw03.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/chunghwa,claa101wb03.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/panasonic,vvx10f004b00.txt [new file with mode: 0644]
Documentation/devicetree/bindings/panel/simple-panel.txt [new file with mode: 0644]
MAINTAINERS
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_panel.c [new file with mode: 0644]
drivers/gpu/drm/panel/Kconfig [new file with mode: 0644]
drivers/gpu/drm/panel/Makefile [new file with mode: 0644]
drivers/gpu/drm/panel/panel-simple.c [new file with mode: 0644]
drivers/gpu/drm/tegra/Kconfig [moved from drivers/gpu/host1x/drm/Kconfig with 88% similarity]
drivers/gpu/drm/tegra/Makefile [new file with mode: 0644]
drivers/gpu/drm/tegra/bus.c [new file with mode: 0644]
drivers/gpu/drm/tegra/dc.c [moved from drivers/gpu/host1x/drm/dc.c with 93% similarity]
drivers/gpu/drm/tegra/dc.h [moved from drivers/gpu/host1x/drm/dc.h with 98% similarity]
drivers/gpu/drm/tegra/drm.c [moved from drivers/gpu/host1x/drm/drm.c with 51% similarity]
drivers/gpu/drm/tegra/drm.h [moved from drivers/gpu/host1x/drm/drm.h with 71% similarity]
drivers/gpu/drm/tegra/dsi.c [new file with mode: 0644]
drivers/gpu/drm/tegra/fb.c [moved from drivers/gpu/host1x/drm/fb.c with 92% similarity]
drivers/gpu/drm/tegra/gem.c [moved from drivers/gpu/host1x/drm/gem.c with 86% similarity]
drivers/gpu/drm/tegra/gem.h [moved from drivers/gpu/host1x/drm/gem.h with 84% similarity]
drivers/gpu/drm/tegra/gr2d.c [new file with mode: 0644]
drivers/gpu/drm/tegra/gr2d.h [new file with mode: 0644]
drivers/gpu/drm/tegra/gr3d.c [new file with mode: 0644]
drivers/gpu/drm/tegra/gr3d.h [new file with mode: 0644]
drivers/gpu/drm/tegra/hdmi.c [moved from drivers/gpu/host1x/drm/hdmi.c with 83% similarity]
drivers/gpu/drm/tegra/hdmi.h [moved from drivers/gpu/host1x/drm/hdmi.h with 72% similarity]
drivers/gpu/drm/tegra/output.c [moved from drivers/gpu/host1x/drm/output.c with 81% similarity]
drivers/gpu/drm/tegra/rgb.c [moved from drivers/gpu/host1x/drm/rgb.c with 96% similarity]
drivers/gpu/host1x/Kconfig
drivers/gpu/host1x/Makefile
drivers/gpu/host1x/bus.c [new file with mode: 0644]
drivers/gpu/host1x/bus.h [moved from drivers/gpu/host1x/host1x_client.h with 60% similarity]
drivers/gpu/host1x/cdma.c
drivers/gpu/host1x/channel.h
drivers/gpu/host1x/dev.c
drivers/gpu/host1x/dev.h
drivers/gpu/host1x/drm/gr2d.c [deleted file]
drivers/gpu/host1x/host1x.h [deleted file]
drivers/gpu/host1x/host1x_bo.h [deleted file]
drivers/gpu/host1x/hw/Makefile [deleted file]
drivers/gpu/host1x/hw/cdma_hw.c
drivers/gpu/host1x/hw/channel_hw.c
drivers/gpu/host1x/hw/debug_hw.c
drivers/gpu/host1x/hw/host1x01.c
drivers/gpu/host1x/hw/host1x02.c [new file with mode: 0644]
drivers/gpu/host1x/hw/host1x02.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x02_channel.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x02_sync.h [new file with mode: 0644]
drivers/gpu/host1x/hw/hw_host1x02_uclass.h [new file with mode: 0644]
drivers/gpu/host1x/hw/intr_hw.c
drivers/gpu/host1x/hw/syncpt_hw.c
drivers/gpu/host1x/job.c
drivers/gpu/host1x/job.h
drivers/gpu/host1x/mipi.c [new file with mode: 0644]
drivers/gpu/host1x/syncpt.c
drivers/gpu/host1x/syncpt.h
drivers/video/Kconfig
include/drm/drmP.h
include/drm/drm_panel.h [new file with mode: 0644]
include/linux/host1x.h [new file with mode: 0644]
include/uapi/drm/tegra_drm.h

index b4fa934ae3a2a2fa0e68e85199d676a4b8a682ff..3f14e81c55bd6fd81b938b32ff860440ba03fa3d 100644 (file)
@@ -67,6 +67,7 @@ of the following host1x client modules:
   - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
   - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
   - nvidia,edid: supplies a binary EDID blob
+  - nvidia,panel: phandle of a display panel
 
 - hdmi: High Definition Multimedia Interface
 
@@ -81,6 +82,7 @@ of the following host1x client modules:
   - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
   - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
   - nvidia,edid: supplies a binary EDID blob
+  - nvidia,panel: phandle of a display panel
 
 - tvo: TV encoder output
 
@@ -94,6 +96,20 @@ of the following host1x client modules:
   Required properties:
   - compatible: "nvidia,tegra<chip>-dsi"
   - reg: Physical base address and length of the controller's registers.
+  - clocks: Should contain phandle and clock specifiers for two clocks:
+    the DSI controller clock and the parent clock of the controller.
+  - clock-names: A list of strings containing the name for each clock in
+    the clocks property. Must contain the following two entries:
+    - "dsi": the DSI controller clock
+    - "parent": parent of the DSI controller clock
+  - calibrate: Should contain a phandle and a specifier specifying which
+    pads are used by this DSI output and need to be calibrated.
+
+  Optional properties:
+  - nvidia,ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
+  - nvidia,hpd-gpio: specifies a GPIO used for hotplug detection
+  - nvidia,edid: supplies a binary EDID blob
+  - nvidia,panel: phandle of a display panel
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/misc/nvidia,tegra114-mipi.txt b/Documentation/devicetree/bindings/misc/nvidia,tegra114-mipi.txt
new file mode 100644 (file)
index 0000000..642c5d8
--- /dev/null
@@ -0,0 +1,37 @@
+NVIDIA Tegra MIPI pad calibration controller
+
+Required properties:
+- compatible: "nvidia,tegra<chip>-mipi"
+- reg: Physical base address and length of the controller's registers.
+- clocks: The clock consumed by the controller.
+- #calibrate-cells: Should be 1. The cell is a bitmask of the pads that need
+  to be calibrated by a given device.
+
+User nodes need to contain a calibrate property that has a phandle to refer
+to the calibration controller node and a bitmask of the pads that need to be
+calibrated.
+
+Example:
+
+       mipi: mipi@700e3000 {
+               compatible = "nvidia,tegra114-mipi";
+               reg = <0x700e3000 0x100>;
+               clocks = <&tegra_car TEGRA114_CLK_MIPI_CAL>;
+               #calibrate-cells = <1>;
+       };
+
+       ...
+
+       host1x@50000000 {
+               ...
+
+               dsi@54300000 {
+                       ...
+
+                       calibrate = <&mipi 0x060>;
+
+                       ...
+               };
+
+               ...
+       };
diff --git a/Documentation/devicetree/bindings/panel/auo,b101aw03.txt b/Documentation/devicetree/bindings/panel/auo,b101aw03.txt
new file mode 100644 (file)
index 0000000..72e088a
--- /dev/null
@@ -0,0 +1,7 @@
+AU Optronics Corporation 10.1" WSVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "auo,b101aw03"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/chunghwa,claa101wb03.txt b/Documentation/devicetree/bindings/panel/chunghwa,claa101wb03.txt
new file mode 100644 (file)
index 0000000..0ab2c05
--- /dev/null
@@ -0,0 +1,7 @@
+Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel
+
+Required properties:
+- compatible: should be "chunghwa,claa101wb03"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/panasonic,vvx10f004b00.txt b/Documentation/devicetree/bindings/panel/panasonic,vvx10f004b00.txt
new file mode 100644 (file)
index 0000000..d328b03
--- /dev/null
@@ -0,0 +1,7 @@
+Panasonic Corporation 10.1" WUXGA TFT LCD panel
+
+Required properties:
+- compatible: should be "panasonic,vvx10f004b00"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/panel/simple-panel.txt b/Documentation/devicetree/bindings/panel/simple-panel.txt
new file mode 100644 (file)
index 0000000..1341bbf
--- /dev/null
@@ -0,0 +1,21 @@
+Simple display panel
+
+Required properties:
+- power-supply: regulator to provide the supply voltage
+
+Optional properties:
+- ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
+- enable-gpios: GPIO pin to enable or disable the panel
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+       panel: panel {
+               compatible = "cptt,claa101wb01";
+               ddc-i2c-bus = <&panelddc>;
+
+               power-supply = <&vdd_pnl_reg>;
+               enable-gpios = <&gpio 90 0>;
+
+               backlight = <&backlight>;
+       };
index 72977cab5c2f46cd1befc921a97fa95c2ce7fe45..480d77d216200178df0f3c6a2111d4164be49287 100644 (file)
@@ -2839,7 +2839,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
 
index e41a2d0311f8b515e47b1103a1cada02bdc91212..e36892b72da5479270e4bc2b662d928c8723ec4f 100644 (file)
@@ -189,3 +189,7 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
 source "drivers/gpu/drm/qxl/Kconfig"
 
 source "drivers/gpu/drm/msm/Kconfig"
+
+source "drivers/gpu/drm/tegra/Kconfig"
+
+source "drivers/gpu/drm/panel/Kconfig"
index 5266e9fae2161b6430b6d8a799834e914093f5c8..c802919356451d0c62ff237ad6a923ab31ecac46 100644 (file)
@@ -18,6 +18,7 @@ drm-y       :=        drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
 drm-$(CONFIG_PCI) += ati_pcigart.o
+drm-$(CONFIG_DRM_PANEL) += drm_panel.o
 
 drm-usb-y   := drm_usb.o
 
@@ -57,4 +58,6 @@ 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/
+obj-y                  += panel/
index 0d6469d74be4411099fa2fd84e1ebc3a09a97e01..80578d807cdc5c877312570c9d1a9edac75bdcfb 100644 (file)
@@ -567,6 +567,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
                                continue;
 
                        connector->encoder = NULL;
+
+                       /*
+                        * drm_helper_disable_unused_functions() ought to be
+                        * doing this, but since we've decoupled the encoder
+                        * from the connector above, the required connection
+                        * between them is henceforth no longer available.
+                        */
+                       connector->dpms = DRM_MODE_DPMS_OFF;
                }
        }
 
index 21bfb68093faa78ea719b41931ce1aee86889bbb..5b7054714475ef2b4ca45e42aa8b723de13b9cc3 100644 (file)
@@ -403,7 +403,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/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
new file mode 100644 (file)
index 0000000..ff6e459
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/err.h>
+#include <linux/export.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+static DEFINE_MUTEX(panel_lock);
+static LIST_HEAD(panel_list);
+
+void drm_panel_init(struct drm_panel *panel)
+{
+       INIT_LIST_HEAD(&panel->list);
+}
+EXPORT_SYMBOL(drm_panel_init);
+
+int drm_panel_add(struct drm_panel *panel)
+{
+       mutex_lock(&panel_lock);
+       list_add_tail(&panel->list, &panel_list);
+       mutex_unlock(&panel_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_panel_add);
+
+void drm_panel_remove(struct drm_panel *panel)
+{
+       mutex_lock(&panel_lock);
+       list_del_init(&panel->list);
+       mutex_unlock(&panel_lock);
+}
+EXPORT_SYMBOL(drm_panel_remove);
+
+int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)
+{
+       if (panel->connector)
+               return -EBUSY;
+
+       panel->connector = connector;
+       panel->drm = connector->dev;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_panel_attach);
+
+int drm_panel_detach(struct drm_panel *panel)
+{
+       panel->connector = NULL;
+       panel->drm = NULL;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_panel_detach);
+
+#ifdef CONFIG_OF
+struct drm_panel *of_drm_find_panel(struct device_node *np)
+{
+       struct drm_panel *panel;
+
+       mutex_lock(&panel_lock);
+
+       list_for_each_entry(panel, &panel_list, list) {
+               if (panel->dev->of_node == np) {
+                       mutex_unlock(&panel_lock);
+                       return panel;
+               }
+       }
+
+       mutex_unlock(&panel_lock);
+       return NULL;
+}
+EXPORT_SYMBOL(of_drm_find_panel);
+#endif
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
new file mode 100644 (file)
index 0000000..843087b
--- /dev/null
@@ -0,0 +1,17 @@
+menuconfig DRM_PANEL
+       bool "DRM panel support"
+       help
+         Panel registration and lookup framework.
+
+if DRM_PANEL
+
+config DRM_PANEL_SIMPLE
+       bool "support for simple panels"
+       depends on OF
+       help
+         DRM panel driver for dumb panels that need at most a regulator and
+         a GPIO to be powered up. Optionally a backlight can be attached so
+         that it can be automatically turned off when the panel goes into a
+         low power state.
+
+endif
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
new file mode 100644 (file)
index 0000000..af9dfa2
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
new file mode 100644 (file)
index 0000000..def3e75
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+struct panel_desc {
+       const struct drm_display_mode *modes;
+       unsigned int num_modes;
+
+       struct {
+               unsigned int width;
+               unsigned int height;
+       } size;
+};
+
+/* TODO: convert to gpiod_*() API once it's been merged */
+#define GPIO_ACTIVE_LOW        (1 << 0)
+
+struct panel_simple {
+       struct drm_panel base;
+       bool enabled;
+
+       const struct panel_desc *desc;
+
+       struct backlight_device *backlight;
+       struct regulator *supply;
+
+       unsigned long enable_gpio_flags;
+       int enable_gpio;
+};
+
+static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
+{
+       return container_of(panel, struct panel_simple, base);
+}
+
+static void panel_simple_disable(struct drm_panel *panel)
+{
+       struct panel_simple *p = to_panel_simple(panel);
+
+       if (!p->enabled)
+               return;
+
+       if (p->backlight) {
+               p->backlight->props.power = FB_BLANK_POWERDOWN;
+               backlight_update_status(p->backlight);
+       }
+
+       if (gpio_is_valid(p->enable_gpio)) {
+               if (p->enable_gpio_flags & GPIO_ACTIVE_LOW)
+                       gpio_set_value(p->enable_gpio, 1);
+               else
+                       gpio_set_value(p->enable_gpio, 0);
+       }
+
+       regulator_disable(p->supply);
+       p->enabled = false;
+}
+
+static void panel_simple_enable(struct drm_panel *panel)
+{
+       struct panel_simple *p = to_panel_simple(panel);
+       int err;
+
+       if (p->enabled)
+               return;
+
+       err = regulator_enable(p->supply);
+       if (err < 0)
+               dev_err(panel->dev, "failed to enable supply: %d\n", err);
+
+       if (gpio_is_valid(p->enable_gpio)) {
+               if (p->enable_gpio_flags & GPIO_ACTIVE_LOW)
+                       gpio_set_value(p->enable_gpio, 0);
+               else
+                       gpio_set_value(p->enable_gpio, 1);
+       }
+
+       if (p->backlight) {
+               p->backlight->props.power = FB_BLANK_UNBLANK;
+               backlight_update_status(p->backlight);
+       }
+
+       p->enabled = true;
+}
+
+static int panel_simple_get_modes(struct drm_panel *panel)
+{
+       struct panel_simple *p = to_panel_simple(panel);
+       struct drm_display_mode *mode;
+       unsigned int i;
+
+       for (i = 0; i < p->desc->num_modes; i++) {
+               mode = drm_mode_duplicate(panel->drm, &p->desc->modes[i]);
+               if (!mode)
+                       return -ENOMEM;
+
+               drm_mode_set_name(mode);
+
+               drm_mode_probed_add(panel->connector, mode);
+       }
+
+       return p->desc->num_modes;
+}
+
+static const struct drm_panel_funcs panel_simple_funcs = {
+       .disable = panel_simple_disable,
+       .enable = panel_simple_enable,
+       .get_modes = panel_simple_get_modes,
+};
+
+static const struct drm_display_mode auo_b101aw03_mode = {
+       .clock = 51450,
+       .hdisplay = 1024,
+       .hsync_start = 1024 + 156,
+       .hsync_end = 1024 + 156 + 8,
+       .htotal = 1024 + 156 + 8 + 156,
+       .vdisplay = 600,
+       .vsync_start = 600 + 16,
+       .vsync_end = 600 + 16 + 6,
+       .vtotal = 600 + 16 + 6 + 16,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc auo_b101aw03 = {
+       .modes = &auo_b101aw03_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 223,
+               .height = 125,
+       },
+};
+
+static const struct drm_display_mode chunghwa_claa101wb01_mode = {
+       .clock = 69300,
+       .hdisplay = 1366,
+       .hsync_start = 1366 + 48,
+       .hsync_end = 1366 + 48 + 32,
+       .htotal = 1366 + 48 + 32 + 20,
+       .vdisplay = 768,
+       .vsync_start = 768 + 16,
+       .vsync_end = 768 + 16 + 8,
+       .vtotal = 768 + 16 + 8 + 16,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc chunghwa_claa101wb01 = {
+       .modes = &chunghwa_claa101wb01_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 223,
+               .height = 125,
+       },
+};
+
+static const struct drm_display_mode panasonic_vvx10f004b00_mode = {
+       .clock = 154700,
+       .hdisplay = 1920,
+       .hsync_start = 1920 + 154,
+       .hsync_end = 1920 + 154 + 16,
+       .htotal = 1920 + 154 + 16 + 32,
+       .vdisplay = 1200,
+       .vsync_start = 1200 + 17,
+       .vsync_end = 1200 + 17 + 2,
+       .vtotal = 1200 + 17 + 2 + 16,
+       .vrefresh = 60,
+};
+
+static const struct panel_desc panasonic_vvx10f004b00 = {
+       .modes = &panasonic_vvx10f004b00_mode,
+       .num_modes = 1,
+       .size = {
+               .width = 217,
+               .height = 136,
+       },
+};
+
+static const struct of_device_id panel_simple_of_match[] = {
+       {
+               .compatible = "auo,b101aw03",
+               .data = &auo_b101aw03,
+       }, {
+               .compatible = "chunghwa,claa101wb01",
+               .data = &chunghwa_claa101wb01
+       }, {
+               .compatible = "panasonic,vvx10f004b00",
+               .data = &panasonic_vvx10f004b00
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, panel_simple_of_match);
+
+static int panel_simple_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *id;
+       struct device_node *backlight;
+       struct panel_simple *panel;
+       enum of_gpio_flags flags;
+       int err;
+
+       panel = devm_kzalloc(&pdev->dev, sizeof(*panel), GFP_KERNEL);
+       if (!panel)
+               return -ENOMEM;
+
+       id = of_match_node(panel_simple_of_match, pdev->dev.of_node);
+       if (!id)
+               return -ENODEV;
+
+       panel->desc = id->data;
+       panel->enabled = false;
+
+       panel->supply = devm_regulator_get(&pdev->dev, "power");
+       if (IS_ERR(panel->supply))
+               return PTR_ERR(panel->supply);
+
+       panel->enable_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+                                                    "enable-gpios", 0,
+                                                    &flags);
+       if (gpio_is_valid(panel->enable_gpio)) {
+               unsigned int value;
+
+               if (flags & OF_GPIO_ACTIVE_LOW)
+                       panel->enable_gpio_flags |= GPIO_ACTIVE_LOW;
+
+               err = gpio_request(panel->enable_gpio, "enable");
+               if (err < 0) {
+                       dev_err(&pdev->dev, "failed to request GPIO#%u: %d\n",
+                               panel->enable_gpio, err);
+                       return err;
+               }
+
+               value = (panel->enable_gpio_flags & GPIO_ACTIVE_LOW) != 0;
+
+               err = gpio_direction_output(panel->enable_gpio, value);
+               if (err < 0) {
+                       dev_err(&pdev->dev, "failed to setup GPIO%u: %d\n",
+                               panel->enable_gpio, err);
+                       goto free_gpio;
+               }
+       }
+
+       backlight = of_parse_phandle(pdev->dev.of_node, "backlight", 0);
+       if (backlight) {
+               panel->backlight = of_find_backlight_by_node(backlight);
+               if (!panel->backlight) {
+                       err = -EPROBE_DEFER;
+                       goto free_gpio;
+               }
+
+               of_node_put(backlight);
+       }
+
+       drm_panel_init(&panel->base);
+       panel->base.dev = &pdev->dev;
+       panel->base.funcs = &panel_simple_funcs;
+
+       err = drm_panel_add(&panel->base);
+       if (err < 0)
+               goto free_gpio;
+
+       platform_set_drvdata(pdev, panel);
+
+       return 0;
+
+free_gpio:
+       if (gpio_is_valid(panel->enable_gpio))
+               gpio_free(panel->enable_gpio);
+
+       return err;
+}
+
+static int panel_simple_remove(struct platform_device *pdev)
+{
+       struct panel_simple *panel = platform_get_drvdata(pdev);
+
+       drm_panel_detach(&panel->base);
+       drm_panel_remove(&panel->base);
+
+       panel_simple_disable(&panel->base);
+
+       if (panel->backlight)
+               put_device(&panel->backlight->dev);
+
+       if (gpio_is_valid(panel->enable_gpio))
+               gpio_free(panel->enable_gpio);
+
+       regulator_disable(panel->supply);
+
+       return 0;
+}
+
+static struct platform_driver panel_simple_driver = {
+       .driver = {
+               .name = "panel-simple",
+               .owner = THIS_MODULE,
+               .of_match_table = panel_simple_of_match,
+       },
+       .probe = panel_simple_probe,
+       .remove = panel_simple_remove,
+};
+module_platform_driver(panel_simple_driver);
+
+MODULE_DESCRIPTION("DRM Driver for Simple Panels");
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_LICENSE("GPL v2");
similarity index 88%
rename from drivers/gpu/host1x/drm/Kconfig
rename to drivers/gpu/drm/tegra/Kconfig
index 0f36ddd74e87ccc317924ee617f8c1150d86ed83..da34dd1b9ddbb062996849a0b7f42b59755aaabc 100644 (file)
@@ -1,8 +1,11 @@
 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 DRM_PANEL
        select FB_SYS_FILLRECT
        select FB_SYS_COPYAREA
        select FB_SYS_IMAGEBLIT
@@ -14,6 +17,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 +30,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 (file)
index 0000000..eb5675c
--- /dev/null
@@ -0,0 +1,16 @@
+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 \
+       dsi.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 (file)
index 0000000..565f8f7
--- /dev/null
@@ -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);
+}
similarity index 93%
rename from drivers/gpu/host1x/drm/dc.c
rename to drivers/gpu/drm/tegra/dc.c
index b1a05ad901c3d4758f4d768efc5b276d2dc4ee8e..ae1cb31ead7e4256c32f6ea045395347780a25a8 100644 (file)
@@ -8,13 +8,9 @@
  */
 
 #include <linux/clk.h>
-#include <linux/debugfs.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
 #include <linux/clk/tegra.h>
+#include <linux/debugfs.h>
 
-#include "host1x_client.h"
 #include "dc.h"
 #include "drm.h"
 #include "gem.h"
@@ -51,6 +47,8 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
        window.dst.h = crtc_h;
        window.format = tegra_dc_format(fb->pixel_format);
        window.bits_per_pixel = fb->bits_per_pixel;
+       window.bottom_up = tegra_fb_is_bottom_up(fb);
+       window.tiled = tegra_fb_is_tiled(fb);
 
        for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
                struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
@@ -97,8 +95,11 @@ static int tegra_plane_disable(struct drm_plane *plane)
 
 static void tegra_plane_destroy(struct drm_plane *plane)
 {
+       struct tegra_plane *p = to_tegra_plane(plane);
+
        tegra_plane_disable(plane);
        drm_plane_cleanup(plane);
+       kfree(p);
 }
 
 static const struct drm_plane_funcs tegra_plane_funcs = {
@@ -124,7 +125,7 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
        for (i = 0; i < 2; i++) {
                struct tegra_plane *plane;
 
-               plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
+               plane = kzalloc(sizeof(*plane), GFP_KERNEL);
                if (!plane)
                        return -ENOMEM;
 
@@ -133,8 +134,10 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
                err = drm_plane_init(drm, &plane->base, 1 << dc->pipe,
                                     &tegra_plane_funcs, plane_formats,
                                     ARRAY_SIZE(plane_formats), false);
-               if (err < 0)
+               if (err < 0) {
+                       kfree(plane);
                        return err;
+               }
        }
 
        return 0;
@@ -145,6 +148,7 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
 {
        unsigned int format = tegra_dc_format(fb->pixel_format);
        struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
+       unsigned int h_offset = 0, v_offset = 0;
        unsigned long value;
 
        tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER);
@@ -156,6 +160,32 @@ static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y,
        tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE);
        tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH);
 
+       if (tegra_fb_is_tiled(fb)) {
+               value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_TILE;
+       } else {
+               value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+       }
+
+       tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
+       /* make sure bottom-up buffers are properly displayed */
+       if (tegra_fb_is_bottom_up(fb)) {
+               value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+               value |= INVERT_V;
+               tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+
+               v_offset += fb->height - 1;
+       } else {
+               value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS);
+               value &= ~INVERT_V;
+               tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
+       }
+
+       tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
+       tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
+
        value = GENERAL_UPDATE | WIN_A_UPDATE;
        tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
 
@@ -255,14 +285,26 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        return 0;
 }
 
+static void drm_crtc_clear(struct drm_crtc *crtc)
+{
+       memset(crtc, 0, sizeof(*crtc));
+}
+
+static void tegra_dc_destroy(struct drm_crtc *crtc)
+{
+       drm_crtc_cleanup(crtc);
+       drm_crtc_clear(crtc);
+}
+
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
        .page_flip = tegra_dc_page_flip,
        .set_config = drm_crtc_helper_set_config,
-       .destroy = drm_crtc_cleanup,
+       .destroy = tegra_dc_destroy,
 };
 
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
+       struct tegra_dc *dc = to_tegra_dc(crtc);
        struct drm_device *drm = crtc->dev;
        struct drm_plane *plane;
 
@@ -277,6 +319,8 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
                        }
                }
        }
+
+       drm_vblank_off(drm, dc->pipe);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -491,9 +535,22 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
                tegra_dc_writel(dc, window->stride[0], DC_WIN_LINE_STRIDE);
        }
 
+       if (window->bottom_up)
+               v_offset += window->src.h - 1;
+
        tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET);
        tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET);
 
+       if (window->tiled) {
+               value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_TILE;
+       } else {
+               value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
+                       DC_WIN_BUFFER_ADDR_MODE_LINEAR;
+       }
+
+       tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE);
+
        value = WIN_ENABLE;
 
        if (yuv) {
@@ -512,6 +569,9 @@ int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index,
                value |= COLOR_EXPAND;
        }
 
+       if (window->bottom_up)
+               value |= INVERT_V;
+
        tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS);
 
        /*
@@ -1041,30 +1101,30 @@ static int tegra_dc_debugfs_exit(struct tegra_dc *dc)
        return 0;
 }
 
-static int tegra_dc_drm_init(struct host1x_client *client,
-                            struct drm_device *drm)
+static int tegra_dc_init(struct host1x_client *client)
 {
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
        struct tegra_dc *dc = host1x_client_to_dc(client);
        int err;
 
-       dc->pipe = drm->mode_config.num_crtc;
+       dc->pipe = tegra->drm->mode_config.num_crtc;
 
-       drm_crtc_init(drm, &dc->base, &tegra_crtc_funcs);
+       drm_crtc_init(tegra->drm, &dc->base, &tegra_crtc_funcs);
        drm_mode_crtc_set_gamma_size(&dc->base, 256);
        drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
 
-       err = tegra_dc_rgb_init(drm, dc);
+       err = tegra_dc_rgb_init(tegra->drm, dc);
        if (err < 0 && err != -ENODEV) {
                dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
                return err;
        }
 
-       err = tegra_dc_add_planes(drm, dc);
+       err = tegra_dc_add_planes(tegra->drm, dc);
        if (err < 0)
                return err;
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
-               err = tegra_dc_debugfs_init(dc, drm->primary);
+               err = tegra_dc_debugfs_init(dc, tegra->drm->primary);
                if (err < 0)
                        dev_err(dc->dev, "debugfs setup failed: %d\n", err);
        }
@@ -1080,7 +1140,7 @@ static int tegra_dc_drm_init(struct host1x_client *client,
        return 0;
 }
 
-static int tegra_dc_drm_exit(struct host1x_client *client)
+static int tegra_dc_exit(struct host1x_client *client)
 {
        struct tegra_dc *dc = host1x_client_to_dc(client);
        int err;
@@ -1103,13 +1163,12 @@ static int tegra_dc_drm_exit(struct host1x_client *client)
 }
 
 static const struct host1x_client_ops dc_client_ops = {
-       .drm_init = tegra_dc_drm_init,
-       .drm_exit = tegra_dc_drm_exit,
+       .init = tegra_dc_init,
+       .exit = tegra_dc_exit,
 };
 
 static int tegra_dc_probe(struct platform_device *pdev)
 {
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
        struct resource *regs;
        struct tegra_dc *dc;
        int err;
@@ -1153,7 +1212,7 @@ static int tegra_dc_probe(struct platform_device *pdev)
                return err;
        }
 
-       err = host1x_register_client(host1x, &dc->client);
+       err = host1x_client_register(&dc->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register host1x client: %d\n",
                        err);
@@ -1167,17 +1226,22 @@ static int tegra_dc_probe(struct platform_device *pdev)
 
 static int tegra_dc_remove(struct platform_device *pdev)
 {
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
        struct tegra_dc *dc = platform_get_drvdata(pdev);
        int err;
 
-       err = host1x_unregister_client(host1x, &dc->client);
+       err = host1x_client_unregister(&dc->client);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
                        err);
                return err;
        }
 
+       err = tegra_dc_rgb_remove(dc);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
+               return err;
+       }
+
        clk_disable_unprepare(dc->clk);
 
        return 0;
similarity index 98%
rename from drivers/gpu/host1x/drm/dc.h
rename to drivers/gpu/drm/tegra/dc.h
index 79eaec9aac7752a27bddd06a4c08cf7f2292eac8..f1b31206a480aa872301e50cd2ae036755ced147 100644 (file)
 
 #define DC_DISP_DISP_WIN_OPTIONS               0x402
 #define HDMI_ENABLE (1 << 30)
+#define DSI_ENABLE  (1 << 29)
 
 #define DC_DISP_DISP_MEM_HIGH_PRIORITY         0x403
 #define CURSOR_THRESHOLD(x)   (((x) & 0x03) << 24)
 #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)
 #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
similarity index 51%
rename from drivers/gpu/host1x/drm/drm.c
rename to drivers/gpu/drm/tegra/drm.c
index df7d90a3a4fa7a18dda505366e7e6c35ba8cfbb7..36360923b424d7a70e628ba07074b95b2684c396 100644 (file)
@@ -7,21 +7,10 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
+#include <linux/host1x.h>
 
-#include <linux/dma-mapping.h>
-#include <asm/dma-iommu.h>
-
-#include <drm/drm.h>
-#include <drm/drmP.h>
-
-#include "host1x_client.h"
-#include "dev.h"
 #include "drm.h"
 #include "gem.h"
-#include "syncpt.h"
 
 #define DRIVER_NAME "tegra"
 #define DRIVER_DESC "NVIDIA Tegra graphics"
 #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)
+static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
 {
-       struct host1x_drm_client *client;
+       struct host1x_device *device = to_host1x_device(drm->dev);
+       struct tegra_drm *tegra;
+       int err;
 
-       client = kzalloc(sizeof(*client), GFP_KERNEL);
-       if (!client)
+       tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
+       if (!tegra)
                return -ENOMEM;
 
-       INIT_LIST_HEAD(&client->list);
-       client->np = of_node_get(np);
+       dev_set_drvdata(drm->dev, tegra);
+       mutex_init(&tegra->clients_lock);
+       INIT_LIST_HEAD(&tegra->clients);
+       drm->dev_private = tegra;
+       tegra->drm = drm;
 
-       list_add_tail(&client->list, &host1x->drm_clients);
+       drm_mode_config_init(drm);
 
-       return 0;
-}
+       err = host1x_device_init(device);
+       if (err < 0)
+               return err;
 
-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);
+       /*
+        * We don't use the drm_irq_install() helpers provided by the DRM
+        * core, so we need to set this manually in order to allow the
+        * DRM_IOCTL_WAIT_VBLANK to operate correctly.
+        */
+       drm->irq_enabled = true;
 
-       return 0;
-}
+       err = drm_vblank_init(drm, drm->mode_config.num_crtc);
+       if (err < 0)
+               return err;
 
-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);
+       err = tegra_drm_fb_init(drm);
+       if (err < 0)
+               return err;
 
-       of_node_put(client->np);
-       kfree(client);
+       drm_kms_helper_poll_init(drm);
 
        return 0;
 }
 
-static int host1x_parse_dt(struct host1x_drm *host1x)
+static int tegra_drm_unload(struct drm_device *drm)
 {
-       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;
+       struct host1x_device *device = to_host1x_device(drm->dev);
        int err;
 
-       for (i = 0; i < ARRAY_SIZE(compat); i++) {
-               struct device_node *np;
+       drm_kms_helper_poll_fini(drm);
+       tegra_drm_fb_exit(drm);
+       drm_vblank_cleanup(drm);
+       drm_mode_config_cleanup(drm);
 
-               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;
-                       }
-               }
-       }
+       err = host1x_device_exit(device);
+       if (err < 0)
+               return err;
 
        return 0;
 }
 
-int host1x_drm_alloc(struct platform_device *pdev)
+static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
 {
-       struct host1x_drm *host1x;
-       int err;
+       struct tegra_drm_file *fpriv;
 
-       host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
-       if (!host1x)
+       fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
+       if (!fpriv)
                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);
+       INIT_LIST_HEAD(&fpriv->contexts);
+       filp->driver_priv = fpriv;
 
        return 0;
 }
 
-int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
+static void tegra_drm_context_free(struct tegra_drm_context *context)
 {
-       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;
+       context->client->ops->close_channel(context);
+       kfree(context);
 }
 
-int host1x_drm_exit(struct host1x_drm *host1x)
+static void tegra_drm_lastclose(struct drm_device *drm)
 {
-       struct platform_device *pdev = to_platform_device(host1x->dev);
-       struct host1x_client *client;
-
-       if (!host1x->drm)
-               return 0;
+       struct tegra_drm *tegra = drm->dev_private;
 
-       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;
-                       }
-               }
-       }
+       tegra_fbdev_restore_mode(tegra->fbdev);
+}
 
-       mutex_unlock(&host1x->clients_lock);
+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;
 
-       drm_platform_exit(&tegra_drm_driver, pdev);
-       host1x->drm = NULL;
+       gem = drm_gem_object_lookup(drm, file, handle);
+       if (!gem)
+               return NULL;
 
-       return 0;
-}
+       mutex_lock(&drm->struct_mutex);
+       drm_gem_object_unreference(gem);
+       mutex_unlock(&drm->struct_mutex);
 
-int host1x_register_client(struct host1x_drm *host1x,
-                          struct host1x_client *client)
-{
-       struct host1x_drm_client *drm, *tmp;
+       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;
 
-       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);
+       /* We don't yet support other than one syncpt_incr struct per submit */
+       if (args->num_syncpts != 1)
+               return -EINVAL;
 
-       if (list_empty(&host1x->drm_clients)) {
-               struct platform_device *pdev = to_platform_device(host1x->dev);
+       job = host1x_job_alloc(context->channel, args->num_cmdbufs,
+                              args->num_relocs, args->num_waitchks);
+       if (!job)
+               return -ENOMEM;
 
-               err = drm_platform_init(&tegra_drm_driver, pdev);
-               if (err < 0) {
-                       dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
-                       return err;
-               }
-       }
+       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;
 
-       return 0;
-}
+       while (num_cmdbufs) {
+               struct drm_tegra_cmdbuf cmdbuf;
+               struct host1x_bo *bo;
 
-int host1x_unregister_client(struct host1x_drm *host1x,
-                            struct host1x_client *client)
-{
-       struct host1x_drm_client *drm, *tmp;
-       int err;
+               err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
+               if (err)
+                       goto fail;
 
-       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;
+               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++;
        }
 
-       mutex_lock(&host1x->clients_lock);
-       list_del_init(&client->list);
-       mutex_unlock(&host1x->clients_lock);
+       err = copy_from_user(job->relocarray, relocs,
+                            sizeof(*relocs) * num_relocs);
+       if (err)
+               goto fail;
 
-       return 0;
-}
+       while (num_relocs--) {
+               struct host1x_reloc *reloc = &job->relocarray[num_relocs];
+               struct host1x_bo *cmdbuf, *target;
 
-static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
-{
-       struct host1x_drm *host1x;
-       int err;
+               cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
+               target = host1x_bo_lookup(drm, file, (u32)reloc->target);
 
-       host1x = host1x_get_drm_data(drm->dev);
-       drm->dev_private = host1x;
-       host1x->drm = drm;
+               reloc->cmdbuf = cmdbuf;
+               reloc->target = target;
 
-       drm_mode_config_init(drm);
+               if (!reloc->target || !reloc->cmdbuf) {
+                       err = -ENOENT;
+                       goto fail;
+               }
+       }
 
-       err = host1x_drm_init(host1x, drm);
-       if (err < 0)
-               return err;
+       err = copy_from_user(job->waitchk, waitchks,
+                            sizeof(*waitchks) * num_waitchks);
+       if (err)
+               goto fail;
 
-       /*
-        * We don't use the drm_irq_install() helpers provided by the DRM
-        * core, so we need to set this manually in order to allow the
-        * DRM_IOCTL_WAIT_VBLANK to operate correctly.
-        */
-       drm->irq_enabled = true;
+       err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
+                            sizeof(syncpt));
+       if (err)
+               goto fail;
 
-       err = drm_vblank_init(drm, drm->mode_config.num_crtc);
-       if (err < 0)
-               return err;
+       job->is_addr_reg = context->client->ops->is_addr_reg;
+       job->syncpt_incrs = syncpt.incrs;
+       job->syncpt_id = syncpt.id;
+       job->timeout = 10000;
 
-       err = tegra_drm_fb_init(drm);
-       if (err < 0)
-               return err;
+       if (args->timeout && args->timeout < 10000)
+               job->timeout = args->timeout;
 
-       drm_kms_helper_poll_init(drm);
+       err = host1x_job_pin(job, context->client->base.dev);
+       if (err)
+               goto fail;
 
-       return 0;
-}
+       err = host1x_job_submit(job);
+       if (err)
+               goto fail_submit;
 
-static int tegra_drm_unload(struct drm_device *drm)
-{
-       drm_kms_helper_poll_fini(drm);
-       tegra_drm_fb_exit(drm);
-
-       drm_mode_config_cleanup(drm);
+       args->fence = job->syncpt_end;
 
+       host1x_job_put(job);
        return 0;
-}
-
-static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
-{
-       struct host1x_drm_file *fpriv;
-
-       fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
-       if (!fpriv)
-               return -ENOMEM;
-
-       INIT_LIST_HEAD(&fpriv->contexts);
-       filp->driver_priv = fpriv;
 
-       return 0;
+fail_submit:
+       host1x_job_unpin(job);
+fail:
+       host1x_job_put(job);
+       return err;
 }
 
-static void host1x_drm_context_free(struct host1x_drm_context *context)
-{
-       context->client->ops->close_channel(context);
-       kfree(context);
-}
 
-static void tegra_drm_lastclose(struct drm_device *drm)
+#ifdef CONFIG_DRM_TEGRA_STAGING
+static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
 {
-       struct host1x_drm *host1x = drm->dev_private;
-
-       tegra_fbdev_restore_mode(host1x->fbdev);
+       return (struct tegra_drm_context *)(uintptr_t)context;
 }
 
-#ifdef CONFIG_DRM_TEGRA_STAGING
-static bool host1x_drm_file_owns_context(struct host1x_drm_file *file,
-                                        struct host1x_drm_context *context)
+static bool tegra_drm_file_owns_context(struct tegra_drm_file *file,
+                                       struct tegra_drm_context *context)
 {
-       struct host1x_drm_context *ctx;
+       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,12 +407,13 @@ 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;
+
+       context = tegra_drm_get_context(args->context);
 
-       if (!host1x_drm_file_owns_context(fpriv, context))
+       if (!tegra_drm_file_owns_context(fpriv, context))
                return -ENODEV;
 
        return context->client->ops->submit(context, args, drm, file);
@@ -559,15 +492,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 +578,116 @@ 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-dsi", },
+       { .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_dsi_driver);
+       if (err < 0)
+               goto unregister_dc;
+
+       err = platform_driver_register(&tegra_hdmi_driver);
+       if (err < 0)
+               goto unregister_dsi;
+
+       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_dsi:
+       platform_driver_unregister(&tegra_dsi_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_dsi_driver);
+       platform_driver_unregister(&tegra_dc_driver);
+       host1x_driver_unregister(&host1x_drm_driver);
+}
+module_exit(host1x_drm_exit);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
+MODULE_LICENSE("GPL v2");
similarity index 71%
rename from drivers/gpu/host1x/drm/drm.h
rename to drivers/gpu/drm/tegra/drm.h
index 02ce020f25754633bca0f9489e6e29c85ec9eea2..7070ec44a797df391e37f4940b4adc6ecdeb6423 100644 (file)
 #ifndef HOST1X_DRM_H
 #define HOST1X_DRM_H 1
 
+#include <uapi/drm/tegra_drm.h>
+#include <linux/host1x.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fixed.h>
-#include <uapi/drm/tegra_drm.h>
-
-#include "host1x.h"
 
 struct tegra_fb {
        struct drm_framebuffer base;
@@ -30,17 +30,8 @@ struct tegra_fbdev {
        struct tegra_fb *fb;
 };
 
-struct host1x_drm {
+struct tegra_drm {
        struct drm_device *drm;
-       struct device *dev;
-       void __iomem *regs;
-       struct clk *clk;
-       int syncpt;
-       int irq;
-
-       struct mutex drm_clients_lock;
-       struct list_head drm_clients;
-       struct list_head drm_active;
 
        struct mutex clients_lock;
        struct list_head clients;
@@ -48,66 +39,60 @@ struct host1x_drm {
        struct tegra_fbdev *fbdev;
 };
 
-struct host1x_client;
+struct tegra_drm_client;
 
-struct host1x_drm_context {
-       struct host1x_client *client;
+struct tegra_drm_context {
+       struct tegra_drm_client *client;
        struct host1x_channel *channel;
        struct list_head list;
 };
 
-struct host1x_client_ops {
-       int (*drm_init)(struct host1x_client *client, struct drm_device *drm);
-       int (*drm_exit)(struct host1x_client *client);
-       int (*open_channel)(struct host1x_client *client,
-                           struct host1x_drm_context *context);
-       void (*close_channel)(struct host1x_drm_context *context);
-       int (*submit)(struct host1x_drm_context *context,
+struct tegra_drm_client_ops {
+       int (*open_channel)(struct tegra_drm_client *client,
+                           struct tegra_drm_context *context);
+       void (*close_channel)(struct tegra_drm_context *context);
+       int (*is_addr_reg)(struct device *dev, u32 class, u32 offset);
+       int (*submit)(struct tegra_drm_context *context,
                      struct drm_tegra_submit *args, struct drm_device *drm,
                      struct drm_file *file);
 };
 
-struct host1x_drm_file {
-       struct list_head contexts;
-};
-
-struct host1x_client {
-       struct host1x_drm *host1x;
-       struct device *dev;
-
-       const struct host1x_client_ops *ops;
-
-       enum host1x_class class;
-       struct host1x_channel *channel;
-
-       struct host1x_syncpt **syncpts;
-       unsigned int num_syncpts;
+int tegra_drm_submit(struct tegra_drm_context *context,
+                    struct drm_tegra_submit *args, struct drm_device *drm,
+                    struct drm_file *file);
 
+struct tegra_drm_client {
+       struct host1x_client base;
        struct list_head list;
+
+       const struct tegra_drm_client_ops *ops;
 };
 
-extern int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm);
-extern int host1x_drm_exit(struct host1x_drm *host1x);
+static inline struct tegra_drm_client *
+host1x_to_drm_client(struct host1x_client *client)
+{
+       return container_of(client, struct tegra_drm_client, base);
+}
+
+extern int 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 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_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 */
@@ -186,6 +174,7 @@ struct tegra_output_ops {
 enum tegra_output_type {
        TEGRA_OUTPUT_RGB,
        TEGRA_OUTPUT_HDMI,
+       TEGRA_OUTPUT_DSI,
 };
 
 struct tegra_output {
@@ -195,6 +184,7 @@ struct tegra_output {
        const struct tegra_output_ops *ops;
        enum tegra_output_type type;
 
+       struct drm_panel *panel;
        struct i2c_adapter *ddc;
        const struct edid *edid;
        unsigned int hpd_irq;
@@ -249,23 +239,35 @@ 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_dsi_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/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
new file mode 100644 (file)
index 0000000..1cfbace
--- /dev/null
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk/tegra.h>
+#include <linux/debugfs.h>
+#include <linux/host1x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "drm.h"
+#include "dc.h"
+
+#define DSI_INCR_SYNCPT                        0x00
+#define DSI_INCR_SYNCPT_CONTROL                0x01
+#define DSI_INCR_SYNCPT_ERROR          0x02
+#define DSI_CTXSW                      0x08
+#define DSI_RD_DATA                    0x09
+#define DSI_WR_DATA                    0x0a
+#define DSI_POWER_CONTROL              0x0b
+#define DSI_POWER_CONTROL_ENABLE       (1 << 0)
+#define DSI_INT_ENABLE                 0x0c
+#define DSI_INT_STATUS                 0x0d
+#define DSI_INT_MASK                   0x0e
+#define DSI_HOST_CONTROL               0x0f
+#define DSI_HOST_CONTROL_RAW           (1 << 6)
+#define DSI_HOST_CONTROL_HS            (1 << 5)
+#define DSI_HOST_CONTROL_CS            (1 << 1)
+#define DSI_HOST_CONTROL_ECC           (1 << 0)
+#define DSI_CONTROL                    0x10
+#define DSI_CONTROL_HS_CLK_CTRL                (1 << 20)
+#define DSI_CONTROL_CHANNEL(c)         (((c) & 0x3) << 16)
+#define DSI_CONTROL_FORMAT(f)          (((f) & 0x3) << 12)
+#define DSI_CONTROL_TX_TRIG(x)         (((x) & 0x3) <<  8)
+#define DSI_CONTROL_LANES(n)           (((n) & 0x3) <<  4)
+#define DSI_CONTROL_DCS_ENABLE         (1 << 3)
+#define DSI_CONTROL_SOURCE(s)          (((s) & 0x1) <<  2)
+#define DSI_CONTROL_VIDEO_ENABLE       (1 << 1)
+#define DSI_CONTROL_HOST_ENABLE                (1 << 0)
+#define DSI_SOL_DELAY                  0x11
+#define DSI_MAX_THRESHOLD              0x12
+#define DSI_TRIGGER                    0x13
+#define DSI_TX_CRC                     0x14
+#define DSI_STATUS                     0x15
+#define DSI_INIT_SEQ_CONTROL           0x1a
+#define DSI_INIT_SEQ_DATA_0            0x1b
+#define DSI_INIT_SEQ_DATA_1            0x1c
+#define DSI_INIT_SEQ_DATA_2            0x1d
+#define DSI_INIT_SEQ_DATA_3            0x1e
+#define DSI_INIT_SEQ_DATA_4            0x1f
+#define DSI_INIT_SEQ_DATA_5            0x20
+#define DSI_INIT_SEQ_DATA_6            0x21
+#define DSI_INIT_SEQ_DATA_7            0x22
+#define DSI_PKT_SEQ_0_LO               0x23
+#define DSI_PKT_SEQ_0_HI               0x24
+#define DSI_PKT_SEQ_1_LO               0x25
+#define DSI_PKT_SEQ_1_HI               0x26
+#define DSI_PKT_SEQ_2_LO               0x27
+#define DSI_PKT_SEQ_2_HI               0x28
+#define DSI_PKT_SEQ_3_LO               0x29
+#define DSI_PKT_SEQ_3_HI               0x2a
+#define DSI_PKT_SEQ_4_LO               0x2b
+#define DSI_PKT_SEQ_4_HI               0x2c
+#define DSI_PKT_SEQ_5_LO               0x2d
+#define DSI_PKT_SEQ_5_HI               0x2e
+#define DSI_DCS_CMDS                   0x33
+#define DSI_PKT_LEN_0_1                        0x34
+#define DSI_PKT_LEN_2_3                        0x35
+#define DSI_PKT_LEN_4_5                        0x36
+#define DSI_PKT_LEN_6_7                        0x37
+#define DSI_PHY_TIMING_0               0x3c
+#define DSI_PHY_TIMING_1               0x3d
+#define DSI_PHY_TIMING_2               0x3e
+#define DSI_BTA_TIMING                 0x3f
+
+#define DSI_TIMING_FIELD(value, period, hwinc) \
+       ((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff)
+
+#define DSI_TIMEOUT_0                  0x44
+#define DSI_TIMEOUT_LRX(x)             (((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_HTX(x)             (((x) & 0xffff) <<  0)
+#define DSI_TIMEOUT_1                  0x45
+#define DSI_TIMEOUT_PR(x)              (((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_TA(x)              (((x) & 0xffff) <<  0)
+#define DSI_TO_TALLY                   0x46
+#define DSI_TALLY_TA(x)                        (((x) & 0xff) << 16)
+#define DSI_TALLY_LRX(x)               (((x) & 0xff) <<  8)
+#define DSI_TALLY_HTX(x)               (((x) & 0xff) <<  0)
+#define DSI_PAD_CONTROL_0              0x4b
+#define DSI_PAD_CONTROL_VS1_PDIO(x)    (((x) & 0xf) <<  0)
+#define DSI_PAD_CONTROL_VS1_PDIO_CLK   (1 <<  8)
+#define DSI_PAD_CONTROL_VS1_PULLDN(x)  (((x) & 0xf) << 16)
+#define DSI_PAD_CONTROL_VS1_PULLDN_CLK (1 << 24)
+#define DSI_PAD_CONTROL_CD             0x4c
+#define DSI_PAD_CD_STATUS              0x4d
+#define DSI_VIDEO_MODE_CONTROL         0x4e
+#define DSI_PAD_CONTROL_1              0x4f
+#define DSI_PAD_CONTROL_2              0x50
+#define DSI_PAD_OUT_CLK(x)             (((x) & 0x7) <<  0)
+#define DSI_PAD_LP_DN(x)               (((x) & 0x7) <<  4)
+#define DSI_PAD_LP_UP(x)               (((x) & 0x7) <<  8)
+#define DSI_PAD_SLEW_DN(x)             (((x) & 0x7) << 12)
+#define DSI_PAD_SLEW_UP(x)             (((x) & 0x7) << 16)
+#define DSI_PAD_CONTROL_3              0x51
+#define DSI_PAD_CONTROL_4              0x52
+#define DSI_GANGED_MODE_CONTROL                0x53
+#define DSI_GANGED_MODE_START          0x54
+#define DSI_GANGED_MODE_SIZE           0x55
+#define DSI_RAW_DATA_BYTE_COUNT                0x56
+#define DSI_ULTRA_LOW_POWER_CONTROL    0x57
+#define DSI_INIT_SEQ_DATA_8            0x58
+#define DSI_INIT_SEQ_DATA_9            0x59
+#define DSI_INIT_SEQ_DATA_10           0x5a
+#define DSI_INIT_SEQ_DATA_11           0x5b
+#define DSI_INIT_SEQ_DATA_12           0x5c
+#define DSI_INIT_SEQ_DATA_13           0x5d
+#define DSI_INIT_SEQ_DATA_14           0x5e
+#define DSI_INIT_SEQ_DATA_15           0x5f
+
+enum dsi_format {
+       DSI_FORMAT_16P,
+       DSI_FORMAT_18NP,
+       DSI_FORMAT_18P,
+       DSI_FORMAT_24P,
+};
+
+/*
+ * D-PHY timing parameters
+ *
+ * A detailed description of these parameters can be found in the  MIPI
+ * Alliance Specification for D-PHY, Section 5.9 "Global Operation Timing
+ * Parameters".
+ *
+ * All parameters are specified in nanoseconds.
+ */
+struct dsi_phy_timing {
+       unsigned int clkmiss;
+       unsigned int clkpost;
+       unsigned int clkpre;
+       unsigned int clkprepare;
+       unsigned int clksettle;
+       unsigned int clktermen;
+       unsigned int clktrail;
+       unsigned int clkzero;
+       unsigned int dtermen;
+       unsigned int eot;
+       unsigned int hsexit;
+       unsigned int hsprepare;
+       unsigned int hszero;
+       unsigned int hssettle;
+       unsigned int hsskip;
+       unsigned int hstrail;
+       unsigned int init;
+       unsigned int lpx;
+       unsigned int taget;
+       unsigned int tago;
+       unsigned int tasure;
+       unsigned int wakeup;
+};
+
+/*
+ * Default D-PHY timings based on MIPI D-PHY specification. Derived from
+ * the valid ranges specified in Section 5.9 of the D-PHY specification
+ * with minor adjustments.
+ */
+static int dsi_phy_timing_get_default(struct dsi_phy_timing *timing,
+                                     unsigned long period)
+{
+       timing->clkmiss = 0;
+       timing->clkpost = 70 + 52 * period;
+       timing->clkpre = 8;
+       timing->clkprepare = 65;
+       timing->clksettle = 95;
+       timing->clktermen = 0;
+       timing->clktrail = 80;
+       timing->clkzero = 260;
+       timing->dtermen = 0;
+       timing->eot = 0;
+       timing->hsexit = 120;
+       timing->hsprepare = 65 + 5 * period;
+       timing->hszero = 145 + 5 * period;
+       timing->hssettle = 85 + 6 * period;
+       timing->hsskip = 40;
+       timing->hstrail = max(8 * period, 60 + 4 * period);
+       timing->init = 100000;
+       timing->lpx = 60;
+       timing->taget = 5 * timing->lpx;
+       timing->tago = 4 * timing->lpx;
+       timing->tasure = 2 * timing->lpx;
+       timing->wakeup = 1000000;
+
+       return 0;
+}
+
+/*
+ * Validate D-PHY timing according to MIPI Alliance Specification for D-PHY,
+ * Section 5.9 "Global Operation Timing Parameters".
+ */
+static int dsi_phy_timing_validate(struct dsi_phy_timing *timing,
+                                  unsigned long period)
+{
+       if (timing->clkmiss > 60)
+               return -EINVAL;
+
+       if (timing->clkpost < (60 + 52 * period))
+               return -EINVAL;
+
+       if (timing->clkpre < 8)
+               return -EINVAL;
+
+       if (timing->clkprepare < 38 || timing->clkprepare > 95)
+               return -EINVAL;
+
+       if (timing->clksettle < 95 || timing->clksettle > 300)
+               return -EINVAL;
+
+       if (timing->clktermen > 38)
+               return -EINVAL;
+
+       if (timing->clktrail < 60)
+               return -EINVAL;
+
+       if (timing->clkprepare + timing->clkzero < 300)
+               return -EINVAL;
+
+       if (timing->dtermen > 35 + 4 * period)
+               return -EINVAL;
+
+       if (timing->eot > 105 + 12 * period)
+               return -EINVAL;
+
+       if (timing->hsexit < 100)
+               return -EINVAL;
+
+       if (timing->hsprepare < 40 + 4 * period ||
+           timing->hsprepare > 85 + 6 * period)
+               return -EINVAL;
+
+       if (timing->hsprepare + timing->hszero < 145 + 10 * period)
+               return -EINVAL;
+
+       if ((timing->hssettle < 85 + 6 * period) ||
+           (timing->hssettle > 145 + 10 * period))
+               return -EINVAL;
+
+       if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period)
+               return -EINVAL;
+
+       if (timing->hstrail < max(8 * period, 60 + 4 * period))
+               return -EINVAL;
+
+       if (timing->init < 100000)
+               return -EINVAL;
+
+       if (timing->lpx < 50)
+               return -EINVAL;
+
+       if (timing->taget != 5 * timing->lpx)
+               return -EINVAL;
+
+       if (timing->tago != 4 * timing->lpx)
+               return -EINVAL;
+
+       if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx)
+               return -EINVAL;
+
+       if (timing->wakeup < 1000000)
+               return -EINVAL;
+
+       return 0;
+}
+
+struct tegra_dsi {
+       struct host1x_client client;
+       struct tegra_output output;
+       struct device *dev;
+
+       void __iomem *regs;
+
+       struct clk *clk_parent;
+       struct clk *clk;
+
+       struct drm_info_list *debugfs_files;
+       struct drm_minor *minor;
+       struct dentry *debugfs;
+
+       enum dsi_format format;
+       unsigned int lanes;
+};
+
+static inline struct tegra_dsi *
+host1x_client_to_dsi(struct host1x_client *client)
+{
+       return container_of(client, struct tegra_dsi, client);
+}
+
+static inline struct tegra_dsi *to_dsi(struct tegra_output *output)
+{
+       return container_of(output, struct tegra_dsi, output);
+}
+
+static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi,
+                                           unsigned long reg)
+{
+       return readl(dsi->regs + (reg << 2));
+}
+
+static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value,
+                                   unsigned long reg)
+{
+       writel(value, dsi->regs + (reg << 2));
+}
+
+static int tegra_dsi_show_regs(struct seq_file *s, void *data)
+{
+       struct drm_info_node *node = s->private;
+       struct tegra_dsi *dsi = node->info_ent->data;
+
+#define DUMP_REG(name)                                         \
+       seq_printf(s, "%-32s %#05x %08lx\n", #name, name,       \
+                  tegra_dsi_readl(dsi, name))
+
+       DUMP_REG(DSI_INCR_SYNCPT);
+       DUMP_REG(DSI_INCR_SYNCPT_CONTROL);
+       DUMP_REG(DSI_INCR_SYNCPT_ERROR);
+       DUMP_REG(DSI_CTXSW);
+       DUMP_REG(DSI_RD_DATA);
+       DUMP_REG(DSI_WR_DATA);
+       DUMP_REG(DSI_POWER_CONTROL);
+       DUMP_REG(DSI_INT_ENABLE);
+       DUMP_REG(DSI_INT_STATUS);
+       DUMP_REG(DSI_INT_MASK);
+       DUMP_REG(DSI_HOST_CONTROL);
+       DUMP_REG(DSI_CONTROL);
+       DUMP_REG(DSI_SOL_DELAY);
+       DUMP_REG(DSI_MAX_THRESHOLD);
+       DUMP_REG(DSI_TRIGGER);
+       DUMP_REG(DSI_TX_CRC);
+       DUMP_REG(DSI_STATUS);
+
+       DUMP_REG(DSI_INIT_SEQ_CONTROL);
+       DUMP_REG(DSI_INIT_SEQ_DATA_0);
+       DUMP_REG(DSI_INIT_SEQ_DATA_1);
+       DUMP_REG(DSI_INIT_SEQ_DATA_2);
+       DUMP_REG(DSI_INIT_SEQ_DATA_3);
+       DUMP_REG(DSI_INIT_SEQ_DATA_4);
+       DUMP_REG(DSI_INIT_SEQ_DATA_5);
+       DUMP_REG(DSI_INIT_SEQ_DATA_6);
+       DUMP_REG(DSI_INIT_SEQ_DATA_7);
+
+       DUMP_REG(DSI_PKT_SEQ_0_LO);
+       DUMP_REG(DSI_PKT_SEQ_0_HI);
+       DUMP_REG(DSI_PKT_SEQ_1_LO);
+       DUMP_REG(DSI_PKT_SEQ_1_HI);
+       DUMP_REG(DSI_PKT_SEQ_2_LO);
+       DUMP_REG(DSI_PKT_SEQ_2_HI);
+       DUMP_REG(DSI_PKT_SEQ_3_LO);
+       DUMP_REG(DSI_PKT_SEQ_3_HI);
+       DUMP_REG(DSI_PKT_SEQ_4_LO);
+       DUMP_REG(DSI_PKT_SEQ_4_HI);
+       DUMP_REG(DSI_PKT_SEQ_5_LO);
+       DUMP_REG(DSI_PKT_SEQ_5_HI);
+
+       DUMP_REG(DSI_DCS_CMDS);
+
+       DUMP_REG(DSI_PKT_LEN_0_1);
+       DUMP_REG(DSI_PKT_LEN_2_3);
+       DUMP_REG(DSI_PKT_LEN_4_5);
+       DUMP_REG(DSI_PKT_LEN_6_7);
+
+       DUMP_REG(DSI_PHY_TIMING_0);
+       DUMP_REG(DSI_PHY_TIMING_1);
+       DUMP_REG(DSI_PHY_TIMING_2);
+       DUMP_REG(DSI_BTA_TIMING);
+
+       DUMP_REG(DSI_TIMEOUT_0);
+       DUMP_REG(DSI_TIMEOUT_1);
+       DUMP_REG(DSI_TO_TALLY);
+
+       DUMP_REG(DSI_PAD_CONTROL_0);
+       DUMP_REG(DSI_PAD_CONTROL_CD);
+       DUMP_REG(DSI_PAD_CD_STATUS);
+       DUMP_REG(DSI_VIDEO_MODE_CONTROL);
+       DUMP_REG(DSI_PAD_CONTROL_1);
+       DUMP_REG(DSI_PAD_CONTROL_2);
+       DUMP_REG(DSI_PAD_CONTROL_3);
+       DUMP_REG(DSI_PAD_CONTROL_4);
+
+       DUMP_REG(DSI_GANGED_MODE_CONTROL);
+       DUMP_REG(DSI_GANGED_MODE_START);
+       DUMP_REG(DSI_GANGED_MODE_SIZE);
+
+       DUMP_REG(DSI_RAW_DATA_BYTE_COUNT);
+       DUMP_REG(DSI_ULTRA_LOW_POWER_CONTROL);
+
+       DUMP_REG(DSI_INIT_SEQ_DATA_8);
+       DUMP_REG(DSI_INIT_SEQ_DATA_9);
+       DUMP_REG(DSI_INIT_SEQ_DATA_10);
+       DUMP_REG(DSI_INIT_SEQ_DATA_11);
+       DUMP_REG(DSI_INIT_SEQ_DATA_12);
+       DUMP_REG(DSI_INIT_SEQ_DATA_13);
+       DUMP_REG(DSI_INIT_SEQ_DATA_14);
+       DUMP_REG(DSI_INIT_SEQ_DATA_15);
+
+#undef DUMP_REG
+
+       return 0;
+}
+
+static struct drm_info_list debugfs_files[] = {
+       { "regs", tegra_dsi_show_regs, 0, NULL },
+};
+
+static int tegra_dsi_debugfs_init(struct tegra_dsi *dsi,
+                                 struct drm_minor *minor)
+{
+       const char *name = dev_name(dsi->dev);
+       unsigned int i;
+       int err;
+
+       dsi->debugfs = debugfs_create_dir(name, minor->debugfs_root);
+       if (!dsi->debugfs)
+               return -ENOMEM;
+
+       dsi->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
+                                    GFP_KERNEL);
+       if (!dsi->debugfs_files) {
+               err = -ENOMEM;
+               goto remove;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(debugfs_files); i++)
+               dsi->debugfs_files[i].data = dsi;
+
+       err = drm_debugfs_create_files(dsi->debugfs_files,
+                                      ARRAY_SIZE(debugfs_files),
+                                      dsi->debugfs, minor);
+       if (err < 0)
+               goto free;
+
+       dsi->minor = minor;
+
+       return 0;
+
+free:
+       kfree(dsi->debugfs_files);
+       dsi->debugfs_files = NULL;
+remove:
+       debugfs_remove(dsi->debugfs);
+       dsi->debugfs = NULL;
+
+       return err;
+}
+
+static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi)
+{
+       drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files),
+                                dsi->minor);
+       dsi->minor = NULL;
+
+       kfree(dsi->debugfs_files);
+       dsi->debugfs_files = NULL;
+
+       debugfs_remove(dsi->debugfs);
+       dsi->debugfs = NULL;
+
+       return 0;
+}
+
+enum {
+       CMD_VS = 0x01,
+       CMD_VE = 0x11,
+
+       CMD_HS = 0x21,
+       CMD_HE = 0x31,
+
+       CMD_EOT = 0x08,
+       CMD_NULL = 0x09,
+       CMD_SHORTW = 0x15,
+       CMD_BLNK = 0x19,
+       CMD_LONGW = 0x39,
+
+       CMD_RGB = 0x00,
+       CMD_RGB_16BPP = 0x0E,
+       CMD_RGB_18BPP = 0x1E,
+       CMD_RGB_18BPPNP = 0x2E,
+       CMD_RGB_24BPP = 0x3E,
+};
+
+#define PKT_ID0(id)    ((((id) & 0x3f) << 3) | (1 << 9))
+#define PKT_LEN0(len)  (((len) & 0x7) << 0)
+#define PKT_ID1(id)    ((((id) & 0x3f) << 13) | (1 << 19))
+#define PKT_LEN1(len)  (((len) & 0x7) << 10)
+#define PKT_ID2(id)    ((((id) & 0x3f) << 23) | (1 << 29))
+#define PKT_LEN2(len)  (((len) & 0x7) << 20)
+#define PKT_ID3(id)    ((((id) & 0x3f) << 3) | (1 << 9))
+#define PKT_LEN3(len)  (((len) & 0x7) << 0)
+#define PKT_ID4(id)    ((((id) & 0x3f) << 13) | (1 << 19))
+#define PKT_LEN4(len)  (((len) & 0x7) << 10)
+#define PKT_ID5(id)    ((((id) & 0x3f) << 23) | (1 << 29))
+#define PKT_LEN5(len)  (((len) & 0x7) << 20)
+#define PKT_LP         (1 << 30)
+#define NUM_PKT_SEQ    12
+
+/* non-burst mode with sync-end */
+static const u32 pkt_seq_vnb_syne[NUM_PKT_SEQ] = {
+       PKT_ID0(CMD_VS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) |
+       PKT_ID2(CMD_HE) | PKT_LEN2(0) | PKT_LP,
+       0,
+       PKT_ID0(CMD_VE) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) |
+       PKT_ID2(CMD_HE) | PKT_LEN2(0) | PKT_LP,
+       0,
+       PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) |
+       PKT_ID2(CMD_HE) | PKT_LEN2(0) | PKT_LP,
+       0,
+       PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) |
+       PKT_ID2(CMD_HE) | PKT_LEN2(0),
+       PKT_ID3(CMD_BLNK) | PKT_LEN3(2) | PKT_ID4(CMD_RGB_24BPP) | PKT_LEN4(3) |
+       PKT_ID5(CMD_BLNK) | PKT_LEN5(4),
+       PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) |
+       PKT_ID2(CMD_HE) | PKT_LEN2(0) | PKT_LP,
+       0,
+       PKT_ID0(CMD_HS) | PKT_LEN0(0) | PKT_ID1(CMD_BLNK) | PKT_LEN1(1) |
+       PKT_ID2(CMD_HE) | PKT_LEN2(0),
+       PKT_ID3(CMD_BLNK) | PKT_LEN3(2) | PKT_ID4(CMD_RGB_24BPP) | PKT_LEN4(3) |
+       PKT_ID5(CMD_BLNK) | PKT_LEN5(4),
+};
+
+static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)
+{
+       struct dsi_phy_timing timing;
+       unsigned long value, period;
+       long rate;
+       int err;
+
+       rate = clk_get_rate(dsi->clk);
+       if (rate < 0)
+               return rate;
+
+       period = DIV_ROUND_CLOSEST(1000000000UL, rate * 2);
+
+       err = dsi_phy_timing_get_default(&timing, period);
+       if (err < 0)
+               return err;
+
+       err = dsi_phy_timing_validate(&timing, period);
+       if (err < 0) {
+               dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err);
+               return err;
+       }
+
+       /*
+        * The D-PHY timing fields below are expressed in byte-clock cycles,
+        * so multiply the period by 8.
+        */
+       period *= 8;
+
+       value = DSI_TIMING_FIELD(timing.hsexit, period, 1) << 24 |
+               DSI_TIMING_FIELD(timing.hstrail, period, 0) << 16 |
+               DSI_TIMING_FIELD(timing.hszero, period, 3) << 8 |
+               DSI_TIMING_FIELD(timing.hsprepare, period, 1);
+       tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0);
+
+       value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 |
+               DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 |
+               DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 |
+               DSI_TIMING_FIELD(timing.lpx, period, 1);
+       tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1);
+
+       value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 |
+               DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 |
+               DSI_TIMING_FIELD(0xff * period, period, 0) << 0;
+       tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2);
+
+       value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 |
+               DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 |
+               DSI_TIMING_FIELD(timing.tago, period, 1);
+       tegra_dsi_writel(dsi, value, DSI_BTA_TIMING);
+
+       return 0;
+}
+
+static int tegra_dsi_get_muldiv(enum dsi_format format, unsigned int *mulp,
+                               unsigned int *divp)
+{
+       switch (format) {
+       case DSI_FORMAT_16P:
+               *mulp = 2;
+               *divp = 1;
+               break;
+
+       case DSI_FORMAT_18NP:
+               *mulp = 9;
+               *divp = 4;
+               break;
+
+       case DSI_FORMAT_18P:
+       case DSI_FORMAT_24P:
+               *mulp = 3;
+               *divp = 1;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int tegra_output_dsi_enable(struct tegra_output *output)
+{
+       struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+       struct drm_display_mode *mode = &dc->base.mode;
+       unsigned int hact, hsw, hbp, hfp, i, mul, div;
+       struct tegra_dsi *dsi = to_dsi(output);
+       /* FIXME: don't hardcode this */
+       const u32 *pkt_seq = pkt_seq_vnb_syne;
+       unsigned long value;
+       int err;
+
+       err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
+       if (err < 0)
+               return err;
+
+       err = clk_enable(dsi->clk);
+       if (err < 0)
+               return err;
+
+       tegra_periph_reset_deassert(dsi->clk);
+
+       value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(dsi->format) |
+               DSI_CONTROL_LANES(dsi->lanes - 1) | DSI_CONTROL_SOURCE(0);
+       tegra_dsi_writel(dsi, value, DSI_CONTROL);
+
+       tegra_dc_writel(dc, DSI_ENABLE, DC_DISP_DISP_WIN_OPTIONS);
+
+       value = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
+               PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
+       tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
+
+       tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
+       tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
+
+       tegra_dsi_writel(dsi, 0x000001e0, DSI_MAX_THRESHOLD);
+
+       value = DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS |
+               DSI_HOST_CONTROL_ECC;
+       tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+
+       value = tegra_dsi_readl(dsi, DSI_CONTROL);
+       value |= DSI_CONTROL_HS_CLK_CTRL;
+       value &= ~DSI_CONTROL_TX_TRIG(3);
+       value &= ~DSI_CONTROL_DCS_ENABLE;
+       value |= DSI_CONTROL_VIDEO_ENABLE;
+       value &= ~DSI_CONTROL_HOST_ENABLE;
+       tegra_dsi_writel(dsi, value, DSI_CONTROL);
+
+       err = tegra_dsi_set_phy_timing(dsi);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < NUM_PKT_SEQ; i++)
+               tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i);
+
+       /* horizontal active pixels */
+       hact = mode->hdisplay * mul / div;
+
+       /* horizontal sync width */
+       hsw = (mode->hsync_end - mode->hsync_start) * mul / div;
+       hsw -= 10;
+
+       /* horizontal back porch */
+       hbp = (mode->htotal - mode->hsync_end) * mul / div;
+       hbp -= 14;
+
+       /* horizontal front porch */
+       hfp = (mode->hsync_start  - mode->hdisplay) * mul / div;
+       hfp -= 8;
+
+       tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1);
+       tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3);
+       tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5);
+       tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
+
+       /* set SOL delay */
+       tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY);
+
+       return 0;
+}
+
+static int tegra_output_dsi_disable(struct tegra_output *output)
+{
+       struct tegra_dsi *dsi = to_dsi(output);
+
+       clk_disable(dsi->clk);
+
+       return 0;
+}
+
+static int tegra_output_dsi_setup_clock(struct tegra_output *output,
+                                       struct clk *clk, unsigned long pclk)
+{
+       struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);
+       struct drm_display_mode *mode = &dc->base.mode;
+       struct tegra_dsi *dsi = to_dsi(output);
+       unsigned long bclk, plld, value;
+       unsigned int timeout, mul, div;
+       struct clk *base;
+       int err;
+
+       err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);
+       if (err < 0)
+               return err;
+
+       pclk = mode->htotal * mode->vtotal * mode->vrefresh;
+       bclk = (pclk * mul) / (div * dsi->lanes);
+       plld = DIV_ROUND_UP(bclk * 8, 1000000);
+       pclk = (plld * 1000000) / 2;
+
+       err = clk_set_parent(clk, dsi->clk_parent);
+       if (err < 0) {
+               dev_err(dsi->dev, "failed to set parent clock: %d\n", err);
+               return err;
+       }
+
+       base = clk_get_parent(dsi->clk_parent);
+
+       /*
+        * This assumes that the parent clock is pll_d_out0 or pll_d2_out
+        * respectively, each of which divides the base pll_d by 2.
+        */
+       err = clk_set_rate(base, pclk * 2);
+       if (err < 0) {
+               dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n",
+                       pclk * 2);
+               return err;
+       }
+
+       /* one frame high-speed transmission timeout */
+       timeout = (bclk / mode->vrefresh) / 512;
+       value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
+       tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0);
+
+       /* 2 ms peripheral timeout for panel */
+       timeout = 2 * bclk / 512 * 1000;
+       value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
+       tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1);
+
+       value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
+       tegra_dsi_writel(dsi, value, DSI_TO_TALLY);
+
+       return 0;
+}
+
+static int tegra_output_dsi_check_mode(struct tegra_output *output,
+                                      struct drm_display_mode *mode,
+                                      enum drm_mode_status *status)
+{
+       /*
+        * FIXME: For now, always assume that the mode is okay.
+        */
+
+       *status = MODE_OK;
+
+       return 0;
+}
+
+static const struct tegra_output_ops dsi_ops = {
+       .enable = tegra_output_dsi_enable,
+       .disable = tegra_output_dsi_disable,
+       .setup_clock = tegra_output_dsi_setup_clock,
+       .check_mode = tegra_output_dsi_check_mode,
+};
+
+static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)
+{
+       unsigned long value;
+
+       value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0);
+       tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0);
+
+       return 0;
+}
+
+static int tegra_dsi_init(struct host1x_client *client)
+{
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       struct tegra_dsi *dsi = host1x_client_to_dsi(client);
+       unsigned long value, i;
+       int err;
+
+       dsi->output.type = TEGRA_OUTPUT_DSI;
+       dsi->output.dev = client->dev;
+       dsi->output.ops = &dsi_ops;
+
+       err = tegra_output_init(tegra->drm, &dsi->output);
+       if (err < 0) {
+               dev_err(client->dev, "output setup failed: %d\n", err);
+               return err;
+       }
+
+       if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+               err = tegra_dsi_debugfs_init(dsi, tegra->drm->primary);
+               if (err < 0)
+                       dev_err(dsi->dev, "debugfs setup failed: %d\n", err);
+       }
+
+       /*
+        * enable high-speed mode, checksum generation, ECC generation and
+        * disable raw mode
+        */
+       value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL);
+       value |= DSI_HOST_CONTROL_ECC | DSI_HOST_CONTROL_CS |
+                DSI_HOST_CONTROL_HS;
+       value &= ~DSI_HOST_CONTROL_RAW;
+       tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);
+
+       tegra_dsi_writel(dsi, 0, DSI_SOL_DELAY);
+       tegra_dsi_writel(dsi, 0, DSI_MAX_THRESHOLD);
+
+       tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_CONTROL);
+
+       for (i = 0; i < 8; i++) {
+               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + i);
+               tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_8 + i);
+       }
+
+       for (i = 0; i < 12; i++)
+               tegra_dsi_writel(dsi, 0, DSI_PKT_SEQ_0_LO + i);
+
+       tegra_dsi_writel(dsi, 0, DSI_DCS_CMDS);
+
+       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);
+       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1);
+       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2);
+       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3);
+       tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4);
+
+       /* start calibration */
+       tegra_dsi_pad_enable(dsi);
+
+       value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) |
+               DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) |
+               DSI_PAD_OUT_CLK(0x0);
+       tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2);
+
+       err = tegra_mipi_calibrate(dsi->dev);
+       if (err < 0) {
+               dev_err(dsi->dev, "MIPI calibration failed: %d\n", err);
+               return err;
+       }
+
+       tegra_dsi_writel(dsi, DSI_POWER_CONTROL_ENABLE, DSI_POWER_CONTROL);
+       usleep_range(300, 1000);
+
+       return 0;
+}
+
+static int tegra_dsi_exit(struct host1x_client *client)
+{
+       struct tegra_dsi *dsi = host1x_client_to_dsi(client);
+       int err;
+
+       if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+               err = tegra_dsi_debugfs_exit(dsi);
+               if (err < 0)
+                       dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err);
+       }
+
+       err = tegra_output_disable(&dsi->output);
+       if (err < 0) {
+               dev_err(client->dev, "output failed to disable: %d\n", err);
+               return err;
+       }
+
+       err = tegra_output_exit(&dsi->output);
+       if (err < 0) {
+               dev_err(client->dev, "output cleanup failed: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static const struct host1x_client_ops dsi_client_ops = {
+       .init = tegra_dsi_init,
+       .exit = tegra_dsi_exit,
+};
+
+static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)
+{
+       struct clk *parent;
+       int err;
+
+       parent = clk_get_parent(dsi->clk);
+       if (!parent)
+               return -EINVAL;
+
+       err = clk_set_parent(parent, dsi->clk_parent);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int tegra_dsi_probe(struct platform_device *pdev)
+{
+       struct tegra_dsi *dsi;
+       struct resource *regs;
+       int err;
+
+       dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+       if (!dsi)
+               return -ENOMEM;
+
+       dsi->output.dev = dsi->dev = &pdev->dev;
+
+       err = tegra_output_probe(&dsi->output);
+       if (err < 0)
+               return err;
+
+       /*
+        * FIXME: Don't hardcode these. Perhaps they should be queried from
+        *        the panel or from the DSI interface's DT node.
+        */
+       dsi->format = DSI_FORMAT_24P;
+       dsi->lanes = 4;
+
+       dsi->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dsi->clk))
+               return PTR_ERR(dsi->clk);;
+
+       err = clk_prepare_enable(dsi->clk);
+       if (err < 0)
+               return err;
+
+       dsi->clk_parent = devm_clk_get(&pdev->dev, "parent");
+       if (IS_ERR(dsi->clk_parent))
+               return PTR_ERR(dsi->clk_parent);
+
+       err = clk_prepare_enable(dsi->clk_parent);
+       if (err < 0)
+               return err;
+
+       err = tegra_dsi_setup_clocks(dsi);
+       if (err < 0)
+               return err;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dsi->regs = devm_ioremap_resource(&pdev->dev, regs);
+       if (!dsi->regs)
+               return -EADDRNOTAVAIL;
+
+       INIT_LIST_HEAD(&dsi->client.list);
+       dsi->client.ops = &dsi_client_ops;
+       dsi->client.dev = &pdev->dev;
+
+       err = host1x_client_register(&dsi->client);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to register host1x client: %d\n",
+                       err);
+               return err;
+       }
+
+       platform_set_drvdata(pdev, dsi);
+
+       return 0;
+}
+
+static int tegra_dsi_remove(struct platform_device *pdev)
+{
+       struct tegra_dsi *dsi = platform_get_drvdata(pdev);
+       int err;
+
+       err = host1x_client_unregister(&dsi->client);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+                       err);
+               return err;
+       }
+
+       clk_disable_unprepare(dsi->clk_parent);
+       clk_disable_unprepare(dsi->clk);
+
+       err = tegra_output_remove(&dsi->output);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to remove output: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id tegra_dsi_of_match[] = {
+       { .compatible = "nvidia,tegra114-dsi", },
+       { },
+};
+
+struct platform_driver tegra_dsi_driver = {
+       .driver = {
+               .name = "tegra-dsi",
+               .of_match_table = tegra_dsi_of_match,
+       },
+       .probe = tegra_dsi_probe,
+       .remove = tegra_dsi_remove,
+};
similarity index 92%
rename from drivers/gpu/host1x/drm/fb.c
rename to drivers/gpu/drm/tegra/fb.c
index 979a3e32b78bbb0f5da3126b6b406eebae6ef497..490f7719e317ed80319f4961a69d3e349d1ad333 100644 (file)
@@ -10,8 +10,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-
 #include "drm.h"
 #include "gem.h"
 
@@ -36,6 +34,26 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
        return fb->planes[index];
 }
 
+bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer)
+{
+       struct tegra_fb *fb = to_tegra_fb(framebuffer);
+
+       if (fb->planes[0]->flags & TEGRA_BO_BOTTOM_UP)
+               return true;
+
+       return false;
+}
+
+bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer)
+{
+       struct tegra_fb *fb = to_tegra_fb(framebuffer);
+
+       if (fb->planes[0]->flags & TEGRA_BO_TILED)
+               return true;
+
+       return false;
+}
+
 static void tegra_fb_destroy(struct drm_framebuffer *framebuffer)
 {
        struct tegra_fb *fb = to_tegra_fb(framebuffer);
@@ -190,7 +208,7 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper,
 
        size = cmd.pitches[0] * cmd.height;
 
-       bo = tegra_bo_create(drm, size);
+       bo = tegra_bo_create(drm, size, 0);
        if (IS_ERR(bo))
                return PTR_ERR(bo);
 
@@ -323,10 +341,10 @@ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
 
 static void tegra_fb_output_poll_changed(struct drm_device *drm)
 {
-       struct host1x_drm *host1x = drm->dev_private;
+       struct tegra_drm *tegra = drm->dev_private;
 
-       if (host1x->fbdev)
-               drm_fb_helper_hotplug_event(&host1x->fbdev->base);
+       if (tegra->fbdev)
+               drm_fb_helper_hotplug_event(&tegra->fbdev->base);
 }
 
 static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
@@ -336,7 +354,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
 
 int tegra_drm_fb_init(struct drm_device *drm)
 {
-       struct host1x_drm *host1x = drm->dev_private;
+       struct tegra_drm *tegra = drm->dev_private;
        struct tegra_fbdev *fbdev;
 
        drm->mode_config.min_width = 0;
@@ -352,16 +370,16 @@ int tegra_drm_fb_init(struct drm_device *drm)
        if (IS_ERR(fbdev))
                return PTR_ERR(fbdev);
 
-       host1x->fbdev = fbdev;
+       tegra->fbdev = fbdev;
 
        return 0;
 }
 
 void tegra_drm_fb_exit(struct drm_device *drm)
 {
-       struct host1x_drm *host1x = drm->dev_private;
+       struct tegra_drm *tegra = drm->dev_private;
 
-       tegra_fbdev_free(host1x->fbdev);
+       tegra_fbdev_free(tegra->fbdev);
 }
 
 void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev)
similarity index 86%
rename from drivers/gpu/host1x/drm/gem.c
rename to drivers/gpu/drm/tegra/gem.c
index 59623de4ee15f3bf9a186d80481f358397fbf99e..28a9cbc07ab95f3a5873fc9aac0f008180bffca5 100644 (file)
  * GNU General Public License for more details.
  */
 
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/export.h>
-#include <linux/dma-mapping.h>
-
-#include <drm/drmP.h>
-#include <drm/drm.h>
+#include <drm/tegra_drm.h>
 
 #include "gem.h"
 
-static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo)
+static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo)
 {
        return container_of(bo, struct tegra_bo, base);
 }
 
 static void tegra_bo_put(struct host1x_bo *bo)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
        struct drm_device *drm = obj->gem.dev;
 
        mutex_lock(&drm->struct_mutex);
@@ -46,7 +39,7 @@ static void tegra_bo_put(struct host1x_bo *bo)
 
 static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
 
        return obj->paddr;
 }
@@ -57,7 +50,7 @@ static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
 
 static void *tegra_bo_mmap(struct host1x_bo *bo)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
 
        return obj->vaddr;
 }
@@ -68,7 +61,7 @@ static void tegra_bo_munmap(struct host1x_bo *bo, void *addr)
 
 static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
 
        return obj->vaddr + page * PAGE_SIZE;
 }
@@ -80,7 +73,7 @@ static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page,
 
 static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo)
 {
-       struct tegra_bo *obj = host1x_to_drm_bo(bo);
+       struct tegra_bo *obj = host1x_to_tegra_bo(bo);
        struct drm_device *drm = obj->gem.dev;
 
        mutex_lock(&drm->struct_mutex);
@@ -106,7 +99,8 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo)
        dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr);
 }
 
-struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
+struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
+                                unsigned long flags)
 {
        struct tegra_bo *bo;
        int err;
@@ -135,6 +129,12 @@ struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size)
        if (err)
                goto err_mmap;
 
+       if (flags & DRM_TEGRA_GEM_CREATE_TILED)
+               bo->flags |= TEGRA_BO_TILED;
+
+       if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP)
+               bo->flags |= TEGRA_BO_BOTTOM_UP;
+
        return bo;
 
 err_mmap:
@@ -149,14 +149,15 @@ err_dma:
 }
 
 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);
 
similarity index 84%
rename from drivers/gpu/host1x/drm/gem.h
rename to drivers/gpu/drm/tegra/gem.h
index 492533a2dacb1bba249cde2ff08b63e1c855c291..7674000bf47d6696ecec6db7507926144832c772 100644 (file)
 #ifndef __HOST1X_GEM_H
 #define __HOST1X_GEM_H
 
+#include <linux/host1x.h>
+
 #include <drm/drm.h>
 #include <drm/drmP.h>
 
-#include "host1x_bo.h"
+#define TEGRA_BO_TILED     (1 << 0)
+#define TEGRA_BO_BOTTOM_UP (1 << 1)
 
 struct tegra_bo {
        struct drm_gem_object gem;
        struct host1x_bo base;
+       unsigned long flags;
        dma_addr_t paddr;
        void *vaddr;
 };
@@ -38,11 +42,13 @@ static inline struct tegra_bo *to_tegra_bo(struct drm_gem_object *gem)
 
 extern const struct host1x_bo_ops tegra_bo_ops;
 
-struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size);
+struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size,
+                                unsigned long flags);
 struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
-                                           struct drm_device *drm,
-                                           unsigned int size,
-                                           unsigned int *handle);
+                                            struct drm_device *drm,
+                                            unsigned int size,
+                                            unsigned long flags,
+                                            unsigned int *handle);
 void tegra_bo_free_object(struct drm_gem_object *gem);
 int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
                         struct drm_mode_create_dumb *args);
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
new file mode 100644 (file)
index 0000000..73b79ba
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2012-2013, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+
+#include "drm.h"
+#include "gem.h"
+#include "gr2d.h"
+
+struct gr2d {
+       struct tegra_drm_client client;
+       struct host1x_channel *channel;
+       struct clk *clk;
+
+       DECLARE_BITMAP(addr_regs, GR2D_NUM_REGS);
+};
+
+static inline struct gr2d *to_gr2d(struct tegra_drm_client *client)
+{
+       return container_of(client, struct gr2d, client);
+}
+
+static int gr2d_init(struct host1x_client *client)
+{
+       struct tegra_drm_client *drm = host1x_to_drm_client(client);
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       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, false);
+       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);
+               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 (file)
index 0000000..4d7304f
--- /dev/null
@@ -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 (file)
index 0000000..dc0f222
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2013 Avionic Design GmbH
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/tegra-powergate.h>
+
+#include "drm.h"
+#include "gem.h"
+#include "gr3d.h"
+
+struct gr3d {
+       struct tegra_drm_client client;
+       struct host1x_channel *channel;
+       struct clk *clk_secondary;
+       struct clk *clk;
+
+       DECLARE_BITMAP(addr_regs, GR3D_NUM_REGS);
+};
+
+static inline struct gr3d *to_gr3d(struct tegra_drm_client *client)
+{
+       return container_of(client, struct gr3d, client);
+}
+
+static int gr3d_init(struct host1x_client *client)
+{
+       struct tegra_drm_client *drm = host1x_to_drm_client(client);
+       struct tegra_drm *tegra = dev_get_drvdata(client->parent);
+       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, 0);
+       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 (file)
index 0000000..0c30a13
--- /dev/null
@@ -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
similarity index 83%
rename from drivers/gpu/host1x/drm/hdmi.c
rename to drivers/gpu/drm/tegra/hdmi.c
index 644d95c7d489997b9305f6eced5e62b638798a54..0cd9bc2056e8c6bfe12cd15c5722c6f18d087e78 100644 (file)
@@ -8,21 +8,33 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk/tegra.h>
 #include <linux/debugfs.h>
-#include <linux/gpio.h>
 #include <linux/hdmi.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
-#include <linux/clk/tegra.h>
-
-#include <drm/drm_edid.h>
 
 #include "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",
similarity index 72%
rename from drivers/gpu/host1x/drm/hdmi.h
rename to drivers/gpu/drm/tegra/hdmi.h
index 52ac36e08ccbef5ec14df106abeabcdd12efe6f2..0aebc485f7fa36be983a609f693bfbb06dcb0214 100644 (file)
 #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
 #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
 #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
 #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 */
similarity index 81%
rename from drivers/gpu/host1x/drm/output.c
rename to drivers/gpu/drm/tegra/output.c
index 137ae81ab80eb164d139742b16996bca8b8922b5..b14f817214712e7db50eaf6442fa5d76720b85c9 100644 (file)
@@ -7,10 +7,9 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
 #include <linux/of_gpio.h>
-#include <linux/i2c.h>
 
+#include <drm/drm_panel.h>
 #include "drm.h"
 
 static int tegra_connector_get_modes(struct drm_connector *connector)
@@ -19,6 +18,9 @@ static int tegra_connector_get_modes(struct drm_connector *connector)
        struct edid *edid = NULL;
        int err = 0;
 
+       if (output->panel)
+               return output->panel->funcs->get_modes(output->panel);
+
        if (output->edid)
                edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
        else if (output->ddc)
@@ -74,6 +76,9 @@ tegra_connector_detect(struct drm_connector *connector, bool force)
                else
                        status = connector_status_connected;
        } else {
+               if (output->panel)
+                       status = connector_status_connected;
+
                if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
                        status = connector_status_connected;
        }
@@ -81,10 +86,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 +105,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 = {
@@ -105,6 +122,15 @@ static const struct drm_encoder_funcs encoder_funcs = {
 
 static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
+       struct tegra_output *output = encoder_to_output(encoder);
+       struct drm_panel *panel = output->panel;
+
+       if (panel && panel->funcs) {
+               if (mode != DRM_MODE_DPMS_ON)
+                       drm_panel_disable(panel);
+               else
+                       drm_panel_enable(panel);
+       }
 }
 
 static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
@@ -151,16 +177,25 @@ 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)
 {
+       struct device_node *ddc, *panel;
        enum of_gpio_flags flags;
-       struct device_node *ddc;
        size_t size;
        int err;
 
        if (!output->of_node)
                output->of_node = output->dev->of_node;
 
+       panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
+       if (panel) {
+               output->panel = of_drm_find_panel(panel);
+               if (!output->panel)
+                       return -EPROBE_DEFER;
+
+               of_node_put(panel);
+       }
+
        output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
 
        ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
@@ -175,20 +210,12 @@ int tegra_output_parse_dt(struct tegra_output *output)
                of_node_put(ddc);
        }
 
-       if (!output->edid && !output->ddc)
+       if (!output->panel && !output->edid && !output->ddc)
                return -ENODEV;
 
        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 +229,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 +243,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;
@@ -232,6 +281,11 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
                encoder = DRM_MODE_ENCODER_TMDS;
                break;
 
+       case TEGRA_OUTPUT_DSI:
+               connector = DRM_MODE_CONNECTOR_DSI;
+               encoder = DRM_MODE_ENCODER_DSI;
+               break;
+
        default:
                connector = DRM_MODE_CONNECTOR_Unknown;
                encoder = DRM_MODE_ENCODER_NONE;
@@ -241,6 +295,10 @@ 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;
+
+       if (output->panel)
+               drm_panel_attach(output->panel, &output->connector);
 
        drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
        drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
@@ -251,22 +309,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;
 }
similarity index 96%
rename from drivers/gpu/host1x/drm/rgb.c
rename to drivers/gpu/drm/tegra/rgb.c
index 5aa66ef7a946f8b1c25a4241cf39f76dad9e91b7..ba47ca4fb880cc239a9b5c454b3b6c5b5adcddc8 100644 (file)
@@ -8,9 +8,6 @@
  */
 
 #include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
 
 #include "drm.h"
 #include "dc.h"
@@ -150,7 +147,7 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
        rgb->output.dev = dc->dev;
        rgb->output.of_node = np;
 
-       err = tegra_output_parse_dt(&rgb->output);
+       err = tegra_output_probe(&rgb->output);
        if (err < 0)
                return err;
 
@@ -177,6 +174,20 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
        return 0;
 }
 
+int tegra_dc_rgb_remove(struct tegra_dc *dc)
+{
+       int err;
+
+       if (!dc->rgb)
+               return 0;
+
+       err = tegra_output_remove(dc->rgb);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc)
 {
        struct tegra_rgb *rgb = to_rgb(dc->rgb);
index ccfd42b236060ffa17329d95a114029ef1faa89c..7d6bed2225422fa2413130a606d2b20fd084cfd4 100644 (file)
@@ -19,6 +19,4 @@ config TEGRA_HOST1X_FIREWALL
 
          If unsure, choose Y.
 
-source "drivers/gpu/host1x/drm/Kconfig"
-
 endif
index 3b037b6e0298454edbc44c30db4f818f3ca8cafc..de305c2d510ea0bab597891bb8c82bca9890301b 100644 (file)
@@ -1,6 +1,5 @@
-ccflags-y = -Idrivers/gpu/host1x
-
 host1x-y = \
+       bus.o \
        syncpt.o \
        dev.o \
        intr.o \
@@ -8,13 +7,8 @@ host1x-y = \
        channel.o \
        job.o \
        debug.o \
-       hw/host1x01.o
-
-ccflags-y += -Iinclude/drm
-ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
+       mipi.o \
+       hw/host1x01.o \
+       hw/host1x02.o
 
-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 (file)
index 0000000..509383f
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2012-2013, NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/host1x.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include "dev.h"
+
+static DEFINE_MUTEX(clients_lock);
+static LIST_HEAD(clients);
+
+static DEFINE_MUTEX(drivers_lock);
+static LIST_HEAD(drivers);
+
+static DEFINE_MUTEX(devices_lock);
+static LIST_HEAD(devices);
+
+struct host1x_subdev {
+       struct host1x_client *client;
+       struct device_node *np;
+       struct list_head list;
+};
+
+/**
+ * host1x_subdev_add() - add a new subdevice with an associated device node
+ */
+static int host1x_subdev_add(struct host1x_device *device,
+                            struct device_node *np)
+{
+       struct host1x_subdev *subdev;
+
+       subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
+       if (!subdev)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&subdev->list);
+       subdev->np = of_node_get(np);
+
+       mutex_lock(&device->subdevs_lock);
+       list_add_tail(&subdev->list, &device->subdevs);
+       mutex_unlock(&device->subdevs_lock);
+
+       return 0;
+}
+
+/**
+ * host1x_subdev_del() - remove subdevice
+ */
+static void host1x_subdev_del(struct host1x_subdev *subdev)
+{
+       list_del(&subdev->list);
+       of_node_put(subdev->np);
+       kfree(subdev);
+}
+
+/**
+ * host1x_device_parse_dt() - scan device tree and add matching subdevices
+ */
+static int host1x_device_parse_dt(struct host1x_device *device)
+{
+       struct device_node *np;
+       int err;
+
+       for_each_child_of_node(device->dev.parent->of_node, np) {
+               if (of_match_node(device->driver->subdevs, np) &&
+                   of_device_is_available(np)) {
+                       err = host1x_subdev_add(device, np);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+static void host1x_subdev_register(struct host1x_device *device,
+                                  struct host1x_subdev *subdev,
+                                  struct host1x_client *client)
+{
+       int err;
+
+       /*
+        * Move the subdevice to the list of active (registered) subdevices
+        * and associate it with a client. At the same time, associate the
+        * client with its parent device.
+        */
+       mutex_lock(&device->subdevs_lock);
+       mutex_lock(&device->clients_lock);
+       list_move_tail(&client->list, &device->clients);
+       list_move_tail(&subdev->list, &device->active);
+       client->parent = &device->dev;
+       subdev->client = client;
+       mutex_unlock(&device->clients_lock);
+       mutex_unlock(&device->subdevs_lock);
+
+       /*
+        * When all subdevices have been registered, the composite device is
+        * ready to be probed.
+        */
+       if (list_empty(&device->subdevs)) {
+               err = device->driver->probe(device);
+               if (err < 0)
+                       dev_err(&device->dev, "probe failed: %d\n", err);
+       }
+}
+
+static void __host1x_subdev_unregister(struct host1x_device *device,
+                                      struct host1x_subdev *subdev)
+{
+       struct host1x_client *client = subdev->client;
+       int err;
+
+       /*
+        * If all subdevices have been activated, we're about to remove the
+        * first active subdevice, so unload the driver first.
+        */
+       if (list_empty(&device->subdevs)) {
+               err = device->driver->remove(device);
+               if (err < 0)
+                       dev_err(&device->dev, "remove failed: %d\n", err);
+       }
+
+       /*
+        * Move the subdevice back to the list of idle subdevices and remove
+        * it from list of clients.
+        */
+       mutex_lock(&device->clients_lock);
+       subdev->client = NULL;
+       client->parent = NULL;
+       list_move_tail(&subdev->list, &device->subdevs);
+       /*
+        * XXX: Perhaps don't do this here, but rather explicitly remove it
+        * when the device is about to be deleted.
+        *
+        * This is somewhat complicated by the fact that this function is
+        * used to remove the subdevice when a client is unregistered but
+        * also when the composite device is about to be removed.
+        */
+       list_del_init(&client->list);
+       mutex_unlock(&device->clients_lock);
+}
+
+static void host1x_subdev_unregister(struct host1x_device *device,
+                                    struct host1x_subdev *subdev)
+{
+       mutex_lock(&device->subdevs_lock);
+       __host1x_subdev_unregister(device, subdev);
+       mutex_unlock(&device->subdevs_lock);
+}
+
+int host1x_device_init(struct host1x_device *device)
+{
+       struct host1x_client *client;
+       int err;
+
+       mutex_lock(&device->clients_lock);
+
+       list_for_each_entry(client, &device->clients, list) {
+               if (client->ops && client->ops->init) {
+                       err = client->ops->init(client);
+                       if (err < 0) {
+                               dev_err(&device->dev,
+                                       "failed to initialize %s: %d\n",
+                                       dev_name(client->dev), err);
+                               mutex_unlock(&device->clients_lock);
+                               return err;
+                       }
+               }
+       }
+
+       mutex_unlock(&device->clients_lock);
+
+       return 0;
+}
+
+int host1x_device_exit(struct host1x_device *device)
+{
+       struct host1x_client *client;
+       int err;
+
+       mutex_lock(&device->clients_lock);
+
+       list_for_each_entry_reverse(client, &device->clients, list) {
+               if (client->ops && client->ops->exit) {
+                       err = client->ops->exit(client);
+                       if (err < 0) {
+                               dev_err(&device->dev,
+                                       "failed to cleanup %s: %d\n",
+                                       dev_name(client->dev), err);
+                               mutex_unlock(&device->clients_lock);
+                               return err;
+                       }
+               }
+       }
+
+       mutex_unlock(&device->clients_lock);
+
+       return 0;
+}
+
+static int host1x_register_client(struct host1x *host1x,
+                                 struct host1x_client *client)
+{
+       struct host1x_device *device;
+       struct host1x_subdev *subdev;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry(device, &host1x->devices, list) {
+               list_for_each_entry(subdev, &device->subdevs, list) {
+                       if (subdev->np == client->dev->of_node) {
+                               host1x_subdev_register(device, subdev, client);
+                               mutex_unlock(&host1x->devices_lock);
+                               return 0;
+                       }
+               }
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+       return -ENODEV;
+}
+
+static int host1x_unregister_client(struct host1x *host1x,
+                                   struct host1x_client *client)
+{
+       struct host1x_device *device, *dt;
+       struct host1x_subdev *subdev;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry_safe(device, dt, &host1x->devices, list) {
+               list_for_each_entry(subdev, &device->active, list) {
+                       if (subdev->client == client) {
+                               host1x_subdev_unregister(device, subdev);
+                               mutex_unlock(&host1x->devices_lock);
+                               return 0;
+                       }
+               }
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+       return -ENODEV;
+}
+
+struct bus_type host1x_bus_type = {
+       .name = "host1x",
+};
+
+int host1x_bus_init(void)
+{
+       return bus_register(&host1x_bus_type);
+}
+
+void host1x_bus_exit(void)
+{
+       bus_unregister(&host1x_bus_type);
+}
+
+static void host1x_device_release(struct device *dev)
+{
+       struct host1x_device *device = to_host1x_device(dev);
+
+       kfree(device);
+}
+
+static int host1x_device_add(struct host1x *host1x,
+                            struct host1x_driver *driver)
+{
+       struct host1x_client *client, *tmp;
+       struct host1x_subdev *subdev;
+       struct host1x_device *device;
+       int err;
+
+       device = kzalloc(sizeof(*device), GFP_KERNEL);
+       if (!device)
+               return -ENOMEM;
+
+       mutex_init(&device->subdevs_lock);
+       INIT_LIST_HEAD(&device->subdevs);
+       INIT_LIST_HEAD(&device->active);
+       mutex_init(&device->clients_lock);
+       INIT_LIST_HEAD(&device->clients);
+       INIT_LIST_HEAD(&device->list);
+       device->driver = driver;
+
+       device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
+       device->dev.dma_mask = &device->dev.coherent_dma_mask;
+       device->dev.release = host1x_device_release;
+       dev_set_name(&device->dev, driver->name);
+       device->dev.bus = &host1x_bus_type;
+       device->dev.parent = host1x->dev;
+
+       err = device_register(&device->dev);
+       if (err < 0)
+               return err;
+
+       err = host1x_device_parse_dt(device);
+       if (err < 0) {
+               device_unregister(&device->dev);
+               return err;
+       }
+
+       mutex_lock(&host1x->devices_lock);
+       list_add_tail(&device->list, &host1x->devices);
+       mutex_unlock(&host1x->devices_lock);
+
+       mutex_lock(&clients_lock);
+
+       list_for_each_entry_safe(client, tmp, &clients, list) {
+               list_for_each_entry(subdev, &device->subdevs, list) {
+                       if (subdev->np == client->dev->of_node) {
+                               host1x_subdev_register(device, subdev, client);
+                               break;
+                       }
+               }
+       }
+
+       mutex_unlock(&clients_lock);
+
+       return 0;
+}
+
+/*
+ * Removes a device by first unregistering any subdevices and then removing
+ * itself from the list of devices.
+ *
+ * This function must be called with the host1x->devices_lock held.
+ */
+static void host1x_device_del(struct host1x *host1x,
+                             struct host1x_device *device)
+{
+       struct host1x_subdev *subdev, *sd;
+       struct host1x_client *client, *cl;
+
+       mutex_lock(&device->subdevs_lock);
+
+       /* unregister subdevices */
+       list_for_each_entry_safe(subdev, sd, &device->active, list) {
+               /*
+                * host1x_subdev_unregister() will remove the client from
+                * any lists, so we'll need to manually add it back to the
+                * list of idle clients.
+                *
+                * XXX: Alternatively, perhaps don't remove the client from
+                * any lists in host1x_subdev_unregister() and instead do
+                * that explicitly from host1x_unregister_client()?
+                */
+               client = subdev->client;
+
+               __host1x_subdev_unregister(device, subdev);
+
+               /* add the client to the list of idle clients */
+               mutex_lock(&clients_lock);
+               list_add_tail(&client->list, &clients);
+               mutex_unlock(&clients_lock);
+       }
+
+       /* remove subdevices */
+       list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
+               host1x_subdev_del(subdev);
+
+       mutex_unlock(&device->subdevs_lock);
+
+       /* move clients to idle list */
+       mutex_lock(&clients_lock);
+       mutex_lock(&device->clients_lock);
+
+       list_for_each_entry_safe(client, cl, &device->clients, list)
+               list_move_tail(&client->list, &clients);
+
+       mutex_unlock(&device->clients_lock);
+       mutex_unlock(&clients_lock);
+
+       /* finally remove the device */
+       list_del_init(&device->list);
+       device_unregister(&device->dev);
+}
+
+static void host1x_attach_driver(struct host1x *host1x,
+                                struct host1x_driver *driver)
+{
+       struct host1x_device *device;
+       int err;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry(device, &host1x->devices, list) {
+               if (device->driver == driver) {
+                       mutex_unlock(&host1x->devices_lock);
+                       return;
+               }
+       }
+
+       mutex_unlock(&host1x->devices_lock);
+
+       err = host1x_device_add(host1x, driver);
+       if (err < 0)
+               dev_err(host1x->dev, "failed to allocate device: %d\n", err);
+}
+
+static void host1x_detach_driver(struct host1x *host1x,
+                                struct host1x_driver *driver)
+{
+       struct host1x_device *device, *tmp;
+
+       mutex_lock(&host1x->devices_lock);
+
+       list_for_each_entry_safe(device, tmp, &host1x->devices, list)
+               if (device->driver == driver)
+                       host1x_device_del(host1x, device);
+
+       mutex_unlock(&host1x->devices_lock);
+}
+
+int host1x_register(struct host1x *host1x)
+{
+       struct host1x_driver *driver;
+
+       mutex_lock(&devices_lock);
+       list_add_tail(&host1x->list, &devices);
+       mutex_unlock(&devices_lock);
+
+       mutex_lock(&drivers_lock);
+
+       list_for_each_entry(driver, &drivers, list)
+               host1x_attach_driver(host1x, driver);
+
+       mutex_unlock(&drivers_lock);
+
+       return 0;
+}
+
+int host1x_unregister(struct host1x *host1x)
+{
+       struct host1x_driver *driver;
+
+       mutex_lock(&drivers_lock);
+
+       list_for_each_entry(driver, &drivers, list)
+               host1x_detach_driver(host1x, driver);
+
+       mutex_unlock(&drivers_lock);
+
+       mutex_lock(&devices_lock);
+       list_del_init(&host1x->list);
+       mutex_unlock(&devices_lock);
+
+       return 0;
+}
+
+int host1x_driver_register(struct host1x_driver *driver)
+{
+       struct host1x *host1x;
+
+       INIT_LIST_HEAD(&driver->list);
+
+       mutex_lock(&drivers_lock);
+       list_add_tail(&driver->list, &drivers);
+       mutex_unlock(&drivers_lock);
+
+       mutex_lock(&devices_lock);
+
+       list_for_each_entry(host1x, &devices, list)
+               host1x_attach_driver(host1x, driver);
+
+       mutex_unlock(&devices_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(host1x_driver_register);
+
+void host1x_driver_unregister(struct host1x_driver *driver)
+{
+       mutex_lock(&drivers_lock);
+       list_del_init(&driver->list);
+       mutex_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(host1x_driver_unregister);
+
+int host1x_client_register(struct host1x_client *client)
+{
+       struct host1x *host1x;
+       int err;
+
+       mutex_lock(&devices_lock);
+
+       list_for_each_entry(host1x, &devices, list) {
+               err = host1x_register_client(host1x, client);
+               if (!err) {
+                       mutex_unlock(&devices_lock);
+                       return 0;
+               }
+       }
+
+       mutex_unlock(&devices_lock);
+
+       mutex_lock(&clients_lock);
+       list_add_tail(&client->list, &clients);
+       mutex_unlock(&clients_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(host1x_client_register);
+
+int host1x_client_unregister(struct host1x_client *client)
+{
+       struct host1x_client *c;
+       struct host1x *host1x;
+       int err;
+
+       mutex_lock(&devices_lock);
+
+       list_for_each_entry(host1x, &devices, list) {
+               err = host1x_unregister_client(host1x, client);
+               if (!err) {
+                       mutex_unlock(&devices_lock);
+                       return 0;
+               }
+       }
+
+       mutex_unlock(&devices_lock);
+       mutex_lock(&clients_lock);
+
+       list_for_each_entry(c, &clients, list) {
+               if (c == client) {
+                       list_del_init(&c->list);
+                       break;
+               }
+       }
+
+       mutex_unlock(&clients_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(host1x_client_unregister);
similarity index 60%
rename from drivers/gpu/host1x/host1x_client.h
rename to drivers/gpu/host1x/bus.h
index 9b85f10f4a44fc9f70a45af7b40f33d95ba9942c..4099e99212c87354377d9a851d5409573792611b 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2013, NVIDIA Corporation.
+ * Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2012-2013, NVIDIA Corporation
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef HOST1X_CLIENT_H
-#define HOST1X_CLIENT_H
+#ifndef HOST1X_BUS_H
+#define HOST1X_BUS_H
 
-struct device;
-struct platform_device;
+struct host1x;
 
-#ifdef CONFIG_DRM_TEGRA
-int host1x_drm_alloc(struct platform_device *pdev);
-#else
-static inline int host1x_drm_alloc(struct platform_device *pdev)
-{
-       return 0;
-}
-#endif
+int host1x_bus_init(void);
+void host1x_bus_exit(void);
 
-void host1x_set_drm_data(struct device *dev, void *data);
-void *host1x_get_drm_data(struct device *dev);
+int host1x_register(struct host1x *host1x);
+int host1x_unregister(struct host1x *host1x);
 
 #endif
index de72172d3b5fc2ac87955f3ef8b711890454434c..3995255b16c753731420cb8283992c17c33ddf2e 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/cacheflush.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
+#include <linux/host1x.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/kfifo.h>
@@ -30,7 +31,6 @@
 #include "channel.h"
 #include "dev.h"
 #include "debug.h"
-#include "host1x_bo.h"
 #include "job.h"
 
 /*
index 48723b8eea42da33e9854a0925bbeedfa391a37a..df767cf90d51e14c2647f42ef192a7ffcf19489b 100644 (file)
@@ -40,12 +40,6 @@ struct host1x_channel {
 /* channel list operations */
 int host1x_channel_list_init(struct host1x *host);
 
-struct host1x_channel *host1x_channel_request(struct device *dev);
-void host1x_channel_free(struct host1x_channel *channel);
-struct host1x_channel *host1x_channel_get(struct host1x_channel *channel);
-void host1x_channel_put(struct host1x_channel *channel);
-int host1x_job_submit(struct host1x_job *job);
-
 #define host1x_for_each_channel(host, channel)                         \
        list_for_each_entry(channel, &host->chlist.list, list)
 
index 471630299878a88bbf300a09a0600da2c1c062eb..12d63336b369c767072c39ca6378e5aa62527b05 100644 (file)
 #define CREATE_TRACE_POINTS
 #include <trace/events/host1x.h>
 
+#include "bus.h"
 #include "dev.h"
 #include "intr.h"
 #include "channel.h"
 #include "debug.h"
 #include "hw/host1x01.h"
-#include "host1x_client.h"
-
-void host1x_set_drm_data(struct device *dev, void *data)
-{
-       struct host1x *host1x = dev_get_drvdata(dev);
-       host1x->drm_data = data;
-}
-
-void *host1x_get_drm_data(struct device *dev)
-{
-       struct host1x *host1x = dev_get_drvdata(dev);
-       return host1x ? host1x->drm_data : NULL;
-}
+#include "hw/host1x02.h"
 
 void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r)
 {
@@ -79,7 +68,17 @@ static const struct host1x_info host1x01_info = {
        .sync_offset    = 0x3000,
 };
 
+static const struct host1x_info host1x02_info = {
+       .nb_channels = 9,
+       .nb_pts = 32,
+       .nb_mlocks = 16,
+       .nb_bases = 12,
+       .init = host1x02_init,
+       .sync_offset = 0x3000,
+};
+
 static struct of_device_id host1x_of_match[] = {
+       { .compatible = "nvidia,tegra114-host1x", .data = &host1x02_info, },
        { .compatible = "nvidia,tegra30-host1x", .data = &host1x01_info, },
        { .compatible = "nvidia,tegra20-host1x", .data = &host1x01_info, },
        { },
@@ -114,6 +113,9 @@ static int host1x_probe(struct platform_device *pdev)
        if (!host)
                return -ENOMEM;
 
+       mutex_init(&host->devices_lock);
+       INIT_LIST_HEAD(&host->devices);
+       INIT_LIST_HEAD(&host->list);
        host->dev = &pdev->dev;
        host->info = id->data;
 
@@ -163,19 +165,24 @@ 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);
        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 +191,45 @@ 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);
+       err = platform_driver_register(&tegra_host1x_driver);
        if (err < 0)
-               goto unregister_dc;
+               goto unregister_bus;
 
-       err = platform_driver_register(&tegra_gr2d_driver);
+       err = platform_driver_register(&tegra_mipi_driver);
        if (err < 0)
-               goto unregister_hdmi;
-#endif
+               goto unregister_host1x;
 
        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);
+unregister_bus:
+       host1x_bus_exit();
        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_mipi_driver);
        platform_driver_unregister(&tegra_host1x_driver);
+       host1x_bus_exit();
 }
 module_exit(tegra_host1x_exit);
 
index bed90a8131be590fef951c533cbf3622708184da..65c80dc4f6cbefad0af16c10f22018752bb0805c 100644 (file)
@@ -125,7 +125,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 +304,6 @@ 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;
+extern struct platform_driver tegra_mipi_driver;
 
 #endif
diff --git a/drivers/gpu/host1x/drm/gr2d.c b/drivers/gpu/host1x/drm/gr2d.c
deleted file mode 100644 (file)
index 27ffcf1..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/export.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-
-#include "channel.h"
-#include "drm.h"
-#include "gem.h"
-#include "job.h"
-#include "host1x.h"
-#include "host1x_bo.h"
-#include "host1x_client.h"
-#include "syncpt.h"
-
-struct gr2d {
-       struct host1x_client client;
-       struct clk *clk;
-       struct host1x_channel *channel;
-       unsigned long *addr_regs;
-};
-
-static inline struct gr2d *to_gr2d(struct host1x_client *client)
-{
-       return container_of(client, struct gr2d, client);
-}
-
-static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg);
-
-static int gr2d_client_init(struct host1x_client *client,
-                           struct drm_device *drm)
-{
-       return 0;
-}
-
-static int gr2d_client_exit(struct host1x_client *client)
-{
-       return 0;
-}
-
-static int gr2d_open_channel(struct host1x_client *client,
-                            struct host1x_drm_context *context)
-{
-       struct gr2d *gr2d = to_gr2d(client);
-
-       context->channel = host1x_channel_get(gr2d->channel);
-
-       if (!context->channel)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void gr2d_close_channel(struct host1x_drm_context *context)
-{
-       host1x_channel_put(context->channel);
-}
-
-static struct host1x_bo *host1x_bo_lookup(struct drm_device *drm,
-                                         struct drm_file *file,
-                                         u32 handle)
-{
-       struct drm_gem_object *gem;
-       struct tegra_bo *bo;
-
-       gem = drm_gem_object_lookup(drm, file, handle);
-       if (!gem)
-               return NULL;
-
-       mutex_lock(&drm->struct_mutex);
-       drm_gem_object_unreference(gem);
-       mutex_unlock(&drm->struct_mutex);
-
-       bo = to_tegra_bo(gem);
-       return &bo->base;
-}
-
-static int gr2d_submit(struct host1x_drm_context *context,
-                      struct drm_tegra_submit *args, struct drm_device *drm,
-                      struct drm_file *file)
-{
-       struct host1x_job *job;
-       unsigned int num_cmdbufs = args->num_cmdbufs;
-       unsigned int num_relocs = args->num_relocs;
-       unsigned int num_waitchks = args->num_waitchks;
-       struct drm_tegra_cmdbuf __user *cmdbufs =
-               (void * __user)(uintptr_t)args->cmdbufs;
-       struct drm_tegra_reloc __user *relocs =
-               (void * __user)(uintptr_t)args->relocs;
-       struct drm_tegra_waitchk __user *waitchks =
-               (void * __user)(uintptr_t)args->waitchks;
-       struct drm_tegra_syncpt syncpt;
-       int err;
-
-       /* We don't yet support other than one syncpt_incr struct per submit */
-       if (args->num_syncpts != 1)
-               return -EINVAL;
-
-       job = host1x_job_alloc(context->channel, args->num_cmdbufs,
-                              args->num_relocs, args->num_waitchks);
-       if (!job)
-               return -ENOMEM;
-
-       job->num_relocs = args->num_relocs;
-       job->num_waitchk = args->num_waitchks;
-       job->client = (u32)args->context;
-       job->class = context->client->class;
-       job->serialize = true;
-
-       while (num_cmdbufs) {
-               struct drm_tegra_cmdbuf cmdbuf;
-               struct host1x_bo *bo;
-
-               err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
-               if (err)
-                       goto fail;
-
-               bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
-               if (!bo) {
-                       err = -ENOENT;
-                       goto fail;
-               }
-
-               host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
-               num_cmdbufs--;
-               cmdbufs++;
-       }
-
-       err = copy_from_user(job->relocarray, relocs,
-                            sizeof(*relocs) * num_relocs);
-       if (err)
-               goto fail;
-
-       while (num_relocs--) {
-               struct host1x_reloc *reloc = &job->relocarray[num_relocs];
-               struct host1x_bo *cmdbuf, *target;
-
-               cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
-               target = host1x_bo_lookup(drm, file, (u32)reloc->target);
-
-               reloc->cmdbuf = cmdbuf;
-               reloc->target = target;
-
-               if (!reloc->target || !reloc->cmdbuf) {
-                       err = -ENOENT;
-                       goto fail;
-               }
-       }
-
-       err = copy_from_user(job->waitchk, waitchks,
-                            sizeof(*waitchks) * num_waitchks);
-       if (err)
-               goto fail;
-
-       err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
-                            sizeof(syncpt));
-       if (err)
-               goto fail;
-
-       job->syncpt_id = syncpt.id;
-       job->syncpt_incrs = syncpt.incrs;
-       job->timeout = 10000;
-       job->is_addr_reg = gr2d_is_addr_reg;
-
-       if (args->timeout && args->timeout < 10000)
-               job->timeout = args->timeout;
-
-       err = host1x_job_pin(job, context->client->dev);
-       if (err)
-               goto fail;
-
-       err = host1x_job_submit(job);
-       if (err)
-               goto fail_submit;
-
-       args->fence = job->syncpt_end;
-
-       host1x_job_put(job);
-       return 0;
-
-fail_submit:
-       host1x_job_unpin(job);
-fail:
-       host1x_job_put(job);
-       return err;
-}
-
-static struct host1x_client_ops gr2d_client_ops = {
-       .drm_init = gr2d_client_init,
-       .drm_exit = gr2d_client_exit,
-       .open_channel = gr2d_open_channel,
-       .close_channel = gr2d_close_channel,
-       .submit = gr2d_submit,
-};
-
-static void gr2d_init_addr_reg_map(struct device *dev, struct gr2d *gr2d)
-{
-       const u32 gr2d_addr_regs[] = {0x1a, 0x1b, 0x26, 0x2b, 0x2c, 0x2d, 0x31,
-                                     0x32, 0x48, 0x49, 0x4a, 0x4b, 0x4c};
-       unsigned long *bitmap;
-       int i;
-
-       bitmap = devm_kzalloc(dev, DIV_ROUND_UP(256, BITS_PER_BYTE),
-                             GFP_KERNEL);
-
-       for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); ++i) {
-               u32 reg = gr2d_addr_regs[i];
-               bitmap[BIT_WORD(reg)] |= BIT_MASK(reg);
-       }
-
-       gr2d->addr_regs = bitmap;
-}
-
-static int gr2d_is_addr_reg(struct device *dev, u32 class, u32 reg)
-{
-       struct gr2d *gr2d = dev_get_drvdata(dev);
-
-       switch (class) {
-       case HOST1X_CLASS_HOST1X:
-               return reg == 0x2b;
-       case HOST1X_CLASS_GR2D:
-       case HOST1X_CLASS_GR2D_SB:
-               reg &= 0xff;
-               if (gr2d->addr_regs[BIT_WORD(reg)] & BIT_MASK(reg))
-                       return 1;
-       default:
-               return 0;
-       }
-}
-
-static const struct of_device_id gr2d_match[] = {
-       { .compatible = "nvidia,tegra30-gr2d" },
-       { .compatible = "nvidia,tegra20-gr2d" },
-       { },
-};
-
-static int gr2d_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct host1x_drm *host1x = host1x_get_drm_data(dev->parent);
-       int err;
-       struct gr2d *gr2d = NULL;
-       struct host1x_syncpt **syncpts;
-
-       gr2d = devm_kzalloc(dev, sizeof(*gr2d), GFP_KERNEL);
-       if (!gr2d)
-               return -ENOMEM;
-
-       syncpts = devm_kzalloc(dev, sizeof(*syncpts), GFP_KERNEL);
-       if (!syncpts)
-               return -ENOMEM;
-
-       gr2d->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(gr2d->clk)) {
-               dev_err(dev, "cannot get clock\n");
-               return PTR_ERR(gr2d->clk);
-       }
-
-       err = clk_prepare_enable(gr2d->clk);
-       if (err) {
-               dev_err(dev, "cannot turn on clock\n");
-               return err;
-       }
-
-       gr2d->channel = host1x_channel_request(dev);
-       if (!gr2d->channel)
-               return -ENOMEM;
-
-       *syncpts = host1x_syncpt_request(dev, false);
-       if (!(*syncpts)) {
-               host1x_channel_free(gr2d->channel);
-               return -ENOMEM;
-       }
-
-       gr2d->client.ops = &gr2d_client_ops;
-       gr2d->client.dev = dev;
-       gr2d->client.class = HOST1X_CLASS_GR2D;
-       gr2d->client.syncpts = syncpts;
-       gr2d->client.num_syncpts = 1;
-
-       err = host1x_register_client(host1x, &gr2d->client);
-       if (err < 0) {
-               dev_err(dev, "failed to register host1x client: %d\n", err);
-               return err;
-       }
-
-       gr2d_init_addr_reg_map(dev, gr2d);
-
-       platform_set_drvdata(pdev, gr2d);
-
-       return 0;
-}
-
-static int __exit gr2d_remove(struct platform_device *pdev)
-{
-       struct host1x_drm *host1x = host1x_get_drm_data(pdev->dev.parent);
-       struct gr2d *gr2d = platform_get_drvdata(pdev);
-       unsigned int i;
-       int err;
-
-       err = host1x_unregister_client(host1x, &gr2d->client);
-       if (err < 0) {
-               dev_err(&pdev->dev, "failed to unregister client: %d\n", err);
-               return err;
-       }
-
-       for (i = 0; i < gr2d->client.num_syncpts; i++)
-               host1x_syncpt_free(gr2d->client.syncpts[i]);
-
-       host1x_channel_free(gr2d->channel);
-       clk_disable_unprepare(gr2d->clk);
-
-       return 0;
-}
-
-struct platform_driver tegra_gr2d_driver = {
-       .probe = gr2d_probe,
-       .remove = __exit_p(gr2d_remove),
-       .driver = {
-               .owner = THIS_MODULE,
-               .name = "gr2d",
-               .of_match_table = gr2d_match,
-       }
-};
diff --git a/drivers/gpu/host1x/host1x.h b/drivers/gpu/host1x/host1x.h
deleted file mode 100644 (file)
index a2bc1e6..0000000
+++ /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 (file)
index 4c1f10b..0000000
+++ /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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _HOST1X_BO_H
-#define _HOST1X_BO_H
-
-struct host1x_bo;
-
-struct host1x_bo_ops {
-       struct host1x_bo *(*get)(struct host1x_bo *bo);
-       void (*put)(struct host1x_bo *bo);
-       dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
-       void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
-       void *(*mmap)(struct host1x_bo *bo);
-       void (*munmap)(struct host1x_bo *bo, void *addr);
-       void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
-       void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
-};
-
-struct host1x_bo {
-       const struct host1x_bo_ops *ops;
-};
-
-static inline void host1x_bo_init(struct host1x_bo *bo,
-                                 const struct host1x_bo_ops *ops)
-{
-       bo->ops = ops;
-}
-
-static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
-{
-       return bo->ops->get(bo);
-}
-
-static inline void host1x_bo_put(struct host1x_bo *bo)
-{
-       bo->ops->put(bo);
-}
-
-static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
-                                      struct sg_table **sgt)
-{
-       return bo->ops->pin(bo, sgt);
-}
-
-static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
-{
-       bo->ops->unpin(bo, sgt);
-}
-
-static inline void *host1x_bo_mmap(struct host1x_bo *bo)
-{
-       return bo->ops->mmap(bo);
-}
-
-static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
-{
-       bo->ops->munmap(bo, addr);
-}
-
-static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
-{
-       return bo->ops->kmap(bo, pagenum);
-}
-
-static inline void host1x_bo_kunmap(struct host1x_bo *bo,
-                                   unsigned int pagenum, void *addr)
-{
-       bo->ops->kunmap(bo, pagenum, addr);
-}
-
-#endif
diff --git a/drivers/gpu/host1x/hw/Makefile b/drivers/gpu/host1x/hw/Makefile
deleted file mode 100644 (file)
index 9b50863..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-ccflags-y = -Idrivers/gpu/host1x
-
-host1x-hw-objs  = \
-       host1x01.o
-
-obj-$(CONFIG_TEGRA_HOST1X) += host1x-hw.o
index 2ee4ad55c4dba344cbb28d72d8febecd7f0181ee..37e2a63241a9d6150b1554896fde15ac0294c157 100644 (file)
 #include <linux/scatterlist.h>
 #include <linux/dma-mapping.h>
 
-#include "cdma.h"
-#include "channel.h"
-#include "dev.h"
-#include "debug.h"
+#include "../cdma.h"
+#include "../channel.h"
+#include "../dev.h"
+#include "../debug.h"
 
 /*
  * Put the restart at the end of pushbuffer memor
index ee199623e36570719ad35fbe3de0d41b00744843..3be0cd296d3a4147101d1deb51ec19becde980c9 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/host1x.h>
 #include <linux/slab.h>
+
 #include <trace/events/host1x.h>
 
-#include "host1x.h"
-#include "host1x_bo.h"
-#include "channel.h"
-#include "dev.h"
-#include "intr.h"
-#include "job.h"
+#include "../channel.h"
+#include "../dev.h"
+#include "../intr.h"
+#include "../job.h"
 
 #define HOST1X_CHANNEL_SIZE 16384
 #define TRACE_MAX_LENGTH 128U
index 334c038052f582cac72c1818d30b3387aa3ae060..640c75ca5a8bbc465dcc9f1350d4f5efdd928d5d 100644 (file)
  *
  */
 
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/mm.h>
-#include <linux/scatterlist.h>
-
-#include <linux/io.h>
-
-#include "dev.h"
-#include "debug.h"
-#include "cdma.h"
-#include "channel.h"
-#include "host1x_bo.h"
+#include "../dev.h"
+#include "../debug.h"
+#include "../cdma.h"
+#include "../channel.h"
 
 #define HOST1X_DEBUG_MAX_PAGE_OFFSET 102400
 
index a14e91cd1e58eee8e5f52db20c672076544a0439..859b73beb4d0c326fcaf24ae1086ff2bc876df09 100644 (file)
  */
 
 /* 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 (file)
index 0000000..e98caca
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Host1x init for Tegra114 SoCs
+ *
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* include hw specification */
+#include "host1x01.h"
+#include "host1x01_hardware.h"
+
+/* include code */
+#include "cdma_hw.c"
+#include "channel_hw.c"
+#include "debug_hw.c"
+#include "intr_hw.c"
+#include "syncpt_hw.c"
+
+#include "../dev.h"
+
+int host1x02_init(struct host1x *host)
+{
+       host->channel_op = &host1x_channel_ops;
+       host->cdma_op = &host1x_cdma_ops;
+       host->cdma_pb_op = &host1x_pushbuffer_ops;
+       host->syncpt_op = &host1x_syncpt_ops;
+       host->intr_op = &host1x_intr_ops;
+       host->debug_op = &host1x_debug_ops;
+
+       return 0;
+}
diff --git a/drivers/gpu/host1x/hw/host1x02.h b/drivers/gpu/host1x/hw/host1x02.h
new file mode 100644 (file)
index 0000000..f748660
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Host1x init for Tegra114 SoCs
+ *
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HOST1X_HOST1X02_H
+#define HOST1X_HOST1X02_H
+
+struct host1x;
+
+int host1x02_init(struct host1x *host);
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x02_channel.h b/drivers/gpu/host1x/hw/hw_host1x02_channel.h
new file mode 100644 (file)
index 0000000..e490bcd
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ /*
+  * Function naming determines intended use:
+  *
+  *     <x>_r(void) : Returns the offset for register <x>.
+  *
+  *     <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+  *
+  *     <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+  *
+  *     <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+  *         and masked to place it at field <y> of register <x>.  This value
+  *         can be |'d with others to produce a full register value for
+  *         register <x>.
+  *
+  *     <x>_<y>_m(void) : Returns a mask for field <y> of register <x>.  This
+  *         value can be ~'d and then &'d to clear the value of field <y> for
+  *         register <x>.
+  *
+  *     <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+  *         to place it at field <y> of register <x>.  This value can be |'d
+  *         with others to produce a full register value for <x>.
+  *
+  *     <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+  *         <x> value 'r' after being shifted to place its LSB at bit 0.
+  *         This value is suitable for direct comparison with other unshifted
+  *         values appropriate for use in field <y> of register <x>.
+  *
+  *     <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+  *         field <y> of register <x>.  This value is suitable for direct
+  *         comparison with unshifted values appropriate for use in field <y>
+  *         of register <x>.
+  */
+
+#ifndef HOST1X_HW_HOST1X02_CHANNEL_H
+#define HOST1X_HW_HOST1X02_CHANNEL_H
+
+static inline u32 host1x_channel_fifostat_r(void)
+{
+       return 0x0;
+}
+#define HOST1X_CHANNEL_FIFOSTAT \
+       host1x_channel_fifostat_r()
+static inline u32 host1x_channel_fifostat_cfempty_v(u32 r)
+{
+       return (r >> 11) & 0x1;
+}
+#define HOST1X_CHANNEL_FIFOSTAT_CFEMPTY_V(r) \
+       host1x_channel_fifostat_cfempty_v(r)
+static inline u32 host1x_channel_dmastart_r(void)
+{
+       return 0x14;
+}
+#define HOST1X_CHANNEL_DMASTART \
+       host1x_channel_dmastart_r()
+static inline u32 host1x_channel_dmaput_r(void)
+{
+       return 0x18;
+}
+#define HOST1X_CHANNEL_DMAPUT \
+       host1x_channel_dmaput_r()
+static inline u32 host1x_channel_dmaget_r(void)
+{
+       return 0x1c;
+}
+#define HOST1X_CHANNEL_DMAGET \
+       host1x_channel_dmaget_r()
+static inline u32 host1x_channel_dmaend_r(void)
+{
+       return 0x20;
+}
+#define HOST1X_CHANNEL_DMAEND \
+       host1x_channel_dmaend_r()
+static inline u32 host1x_channel_dmactrl_r(void)
+{
+       return 0x24;
+}
+#define HOST1X_CHANNEL_DMACTRL \
+       host1x_channel_dmactrl_r()
+static inline u32 host1x_channel_dmactrl_dmastop(void)
+{
+       return 1 << 0;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMASTOP \
+       host1x_channel_dmactrl_dmastop()
+static inline u32 host1x_channel_dmactrl_dmastop_v(u32 r)
+{
+       return (r >> 0) & 0x1;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMASTOP_V(r) \
+       host1x_channel_dmactrl_dmastop_v(r)
+static inline u32 host1x_channel_dmactrl_dmagetrst(void)
+{
+       return 1 << 1;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMAGETRST \
+       host1x_channel_dmactrl_dmagetrst()
+static inline u32 host1x_channel_dmactrl_dmainitget(void)
+{
+       return 1 << 2;
+}
+#define HOST1X_CHANNEL_DMACTRL_DMAINITGET \
+       host1x_channel_dmactrl_dmainitget()
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x02_sync.h b/drivers/gpu/host1x/hw/hw_host1x02_sync.h
new file mode 100644 (file)
index 0000000..4495401
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ /*
+  * Function naming determines intended use:
+  *
+  *     <x>_r(void) : Returns the offset for register <x>.
+  *
+  *     <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+  *
+  *     <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+  *
+  *     <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+  *         and masked to place it at field <y> of register <x>.  This value
+  *         can be |'d with others to produce a full register value for
+  *         register <x>.
+  *
+  *     <x>_<y>_m(void) : Returns a mask for field <y> of register <x>.  This
+  *         value can be ~'d and then &'d to clear the value of field <y> for
+  *         register <x>.
+  *
+  *     <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+  *         to place it at field <y> of register <x>.  This value can be |'d
+  *         with others to produce a full register value for <x>.
+  *
+  *     <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+  *         <x> value 'r' after being shifted to place its LSB at bit 0.
+  *         This value is suitable for direct comparison with other unshifted
+  *         values appropriate for use in field <y> of register <x>.
+  *
+  *     <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+  *         field <y> of register <x>.  This value is suitable for direct
+  *         comparison with unshifted values appropriate for use in field <y>
+  *         of register <x>.
+  */
+
+#ifndef HOST1X_HW_HOST1X02_SYNC_H
+#define HOST1X_HW_HOST1X02_SYNC_H
+
+#define REGISTER_STRIDE        4
+
+static inline u32 host1x_sync_syncpt_r(unsigned int id)
+{
+       return 0x400 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT(id) \
+       host1x_sync_syncpt_r(id)
+static inline u32 host1x_sync_syncpt_thresh_cpu0_int_status_r(unsigned int id)
+{
+       return 0x40 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id) \
+       host1x_sync_syncpt_thresh_cpu0_int_status_r(id)
+static inline u32 host1x_sync_syncpt_thresh_int_disable_r(unsigned int id)
+{
+       return 0x60 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id) \
+       host1x_sync_syncpt_thresh_int_disable_r(id)
+static inline u32 host1x_sync_syncpt_thresh_int_enable_cpu0_r(unsigned int id)
+{
+       return 0x68 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id) \
+       host1x_sync_syncpt_thresh_int_enable_cpu0_r(id)
+static inline u32 host1x_sync_cf_setup_r(unsigned int channel)
+{
+       return 0x80 + channel * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_CF_SETUP(channel) \
+       host1x_sync_cf_setup_r(channel)
+static inline u32 host1x_sync_cf_setup_base_v(u32 r)
+{
+       return (r >> 0) & 0x3ff;
+}
+#define HOST1X_SYNC_CF_SETUP_BASE_V(r) \
+       host1x_sync_cf_setup_base_v(r)
+static inline u32 host1x_sync_cf_setup_limit_v(u32 r)
+{
+       return (r >> 16) & 0x3ff;
+}
+#define HOST1X_SYNC_CF_SETUP_LIMIT_V(r) \
+       host1x_sync_cf_setup_limit_v(r)
+static inline u32 host1x_sync_cmdproc_stop_r(void)
+{
+       return 0xac;
+}
+#define HOST1X_SYNC_CMDPROC_STOP \
+       host1x_sync_cmdproc_stop_r()
+static inline u32 host1x_sync_ch_teardown_r(void)
+{
+       return 0xb0;
+}
+#define HOST1X_SYNC_CH_TEARDOWN \
+       host1x_sync_ch_teardown_r()
+static inline u32 host1x_sync_usec_clk_r(void)
+{
+       return 0x1a4;
+}
+#define HOST1X_SYNC_USEC_CLK \
+       host1x_sync_usec_clk_r()
+static inline u32 host1x_sync_ctxsw_timeout_cfg_r(void)
+{
+       return 0x1a8;
+}
+#define HOST1X_SYNC_CTXSW_TIMEOUT_CFG \
+       host1x_sync_ctxsw_timeout_cfg_r()
+static inline u32 host1x_sync_ip_busy_timeout_r(void)
+{
+       return 0x1bc;
+}
+#define HOST1X_SYNC_IP_BUSY_TIMEOUT \
+       host1x_sync_ip_busy_timeout_r()
+static inline u32 host1x_sync_mlock_owner_r(unsigned int id)
+{
+       return 0x340 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_MLOCK_OWNER(id) \
+       host1x_sync_mlock_owner_r(id)
+static inline u32 host1x_sync_mlock_owner_chid_f(u32 v)
+{
+       return (v & 0xf) << 8;
+}
+#define HOST1X_SYNC_MLOCK_OWNER_CHID_F(v) \
+       host1x_sync_mlock_owner_chid_f(v)
+static inline u32 host1x_sync_mlock_owner_cpu_owns_v(u32 r)
+{
+       return (r >> 1) & 0x1;
+}
+#define HOST1X_SYNC_MLOCK_OWNER_CPU_OWNS_V(r) \
+       host1x_sync_mlock_owner_cpu_owns_v(r)
+static inline u32 host1x_sync_mlock_owner_ch_owns_v(u32 r)
+{
+       return (r >> 0) & 0x1;
+}
+#define HOST1X_SYNC_MLOCK_OWNER_CH_OWNS_V(r) \
+       host1x_sync_mlock_owner_ch_owns_v(r)
+static inline u32 host1x_sync_syncpt_int_thresh_r(unsigned int id)
+{
+       return 0x500 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_INT_THRESH(id) \
+       host1x_sync_syncpt_int_thresh_r(id)
+static inline u32 host1x_sync_syncpt_base_r(unsigned int id)
+{
+       return 0x600 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_BASE(id) \
+       host1x_sync_syncpt_base_r(id)
+static inline u32 host1x_sync_syncpt_cpu_incr_r(unsigned int id)
+{
+       return 0x700 + id * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_SYNCPT_CPU_INCR(id) \
+       host1x_sync_syncpt_cpu_incr_r(id)
+static inline u32 host1x_sync_cbread_r(unsigned int channel)
+{
+       return 0x720 + channel * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_CBREAD(channel) \
+       host1x_sync_cbread_r(channel)
+static inline u32 host1x_sync_cfpeek_ctrl_r(void)
+{
+       return 0x74c;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL \
+       host1x_sync_cfpeek_ctrl_r()
+static inline u32 host1x_sync_cfpeek_ctrl_addr_f(u32 v)
+{
+       return (v & 0x3ff) << 0;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL_ADDR_F(v) \
+       host1x_sync_cfpeek_ctrl_addr_f(v)
+static inline u32 host1x_sync_cfpeek_ctrl_channr_f(u32 v)
+{
+       return (v & 0xf) << 16;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL_CHANNR_F(v) \
+       host1x_sync_cfpeek_ctrl_channr_f(v)
+static inline u32 host1x_sync_cfpeek_ctrl_ena_f(u32 v)
+{
+       return (v & 0x1) << 31;
+}
+#define HOST1X_SYNC_CFPEEK_CTRL_ENA_F(v) \
+       host1x_sync_cfpeek_ctrl_ena_f(v)
+static inline u32 host1x_sync_cfpeek_read_r(void)
+{
+       return 0x750;
+}
+#define HOST1X_SYNC_CFPEEK_READ \
+       host1x_sync_cfpeek_read_r()
+static inline u32 host1x_sync_cfpeek_ptrs_r(void)
+{
+       return 0x754;
+}
+#define HOST1X_SYNC_CFPEEK_PTRS \
+       host1x_sync_cfpeek_ptrs_r()
+static inline u32 host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(u32 r)
+{
+       return (r >> 0) & 0x3ff;
+}
+#define HOST1X_SYNC_CFPEEK_PTRS_CF_RD_PTR_V(r) \
+       host1x_sync_cfpeek_ptrs_cf_rd_ptr_v(r)
+static inline u32 host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(u32 r)
+{
+       return (r >> 16) & 0x3ff;
+}
+#define HOST1X_SYNC_CFPEEK_PTRS_CF_WR_PTR_V(r) \
+       host1x_sync_cfpeek_ptrs_cf_wr_ptr_v(r)
+static inline u32 host1x_sync_cbstat_r(unsigned int channel)
+{
+       return 0x758 + channel * REGISTER_STRIDE;
+}
+#define HOST1X_SYNC_CBSTAT(channel) \
+       host1x_sync_cbstat_r(channel)
+static inline u32 host1x_sync_cbstat_cboffset_v(u32 r)
+{
+       return (r >> 0) & 0xffff;
+}
+#define HOST1X_SYNC_CBSTAT_CBOFFSET_V(r) \
+       host1x_sync_cbstat_cboffset_v(r)
+static inline u32 host1x_sync_cbstat_cbclass_v(u32 r)
+{
+       return (r >> 16) & 0x3ff;
+}
+#define HOST1X_SYNC_CBSTAT_CBCLASS_V(r) \
+       host1x_sync_cbstat_cbclass_v(r)
+
+#endif
diff --git a/drivers/gpu/host1x/hw/hw_host1x02_uclass.h b/drivers/gpu/host1x/hw/hw_host1x02_uclass.h
new file mode 100644 (file)
index 0000000..a3b3c98
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2013 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ /*
+  * Function naming determines intended use:
+  *
+  *     <x>_r(void) : Returns the offset for register <x>.
+  *
+  *     <x>_w(void) : Returns the word offset for word (4 byte) element <x>.
+  *
+  *     <x>_<y>_s(void) : Returns size of field <y> of register <x> in bits.
+  *
+  *     <x>_<y>_f(u32 v) : Returns a value based on 'v' which has been shifted
+  *         and masked to place it at field <y> of register <x>.  This value
+  *         can be |'d with others to produce a full register value for
+  *         register <x>.
+  *
+  *     <x>_<y>_m(void) : Returns a mask for field <y> of register <x>.  This
+  *         value can be ~'d and then &'d to clear the value of field <y> for
+  *         register <x>.
+  *
+  *     <x>_<y>_<z>_f(void) : Returns the constant value <z> after being shifted
+  *         to place it at field <y> of register <x>.  This value can be |'d
+  *         with others to produce a full register value for <x>.
+  *
+  *     <x>_<y>_v(u32 r) : Returns the value of field <y> from a full register
+  *         <x> value 'r' after being shifted to place its LSB at bit 0.
+  *         This value is suitable for direct comparison with other unshifted
+  *         values appropriate for use in field <y> of register <x>.
+  *
+  *     <x>_<y>_<z>_v(void) : Returns the constant value for <z> defined for
+  *         field <y> of register <x>.  This value is suitable for direct
+  *         comparison with unshifted values appropriate for use in field <y>
+  *         of register <x>.
+  */
+
+#ifndef HOST1X_HW_HOST1X02_UCLASS_H
+#define HOST1X_HW_HOST1X02_UCLASS_H
+
+static inline u32 host1x_uclass_incr_syncpt_r(void)
+{
+       return 0x0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT \
+       host1x_uclass_incr_syncpt_r()
+static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
+{
+       return (v & 0xff) << 8;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_COND_F(v) \
+       host1x_uclass_incr_syncpt_cond_f(v)
+static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
+{
+       return (v & 0xff) << 0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
+       host1x_uclass_incr_syncpt_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_r(void)
+{
+       return 0x8;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT \
+       host1x_uclass_wait_syncpt_r()
+static inline u32 host1x_uclass_wait_syncpt_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_INDX_F(v) \
+       host1x_uclass_wait_syncpt_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_thresh_f(u32 v)
+{
+       return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_THRESH_F(v) \
+       host1x_uclass_wait_syncpt_thresh_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_r(void)
+{
+       return 0x9;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE \
+       host1x_uclass_wait_syncpt_base_r()
+static inline u32 host1x_uclass_wait_syncpt_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_INDX_F(v) \
+       host1x_uclass_wait_syncpt_base_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 16;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_BASE_INDX_F(v) \
+       host1x_uclass_wait_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_wait_syncpt_base_offset_f(u32 v)
+{
+       return (v & 0xffff) << 0;
+}
+#define HOST1X_UCLASS_WAIT_SYNCPT_BASE_OFFSET_F(v) \
+       host1x_uclass_wait_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_BASE_INDX_F(v) \
+       host1x_uclass_load_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_load_syncpt_base_value_f(u32 v)
+{
+       return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_LOAD_SYNCPT_BASE_VALUE_F(v) \
+       host1x_uclass_load_syncpt_base_value_f(v)
+static inline u32 host1x_uclass_incr_syncpt_base_base_indx_f(u32 v)
+{
+       return (v & 0xff) << 24;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_BASE_BASE_INDX_F(v) \
+       host1x_uclass_incr_syncpt_base_base_indx_f(v)
+static inline u32 host1x_uclass_incr_syncpt_base_offset_f(u32 v)
+{
+       return (v & 0xffffff) << 0;
+}
+#define HOST1X_UCLASS_INCR_SYNCPT_BASE_OFFSET_F(v) \
+       host1x_uclass_incr_syncpt_base_offset_f(v)
+static inline u32 host1x_uclass_indoff_r(void)
+{
+       return 0x2d;
+}
+#define HOST1X_UCLASS_INDOFF \
+       host1x_uclass_indoff_r()
+static inline u32 host1x_uclass_indoff_indbe_f(u32 v)
+{
+       return (v & 0xf) << 28;
+}
+#define HOST1X_UCLASS_INDOFF_INDBE_F(v) \
+       host1x_uclass_indoff_indbe_f(v)
+static inline u32 host1x_uclass_indoff_autoinc_f(u32 v)
+{
+       return (v & 0x1) << 27;
+}
+#define HOST1X_UCLASS_INDOFF_AUTOINC_F(v) \
+       host1x_uclass_indoff_autoinc_f(v)
+static inline u32 host1x_uclass_indoff_indmodid_f(u32 v)
+{
+       return (v & 0xff) << 18;
+}
+#define HOST1X_UCLASS_INDOFF_INDMODID_F(v) \
+       host1x_uclass_indoff_indmodid_f(v)
+static inline u32 host1x_uclass_indoff_indroffset_f(u32 v)
+{
+       return (v & 0xffff) << 2;
+}
+#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
+       host1x_uclass_indoff_indroffset_f(v)
+static inline u32 host1x_uclass_indoff_rwn_read_v(void)
+{
+       return 1;
+}
+#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
+       host1x_uclass_indoff_indroffset_f(v)
+
+#endif
index b592eef1efcb9babfe0355d418f4d276682a0841..b26dcc83bc1b373400d72c46aad21d04fb97f52f 100644 (file)
@@ -22,8 +22,8 @@
 #include <linux/io.h>
 #include <asm/mach/irq.h>
 
-#include "intr.h"
-#include "dev.h"
+#include "../intr.h"
+#include "../dev.h"
 
 /*
  * Sync point threshold interrupt service function
index 0cf6095d33678f51da127f4feac313f9422bc9e8..56e85395ac24112faf17b4b06a1aa950e671df0b 100644 (file)
@@ -18,8 +18,8 @@
 
 #include <linux/io.h>
 
-#include "dev.h"
-#include "syncpt.h"
+#include "../dev.h"
+#include "../syncpt.h"
 
 /*
  * Write the current syncpoint value back to hw.
index c4e1050f2252679e448e58e1c2b3edffffd08073..de5ec333ce1adc1974001d01bad56d2544d472d3 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
+#include <linux/host1x.h>
 #include <linux/kref.h>
 #include <linux/module.h>
 #include <linux/scatterlist.h>
@@ -27,7 +28,6 @@
 
 #include "channel.h"
 #include "dev.h"
-#include "host1x_bo.h"
 #include "job.h"
 #include "syncpt.h"
 
@@ -264,7 +264,7 @@ static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf)
 }
 
 static bool check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf,
-                      unsigned int offset)
+                       unsigned int offset)
 {
        offset *= sizeof(u32);
 
@@ -281,7 +281,7 @@ struct host1x_firewall {
        unsigned int num_relocs;
        struct host1x_reloc *reloc;
 
-       struct host1x_bo *cmdbuf_id;
+       struct host1x_bo *cmdbuf;
        unsigned int offset;
 
        u32 words;
@@ -291,25 +291,37 @@ struct host1x_firewall {
        u32 count;
 };
 
+static int check_register(struct host1x_firewall *fw, unsigned long offset)
+{
+       if (fw->job->is_addr_reg(fw->dev, fw->class, offset)) {
+               if (!fw->num_relocs)
+                       return -EINVAL;
+
+               if (!check_reloc(fw->reloc, fw->cmdbuf, fw->offset))
+                       return -EINVAL;
+
+               fw->num_relocs--;
+               fw->reloc++;
+       }
+
+       return 0;
+}
+
 static int check_mask(struct host1x_firewall *fw)
 {
        u32 mask = fw->mask;
        u32 reg = fw->reg;
+       int ret;
 
        while (mask) {
                if (fw->words == 0)
                        return -EINVAL;
 
                if (mask & 1) {
-                       if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
-                               if (!fw->num_relocs)
-                                       return -EINVAL;
-                               if (!check_reloc(fw->reloc, fw->cmdbuf_id,
-                                                fw->offset))
-                                       return -EINVAL;
-                               fw->reloc++;
-                               fw->num_relocs--;
-                       }
+                       ret = check_register(fw, reg);
+                       if (ret < 0)
+                               return ret;
+
                        fw->words--;
                        fw->offset++;
                }
@@ -324,19 +336,16 @@ static int check_incr(struct host1x_firewall *fw)
 {
        u32 count = fw->count;
        u32 reg = fw->reg;
+       int ret;
 
        while (count) {
                if (fw->words == 0)
                        return -EINVAL;
 
-               if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) {
-                       if (!fw->num_relocs)
-                               return -EINVAL;
-                       if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
-                               return -EINVAL;
-                       fw->reloc++;
-                       fw->num_relocs--;
-               }
+               ret = check_register(fw, reg);
+               if (ret < 0)
+                       return ret;
+
                reg++;
                fw->words--;
                fw->offset++;
@@ -348,21 +357,17 @@ static int check_incr(struct host1x_firewall *fw)
 
 static int check_nonincr(struct host1x_firewall *fw)
 {
-       int is_addr_reg = fw->job->is_addr_reg(fw->dev, fw->class, fw->reg);
        u32 count = fw->count;
+       int ret;
 
        while (count) {
                if (fw->words == 0)
                        return -EINVAL;
 
-               if (is_addr_reg) {
-                       if (!fw->num_relocs)
-                               return -EINVAL;
-                       if (!check_reloc(fw->reloc, fw->cmdbuf_id, fw->offset))
-                               return -EINVAL;
-                       fw->reloc++;
-                       fw->num_relocs--;
-               }
+               ret = check_register(fw, fw->reg);
+               if (ret < 0)
+                       return ret;
+
                fw->words--;
                fw->offset++;
                count--;
@@ -381,7 +386,7 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
                return 0;
 
        fw->words = g->words;
-       fw->cmdbuf_id = g->bo;
+       fw->cmdbuf = g->bo;
        fw->offset = 0;
 
        while (fw->words && !err) {
@@ -436,10 +441,6 @@ static int validate(struct host1x_firewall *fw, struct host1x_job_gather *g)
                }
        }
 
-       /* No relocs should remain at this point */
-       if (fw->num_relocs)
-               err = -EINVAL;
-
 out:
        return err;
 }
@@ -493,6 +494,10 @@ static inline int copy_gathers(struct host1x_job *job, struct device *dev)
                offset += g->words * sizeof(u32);
        }
 
+       /* No relocs should remain at this point */
+       if (fw.num_relocs)
+               return -EINVAL;
+
        return 0;
 }
 
index fba45f20458e2d1a4b4e83a795c2d948eaa5ea08..33a697d6dcefea290c3bac2981e2399e27f99a14 100644 (file)
@@ -34,15 +34,6 @@ struct host1x_cmdbuf {
        u32 pad;
 };
 
-struct host1x_reloc {
-       struct host1x_bo *cmdbuf;
-       u32 cmdbuf_offset;
-       struct host1x_bo *target;
-       u32 target_offset;
-       u32 shift;
-       u32 pad;
-};
-
 struct host1x_waitchk {
        struct host1x_bo *bo;
        u32 offset;
@@ -55,105 +46,6 @@ struct host1x_job_unpin_data {
        struct sg_table *sgt;
 };
 
-/*
- * Each submit is tracked as a host1x_job.
- */
-struct host1x_job {
-       /* When refcount goes to zero, job can be freed */
-       struct kref ref;
-
-       /* List entry */
-       struct list_head list;
-
-       /* Channel where job is submitted to */
-       struct host1x_channel *channel;
-
-       u32 client;
-
-       /* Gathers and their memory */
-       struct host1x_job_gather *gathers;
-       unsigned int num_gathers;
-
-       /* Wait checks to be processed at submit time */
-       struct host1x_waitchk *waitchk;
-       unsigned int num_waitchk;
-       u32 waitchk_mask;
-
-       /* Array of handles to be pinned & unpinned */
-       struct host1x_reloc *relocarray;
-       unsigned int num_relocs;
-       struct host1x_job_unpin_data *unpins;
-       unsigned int num_unpins;
-
-       dma_addr_t *addr_phys;
-       dma_addr_t *gather_addr_phys;
-       dma_addr_t *reloc_addr_phys;
-
-       /* Sync point id, number of increments and end related to the submit */
-       u32 syncpt_id;
-       u32 syncpt_incrs;
-       u32 syncpt_end;
-
-       /* Maximum time to wait for this job */
-       unsigned int timeout;
-
-       /* Index and number of slots used in the push buffer */
-       unsigned int first_get;
-       unsigned int num_slots;
-
-       /* Copy of gathers */
-       size_t gather_copy_size;
-       dma_addr_t gather_copy;
-       u8 *gather_copy_mapped;
-
-       /* Check if register is marked as an address reg */
-       int (*is_addr_reg)(struct device *dev, u32 reg, u32 class);
-
-       /* Request a SETCLASS to this class */
-       u32 class;
-
-       /* Add a channel wait for previous ops to complete */
-       bool serialize;
-};
-/*
- * Allocate memory for a job. Just enough memory will be allocated to
- * accomodate the submit.
- */
-struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
-                                   u32 num_cmdbufs, u32 num_relocs,
-                                   u32 num_waitchks);
-
-/*
- * Add a gather to a job.
- */
-void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *mem_id,
-                          u32 words, u32 offset);
-
-/*
- * Increment reference going to host1x_job.
- */
-struct host1x_job *host1x_job_get(struct host1x_job *job);
-
-/*
- * Decrement reference job, free if goes to zero.
- */
-void host1x_job_put(struct host1x_job *job);
-
-/*
- * Pin memory related to job. This handles relocation of addresses to the
- * host1x address space. Handles both the gather memory and any other memory
- * referred to from the gather buffers.
- *
- * Handles also patching out host waits that would wait for an expired sync
- * point value.
- */
-int host1x_job_pin(struct host1x_job *job, struct device *dev);
-
-/*
- * Unpin memory related to job.
- */
-void host1x_job_unpin(struct host1x_job *job);
-
 /*
  * Dump contents of job to debug output.
  */
diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
new file mode 100644 (file)
index 0000000..8a54613
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define MIPI_CAL_CTRL                  0x00
+#define MIPI_CAL_CTRL_START            (1 << 0)
+
+#define MIPI_CAL_AUTOCAL_CTRL          0x01
+
+#define MIPI_CAL_STATUS                        0x02
+#define MIPI_CAL_STATUS_DONE           (1 << 16)
+#define MIPI_CAL_STATUS_ACTIVE         (1 <<  0)
+
+#define MIPI_CAL_CONFIG_CSIA           0x05
+#define MIPI_CAL_CONFIG_CSIB           0x06
+#define MIPI_CAL_CONFIG_CSIC           0x07
+#define MIPI_CAL_CONFIG_CSID           0x08
+#define MIPI_CAL_CONFIG_CSIE           0x09
+#define MIPI_CAL_CONFIG_DSIA           0x0e
+#define MIPI_CAL_CONFIG_DSIB           0x0f
+#define MIPI_CAL_CONFIG_DSIC           0x10
+#define MIPI_CAL_CONFIG_DSID           0x11
+
+#define MIPI_CAL_CONFIG_SELECT         (1 << 21)
+#define MIPI_CAL_CONFIG_HSPDOS(x)      (((x) & 0x1f) << 16)
+#define MIPI_CAL_CONFIG_HSPUOS(x)      (((x) & 0x1f) <<  8)
+#define MIPI_CAL_CONFIG_TERMOS(x)      (((x) & 0x1f) <<  0)
+
+#define MIPI_CAL_BIAS_PAD_CFG0         0x16
+#define MIPI_CAL_BIAS_PAD_PDVCLAMP     (1 << 1)
+#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
+
+#define MIPI_CAL_BIAS_PAD_CFG1         0x17
+
+#define MIPI_CAL_BIAS_PAD_CFG2         0x18
+#define MIPI_CAL_BIAS_PAD_PDVREG       (1 << 1)
+
+static const struct module {
+       unsigned long reg;
+} modules[] = {
+       { .reg = MIPI_CAL_CONFIG_CSIA },
+       { .reg = MIPI_CAL_CONFIG_CSIB },
+       { .reg = MIPI_CAL_CONFIG_CSIC },
+       { .reg = MIPI_CAL_CONFIG_CSID },
+       { .reg = MIPI_CAL_CONFIG_CSIE },
+       { .reg = MIPI_CAL_CONFIG_DSIA },
+       { .reg = MIPI_CAL_CONFIG_DSIB },
+       { .reg = MIPI_CAL_CONFIG_DSIC },
+       { .reg = MIPI_CAL_CONFIG_DSID },
+};
+
+struct tegra_mipi {
+       void __iomem *regs;
+       struct mutex lock;
+       struct clk *clk;
+};
+
+static inline unsigned long tegra_mipi_readl(struct tegra_mipi *mipi,
+                                            unsigned long reg)
+{
+       return readl(mipi->regs + (reg << 2));
+}
+
+static inline void tegra_mipi_writel(struct tegra_mipi *mipi,
+                                    unsigned long value, unsigned long reg)
+{
+       writel(value, mipi->regs + (reg << 2));
+}
+
+int tegra_mipi_calibrate(struct device *device)
+{
+       struct platform_device *pdev;
+       unsigned int timeout = 20, i;
+       struct of_phandle_args args;
+       unsigned long value, pads;
+       struct tegra_mipi *mipi;
+       int err;
+
+       err = of_parse_phandle_with_args(device->of_node, "calibrate",
+                                        "#calibrate-cells", 0, &args);
+       if (err < 0)
+               return err;
+
+       pdev = of_find_device_by_node(args.np);
+       if (!pdev) {
+               of_node_put(args.np);
+               return -ENODEV;
+       }
+
+       of_node_put(args.np);
+       pads = args.args[0];
+
+       mipi = platform_get_drvdata(pdev);
+       if (!mipi) {
+               err = -ENODEV;
+               goto out;
+       }
+
+       err = clk_enable(mipi->clk);
+       if (err < 0)
+               goto out;
+
+       mutex_lock(&mipi->lock);
+
+       value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
+       value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
+       value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
+       tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
+
+       value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
+       value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
+       tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
+
+       for (i = 0; i < ARRAY_SIZE(modules); i++) {
+               if (pads & BIT(i))
+                       value = MIPI_CAL_CONFIG_SELECT |
+                               MIPI_CAL_CONFIG_HSPDOS(0) |
+                               MIPI_CAL_CONFIG_HSPUOS(4) |
+                               MIPI_CAL_CONFIG_TERMOS(5);
+               else
+                       value = 0;
+
+               tegra_mipi_writel(mipi, value, modules[i].reg);
+       }
+
+       tegra_mipi_writel(mipi, MIPI_CAL_CTRL_START, MIPI_CAL_CTRL);
+
+       while (timeout) {
+               value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
+               if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
+                   (value & MIPI_CAL_STATUS_DONE) != 0)
+                       break;
+
+               usleep_range(10, 100);
+               timeout--;
+       }
+
+       mutex_unlock(&mipi->lock);
+       clk_disable(mipi->clk);
+
+       if (timeout == 0)
+               err = -ETIMEDOUT;
+       else
+               err = 0;
+
+out:
+       platform_device_put(pdev);
+       return err;
+}
+EXPORT_SYMBOL_GPL(tegra_mipi_calibrate);
+
+static int tegra_mipi_probe(struct platform_device *pdev)
+{
+       struct tegra_mipi *mipi;
+       struct resource *res;
+       int err;
+
+       mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
+       if (!mipi)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       mipi->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(mipi->regs))
+               return PTR_ERR(mipi->regs);
+
+       mutex_init(&mipi->lock);
+
+       mipi->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(mipi->clk)) {
+               dev_err(&pdev->dev, "failed to get clock\n");
+               return PTR_ERR(mipi->clk);
+       }
+
+       err = clk_prepare(mipi->clk);
+       if (err < 0)
+               return err;
+
+       platform_set_drvdata(pdev, mipi);
+
+       return 0;
+}
+
+static int tegra_mipi_remove(struct platform_device *pdev)
+{
+       struct tegra_mipi *mipi = platform_get_drvdata(pdev);
+
+       clk_unprepare(mipi->clk);
+
+       return 0;
+}
+
+static struct of_device_id tegra_mipi_of_match[] = {
+       { .compatible = "nvidia,tegra114-mipi", },
+       { },
+};
+
+struct platform_driver tegra_mipi_driver = {
+       .driver = {
+               .name = "tegra-mipi",
+               .of_match_table = tegra_mipi_of_match,
+       },
+       .probe = tegra_mipi_probe,
+       .remove = tegra_mipi_remove,
+};
index 409745b949dbfa7d9ecd897409147ae0225623e3..03cf2922e469bccacd132c9ec07e3190ebafcf4f 100644 (file)
@@ -354,6 +354,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;
index 267c0b9d3647d24f2a3bf7dc81504eacaf957005..4eb933a497fd70022029f5e15c9fb4b06b7063ed 100644 (file)
@@ -20,6 +20,7 @@
 #define __HOST1X_SYNCPT_H
 
 #include <linux/atomic.h>
+#include <linux/host1x.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 
@@ -50,25 +51,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 +94,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 +109,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 +121,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
index 84b685f7ab6e0a3389c534b56e8f8187619e1e84..a312f048656f76f017c2a730836762cadd1e8553 100644 (file)
@@ -19,10 +19,10 @@ source "drivers/char/agp/Kconfig"
 
 source "drivers/gpu/vga/Kconfig"
 
-source "drivers/gpu/drm/Kconfig"
-
 source "drivers/gpu/host1x/Kconfig"
 
+source "drivers/gpu/drm/Kconfig"
+
 config VGASTATE
        tristate
        default n
index 7062307a4a2d7f656950a70bcd72bdf0fac4a4dc..e6d0cd9f518efa36733f5769e9836dced310e628 100644 (file)
@@ -150,6 +150,7 @@ int drm_err(const char *func, const char *format, ...);
 #define DRIVER_BUS_PCI 0x1
 #define DRIVER_BUS_PLATFORM 0x2
 #define DRIVER_BUS_USB 0x3
+#define DRIVER_BUS_HOST1X 0x4
 
 /***********************************************************************/
 /** \name Begin the DRM... */
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
new file mode 100644 (file)
index 0000000..4c8760b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013, NVIDIA Corporation.  All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __DRM_PANEL_H__
+#define __DRM_PANEL_H__
+
+#include <linux/list.h>
+
+struct drm_connector;
+struct drm_device;
+struct drm_panel;
+
+struct drm_panel_funcs {
+       void (*disable)(struct drm_panel *panel);
+       void (*enable)(struct drm_panel *panel);
+       int (*get_modes)(struct drm_panel *panel);
+};
+
+struct drm_panel {
+       struct drm_device *drm;
+       struct drm_connector *connector;
+       struct device *dev;
+
+       const struct drm_panel_funcs *funcs;
+
+       struct list_head list;
+};
+
+static inline void drm_panel_disable(struct drm_panel *panel)
+{
+       if (panel && panel->funcs && panel->funcs->disable)
+               panel->funcs->disable(panel);
+}
+
+static inline void drm_panel_enable(struct drm_panel *panel)
+{
+       if (panel && panel->funcs && panel->funcs->enable)
+               panel->funcs->enable(panel);
+}
+
+void drm_panel_init(struct drm_panel *panel);
+
+int drm_panel_add(struct drm_panel *panel);
+void drm_panel_remove(struct drm_panel *panel);
+
+int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector);
+int drm_panel_detach(struct drm_panel *panel);
+
+#ifdef CONFIG_OF
+struct drm_panel *of_drm_find_panel(struct device_node *np);
+#else
+static inline struct drm_panel *of_drm_find_panel(struct device_node *np)
+{
+       return NULL;
+}
+#endif
+
+#endif
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
new file mode 100644 (file)
index 0000000..1198002
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2009-2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LINUX_HOST1X_H
+#define __LINUX_HOST1X_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+enum host1x_class {
+       HOST1X_CLASS_HOST1X = 0x1,
+       HOST1X_CLASS_GR2D = 0x51,
+       HOST1X_CLASS_GR2D_SB = 0x52,
+       HOST1X_CLASS_GR3D = 0x60,
+};
+
+struct host1x_client;
+
+struct host1x_client_ops {
+       int (*init)(struct host1x_client *client);
+       int (*exit)(struct host1x_client *client);
+};
+
+struct host1x_client {
+       struct list_head list;
+       struct device *parent;
+       struct device *dev;
+
+       const struct host1x_client_ops *ops;
+
+       enum host1x_class class;
+       struct host1x_channel *channel;
+
+       struct host1x_syncpt **syncpts;
+       unsigned int num_syncpts;
+};
+
+/*
+ * host1x buffer objects
+ */
+
+struct host1x_bo;
+struct sg_table;
+
+struct host1x_bo_ops {
+       struct host1x_bo *(*get)(struct host1x_bo *bo);
+       void (*put)(struct host1x_bo *bo);
+       dma_addr_t (*pin)(struct host1x_bo *bo, struct sg_table **sgt);
+       void (*unpin)(struct host1x_bo *bo, struct sg_table *sgt);
+       void *(*mmap)(struct host1x_bo *bo);
+       void (*munmap)(struct host1x_bo *bo, void *addr);
+       void *(*kmap)(struct host1x_bo *bo, unsigned int pagenum);
+       void (*kunmap)(struct host1x_bo *bo, unsigned int pagenum, void *addr);
+};
+
+struct host1x_bo {
+       const struct host1x_bo_ops *ops;
+};
+
+static inline void host1x_bo_init(struct host1x_bo *bo,
+                                 const struct host1x_bo_ops *ops)
+{
+       bo->ops = ops;
+}
+
+static inline struct host1x_bo *host1x_bo_get(struct host1x_bo *bo)
+{
+       return bo->ops->get(bo);
+}
+
+static inline void host1x_bo_put(struct host1x_bo *bo)
+{
+       bo->ops->put(bo);
+}
+
+static inline dma_addr_t host1x_bo_pin(struct host1x_bo *bo,
+                                      struct sg_table **sgt)
+{
+       return bo->ops->pin(bo, sgt);
+}
+
+static inline void host1x_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt)
+{
+       bo->ops->unpin(bo, sgt);
+}
+
+static inline void *host1x_bo_mmap(struct host1x_bo *bo)
+{
+       return bo->ops->mmap(bo);
+}
+
+static inline void host1x_bo_munmap(struct host1x_bo *bo, void *addr)
+{
+       bo->ops->munmap(bo, addr);
+}
+
+static inline void *host1x_bo_kmap(struct host1x_bo *bo, unsigned int pagenum)
+{
+       return bo->ops->kmap(bo, pagenum);
+}
+
+static inline void host1x_bo_kunmap(struct host1x_bo *bo,
+                                   unsigned int pagenum, void *addr)
+{
+       bo->ops->kunmap(bo, pagenum, addr);
+}
+
+/*
+ * host1x syncpoints
+ */
+
+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,
+                                           bool client_managed);
+void host1x_syncpt_free(struct host1x_syncpt *sp);
+
+/*
+ * 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);
+
+int tegra_mipi_calibrate(struct device *device);
+
+#endif
index 73bde4eaf16c2042038ce6435072ab8d39632e41..0f8575f58db8aa343e4c24eb107d4322fbf9798a 100644 (file)
@@ -19,6 +19,9 @@
 
 #include <drm/drm.h>
 
+#define DRM_TEGRA_GEM_CREATE_TILED     (1 << 0)
+#define DRM_TEGRA_GEM_CREATE_BOTTOM_UP (1 << 1)
+
 struct drm_tegra_gem_create {
        __u64 size;
        __u32 flags;