summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/panel/ampire,am800480r3tmqwa1h.txt7
-rw-r--r--Documentation/devicetree/bindings/panel/auo,b101ean01.txt7
-rw-r--r--Documentation/devicetree/bindings/panel/innolux,at043tn24.txt7
-rw-r--r--Documentation/devicetree/bindings/panel/innolux,zj070na-01p.txt7
-rw-r--r--Documentation/devicetree/bindings/panel/ortustech,com43h4m85ulc.txt7
-rw-r--r--Documentation/devicetree/bindings/panel/samsung,ltn140at29-301.txt7
-rw-r--r--Documentation/devicetree/bindings/panel/shelly,sca07010-bfn-lnn.txt7
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt2
-rw-r--r--drivers/gpu/drm/Kconfig9
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/Kconfig11
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/ps8622.c684
-rw-r--r--drivers/gpu/drm/bridge/ptn3460.c2
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c144
-rw-r--r--drivers/gpu/drm/omapdrm/omap_connector.c12
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c622
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_priv.h8
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c80
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.h1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c241
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.h23
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c66
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c57
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c10
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c20
-rw-r--r--drivers/gpu/drm/omapdrm/omap_irq.c2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_plane.c146
-rw-r--r--drivers/gpu/drm/panel/Kconfig1
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c256
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c19
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c3
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c25
-rw-r--r--drivers/gpu/drm/tegra/dc.c99
-rw-r--r--drivers/gpu/drm/tegra/dc.h7
-rw-r--r--drivers/gpu/drm/tegra/drm.c18
-rw-r--r--drivers/gpu/drm/tegra/drm.h4
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c4
-rw-r--r--drivers/gpu/drm/tegra/hdmi.h2
-rw-r--r--drivers/gpu/drm/tegra/sor.c202
-rw-r--r--drivers/gpu/drm/vgem/Makefile4
-rw-r--r--drivers/gpu/drm/vgem/vgem_dma_buf.c94
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.c364
-rw-r--r--drivers/gpu/drm/vgem/vgem_drv.h57
-rw-r--r--drivers/gpu/host1x/syncpt.c6
-rw-r--r--include/drm/drm_atomic_helper.h14
-rw-r--r--include/drm/drm_crtc.h1
-rw-r--r--include/drm/drm_panel.h5
-rw-r--r--include/linux/host1x.h1
-rw-r--r--include/uapi/drm/tegra_drm.h3
50 files changed, 2628 insertions, 752 deletions
diff --git a/Documentation/devicetree/bindings/panel/ampire,am800480r3tmqwa1h.txt b/Documentation/devicetree/bindings/panel/ampire,am800480r3tmqwa1h.txt
new file mode 100644
index 000000000000..83e2cae1cc1b
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/ampire,am800480r3tmqwa1h.txt
@@ -0,0 +1,7 @@
+Ampire AM-800480R3TMQW-A1H 7.0" WVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "ampire,am800480r3tmqwa1h"
+
+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/auo,b101ean01.txt b/Documentation/devicetree/bindings/panel/auo,b101ean01.txt
new file mode 100644
index 000000000000..3590b0741619
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/auo,b101ean01.txt
@@ -0,0 +1,7 @@
+AU Optronics Corporation 10.1" WSVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "auo,b101ean01"
+
+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/innolux,at043tn24.txt b/Documentation/devicetree/bindings/panel/innolux,at043tn24.txt
new file mode 100644
index 000000000000..4104226b61bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/innolux,at043tn24.txt
@@ -0,0 +1,7 @@
+Innolux AT043TN24 4.3" WQVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "innolux,at043tn24"
+
+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/innolux,zj070na-01p.txt b/Documentation/devicetree/bindings/panel/innolux,zj070na-01p.txt
new file mode 100644
index 000000000000..824f87f1526d
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/innolux,zj070na-01p.txt
@@ -0,0 +1,7 @@
+Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
+
+Required properties:
+- compatible: should be "innolux,zj070na-01p"
+
+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/ortustech,com43h4m85ulc.txt b/Documentation/devicetree/bindings/panel/ortustech,com43h4m85ulc.txt
new file mode 100644
index 000000000000..de19e9398618
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/ortustech,com43h4m85ulc.txt
@@ -0,0 +1,7 @@
+OrtusTech COM43H4M85ULC Blanview 3.7" TFT-LCD panel
+
+Required properties:
+- compatible: should be "ortustech,com43h4m85ulc"
+
+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/samsung,ltn140at29-301.txt b/Documentation/devicetree/bindings/panel/samsung,ltn140at29-301.txt
new file mode 100644
index 000000000000..e7f969d891cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/samsung,ltn140at29-301.txt
@@ -0,0 +1,7 @@
+Samsung Electronics 14" WXGA (1366x768) TFT LCD panel
+
+Required properties:
+- compatible: should be "samsung,ltn140at29-301"
+
+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/shelly,sca07010-bfn-lnn.txt b/Documentation/devicetree/bindings/panel/shelly,sca07010-bfn-lnn.txt
new file mode 100644
index 000000000000..fc1ea9e26c94
--- /dev/null
+++ b/Documentation/devicetree/bindings/panel/shelly,sca07010-bfn-lnn.txt
@@ -0,0 +1,7 @@
+Shelly SCA07010-BFN-LNN 7.0" WVGA TFT LCD panel
+
+Required properties:
+- compatible: should be "shelly,sca07010-bfn-lnn"
+
+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/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index fae26d014aaf..772b24f86388 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -17,6 +17,7 @@ altr Altera Corp.
amcc Applied Micro Circuits Corporation (APM, formally AMCC)
amd Advanced Micro Devices (AMD), Inc.
amlogic Amlogic, Inc.
+ampire Ampire Co., Ltd.
ams AMS AG
amstaos AMS-Taos Inc.
apm Applied Micro Circuits Corporation (APM)
@@ -132,6 +133,7 @@ nvidia NVIDIA
nxp NXP Semiconductors
onnn ON Semiconductor Corp.
opencores OpenCores.org
+ortustech Ortus Technology Co., Ltd.
ovti OmniVision Technologies
panasonic Panasonic Corporation
parade Parade Technologies Inc.
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 151a050129e7..47f2ce81b412 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -165,6 +165,15 @@ config DRM_SAVAGE
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage.
+config DRM_VGEM
+ tristate "Virtual GEM provider"
+ depends on DRM
+ help
+ Choose this option to get a virtual graphics memory manager,
+ as used by Mesa's software renderer for enhanced performance.
+ If M is selected the module will be called vgem.
+
+
source "drivers/gpu/drm/exynos/Kconfig"
source "drivers/gpu/drm/rockchip/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2c239b99de64..7d4944e1a60c 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_DRM_SIS) += sis/
obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/
+obj-$(CONFIG_DRM_VGEM) += vgem/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f38bbcdf929b..acef3223772c 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -11,3 +11,14 @@ config DRM_PTN3460
select DRM_PANEL
---help---
ptn3460 eDP-LVDS bridge chip driver.
+
+config DRM_PS8622
+ tristate "Parade eDP/LVDS bridge"
+ depends on DRM
+ depends on OF
+ select DRM_PANEL
+ select DRM_KMS_HELPER
+ select BACKLIGHT_LCD_SUPPORT
+ select BACKLIGHT_CLASS_DEVICE
+ ---help---
+ parade eDP-LVDS bridge chip driver.
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index d8a8cfd12fbb..8dfebd984370 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -1,4 +1,5 @@
ccflags-y := -Iinclude/drm
+obj-$(CONFIG_DRM_PS8622) += ps8622.o
obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c
new file mode 100644
index 000000000000..e895aa7ea353
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ps8622.c
@@ -0,0 +1,684 @@
+/*
+ * Parade PS8622 eDP/LVDS bridge driver
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_panel.h>
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+
+/* Brightness scale on the Parade chip */
+#define PS8622_MAX_BRIGHTNESS 0xff
+
+/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */
+#define PS8622_POWER_RISE_T1_MIN_US 10
+#define PS8622_POWER_RISE_T1_MAX_US 10000
+#define PS8622_RST_HIGH_T2_MIN_US 3000
+#define PS8622_RST_HIGH_T2_MAX_US 30000
+#define PS8622_PWMO_END_T12_MS 200
+#define PS8622_POWER_FALL_T16_MAX_US 10000
+#define PS8622_POWER_OFF_T17_MS 500
+
+#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \
+ (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US))
+#error "T2.min + T1.max must be less than T2.max + T1.min"
+#endif
+
+struct ps8622_bridge {
+ struct drm_connector connector;
+ struct i2c_client *client;
+ struct drm_bridge bridge;
+ struct drm_panel *panel;
+ struct regulator *v12;
+ struct backlight_device *bl;
+
+ struct gpio_desc *gpio_slp;
+ struct gpio_desc *gpio_rst;
+
+ u32 max_lane_count;
+ u32 lane_count;
+
+ bool enabled;
+};
+
+static inline struct ps8622_bridge *
+ bridge_to_ps8622(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct ps8622_bridge, bridge);
+}
+
+static inline struct ps8622_bridge *
+ connector_to_ps8622(struct drm_connector *connector)
+{
+ return container_of(connector, struct ps8622_bridge, connector);
+}
+
+static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val)
+{
+ int ret;
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ u8 data[] = {reg, val};
+
+ msg.addr = client->addr + page;
+ msg.flags = 0;
+ msg.len = sizeof(data);
+ msg.buf = data;
+
+ ret = i2c_transfer(adap, &msg, 1);
+ if (ret != 1)
+ pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n",
+ client->addr + page, reg, val, ret);
+ return !(ret == 1);
+}
+
+static int ps8622_send_config(struct ps8622_bridge *ps8622)
+{
+ struct i2c_client *cl = ps8622->client;
+ int err = 0;
+
+ /* HPD low */
+ err = ps8622_set(cl, 0x02, 0xa1, 0x01);
+ if (err)
+ goto error;
+
+ /* SW setting: [1:0] SW output 1.2V voltage is lower to 96% */
+ err = ps8622_set(cl, 0x04, 0x14, 0x01);
+ if (err)
+ goto error;
+
+ /* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */
+ err = ps8622_set(cl, 0x04, 0xe3, 0x20);
+ if (err)
+ goto error;
+
+ /* [7] RCO SS enable */
+ err = ps8622_set(cl, 0x04, 0xe2, 0x80);
+ if (err)
+ goto error;
+
+ /* RPHY Setting
+ * [3:2] CDR tune wait cycle before measure for fine tune
+ * b00: 1us b01: 0.5us b10:2us, b11: 4us
+ */
+ err = ps8622_set(cl, 0x04, 0x8a, 0x0c);
+ if (err)
+ goto error;
+
+ /* [3] RFD always on */
+ err = ps8622_set(cl, 0x04, 0x89, 0x08);
+ if (err)
+ goto error;
+
+ /* CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times. */
+ err = ps8622_set(cl, 0x04, 0x71, 0x2d);
+ if (err)
+ goto error;
+
+ /* 2.7G CDR settings: NOF=40LSB for HBR CDR setting */
+ err = ps8622_set(cl, 0x04, 0x7d, 0x07);
+ if (err)
+ goto error;
+
+ /* [1:0] Fmin=+4bands */
+ err = ps8622_set(cl, 0x04, 0x7b, 0x00);
+ if (err)
+ goto error;
+
+ /* [7:5] DCO_FTRNG=+-40% */
+ err = ps8622_set(cl, 0x04, 0x7a, 0xfd);
+ if (err)
+ goto error;
+
+ /* 1.62G CDR settings: [5:2]NOF=64LSB [1:0]DCO scale is 2/5 */
+ err = ps8622_set(cl, 0x04, 0xc0, 0x12);
+ if (err)
+ goto error;
+
+ /* Gitune=-37% */
+ err = ps8622_set(cl, 0x04, 0xc1, 0x92);
+ if (err)
+ goto error;
+
+ /* Fbstep=100% */
+ err = ps8622_set(cl, 0x04, 0xc2, 0x1c);
+ if (err)
+ goto error;
+
+ /* [7] LOS signal disable */
+ err = ps8622_set(cl, 0x04, 0x32, 0x80);
+ if (err)
+ goto error;
+
+ /* RPIO Setting: [7:4] LVDS driver bias current : 75% (250mV swing) */
+ err = ps8622_set(cl, 0x04, 0x00, 0xb0);
+ if (err)
+ goto error;
+
+ /* [7:6] Right-bar GPIO output strength is 8mA */
+ err = ps8622_set(cl, 0x04, 0x15, 0x40);
+ if (err)
+ goto error;
+
+ /* EQ Training State Machine Setting, RCO calibration start */
+ err = ps8622_set(cl, 0x04, 0x54, 0x10);
+ if (err)
+ goto error;
+
+ /* Logic, needs more than 10 I2C command */
+ /* [4:0] MAX_LANE_COUNT set to max supported lanes */
+ err = ps8622_set(cl, 0x01, 0x02, 0x80 | ps8622->max_lane_count);
+ if (err)
+ goto error;
+
+ /* [4:0] LANE_COUNT_SET set to chosen lane count */
+ err = ps8622_set(cl, 0x01, 0x21, 0x80 | ps8622->lane_count);
+ if (err)
+ goto error;
+
+ err = ps8622_set(cl, 0x00, 0x52, 0x20);
+ if (err)
+ goto error;
+
+ /* HPD CP toggle enable */
+ err = ps8622_set(cl, 0x00, 0xf1, 0x03);
+ if (err)
+ goto error;
+
+ err = ps8622_set(cl, 0x00, 0x62, 0x41);
+ if (err)
+ goto error;
+
+ /* Counter number, add 1ms counter delay */
+ err = ps8622_set(cl, 0x00, 0xf6, 0x01);
+ if (err)
+ goto error;
+
+ /* [6]PWM function control by DPCD0040f[7], default is PWM block */
+ err = ps8622_set(cl, 0x00, 0x77, 0x06);
+ if (err)
+ goto error;
+
+ /* 04h Adjust VTotal toleranceto fix the 30Hz no display issue */
+ err = ps8622_set(cl, 0x00, 0x4c, 0x04);
+ if (err)
+ goto error;
+
+ /* DPCD00400='h00, Parade OUI ='h001cf8 */
+ err = ps8622_set(cl, 0x01, 0xc0, 0x00);
+ if (err)
+ goto error;
+
+ /* DPCD00401='h1c */
+ err = ps8622_set(cl, 0x01, 0xc1, 0x1c);
+ if (err)
+ goto error;
+
+ /* DPCD00402='hf8 */
+ err = ps8622_set(cl, 0x01, 0xc2, 0xf8);
+ if (err)
+ goto error;
+
+ /* DPCD403~408 = ASCII code, D2SLV5='h4432534c5635 */
+ err = ps8622_set(cl, 0x01, 0xc3, 0x44);
+ if (err)
+ goto error;
+
+ /* DPCD404 */
+ err = ps8622_set(cl, 0x01, 0xc4, 0x32);
+ if (err)
+ goto error;
+
+ /* DPCD405 */
+ err = ps8622_set(cl, 0x01, 0xc5, 0x53);
+ if (err)
+ goto error;
+
+ /* DPCD406 */
+ err = ps8622_set(cl, 0x01, 0xc6, 0x4c);
+ if (err)
+ goto error;
+
+ /* DPCD407 */
+ err = ps8622_set(cl, 0x01, 0xc7, 0x56);
+ if (err)
+ goto error;
+
+ /* DPCD408 */
+ err = ps8622_set(cl, 0x01, 0xc8, 0x35);
+ if (err)
+ goto error;
+
+ /* DPCD40A, Initial Code major revision '01' */
+ err = ps8622_set(cl, 0x01, 0xca, 0x01);
+ if (err)
+ goto error;
+
+ /* DPCD40B, Initial Code minor revision '05' */
+ err = ps8622_set(cl, 0x01, 0xcb, 0x05);
+ if (err)
+ goto error;
+
+
+ if (ps8622->bl) {
+ /* DPCD720, internal PWM */
+ err = ps8622_set(cl, 0x01, 0xa5, 0xa0);
+ if (err)
+ goto error;
+
+ /* FFh for 100% brightness, 0h for 0% brightness */
+ err = ps8622_set(cl, 0x01, 0xa7,
+ ps8622->bl->props.brightness);
+ if (err)
+ goto error;
+ } else {
+ /* DPCD720, external PWM */
+ err = ps8622_set(cl, 0x01, 0xa5, 0x80);
+ if (err)
+ goto error;
+ }
+
+ /* Set LVDS output as 6bit-VESA mapping, single LVDS channel */
+ err = ps8622_set(cl, 0x01, 0xcc, 0x13);
+ if (err)
+ goto error;
+
+ /* Enable SSC set by register */
+ err = ps8622_set(cl, 0x02, 0xb1, 0x20);
+ if (err)
+ goto error;
+
+ /* Set SSC enabled and +/-1% central spreading */
+ err = ps8622_set(cl, 0x04, 0x10, 0x16);
+ if (err)
+ goto error;
+
+ /* Logic end */
+ /* MPU Clock source: LC => RCO */
+ err = ps8622_set(cl, 0x04, 0x59, 0x60);
+ if (err)
+ goto error;
+
+ /* LC -> RCO */
+ err = ps8622_set(cl, 0x04, 0x54, 0x14);
+ if (err)
+ goto error;
+
+ /* HPD high */
+ err = ps8622_set(cl, 0x02, 0xa1, 0x91);
+
+error:
+ return err ? -EIO : 0;
+}
+
+static int ps8622_backlight_update(struct backlight_device *bl)
+{
+ struct ps8622_bridge *ps8622 = dev_get_drvdata(&bl->dev);
+ int ret, brightness = bl->props.brightness;
+
+ if (bl->props.power != FB_BLANK_UNBLANK ||
+ bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
+ brightness = 0;
+
+ if (!ps8622->enabled)
+ return -EINVAL;
+
+ ret = ps8622_set(ps8622->client, 0x01, 0xa7, brightness);
+
+ return ret;
+}
+
+static const struct backlight_ops ps8622_backlight_ops = {
+ .update_status = ps8622_backlight_update,
+};
+
+static void ps8622_pre_enable(struct drm_bridge *bridge)
+{
+ struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
+ int ret;
+
+ if (ps8622->enabled)
+ return;
+
+ gpiod_set_value(ps8622->gpio_rst, 0);
+
+ if (ps8622->v12) {
+ ret = regulator_enable(ps8622->v12);
+ if (ret)
+ DRM_ERROR("fails to enable ps8622->v12");
+ }
+
+ if (drm_panel_prepare(ps8622->panel)) {
+ DRM_ERROR("failed to prepare panel\n");
+ return;
+ }
+
+ gpiod_set_value(ps8622->gpio_slp, 1);
+
+ /*
+ * T1 is the range of time that it takes for the power to rise after we
+ * enable the lcd/ps8622 fet. T2 is the range of time in which the
+ * data sheet specifies we should deassert the reset pin.
+ *
+ * If it takes T1.max for the power to rise, we need to wait atleast
+ * T2.min before deasserting the reset pin. If it takes T1.min for the
+ * power to rise, we need to wait at most T2.max before deasserting the
+ * reset pin.
+ */
+ usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US,
+ PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US);
+
+ gpiod_set_value(ps8622->gpio_rst, 1);
+
+ /* wait 20ms after RST high */
+ usleep_range(20000, 30000);
+
+ ret = ps8622_send_config(ps8622);
+ if (ret) {
+ DRM_ERROR("Failed to send config to bridge (%d)\n", ret);
+ return;
+ }
+
+ ps8622->enabled = true;
+}
+
+static void ps8622_enable(struct drm_bridge *bridge)
+{
+ struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
+
+ if (drm_panel_enable(ps8622->panel)) {
+ DRM_ERROR("failed to enable panel\n");
+ return;
+ }
+}
+
+static void ps8622_disable(struct drm_bridge *bridge)
+{
+ struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
+
+ if (drm_panel_disable(ps8622->panel)) {
+ DRM_ERROR("failed to disable panel\n");
+ return;
+ }
+ msleep(PS8622_PWMO_END_T12_MS);
+}
+
+static void ps8622_post_disable(struct drm_bridge *bridge)
+{
+ struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
+
+ if (!ps8622->enabled)
+ return;
+
+ ps8622->enabled = false;
+
+ /*
+ * This doesn't matter if the regulators are turned off, but something
+ * else might keep them on. In that case, we want to assert the slp gpio
+ * to lower power.
+ */
+ gpiod_set_value(ps8622->gpio_slp, 0);
+
+ if (drm_panel_unprepare(ps8622->panel)) {
+ DRM_ERROR("failed to unprepare panel\n");
+ return;
+ }
+
+ if (ps8622->v12)
+ regulator_disable(ps8622->v12);
+
+ /*
+ * Sleep for at least the amount of time that it takes the power rail to
+ * fall to prevent asserting the rst gpio from doing anything.
+ */
+ usleep_range(PS8622_POWER_FALL_T16_MAX_US,
+ 2 * PS8622_POWER_FALL_T16_MAX_US);
+ gpiod_set_value(ps8622->gpio_rst, 0);
+
+ msleep(PS8622_POWER_OFF_T17_MS);
+}
+
+static int ps8622_get_modes(struct drm_connector *connector)
+{
+ struct ps8622_bridge *ps8622;
+
+ ps8622 = connector_to_ps8622(connector);
+
+ return drm_panel_get_modes(ps8622->panel);
+}
+
+static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
+{
+ struct ps8622_bridge *ps8622;
+
+ ps8622 = connector_to_ps8622(connector);
+
+ return ps8622->bridge.encoder;
+}
+
+static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
+ .get_modes = ps8622_get_modes,
+ .best_encoder = ps8622_best_encoder,
+};
+
+static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
+ bool force)
+{
+ return connector_status_connected;
+}
+
+static void ps8622_connector_destroy(struct drm_connector *connector)
+{
+ drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ps8622_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = ps8622_detect,
+ .destroy = ps8622_connector_destroy,
+};
+
+static int ps8622_attach(struct drm_bridge *bridge)
+{
+ struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
+ int ret;
+
+ if (!bridge->encoder) {
+ DRM_ERROR("Parent encoder object not found");
+ return -ENODEV;
+ }
+
+ ps8622->connector.polled = DRM_CONNECTOR_POLL_HPD;
+ ret = drm_connector_init(bridge->dev, &ps8622->connector,
+ &ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+ if (ret) {
+ DRM_ERROR("Failed to initialize connector with drm\n");
+ return ret;
+ }
+ drm_connector_helper_add(&ps8622->connector,
+ &ps8622_connector_helper_funcs);
+ drm_connector_register(&ps8622->connector);
+ drm_mode_connector_attach_encoder(&ps8622->connector,
+ bridge->encoder);
+
+ if (ps8622->panel)
+ drm_panel_attach(ps8622->panel, &ps8622->connector);
+
+ drm_helper_hpd_irq_event(ps8622->connector.dev);
+
+ return ret;
+}
+
+static const struct drm_bridge_funcs ps8622_bridge_funcs = {
+ .pre_enable = ps8622_pre_enable,
+ .enable = ps8622_enable,
+ .disable = ps8622_disable,
+ .post_disable = ps8622_post_disable,
+ .attach = ps8622_attach,
+};
+
+static const struct of_device_id ps8622_devices[] = {
+ {.compatible = "parade,ps8622",},
+ {.compatible = "parade,ps8625",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ps8622_devices);
+
+static int ps8622_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device_node *endpoint, *panel_node;
+ struct ps8622_bridge *ps8622;
+ int ret;
+
+ ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL);
+ if (!ps8622)
+ return -ENOMEM;
+
+ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (endpoint) {
+ panel_node = of_graph_get_remote_port_parent(endpoint);
+ if (panel_node) {
+ ps8622->panel = of_drm_find_panel(panel_node);
+ of_node_put(panel_node);
+ if (!ps8622->panel)
+ return -EPROBE_DEFER;
+ }
+ }
+
+ ps8622->client = client;
+
+ ps8622->v12 = devm_regulator_get(dev, "vdd12");
+ if (IS_ERR(ps8622->v12)) {
+ dev_info(dev, "no 1.2v regulator found for PS8622\n");
+ ps8622->v12 = NULL;
+ }
+
+ ps8622->gpio_slp = devm_gpiod_get(dev, "sleep");
+ if (IS_ERR(ps8622->gpio_slp)) {
+ ret = PTR_ERR(ps8622->gpio_slp);
+ dev_err(dev, "cannot get gpio_slp %d\n", ret);
+ return ret;
+ }
+ ret = gpiod_direction_output(ps8622->gpio_slp, 1);
+ if (ret) {
+ dev_err(dev, "cannot configure gpio_slp\n");
+ return ret;
+ }
+
+ ps8622->gpio_rst = devm_gpiod_get(dev, "reset");
+ if (IS_ERR(ps8622->gpio_rst)) {
+ ret = PTR_ERR(ps8622->gpio_rst);
+ dev_err(dev, "cannot get gpio_rst %d\n", ret);
+ return ret;
+ }
+ /*
+ * Assert the reset pin high to avoid the bridge being
+ * initialized prematurely
+ */
+ ret = gpiod_direction_output(ps8622->gpio_rst, 1);
+ if (ret) {
+ dev_err(dev, "cannot configure gpio_rst\n");
+ return ret;
+ }
+
+ ps8622->max_lane_count = id->driver_data;
+
+ if (of_property_read_u32(dev->of_node, "lane-count",
+ &ps8622->lane_count)) {
+ ps8622->lane_count = ps8622->max_lane_count;
+ } else if (ps8622->lane_count > ps8622->max_lane_count) {
+ dev_info(dev, "lane-count property is too high,"
+ "using max_lane_count\n");
+ ps8622->lane_count = ps8622->max_lane_count;
+ }
+
+ if (!of_find_property(dev->of_node, "use-external-pwm", NULL)) {
+ ps8622->bl = backlight_device_register("ps8622-backlight",
+ dev, ps8622, &ps8622_backlight_ops,
+ NULL);
+ if (IS_ERR(ps8622->bl)) {
+ DRM_ERROR("failed to register backlight\n");
+ ret = PTR_ERR(ps8622->bl);
+ ps8622->bl = NULL;
+ return ret;
+ }
+ ps8622->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS;
+ ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS;
+ }
+
+ ps8622->bridge.funcs = &ps8622_bridge_funcs;
+ ps8622->bridge.of_node = dev->of_node;
+ ret = drm_bridge_add(&ps8622->bridge);
+ if (ret) {
+ DRM_ERROR("Failed to add bridge\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(client, ps8622);
+
+ return 0;
+}
+
+static int ps8622_remove(struct i2c_client *client)
+{
+ struct ps8622_bridge *ps8622 = i2c_get_clientdata(client);
+
+ if (ps8622->bl)
+ backlight_device_unregister(ps8622->bl);
+
+ drm_bridge_remove(&ps8622->bridge);
+
+ return 0;
+}
+
+static const struct i2c_device_id ps8622_i2c_table[] = {
+ /* Device type, max_lane_count */
+ {"ps8622", 1},
+ {"ps8625", 2},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table);
+
+static struct i2c_driver ps8622_driver = {
+ .id_table = ps8622_i2c_table,
+ .probe = ps8622_probe,
+ .remove = ps8622_remove,
+ .driver = {
+ .name = "ps8622",
+ .owner = THIS_MODULE,
+ .of_match_table = ps8622_devices,
+ },
+};
+module_i2c_driver(ps8622_driver);
+
+MODULE_AUTHOR("Vincent Palatin <vpalatin@chromium.org>");
+MODULE_DESCRIPTION("Parade ps8622/ps8625 eDP-LVDS converter driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c
index 826833e396f0..9d2f053382e1 100644
--- a/drivers/gpu/drm/bridge/ptn3460.c
+++ b/drivers/gpu/drm/bridge/ptn3460.c
@@ -265,7 +265,7 @@ static struct drm_connector_funcs ptn3460_connector_funcs = {
.destroy = ptn3460_connector_destroy,
};
-int ptn3460_bridge_attach(struct drm_bridge *bridge)
+static int ptn3460_bridge_attach(struct drm_bridge *bridge)
{
struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
int ret;
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index e67d4d69faf7..41c38edade74 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -2069,6 +2069,26 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
/**
+ * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state
+ * @crtc: CRTC object
+ * @state: atomic CRTC state
+ *
+ * Copies atomic state from a CRTC's current state and resets inferred values.
+ * This is useful for drivers that subclass the CRTC state.
+ */
+void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ memcpy(state, crtc->state, sizeof(*state));
+
+ state->mode_changed = false;
+ state->active_changed = false;
+ state->planes_changed = false;
+ state->event = NULL;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);
+
+/**
* drm_atomic_helper_crtc_duplicate_state - default state duplicate hook
* @crtc: drm CRTC
*
@@ -2083,20 +2103,35 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
if (WARN_ON(!crtc->state))
return NULL;
- state = kmemdup(crtc->state, sizeof(*crtc->state), GFP_KERNEL);
-
- if (state) {
- state->mode_changed = false;
- state->active_changed = false;
- state->planes_changed = false;
- state->event = NULL;
- }
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state)
+ __drm_atomic_helper_crtc_duplicate_state(crtc, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
/**
+ * __drm_atomic_helper_crtc_destroy_state - release CRTC state
+ * @crtc: CRTC object
+ * @state: CRTC state object to release
+ *
+ * Releases all resources stored in the CRTC state without actually freeing
+ * the memory of the CRTC state. This is useful for drivers that subclass the
+ * CRTC state.
+ */
+void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ /*
+ * This is currently a placeholder so that drivers that subclass the
+ * state will automatically do the right thing if code is ever added
+ * to this function.
+ */
+}
+EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
+
+/**
* drm_atomic_helper_crtc_destroy_state - default state destroy hook
* @crtc: drm CRTC
* @state: CRTC state object to release
@@ -2107,6 +2142,7 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
+ __drm_atomic_helper_crtc_destroy_state(crtc, state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state);
@@ -2132,6 +2168,24 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
/**
+ * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state
+ * @plane: plane object
+ * @state: atomic plane state
+ *
+ * Copies atomic state from a plane's current state. This is useful for
+ * drivers that subclass the plane state.
+ */
+void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ memcpy(state, plane->state, sizeof(*state));
+
+ if (state->fb)
+ drm_framebuffer_reference(state->fb);
+}
+EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
+
+/**
* drm_atomic_helper_plane_duplicate_state - default state duplicate hook
* @plane: drm plane
*
@@ -2146,16 +2200,32 @@ drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane)
if (WARN_ON(!plane->state))
return NULL;
- state = kmemdup(plane->state, sizeof(*plane->state), GFP_KERNEL);
-
- if (state && state->fb)
- drm_framebuffer_reference(state->fb);
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state)
+ __drm_atomic_helper_plane_duplicate_state(plane, state);
return state;
}
EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
/**
+ * __drm_atomic_helper_plane_destroy_state - release plane state
+ * @plane: plane object
+ * @state: plane state object to release
+ *
+ * Releases all resources stored in the plane state without actually freeing
+ * the memory of the plane state. This is useful for drivers that subclass the
+ * plane state.
+ */
+void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ if (state->fb)
+ drm_framebuffer_unreference(state->fb);
+}
+EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state);
+
+/**
* drm_atomic_helper_plane_destroy_state - default state destroy hook
* @plane: drm plane
* @state: plane state object to release
@@ -2166,9 +2236,7 @@ EXPORT_SYMBOL(drm_atomic_helper_plane_duplicate_state);
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
- if (state->fb)
- drm_framebuffer_unreference(state->fb);
-
+ __drm_atomic_helper_plane_destroy_state(plane, state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state);
@@ -2192,6 +2260,22 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
/**
+ * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
+ * @connector: connector object
+ * @state: atomic connector state
+ *
+ * Copies atomic state from a connector's current state. This is useful for
+ * drivers that subclass the connector state.
+ */
+void
+__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ memcpy(state, connector->state, sizeof(*state));
+}
+EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
+
+/**
* drm_atomic_helper_connector_duplicate_state - default state duplicate hook
* @connector: drm connector
*
@@ -2201,14 +2285,41 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_reset);
struct drm_connector_state *
drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector)
{
+ struct drm_connector_state *state;
+
if (WARN_ON(!connector->state))
return NULL;
- return kmemdup(connector->state, sizeof(*connector->state), GFP_KERNEL);
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state)
+ __drm_atomic_helper_connector_duplicate_state(connector, state);
+
+ return state;
}
EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
/**
+ * __drm_atomic_helper_connector_destroy_state - release connector state
+ * @connector: connector object
+ * @state: connector state object to release
+ *
+ * Releases all resources stored in the connector state without actually
+ * freeing the memory of the connector state. This is useful for drivers that
+ * subclass the connector state.
+ */
+void
+__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
+ struct drm_connector_state *state)
+{
+ /*
+ * This is currently a placeholder so that drivers that subclass the
+ * state will automatically do the right thing if code is ever added
+ * to this function.
+ */
+}
+EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state);
+
+/**
* drm_atomic_helper_connector_destroy_state - default state destroy hook
* @connector: drm connector
* @state: connector state object to release
@@ -2219,6 +2330,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
{
+ __drm_atomic_helper_connector_destroy_state(connector, state);
kfree(state);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index a94b11f7859d..b41965c2888d 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -271,18 +271,6 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
.best_encoder = omap_connector_attached_encoder,
};
-/* flush an area of the framebuffer (in case of manual update display that
- * is not automatically flushed)
- */
-void omap_connector_flush(struct drm_connector *connector,
- int x, int y, int w, int h)
-{
- struct omap_connector *omap_connector = to_omap_connector(connector);
-
- /* TODO: enable when supported in dss */
- VERB("%s: %d,%d, %dx%d", omap_connector->dssdev->name, x, y, w, h);
-}
-
/* initialize connector */
struct drm_connector *omap_connector_init(struct drm_device *dev,
int connector_type, struct omap_dss_device *dssdev,
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index b0566a1ca28f..f456544bf300 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -28,7 +28,6 @@
struct omap_crtc {
struct drm_crtc base;
- struct drm_plane *plane;
const char *name;
int pipe;
@@ -46,7 +45,6 @@ struct omap_crtc {
struct omap_video_timings timings;
bool enabled;
- bool full_update;
struct omap_drm_apply apply;
@@ -74,8 +72,14 @@ struct omap_crtc {
* XXX maybe fold into apply_work??
*/
struct work_struct page_flip_work;
+
+ bool ignore_digit_sync_lost;
};
+/* -----------------------------------------------------------------------------
+ * Helper Functions
+ */
+
uint32_t pipe2vbl(struct drm_crtc *crtc)
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
@@ -83,6 +87,22 @@ uint32_t pipe2vbl(struct drm_crtc *crtc)
return dispc_mgr_get_vsync_irq(omap_crtc->channel);
}
+const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ return &omap_crtc->timings;
+}
+
+enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ return omap_crtc->channel;
+}
+
+/* -----------------------------------------------------------------------------
+ * DSS Manager Functions
+ */
+
/*
* Manager-ops, callbacks from output when they need to configure
* the upstream part of the video pipe.
@@ -122,7 +142,63 @@ static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
{
}
-static void set_enabled(struct drm_crtc *crtc, bool enable);
+/* Called only from CRTC pre_apply and suspend/resume handlers. */
+static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
+{
+ struct drm_device *dev = crtc->dev;
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ enum omap_channel channel = omap_crtc->channel;
+ struct omap_irq_wait *wait;
+ u32 framedone_irq, vsync_irq;
+ int ret;
+
+ if (dispc_mgr_is_enabled(channel) == enable)
+ return;
+
+ if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
+ /*
+ * Digit output produces some sync lost interrupts during the
+ * first frame when enabling, so we need to ignore those.
+ */
+ omap_crtc->ignore_digit_sync_lost = true;
+ }
+
+ framedone_irq = dispc_mgr_get_framedone_irq(channel);
+ vsync_irq = dispc_mgr_get_vsync_irq(channel);
+
+ if (enable) {
+ wait = omap_irq_wait_init(dev, vsync_irq, 1);
+ } else {
+ /*
+ * When we disable the digit output, we need to wait for
+ * FRAMEDONE to know that DISPC has finished with the output.
+ *
+ * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
+ * that case we need to use vsync interrupt, and wait for both
+ * even and odd frames.
+ */
+
+ if (framedone_irq)
+ wait = omap_irq_wait_init(dev, framedone_irq, 1);
+ else
+ wait = omap_irq_wait_init(dev, vsync_irq, 2);
+ }
+
+ dispc_mgr_enable(channel, enable);
+
+ ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
+ if (ret) {
+ dev_err(dev->dev, "%s: timeout waiting for %s\n",
+ omap_crtc->name, enable ? "enable" : "disable");
+ }
+
+ if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
+ omap_crtc->ignore_digit_sync_lost = false;
+ /* make sure the irq handler sees the value above */
+ mb();
+ }
+}
+
static int omap_crtc_enable(struct omap_overlay_manager *mgr)
{
@@ -131,7 +207,7 @@ static int omap_crtc_enable(struct omap_overlay_manager *mgr)
dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
dispc_mgr_set_timings(omap_crtc->channel,
&omap_crtc->timings);
- set_enabled(&omap_crtc->base, true);
+ omap_crtc_set_enabled(&omap_crtc->base, true);
return 0;
}
@@ -140,7 +216,7 @@ static void omap_crtc_disable(struct omap_overlay_manager *mgr)
{
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
- set_enabled(&omap_crtc->base, false);
+ omap_crtc_set_enabled(&omap_crtc->base, false);
}
static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
@@ -149,7 +225,6 @@ static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
struct omap_crtc *omap_crtc = omap_crtcs[mgr->id];
DBG("%s", omap_crtc->name);
omap_crtc->timings = *timings;
- omap_crtc->full_update = true;
}
static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
@@ -174,19 +249,201 @@ static void omap_crtc_unregister_framedone_handler(
}
static const struct dss_mgr_ops mgr_ops = {
- .connect = omap_crtc_connect,
- .disconnect = omap_crtc_disconnect,
- .start_update = omap_crtc_start_update,
- .enable = omap_crtc_enable,
- .disable = omap_crtc_disable,
- .set_timings = omap_crtc_set_timings,
- .set_lcd_config = omap_crtc_set_lcd_config,
- .register_framedone_handler = omap_crtc_register_framedone_handler,
- .unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
+ .connect = omap_crtc_connect,
+ .disconnect = omap_crtc_disconnect,
+ .start_update = omap_crtc_start_update,
+ .enable = omap_crtc_enable,
+ .disable = omap_crtc_disable,
+ .set_timings = omap_crtc_set_timings,
+ .set_lcd_config = omap_crtc_set_lcd_config,
+ .register_framedone_handler = omap_crtc_register_framedone_handler,
+ .unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
};
-/*
- * CRTC funcs:
+/* -----------------------------------------------------------------------------
+ * Apply Logic
+ */
+
+static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+{
+ struct omap_crtc *omap_crtc =
+ container_of(irq, struct omap_crtc, error_irq);
+
+ if (omap_crtc->ignore_digit_sync_lost) {
+ irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
+ if (!irqstatus)
+ return;
+ }
+
+ DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
+}
+
+static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
+{
+ struct omap_crtc *omap_crtc =
+ container_of(irq, struct omap_crtc, apply_irq);
+ struct drm_crtc *crtc = &omap_crtc->base;
+
+ if (!dispc_mgr_go_busy(omap_crtc->channel)) {
+ struct omap_drm_private *priv =
+ crtc->dev->dev_private;
+ DBG("%s: apply done", omap_crtc->name);
+ __omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
+ queue_work(priv->wq, &omap_crtc->apply_work);
+ }
+}
+
+static void apply_worker(struct work_struct *work)
+{
+ struct omap_crtc *omap_crtc =
+ container_of(work, struct omap_crtc, apply_work);
+ struct drm_crtc *crtc = &omap_crtc->base;
+ struct drm_device *dev = crtc->dev;
+ struct omap_drm_apply *apply, *n;
+ bool need_apply;
+
+ /*
+ * Synchronize everything on mode_config.mutex, to keep
+ * the callbacks and list modification all serialized
+ * with respect to modesetting ioctls from userspace.
+ */
+ drm_modeset_lock(&crtc->mutex, NULL);
+ dispc_runtime_get();
+
+ /*
+ * If we are still pending a previous update, wait.. when the
+ * pending update completes, we get kicked again.
+ */
+ if (omap_crtc->apply_irq.registered)
+ goto out;
+
+ /* finish up previous apply's: */
+ list_for_each_entry_safe(apply, n,
+ &omap_crtc->pending_applies, pending_node) {
+ apply->post_apply(apply);
+ list_del(&apply->pending_node);
+ }
+
+ need_apply = !list_empty(&omap_crtc->queued_applies);
+
+ /* then handle the next round of of queued apply's: */
+ list_for_each_entry_safe(apply, n,
+ &omap_crtc->queued_applies, queued_node) {
+ apply->pre_apply(apply);
+ list_del(&apply->queued_node);
+ apply->queued = false;
+ list_add_tail(&apply->pending_node,
+ &omap_crtc->pending_applies);
+ }
+
+ if (need_apply) {
+ enum omap_channel channel = omap_crtc->channel;
+
+ DBG("%s: GO", omap_crtc->name);
+
+ if (dispc_mgr_is_enabled(channel)) {
+ dispc_mgr_go(channel);
+ omap_irq_register(dev, &omap_crtc->apply_irq);
+ } else {
+ struct omap_drm_private *priv = dev->dev_private;
+ queue_work(priv->wq, &omap_crtc->apply_work);
+ }
+ }
+
+out:
+ dispc_runtime_put();
+ drm_modeset_unlock(&crtc->mutex);
+}
+
+int omap_crtc_apply(struct drm_crtc *crtc,
+ struct omap_drm_apply *apply)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+
+ WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
+
+ /* no need to queue it again if it is already queued: */
+ if (apply->queued)
+ return 0;
+
+ apply->queued = true;
+ list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
+
+ /*
+ * If there are no currently pending updates, then go ahead and
+ * kick the worker immediately, otherwise it will run again when
+ * the current update finishes.
+ */
+ if (list_empty(&omap_crtc->pending_applies)) {
+ struct omap_drm_private *priv = crtc->dev->dev_private;
+ queue_work(priv->wq, &omap_crtc->apply_work);
+ }
+
+ return 0;
+}
+
+static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
+{
+ struct omap_crtc *omap_crtc =
+ container_of(apply, struct omap_crtc, apply);
+ struct drm_crtc *crtc = &omap_crtc->base;
+ struct omap_drm_private *priv = crtc->dev->dev_private;
+ struct drm_encoder *encoder = NULL;
+ unsigned int i;
+
+ DBG("%s: enabled=%d", omap_crtc->name, omap_crtc->enabled);
+
+ for (i = 0; i < priv->num_encoders; i++) {
+ if (priv->encoders[i]->crtc == crtc) {
+ encoder = priv->encoders[i];
+ break;
+ }
+ }
+
+ if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder)
+ omap_encoder_set_enabled(omap_crtc->current_encoder, false);
+
+ omap_crtc->current_encoder = encoder;
+
+ if (!omap_crtc->enabled) {
+ if (encoder)
+ omap_encoder_set_enabled(encoder, false);
+ } else {
+ if (encoder) {
+ omap_encoder_set_enabled(encoder, false);
+ omap_encoder_update(encoder, omap_crtc->mgr,
+ &omap_crtc->timings);
+ omap_encoder_set_enabled(encoder, true);
+ }
+ }
+}
+
+static void omap_crtc_post_apply(struct omap_drm_apply *apply)
+{
+ /* nothing needed for post-apply */
+}
+
+void omap_crtc_flush(struct drm_crtc *crtc)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ int loops = 0;
+
+ while (!list_empty(&omap_crtc->pending_applies) ||
+ !list_empty(&omap_crtc->queued_applies) ||
+ omap_crtc->event || omap_crtc->old_fb) {
+
+ if (++loops > 10) {
+ dev_err(crtc->dev->dev,
+ "omap_crtc_flush() timeout\n");
+ break;
+ }
+
+ schedule_timeout_uninterruptible(msecs_to_jiffies(20));
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * CRTC Functions
*/
static void omap_crtc_destroy(struct drm_crtc *crtc)
@@ -214,17 +471,13 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
if (enabled != omap_crtc->enabled) {
omap_crtc->enabled = enabled;
- omap_crtc->full_update = true;
omap_crtc_apply(crtc, &omap_crtc->apply);
- /* also enable our private plane: */
- WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
-
- /* and any attached overlay planes: */
+ /* Enable/disable all planes associated with the CRTC. */
for (i = 0; i < priv->num_planes; i++) {
struct drm_plane *plane = priv->planes[i];
if (plane->crtc == crtc)
- WARN_ON(omap_plane_dpms(plane, mode));
+ WARN_ON(omap_plane_set_enable(plane, enabled));
}
}
}
@@ -256,13 +509,17 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc,
mode->type, mode->flags);
copy_timings_drm_to_omap(&omap_crtc->timings, mode);
- omap_crtc->full_update = true;
- return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
- 0, 0, mode->hdisplay, mode->vdisplay,
- x << 16, y << 16,
- mode->hdisplay << 16, mode->vdisplay << 16,
- NULL, NULL);
+ /*
+ * The primary plane CRTC can be reset if the plane is disabled directly
+ * through the universal plane API. Set it again here.
+ */
+ crtc->primary->crtc = crtc;
+
+ return omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
+ 0, 0, mode->hdisplay, mode->vdisplay,
+ x, y, mode->hdisplay, mode->vdisplay,
+ NULL, NULL);
}
static void omap_crtc_prepare(struct drm_crtc *crtc)
@@ -282,15 +539,13 @@ static void omap_crtc_commit(struct drm_crtc *crtc)
static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- struct drm_plane *plane = omap_crtc->plane;
+ struct drm_plane *plane = crtc->primary;
struct drm_display_mode *mode = &crtc->mode;
return omap_plane_mode_set(plane, crtc, crtc->primary->fb,
- 0, 0, mode->hdisplay, mode->vdisplay,
- x << 16, y << 16,
- mode->hdisplay << 16, mode->vdisplay << 16,
- NULL, NULL);
+ 0, 0, mode->hdisplay, mode->vdisplay,
+ x, y, mode->hdisplay, mode->vdisplay,
+ NULL, NULL);
}
static void vblank_cb(void *arg)
@@ -299,6 +554,7 @@ static void vblank_cb(void *arg)
struct drm_device *dev = crtc->dev;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
unsigned long flags;
+ struct drm_framebuffer *fb;
spin_lock_irqsave(&dev->event_lock, flags);
@@ -306,10 +562,15 @@ static void vblank_cb(void *arg)
if (omap_crtc->event)
drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
+ fb = omap_crtc->old_fb;
+
omap_crtc->event = NULL;
omap_crtc->old_fb = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ if (fb)
+ drm_framebuffer_unreference(fb);
}
static void page_flip_worker(struct work_struct *work)
@@ -321,11 +582,10 @@ static void page_flip_worker(struct work_struct *work)
struct drm_gem_object *bo;
drm_modeset_lock(&crtc->mutex, NULL);
- omap_plane_mode_set(omap_crtc->plane, crtc, crtc->primary->fb,
- 0, 0, mode->hdisplay, mode->vdisplay,
- crtc->x << 16, crtc->y << 16,
- mode->hdisplay << 16, mode->vdisplay << 16,
- vblank_cb, crtc);
+ omap_plane_mode_set(crtc->primary, crtc, crtc->primary->fb,
+ 0, 0, mode->hdisplay, mode->vdisplay,
+ crtc->x, crtc->y, mode->hdisplay, mode->vdisplay,
+ vblank_cb, crtc);
drm_modeset_unlock(&crtc->mutex);
bo = omap_framebuffer_bo(crtc->primary->fb, 0);
@@ -361,11 +621,12 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
if (omap_crtc->old_fb) {
spin_unlock_irqrestore(&dev->event_lock, flags);
dev_err(dev->dev, "already a pending flip\n");
- return -EINVAL;
+ return -EBUSY;
}
omap_crtc->event = event;
omap_crtc->old_fb = primary->fb = fb;
+ drm_framebuffer_reference(omap_crtc->old_fb);
spin_unlock_irqrestore(&dev->event_lock, flags);
@@ -385,7 +646,6 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
static int omap_crtc_set_property(struct drm_crtc *crtc,
struct drm_property *property, uint64_t val)
{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct omap_drm_private *priv = crtc->dev->dev_private;
if (property == priv->rotation_prop) {
@@ -393,7 +653,7 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
}
- return omap_plane_set_property(omap_crtc->plane, property, val);
+ return omap_plane_set_property(crtc->primary, property, val);
}
static const struct drm_crtc_funcs omap_crtc_funcs = {
@@ -412,256 +672,15 @@ static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
.mode_set_base = omap_crtc_mode_set_base,
};
-const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
-{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- return &omap_crtc->timings;
-}
-
-enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
-{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- return omap_crtc->channel;
-}
-
-static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
-{
- struct omap_crtc *omap_crtc =
- container_of(irq, struct omap_crtc, error_irq);
- struct drm_crtc *crtc = &omap_crtc->base;
- DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
- /* avoid getting in a flood, unregister the irq until next vblank */
- __omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
-}
-
-static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
-{
- struct omap_crtc *omap_crtc =
- container_of(irq, struct omap_crtc, apply_irq);
- struct drm_crtc *crtc = &omap_crtc->base;
-
- if (!omap_crtc->error_irq.registered)
- __omap_irq_register(crtc->dev, &omap_crtc->error_irq);
-
- if (!dispc_mgr_go_busy(omap_crtc->channel)) {
- struct omap_drm_private *priv =
- crtc->dev->dev_private;
- DBG("%s: apply done", omap_crtc->name);
- __omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
- queue_work(priv->wq, &omap_crtc->apply_work);
- }
-}
-
-static void apply_worker(struct work_struct *work)
-{
- struct omap_crtc *omap_crtc =
- container_of(work, struct omap_crtc, apply_work);
- struct drm_crtc *crtc = &omap_crtc->base;
- struct drm_device *dev = crtc->dev;
- struct omap_drm_apply *apply, *n;
- bool need_apply;
-
- /*
- * Synchronize everything on mode_config.mutex, to keep
- * the callbacks and list modification all serialized
- * with respect to modesetting ioctls from userspace.
- */
- drm_modeset_lock(&crtc->mutex, NULL);
- dispc_runtime_get();
-
- /*
- * If we are still pending a previous update, wait.. when the
- * pending update completes, we get kicked again.
- */
- if (omap_crtc->apply_irq.registered)
- goto out;
-
- /* finish up previous apply's: */
- list_for_each_entry_safe(apply, n,
- &omap_crtc->pending_applies, pending_node) {
- apply->post_apply(apply);
- list_del(&apply->pending_node);
- }
-
- need_apply = !list_empty(&omap_crtc->queued_applies);
-
- /* then handle the next round of of queued apply's: */
- list_for_each_entry_safe(apply, n,
- &omap_crtc->queued_applies, queued_node) {
- apply->pre_apply(apply);
- list_del(&apply->queued_node);
- apply->queued = false;
- list_add_tail(&apply->pending_node,
- &omap_crtc->pending_applies);
- }
-
- if (need_apply) {
- enum omap_channel channel = omap_crtc->channel;
-
- DBG("%s: GO", omap_crtc->name);
-
- if (dispc_mgr_is_enabled(channel)) {
- omap_irq_register(dev, &omap_crtc->apply_irq);
- dispc_mgr_go(channel);
- } else {
- struct omap_drm_private *priv = dev->dev_private;
- queue_work(priv->wq, &omap_crtc->apply_work);
- }
- }
-
-out:
- dispc_runtime_put();
- drm_modeset_unlock(&crtc->mutex);
-}
-
-int omap_crtc_apply(struct drm_crtc *crtc,
- struct omap_drm_apply *apply)
-{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-
- WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
-
- /* no need to queue it again if it is already queued: */
- if (apply->queued)
- return 0;
-
- apply->queued = true;
- list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
-
- /*
- * If there are no currently pending updates, then go ahead and
- * kick the worker immediately, otherwise it will run again when
- * the current update finishes.
- */
- if (list_empty(&omap_crtc->pending_applies)) {
- struct omap_drm_private *priv = crtc->dev->dev_private;
- queue_work(priv->wq, &omap_crtc->apply_work);
- }
-
- return 0;
-}
-
-/* called only from apply */
-static void set_enabled(struct drm_crtc *crtc, bool enable)
-{
- struct drm_device *dev = crtc->dev;
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- enum omap_channel channel = omap_crtc->channel;
- struct omap_irq_wait *wait;
- u32 framedone_irq, vsync_irq;
- int ret;
-
- if (dispc_mgr_is_enabled(channel) == enable)
- return;
-
- /*
- * Digit output produces some sync lost interrupts during the first
- * frame when enabling, so we need to ignore those.
- */
- omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
-
- framedone_irq = dispc_mgr_get_framedone_irq(channel);
- vsync_irq = dispc_mgr_get_vsync_irq(channel);
-
- if (enable) {
- wait = omap_irq_wait_init(dev, vsync_irq, 1);
- } else {
- /*
- * When we disable the digit output, we need to wait for
- * FRAMEDONE to know that DISPC has finished with the output.
- *
- * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
- * that case we need to use vsync interrupt, and wait for both
- * even and odd frames.
- */
-
- if (framedone_irq)
- wait = omap_irq_wait_init(dev, framedone_irq, 1);
- else
- wait = omap_irq_wait_init(dev, vsync_irq, 2);
- }
-
- dispc_mgr_enable(channel, enable);
-
- ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
- if (ret) {
- dev_err(dev->dev, "%s: timeout waiting for %s\n",
- omap_crtc->name, enable ? "enable" : "disable");
- }
-
- omap_irq_register(crtc->dev, &omap_crtc->error_irq);
-}
-
-static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
-{
- struct omap_crtc *omap_crtc =
- container_of(apply, struct omap_crtc, apply);
- struct drm_crtc *crtc = &omap_crtc->base;
- struct drm_encoder *encoder = NULL;
-
- DBG("%s: enabled=%d, full=%d", omap_crtc->name,
- omap_crtc->enabled, omap_crtc->full_update);
-
- if (omap_crtc->full_update) {
- struct omap_drm_private *priv = crtc->dev->dev_private;
- int i;
- for (i = 0; i < priv->num_encoders; i++) {
- if (priv->encoders[i]->crtc == crtc) {
- encoder = priv->encoders[i];
- break;
- }
- }
- }
-
- if (omap_crtc->current_encoder && encoder != omap_crtc->current_encoder)
- omap_encoder_set_enabled(omap_crtc->current_encoder, false);
-
- omap_crtc->current_encoder = encoder;
-
- if (!omap_crtc->enabled) {
- if (encoder)
- omap_encoder_set_enabled(encoder, false);
- } else {
- if (encoder) {
- omap_encoder_set_enabled(encoder, false);
- omap_encoder_update(encoder, omap_crtc->mgr,
- &omap_crtc->timings);
- omap_encoder_set_enabled(encoder, true);
- }
- }
-
- omap_crtc->full_update = false;
-}
-
-static void omap_crtc_post_apply(struct omap_drm_apply *apply)
-{
- /* nothing needed for post-apply */
-}
-
-void omap_crtc_flush(struct drm_crtc *crtc)
-{
- struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- int loops = 0;
-
- while (!list_empty(&omap_crtc->pending_applies) ||
- !list_empty(&omap_crtc->queued_applies) ||
- omap_crtc->event || omap_crtc->old_fb) {
-
- if (++loops > 10) {
- dev_err(crtc->dev->dev,
- "omap_crtc_flush() timeout\n");
- break;
- }
-
- schedule_timeout_uninterruptible(msecs_to_jiffies(20));
- }
-}
+/* -----------------------------------------------------------------------------
+ * Init and Cleanup
+ */
static const char *channel_names[] = {
- [OMAP_DSS_CHANNEL_LCD] = "lcd",
- [OMAP_DSS_CHANNEL_DIGIT] = "tv",
- [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
- [OMAP_DSS_CHANNEL_LCD3] = "lcd3",
+ [OMAP_DSS_CHANNEL_LCD] = "lcd",
+ [OMAP_DSS_CHANNEL_DIGIT] = "tv",
+ [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
+ [OMAP_DSS_CHANNEL_LCD3] = "lcd3",
};
void omap_crtc_pre_init(void)
@@ -681,12 +700,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
struct drm_crtc *crtc = NULL;
struct omap_crtc *omap_crtc;
struct omap_overlay_manager_info *info;
+ int ret;
DBG("%s", channel_names[channel]);
omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
if (!omap_crtc)
- goto fail;
+ return NULL;
crtc = &omap_crtc->base;
@@ -700,8 +720,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
omap_crtc->apply.post_apply = omap_crtc_post_apply;
omap_crtc->channel = channel;
- omap_crtc->plane = plane;
- omap_crtc->plane->crtc = crtc;
omap_crtc->name = channel_names[channel];
omap_crtc->pipe = id;
@@ -723,18 +741,18 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
info->trans_enabled = false;
- drm_crtc_init(dev, crtc, &omap_crtc_funcs);
+ ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
+ &omap_crtc_funcs);
+ if (ret < 0) {
+ kfree(omap_crtc);
+ return NULL;
+ }
+
drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
- omap_plane_install_properties(omap_crtc->plane, &crtc->base);
+ omap_plane_install_properties(crtc->primary, &crtc->base);
omap_crtcs[channel] = omap_crtc;
return crtc;
-
-fail:
- if (crtc)
- omap_crtc_destroy(crtc);
-
- return NULL;
}
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
index 58bcd6ae0255..9f32a83ca507 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h
@@ -148,11 +148,15 @@ struct refill_engine {
bool async;
- wait_queue_head_t wait_for_refill;
+ struct completion compl;
struct list_head idle_node;
};
+struct dmm_platform_data {
+ uint32_t cpu_cache_flags;
+};
+
struct dmm {
struct device *dev;
void __iomem *base;
@@ -183,6 +187,8 @@ struct dmm {
/* allocation list and lock */
struct list_head alloc_head;
+
+ const struct dmm_platform_data *plat_data;
};
#endif
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 56c60552abba..042038e8a662 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -29,6 +29,7 @@
#include <linux/mm.h>
#include <linux/time.h>
#include <linux/list.h>
+#include <linux/completion.h>
#include "omap_dmm_tiler.h"
#include "omap_dmm_priv.h"
@@ -39,6 +40,10 @@
static struct tcm *containers[TILFMT_NFORMATS];
static struct dmm *omap_dmm;
+#if defined(CONFIG_OF)
+static const struct of_device_id dmm_of_match[];
+#endif
+
/* global spinlock for protecting lists */
static DEFINE_SPINLOCK(list_lock);
@@ -58,19 +63,19 @@ static const struct {
uint32_t slot_w; /* width of each slot (in pixels) */
uint32_t slot_h; /* height of each slot (in pixels) */
} geom[TILFMT_NFORMATS] = {
- [TILFMT_8BIT] = GEOM(0, 0, 1),
- [TILFMT_16BIT] = GEOM(0, 1, 2),
- [TILFMT_32BIT] = GEOM(1, 1, 4),
- [TILFMT_PAGE] = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
+ [TILFMT_8BIT] = GEOM(0, 0, 1),
+ [TILFMT_16BIT] = GEOM(0, 1, 2),
+ [TILFMT_32BIT] = GEOM(1, 1, 4),
+ [TILFMT_PAGE] = GEOM(SLOT_WIDTH_BITS, SLOT_HEIGHT_BITS, 1),
};
/* lookup table for registers w/ per-engine instances */
static const uint32_t reg[][4] = {
- [PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
- DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
- [PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
- DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
+ [PAT_STATUS] = {DMM_PAT_STATUS__0, DMM_PAT_STATUS__1,
+ DMM_PAT_STATUS__2, DMM_PAT_STATUS__3},
+ [PAT_DESCR] = {DMM_PAT_DESCR__0, DMM_PAT_DESCR__1,
+ DMM_PAT_DESCR__2, DMM_PAT_DESCR__3},
};
/* simple allocator to grab next 16 byte aligned memory from txn */
@@ -142,10 +147,10 @@ static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
for (i = 0; i < dmm->num_engines; i++) {
if (status & DMM_IRQSTAT_LST) {
- wake_up_interruptible(&dmm->engines[i].wait_for_refill);
-
if (dmm->engines[i].async)
release_engine(&dmm->engines[i]);
+
+ complete(&dmm->engines[i].compl);
}
status >>= 8;
@@ -269,15 +274,17 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait)
/* mark whether it is async to denote list management in IRQ handler */
engine->async = wait ? false : true;
+ reinit_completion(&engine->compl);
+ /* verify that the irq handler sees the 'async' and completion value */
+ smp_mb();
/* kick reload */
writel(engine->refill_pa,
dmm->base + reg[PAT_DESCR][engine->id]);
if (wait) {
- if (wait_event_interruptible_timeout(engine->wait_for_refill,
- wait_status(engine, DMM_PATSTATUS_READY) == 0,
- msecs_to_jiffies(1)) <= 0) {
+ if (!wait_for_completion_timeout(&engine->compl,
+ msecs_to_jiffies(1))) {
dev_err(dmm->dev, "timed out waiting for done\n");
ret = -ETIMEDOUT;
}
@@ -529,6 +536,11 @@ size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h)
return round_up(geom[fmt].cpp * w, PAGE_SIZE) * h;
}
+uint32_t tiler_get_cpu_cache_flags(void)
+{
+ return omap_dmm->plat_data->cpu_cache_flags;
+}
+
bool dmm_is_available(void)
{
return omap_dmm ? true : false;
@@ -592,6 +604,18 @@ static int omap_dmm_probe(struct platform_device *dev)
init_waitqueue_head(&omap_dmm->engine_queue);
+ if (dev->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_node(dmm_of_match, dev->dev.of_node);
+ if (!match) {
+ dev_err(&dev->dev, "failed to find matching device node\n");
+ return -ENODEV;
+ }
+
+ omap_dmm->plat_data = match->data;
+ }
+
/* lookup hwmod data - base address and irq */
mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!mem) {
@@ -696,7 +720,7 @@ static int omap_dmm_probe(struct platform_device *dev)
(REFILL_BUFFER_SIZE * i);
omap_dmm->engines[i].refill_pa = omap_dmm->refill_pa +
(REFILL_BUFFER_SIZE * i);
- init_waitqueue_head(&omap_dmm->engines[i].wait_for_refill);
+ init_completion(&omap_dmm->engines[i].compl);
list_add(&omap_dmm->engines[i].idle_node, &omap_dmm->idle_head);
}
@@ -941,7 +965,7 @@ error:
}
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int omap_dmm_resume(struct device *dev)
{
struct tcm_area area;
@@ -965,16 +989,28 @@ static int omap_dmm_resume(struct device *dev)
return 0;
}
-
-static const struct dev_pm_ops omap_dmm_pm_ops = {
- .resume = omap_dmm_resume,
-};
#endif
+static SIMPLE_DEV_PM_OPS(omap_dmm_pm_ops, NULL, omap_dmm_resume);
+
#if defined(CONFIG_OF)
+static const struct dmm_platform_data dmm_omap4_platform_data = {
+ .cpu_cache_flags = OMAP_BO_WC,
+};
+
+static const struct dmm_platform_data dmm_omap5_platform_data = {
+ .cpu_cache_flags = OMAP_BO_UNCACHED,
+};
+
static const struct of_device_id dmm_of_match[] = {
- { .compatible = "ti,omap4-dmm", },
- { .compatible = "ti,omap5-dmm", },
+ {
+ .compatible = "ti,omap4-dmm",
+ .data = &dmm_omap4_platform_data,
+ },
+ {
+ .compatible = "ti,omap5-dmm",
+ .data = &dmm_omap5_platform_data,
+ },
{},
};
#endif
@@ -986,9 +1022,7 @@ struct platform_driver omap_dmm_driver = {
.owner = THIS_MODULE,
.name = DMM_DRIVER_NAME,
.of_match_table = of_match_ptr(dmm_of_match),
-#ifdef CONFIG_PM
.pm = &omap_dmm_pm_ops,
-#endif
},
};
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
index 4fdd61e54bd2..e83c78372db8 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h
@@ -106,6 +106,7 @@ uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient);
size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
+uint32_t tiler_get_cpu_cache_flags(void);
bool dmm_is_available(void);
extern struct platform_driver omap_dmm_driver;
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 8241ed9b353c..94920d47e3b6 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -128,6 +128,29 @@ cleanup:
return r;
}
+static int omap_modeset_create_crtc(struct drm_device *dev, int id,
+ enum omap_channel channel)
+{
+ struct omap_drm_private *priv = dev->dev_private;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+
+ plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_PRIMARY);
+ if (IS_ERR(plane))
+ return PTR_ERR(plane);
+
+ crtc = omap_crtc_init(dev, plane, channel, id);
+
+ BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
+ priv->crtcs[id] = crtc;
+ priv->num_crtcs++;
+
+ priv->planes[id] = plane;
+ priv->num_planes++;
+
+ return 0;
+}
+
static int omap_modeset_init(struct drm_device *dev)
{
struct omap_drm_private *priv = dev->dev_private;
@@ -136,6 +159,7 @@ static int omap_modeset_init(struct drm_device *dev)
int num_mgrs = dss_feat_get_num_mgrs();
int num_crtcs;
int i, id = 0;
+ int ret;
drm_mode_config_init(dev);
@@ -209,18 +233,13 @@ static int omap_modeset_init(struct drm_device *dev)
* allocated crtc, we create a new crtc for it
*/
if (!channel_used(dev, channel)) {
- struct drm_plane *plane;
- struct drm_crtc *crtc;
-
- plane = omap_plane_init(dev, id, true);
- crtc = omap_crtc_init(dev, plane, channel, id);
-
- BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
- priv->crtcs[id] = crtc;
- priv->num_crtcs++;
-
- priv->planes[id] = plane;
- priv->num_planes++;
+ ret = omap_modeset_create_crtc(dev, id, channel);
+ if (ret < 0) {
+ dev_err(dev->dev,
+ "could not create CRTC (channel %u)\n",
+ channel);
+ return ret;
+ }
id++;
}
@@ -234,26 +253,8 @@ static int omap_modeset_init(struct drm_device *dev)
/* find a free manager for this crtc */
for (i = 0; i < num_mgrs; i++) {
- if (!channel_used(dev, i)) {
- struct drm_plane *plane;
- struct drm_crtc *crtc;
-
- plane = omap_plane_init(dev, id, true);
- crtc = omap_crtc_init(dev, plane, i, id);
-
- BUG_ON(priv->num_crtcs >=
- ARRAY_SIZE(priv->crtcs));
-
- priv->crtcs[id] = crtc;
- priv->num_crtcs++;
-
- priv->planes[id] = plane;
- priv->num_planes++;
-
+ if (!channel_used(dev, i))
break;
- } else {
- continue;
- }
}
if (i == num_mgrs) {
@@ -261,13 +262,24 @@ static int omap_modeset_init(struct drm_device *dev)
dev_err(dev->dev, "no managers left for crtc\n");
return -ENOMEM;
}
+
+ ret = omap_modeset_create_crtc(dev, id, i);
+ if (ret < 0) {
+ dev_err(dev->dev,
+ "could not create CRTC (channel %u)\n", i);
+ return ret;
+ }
}
/*
* Create normal planes for the remaining overlays:
*/
for (; id < num_ovls; id++) {
- struct drm_plane *plane = omap_plane_init(dev, id, false);
+ struct drm_plane *plane;
+
+ plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_OVERLAY);
+ if (IS_ERR(plane))
+ return PTR_ERR(plane);
BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
priv->planes[priv->num_planes++] = plane;
@@ -286,14 +298,13 @@ static int omap_modeset_init(struct drm_device *dev)
for (id = 0; id < priv->num_crtcs; id++) {
struct drm_crtc *crtc = priv->crtcs[id];
enum omap_channel crtc_channel;
- enum omap_dss_output_id supported_outputs;
crtc_channel = omap_crtc_channel(crtc);
- supported_outputs =
- dss_feat_get_supported_outputs(crtc_channel);
- if (supported_outputs & output->id)
+ if (output->dispc_channel == crtc_channel) {
encoder->possible_crtcs |= (1 << id);
+ break;
+ }
}
omap_dss_put_device(output);
@@ -480,6 +491,7 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
priv->wq = alloc_ordered_workqueue("omapdrm", 0);
+ spin_lock_init(&priv->list_lock);
INIT_LIST_HEAD(&priv->obj_list);
omap_gem_init(dev);
@@ -519,7 +531,8 @@ static int dev_unload(struct drm_device *dev)
drm_kms_helper_poll_fini(dev);
- omap_fbdev_free(dev);
+ if (priv->fbdev)
+ omap_fbdev_free(dev);
/* flush crtcs so the fbs get released */
for (i = 0; i < priv->num_crtcs; i++)
@@ -588,9 +601,11 @@ static void dev_lastclose(struct drm_device *dev)
}
}
- ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
- if (ret)
- DBG("failed to restore crtc mode");
+ if (priv->fbdev) {
+ ret = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
+ if (ret)
+ DBG("failed to restore crtc mode");
+ }
}
static void dev_preclose(struct drm_device *dev, struct drm_file *file)
@@ -610,74 +625,57 @@ static const struct vm_operations_struct omap_gem_vm_ops = {
};
static const struct file_operations omapdriver_fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .unlocked_ioctl = drm_ioctl,
- .release = drm_release,
- .mmap = omap_gem_mmap,
- .poll = drm_poll,
- .read = drm_read,
- .llseek = noop_llseek,
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ .mmap = omap_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .llseek = noop_llseek,
};
static struct drm_driver omap_drm_driver = {
- .driver_features =
- DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
- .load = dev_load,
- .unload = dev_unload,
- .open = dev_open,
- .lastclose = dev_lastclose,
- .preclose = dev_preclose,
- .postclose = dev_postclose,
- .set_busid = drm_platform_set_busid,
- .get_vblank_counter = drm_vblank_count,
- .enable_vblank = omap_irq_enable_vblank,
- .disable_vblank = omap_irq_disable_vblank,
- .irq_preinstall = omap_irq_preinstall,
- .irq_postinstall = omap_irq_postinstall,
- .irq_uninstall = omap_irq_uninstall,
- .irq_handler = omap_irq_handler,
+ .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM
+ | DRIVER_PRIME,
+ .load = dev_load,
+ .unload = dev_unload,
+ .open = dev_open,
+ .lastclose = dev_lastclose,
+ .preclose = dev_preclose,
+ .postclose = dev_postclose,
+ .set_busid = drm_platform_set_busid,
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = omap_irq_enable_vblank,
+ .disable_vblank = omap_irq_disable_vblank,
+ .irq_preinstall = omap_irq_preinstall,
+ .irq_postinstall = omap_irq_postinstall,
+ .irq_uninstall = omap_irq_uninstall,
+ .irq_handler = omap_irq_handler,
#ifdef CONFIG_DEBUG_FS
- .debugfs_init = omap_debugfs_init,
- .debugfs_cleanup = omap_debugfs_cleanup,
+ .debugfs_init = omap_debugfs_init,
+ .debugfs_cleanup = omap_debugfs_cleanup,
#endif
- .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
- .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_export = omap_gem_prime_export,
- .gem_prime_import = omap_gem_prime_import,
- .gem_free_object = omap_gem_free_object,
- .gem_vm_ops = &omap_gem_vm_ops,
- .dumb_create = omap_gem_dumb_create,
- .dumb_map_offset = omap_gem_dumb_map_offset,
- .dumb_destroy = drm_gem_dumb_destroy,
- .ioctls = ioctls,
- .num_ioctls = DRM_OMAP_NUM_IOCTLS,
- .fops = &omapdriver_fops,
- .name = DRIVER_NAME,
- .desc = DRIVER_DESC,
- .date = DRIVER_DATE,
- .major = DRIVER_MAJOR,
- .minor = DRIVER_MINOR,
- .patchlevel = DRIVER_PATCHLEVEL,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = omap_gem_prime_export,
+ .gem_prime_import = omap_gem_prime_import,
+ .gem_free_object = omap_gem_free_object,
+ .gem_vm_ops = &omap_gem_vm_ops,
+ .dumb_create = omap_gem_dumb_create,
+ .dumb_map_offset = omap_gem_dumb_map_offset,
+ .dumb_destroy = drm_gem_dumb_destroy,
+ .ioctls = ioctls,
+ .num_ioctls = DRM_OMAP_NUM_IOCTLS,
+ .fops = &omapdriver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
};
-static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
-{
- DBG("");
- return 0;
-}
-
-static int pdev_resume(struct platform_device *device)
-{
- DBG("");
- return 0;
-}
-
-static void pdev_shutdown(struct platform_device *device)
-{
- DBG("");
-}
-
static int pdev_probe(struct platform_device *device)
{
int r;
@@ -709,24 +707,35 @@ static int pdev_remove(struct platform_device *device)
return 0;
}
-#ifdef CONFIG_PM
-static const struct dev_pm_ops omapdrm_pm_ops = {
- .resume = omap_gem_resume,
-};
+#ifdef CONFIG_PM_SLEEP
+static int omap_drm_suspend(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ drm_kms_helper_poll_disable(drm_dev);
+
+ return 0;
+}
+
+static int omap_drm_resume(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_get_drvdata(dev);
+
+ drm_kms_helper_poll_enable(drm_dev);
+
+ return omap_gem_resume(dev);
+}
#endif
+static SIMPLE_DEV_PM_OPS(omapdrm_pm_ops, omap_drm_suspend, omap_drm_resume);
+
static struct platform_driver pdev = {
- .driver = {
- .name = DRIVER_NAME,
-#ifdef CONFIG_PM
- .pm = &omapdrm_pm_ops,
-#endif
- },
- .probe = pdev_probe,
- .remove = pdev_remove,
- .suspend = pdev_suspend,
- .resume = pdev_resume,
- .shutdown = pdev_shutdown,
+ .driver = {
+ .name = DRIVER_NAME,
+ .pm = &omapdrm_pm_ops,
+ },
+ .probe = pdev_probe,
+ .remove = pdev_remove,
};
static int __init omap_drm_init(void)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
index 60e47b33c801..b31c79f15aed 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
@@ -105,6 +105,9 @@ struct omap_drm_private {
struct workqueue_struct *wq;
+ /* lock for obj_list below */
+ spinlock_t list_lock;
+
/* list of GEM objects: */
struct list_head obj_list;
@@ -160,15 +163,15 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
void omap_crtc_flush(struct drm_crtc *crtc);
struct drm_plane *omap_plane_init(struct drm_device *dev,
- int plane_id, bool private_plane);
-int omap_plane_dpms(struct drm_plane *plane, int mode);
+ int id, enum drm_plane_type type);
+int omap_plane_set_enable(struct drm_plane *plane, bool enable);
int omap_plane_mode_set(struct drm_plane *plane,
- struct drm_crtc *crtc, struct drm_framebuffer *fb,
- int crtc_x, int crtc_y,
- unsigned int crtc_w, unsigned int crtc_h,
- uint32_t src_x, uint32_t src_y,
- uint32_t src_w, uint32_t src_h,
- void (*fxn)(void *), void *arg);
+ struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ unsigned int src_x, unsigned int src_y,
+ unsigned int src_w, unsigned int src_h,
+ void (*fxn)(void *), void *arg);
void omap_plane_install_properties(struct drm_plane *plane,
struct drm_mode_object *obj);
int omap_plane_set_property(struct drm_plane *plane,
@@ -186,8 +189,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
struct drm_encoder *encoder);
struct drm_encoder *omap_connector_attached_encoder(
struct drm_connector *connector);
-void omap_connector_flush(struct drm_connector *connector,
- int x, int y, int w, int h);
bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
void copy_timings_omap_to_drm(struct drm_display_mode *mode,
@@ -208,8 +209,6 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
struct omap_drm_window *win, struct omap_overlay_info *info);
struct drm_connector *omap_framebuffer_get_next_connector(
struct drm_framebuffer *fb, struct drm_connector *from);
-void omap_framebuffer_flush(struct drm_framebuffer *fb,
- int x, int y, int w, int h);
void omap_gem_init(struct drm_device *dev);
void omap_gem_deinit(struct drm_device *dev);
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index 2a5cacdc344b..b2c1a29cc12b 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -86,6 +86,7 @@ struct plane {
struct omap_framebuffer {
struct drm_framebuffer base;
+ int pin_count;
const struct format *format;
struct plane planes[4];
};
@@ -121,18 +122,6 @@ static int omap_framebuffer_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned flags, unsigned color,
struct drm_clip_rect *clips, unsigned num_clips)
{
- int i;
-
- drm_modeset_lock_all(fb->dev);
-
- for (i = 0; i < num_clips; i++) {
- omap_framebuffer_flush(fb, clips[i].x1, clips[i].y1,
- clips[i].x2 - clips[i].x1,
- clips[i].y2 - clips[i].y1);
- }
-
- drm_modeset_unlock_all(fb->dev);
-
return 0;
}
@@ -261,6 +250,11 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb)
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
int ret, i, n = drm_format_num_planes(fb->pixel_format);
+ if (omap_fb->pin_count > 0) {
+ omap_fb->pin_count++;
+ return 0;
+ }
+
for (i = 0; i < n; i++) {
struct plane *plane = &omap_fb->planes[i];
ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true);
@@ -269,6 +263,8 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb)
omap_gem_dma_sync(plane->bo, DMA_TO_DEVICE);
}
+ omap_fb->pin_count++;
+
return 0;
fail:
@@ -287,6 +283,11 @@ int omap_framebuffer_unpin(struct drm_framebuffer *fb)
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
int ret, i, n = drm_format_num_planes(fb->pixel_format);
+ omap_fb->pin_count--;
+
+ if (omap_fb->pin_count > 0)
+ return 0;
+
for (i = 0; i < n; i++) {
struct plane *plane = &omap_fb->planes[i];
ret = omap_gem_put_paddr(plane->bo);
@@ -336,34 +337,6 @@ struct drm_connector *omap_framebuffer_get_next_connector(
return NULL;
}
-/* flush an area of the framebuffer (in case of manual update display that
- * is not automatically flushed)
- */
-void omap_framebuffer_flush(struct drm_framebuffer *fb,
- int x, int y, int w, int h)
-{
- struct drm_connector *connector = NULL;
-
- VERB("flush: %d,%d %dx%d, fb=%p", x, y, w, h, fb);
-
- /* FIXME: This is racy - no protection against modeset config changes. */
- while ((connector = omap_framebuffer_get_next_connector(fb, connector))) {
- /* only consider connectors that are part of a chain */
- if (connector->encoder && connector->encoder->crtc) {
- /* TODO: maybe this should propagate thru the crtc who
- * could do the coordinate translation..
- */
- struct drm_crtc *crtc = connector->encoder->crtc;
- int cx = max(0, x - crtc->x);
- int cy = max(0, y - crtc->y);
- int cw = w + (x - crtc->x) - cx;
- int ch = h + (y - crtc->y) - cy;
-
- omap_connector_flush(connector, cx, cy, cw, ch);
- }
- }
-}
-
#ifdef CONFIG_DEBUG_FS
void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
{
@@ -407,7 +380,7 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos)
{
- struct omap_framebuffer *omap_fb;
+ struct omap_framebuffer *omap_fb = NULL;
struct drm_framebuffer *fb = NULL;
const struct format *format = NULL;
int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format);
@@ -450,6 +423,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
goto fail;
}
+ if (pitch % format->planes[i].stride_bpp != 0) {
+ dev_err(dev->dev,
+ "buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n",
+ pitch, format->planes[i].stride_bpp);
+ ret = -EINVAL;
+ goto fail;
+ }
+
size = pitch * mode_cmd->height / format->planes[i].sub_y;
if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) {
@@ -478,8 +459,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
return fb;
fail:
- if (fb)
- omap_framebuffer_destroy(fb);
+ kfree(omap_fb);
return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index d292d24b3a6e..950cd3389092 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -42,42 +42,8 @@ struct omap_fbdev {
struct work_struct work;
};
-static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h);
static struct drm_fb_helper *get_fb(struct fb_info *fbi);
-static ssize_t omap_fbdev_write(struct fb_info *fbi, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- ssize_t res;
-
- res = fb_sys_write(fbi, buf, count, ppos);
- omap_fbdev_flush(fbi, 0, 0, fbi->var.xres, fbi->var.yres);
-
- return res;
-}
-
-static void omap_fbdev_fillrect(struct fb_info *fbi,
- const struct fb_fillrect *rect)
-{
- sys_fillrect(fbi, rect);
- omap_fbdev_flush(fbi, rect->dx, rect->dy, rect->width, rect->height);
-}
-
-static void omap_fbdev_copyarea(struct fb_info *fbi,
- const struct fb_copyarea *area)
-{
- sys_copyarea(fbi, area);
- omap_fbdev_flush(fbi, area->dx, area->dy, area->width, area->height);
-}
-
-static void omap_fbdev_imageblit(struct fb_info *fbi,
- const struct fb_image *image)
-{
- sys_imageblit(fbi, image);
- omap_fbdev_flush(fbi, image->dx, image->dy,
- image->width, image->height);
-}
-
static void pan_worker(struct work_struct *work)
{
struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work);
@@ -121,10 +87,10 @@ static struct fb_ops omap_fb_ops = {
* basic fbdev ops which write to the framebuffer
*/
.fb_read = fb_sys_read,
- .fb_write = omap_fbdev_write,
- .fb_fillrect = omap_fbdev_fillrect,
- .fb_copyarea = omap_fbdev_copyarea,
- .fb_imageblit = omap_fbdev_imageblit,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
@@ -294,21 +260,6 @@ static struct drm_fb_helper *get_fb(struct fb_info *fbi)
return fbi->par;
}
-/* flush an area of the framebuffer (in case of manual update display that
- * is not automatically flushed)
- */
-static void omap_fbdev_flush(struct fb_info *fbi, int x, int y, int w, int h)
-{
- struct drm_fb_helper *helper = get_fb(fbi);
-
- if (!helper)
- return;
-
- VERB("flush fbdev: %d,%d %dx%d, fbi=%p", x, y, w, h, fbi);
-
- omap_framebuffer_flush(helper->fb, x, y, w, h);
-}
-
/* initialize fbdev helper */
struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
{
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index aeb91ed653c9..e9718b99a8a9 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -828,6 +828,7 @@ int omap_gem_put_paddr(struct drm_gem_object *obj)
dev_err(obj->dev->dev,
"could not release unmap: %d\n", ret);
}
+ omap_obj->paddr = 0;
omap_obj->block = NULL;
}
}
@@ -1272,13 +1273,16 @@ unlock:
void omap_gem_free_object(struct drm_gem_object *obj)
{
struct drm_device *dev = obj->dev;
+ struct omap_drm_private *priv = dev->dev_private;
struct omap_gem_object *omap_obj = to_omap_bo(obj);
evict(obj);
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+ spin_lock(&priv->list_lock);
list_del(&omap_obj->mm_list);
+ spin_unlock(&priv->list_lock);
drm_gem_free_mmap_offset(obj);
@@ -1358,8 +1362,8 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
/* currently don't allow cached buffers.. there is some caching
* stuff that needs to be handled better
*/
- flags &= ~(OMAP_BO_CACHED|OMAP_BO_UNCACHED);
- flags |= OMAP_BO_WC;
+ flags &= ~(OMAP_BO_CACHED|OMAP_BO_WC|OMAP_BO_UNCACHED);
+ flags |= tiler_get_cpu_cache_flags();
/* align dimensions to slot boundaries... */
tiler_align(gem2fmt(flags),
@@ -1376,7 +1380,9 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
if (!omap_obj)
goto fail;
+ spin_lock(&priv->list_lock);
list_add(&omap_obj->mm_list, &priv->obj_list);
+ spin_unlock(&priv->list_lock);
obj = &omap_obj->base;
diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
index a2dbfb1737b4..b46dabd9faf7 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c
@@ -156,16 +156,16 @@ static int omap_gem_dmabuf_mmap(struct dma_buf *buffer,
}
static struct dma_buf_ops omap_dmabuf_ops = {
- .map_dma_buf = omap_gem_map_dma_buf,
- .unmap_dma_buf = omap_gem_unmap_dma_buf,
- .release = omap_gem_dmabuf_release,
- .begin_cpu_access = omap_gem_dmabuf_begin_cpu_access,
- .end_cpu_access = omap_gem_dmabuf_end_cpu_access,
- .kmap_atomic = omap_gem_dmabuf_kmap_atomic,
- .kunmap_atomic = omap_gem_dmabuf_kunmap_atomic,
- .kmap = omap_gem_dmabuf_kmap,
- .kunmap = omap_gem_dmabuf_kunmap,
- .mmap = omap_gem_dmabuf_mmap,
+ .map_dma_buf = omap_gem_map_dma_buf,
+ .unmap_dma_buf = omap_gem_unmap_dma_buf,
+ .release = omap_gem_dmabuf_release,
+ .begin_cpu_access = omap_gem_dmabuf_begin_cpu_access,
+ .end_cpu_access = omap_gem_dmabuf_end_cpu_access,
+ .kmap_atomic = omap_gem_dmabuf_kmap_atomic,
+ .kunmap_atomic = omap_gem_dmabuf_kunmap_atomic,
+ .kmap = omap_gem_dmabuf_kmap,
+ .kunmap = omap_gem_dmabuf_kunmap,
+ .mmap = omap_gem_dmabuf_mmap,
};
struct dma_buf *omap_gem_prime_export(struct drm_device *dev,
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
index f035d2bceae7..3eb097efc488 100644
--- a/drivers/gpu/drm/omapdrm/omap_irq.c
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
@@ -34,7 +34,7 @@ static void omap_irq_update(struct drm_device *dev)
struct omap_drm_irq *irq;
uint32_t irqmask = priv->vblank_mask;
- BUG_ON(!spin_is_locked(&list_lock));
+ assert_spin_locked(&list_lock);
list_for_each_entry(irq, &priv->irq_list, node)
irqmask |= irq->irqmask;
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index ee8e2b3a117e..1c6b63f39474 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -65,12 +65,16 @@ struct omap_plane {
struct callback apply_done_cb;
};
-static void unpin_worker(struct drm_flip_work *work, void *val)
+static void omap_plane_unpin_worker(struct drm_flip_work *work, void *val)
{
struct omap_plane *omap_plane =
container_of(work, struct omap_plane, unpin_work);
struct drm_device *dev = omap_plane->base.dev;
+ /*
+ * omap_framebuffer_pin/unpin are always called from priv->wq,
+ * so there's no need for locking here.
+ */
omap_framebuffer_unpin(val);
mutex_lock(&dev->mode_config.mutex);
drm_framebuffer_unreference(val);
@@ -78,7 +82,8 @@ static void unpin_worker(struct drm_flip_work *work, void *val)
}
/* update which fb (if any) is pinned for scanout */
-static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
+static int omap_plane_update_pin(struct drm_plane *plane,
+ struct drm_framebuffer *fb)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
struct drm_framebuffer *pinned_fb = omap_plane->pinned_fb;
@@ -121,13 +126,12 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
struct drm_crtc *crtc = plane->crtc;
enum omap_channel channel;
bool enabled = omap_plane->enabled && crtc;
- bool ilace, replication;
int ret;
DBG("%s, enabled=%d", omap_plane->name, enabled);
/* if fb has changed, pin new fb: */
- update_pin(plane, enabled ? plane->fb : NULL);
+ omap_plane_update_pin(plane, enabled ? plane->fb : NULL);
if (!enabled) {
dispc_ovl_enable(omap_plane->id, false);
@@ -145,20 +149,17 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
DBG("%d,%d %pad %pad", info->pos_x, info->pos_y,
&info->paddr, &info->p_uv_addr);
- /* TODO: */
- ilace = false;
- replication = false;
+ dispc_ovl_set_channel_out(omap_plane->id, channel);
/* and finally, update omapdss: */
- ret = dispc_ovl_setup(omap_plane->id, info,
- replication, omap_crtc_timings(crtc), false);
+ ret = dispc_ovl_setup(omap_plane->id, info, false,
+ omap_crtc_timings(crtc), false);
if (ret) {
dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
return;
}
dispc_ovl_enable(omap_plane->id, true);
- dispc_ovl_set_channel_out(omap_plane->id, channel);
}
static void omap_plane_post_apply(struct omap_drm_apply *apply)
@@ -167,7 +168,6 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply)
container_of(apply, struct omap_plane, apply);
struct drm_plane *plane = &omap_plane->base;
struct omap_drm_private *priv = plane->dev->dev_private;
- struct omap_overlay_info *info = &omap_plane->info;
struct callback cb;
cb = omap_plane->apply_done_cb;
@@ -177,14 +177,9 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply)
if (cb.fxn)
cb.fxn(cb.arg);
-
- if (omap_plane->enabled) {
- omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
- info->out_width, info->out_height);
- }
}
-static int apply(struct drm_plane *plane)
+static int omap_plane_apply(struct drm_plane *plane)
{
if (plane->crtc) {
struct omap_plane *omap_plane = to_omap_plane(plane);
@@ -194,12 +189,12 @@ static int apply(struct drm_plane *plane)
}
int omap_plane_mode_set(struct drm_plane *plane,
- struct drm_crtc *crtc, struct drm_framebuffer *fb,
- int crtc_x, int crtc_y,
- unsigned int crtc_w, unsigned int crtc_h,
- uint32_t src_x, uint32_t src_y,
- uint32_t src_w, uint32_t src_h,
- void (*fxn)(void *), void *arg)
+ struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int crtc_x, int crtc_y,
+ unsigned int crtc_w, unsigned int crtc_h,
+ unsigned int src_x, unsigned int src_y,
+ unsigned int src_w, unsigned int src_h,
+ void (*fxn)(void *), void *arg)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
struct omap_drm_window *win = &omap_plane->win;
@@ -209,11 +204,10 @@ int omap_plane_mode_set(struct drm_plane *plane,
win->crtc_w = crtc_w;
win->crtc_h = crtc_h;
- /* src values are in Q16 fixed point, convert to integer: */
- win->src_x = src_x >> 16;
- win->src_y = src_y >> 16;
- win->src_w = src_w >> 16;
- win->src_h = src_h >> 16;
+ win->src_x = src_x;
+ win->src_y = src_y;
+ win->src_w = src_w;
+ win->src_h = src_h;
if (fxn) {
/* omap_crtc should ensure that a new page flip
@@ -225,15 +219,7 @@ int omap_plane_mode_set(struct drm_plane *plane,
omap_plane->apply_done_cb.arg = arg;
}
- if (plane->fb)
- drm_framebuffer_unreference(plane->fb);
-
- drm_framebuffer_reference(fb);
-
- plane->fb = fb;
- plane->crtc = crtc;
-
- return apply(plane);
+ return omap_plane_apply(plane);
}
static int omap_plane_update(struct drm_plane *plane,
@@ -254,17 +240,29 @@ static int omap_plane_update(struct drm_plane *plane,
break;
}
+ /*
+ * We don't need to take a reference to the framebuffer as the DRM core
+ * has already done so for the purpose of setting plane->fb.
+ */
+ plane->fb = fb;
+ plane->crtc = crtc;
+
+ /* src values are in Q16 fixed point, convert to integer: */
return omap_plane_mode_set(plane, crtc, fb,
crtc_x, crtc_y, crtc_w, crtc_h,
- src_x, src_y, src_w, src_h,
+ src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
NULL, NULL);
}
static int omap_plane_disable(struct drm_plane *plane)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
+
omap_plane->win.rotation = BIT(DRM_ROTATE_0);
- return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+ omap_plane->info.zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
+ ? 0 : omap_plane->id;
+
+ return omap_plane_set_enable(plane, false);
}
static void omap_plane_destroy(struct drm_plane *plane)
@@ -275,7 +273,6 @@ static void omap_plane_destroy(struct drm_plane *plane)
omap_irq_unregister(plane->dev, &omap_plane->error_irq);
- omap_plane_disable(plane);
drm_plane_cleanup(plane);
drm_flip_work_cleanup(&omap_plane->unpin_work);
@@ -283,18 +280,15 @@ static void omap_plane_destroy(struct drm_plane *plane)
kfree(omap_plane);
}
-int omap_plane_dpms(struct drm_plane *plane, int mode)
+int omap_plane_set_enable(struct drm_plane *plane, bool enable)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
- bool enabled = (mode == DRM_MODE_DPMS_ON);
- int ret = 0;
- if (enabled != omap_plane->enabled) {
- omap_plane->enabled = enabled;
- ret = apply(plane);
- }
+ if (enable == omap_plane->enabled)
+ return 0;
- return ret;
+ omap_plane->enabled = enable;
+ return omap_plane_apply(plane);
}
/* helper to install properties which are common to planes and crtcs */
@@ -342,61 +336,63 @@ int omap_plane_set_property(struct drm_plane *plane,
if (property == priv->rotation_prop) {
DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
omap_plane->win.rotation = val;
- ret = apply(plane);
+ ret = omap_plane_apply(plane);
} else if (property == priv->zorder_prop) {
DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
omap_plane->info.zorder = val;
- ret = apply(plane);
+ ret = omap_plane_apply(plane);
}
return ret;
}
static const struct drm_plane_funcs omap_plane_funcs = {
- .update_plane = omap_plane_update,
- .disable_plane = omap_plane_disable,
- .destroy = omap_plane_destroy,
- .set_property = omap_plane_set_property,
+ .update_plane = omap_plane_update,
+ .disable_plane = omap_plane_disable,
+ .destroy = omap_plane_destroy,
+ .set_property = omap_plane_set_property,
};
static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
{
struct omap_plane *omap_plane =
container_of(irq, struct omap_plane, error_irq);
- DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
+ DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name,
+ irqstatus);
}
static const char *plane_names[] = {
- [OMAP_DSS_GFX] = "gfx",
- [OMAP_DSS_VIDEO1] = "vid1",
- [OMAP_DSS_VIDEO2] = "vid2",
- [OMAP_DSS_VIDEO3] = "vid3",
+ [OMAP_DSS_GFX] = "gfx",
+ [OMAP_DSS_VIDEO1] = "vid1",
+ [OMAP_DSS_VIDEO2] = "vid2",
+ [OMAP_DSS_VIDEO3] = "vid3",
};
static const uint32_t error_irqs[] = {
- [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
- [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
- [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
- [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
+ [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
+ [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
+ [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
+ [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
/* initialize plane */
struct drm_plane *omap_plane_init(struct drm_device *dev,
- int id, bool private_plane)
+ int id, enum drm_plane_type type)
{
struct omap_drm_private *priv = dev->dev_private;
- struct drm_plane *plane = NULL;
+ struct drm_plane *plane;
struct omap_plane *omap_plane;
struct omap_overlay_info *info;
+ int ret;
- DBG("%s: priv=%d", plane_names[id], private_plane);
+ DBG("%s: type=%d", plane_names[id], type);
omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
if (!omap_plane)
- return NULL;
+ return ERR_PTR(-ENOMEM);
drm_flip_work_init(&omap_plane->unpin_work,
- "unpin", unpin_worker);
+ "unpin", omap_plane_unpin_worker);
omap_plane->nformats = omap_framebuffer_get_formats(
omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
@@ -413,8 +409,11 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
omap_plane->error_irq.irq = omap_plane_error_irq;
omap_irq_register(dev, &omap_plane->error_irq);
- drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
- omap_plane->formats, omap_plane->nformats, private_plane);
+ ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1,
+ &omap_plane_funcs, omap_plane->formats,
+ omap_plane->nformats, type);
+ if (ret < 0)
+ goto error;
omap_plane_install_properties(plane, &plane->base);
@@ -432,10 +431,15 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
* TODO add ioctl to give userspace an API to change this.. this
* will come in a subsequent patch.
*/
- if (private_plane)
+ if (type == DRM_PLANE_TYPE_PRIMARY)
omap_plane->info.zorder = 0;
else
omap_plane->info.zorder = id;
return plane;
+
+error:
+ omap_irq_unregister(plane->dev, &omap_plane->error_irq);
+ kfree(omap_plane);
+ return NULL;
}
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d84583776d50..6d64c7bb908b 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -11,6 +11,7 @@ config DRM_PANEL_SIMPLE
tristate "support for simple panels"
depends on OF
depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
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
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 39806c335339..30904a9b2a4c 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -33,9 +33,14 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
struct panel_desc {
const struct drm_display_mode *modes;
unsigned int num_modes;
+ const struct display_timing *timings;
+ unsigned int num_timings;
unsigned int bpc;
@@ -94,6 +99,25 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
if (!panel->desc)
return 0;
+ for (i = 0; i < panel->desc->num_timings; i++) {
+ const struct display_timing *dt = &panel->desc->timings[i];
+ struct videomode vm;
+
+ videomode_from_timing(dt, &vm);
+ mode = drm_mode_create(drm);
+ if (!mode) {
+ dev_err(drm->dev, "failed to add mode %ux%u\n",
+ dt->hactive.typ, dt->vactive.typ);
+ continue;
+ }
+
+ drm_display_mode_from_videomode(&vm, mode);
+ drm_mode_set_name(mode);
+
+ drm_mode_probed_add(connector, mode);
+ num++;
+ }
+
for (i = 0; i < panel->desc->num_modes; i++) {
const struct drm_display_mode *m = &panel->desc->modes[i];
@@ -226,12 +250,30 @@ static int panel_simple_get_modes(struct drm_panel *panel)
return num;
}
+static int panel_simple_get_timings(struct drm_panel *panel,
+ unsigned int num_timings,
+ struct display_timing *timings)
+{
+ struct panel_simple *p = to_panel_simple(panel);
+ unsigned int i;
+
+ if (p->desc->num_timings < num_timings)
+ num_timings = p->desc->num_timings;
+
+ if (timings)
+ for (i = 0; i < num_timings; i++)
+ timings[i] = p->desc->timings[i];
+
+ return p->desc->num_timings;
+}
+
static const struct drm_panel_funcs panel_simple_funcs = {
.disable = panel_simple_disable,
.unprepare = panel_simple_unprepare,
.prepare = panel_simple_prepare,
.enable = panel_simple_enable,
.get_modes = panel_simple_get_modes,
+ .get_timings = panel_simple_get_timings,
};
static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
@@ -327,6 +369,31 @@ static void panel_simple_shutdown(struct device *dev)
panel_simple_disable(&panel->base);
}
+static const struct drm_display_mode ampire_am800480r3tmqwa1h_mode = {
+ .clock = 33333,
+ .hdisplay = 800,
+ .hsync_start = 800 + 0,
+ .hsync_end = 800 + 0 + 255,
+ .htotal = 800 + 0 + 255 + 0,
+ .vdisplay = 480,
+ .vsync_start = 480 + 2,
+ .vsync_end = 480 + 2 + 45,
+ .vtotal = 480 + 2 + 45 + 0,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
+};
+
+static const struct panel_desc ampire_am800480r3tmqwa1h = {
+ .modes = &ampire_am800480r3tmqwa1h_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 152,
+ .height = 91,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+};
+
static const struct drm_display_mode auo_b101aw03_mode = {
.clock = 51450,
.hdisplay = 1024,
@@ -350,6 +417,29 @@ static const struct panel_desc auo_b101aw03 = {
},
};
+static const struct drm_display_mode auo_b101ean01_mode = {
+ .clock = 72500,
+ .hdisplay = 1280,
+ .hsync_start = 1280 + 119,
+ .hsync_end = 1280 + 119 + 32,
+ .htotal = 1280 + 119 + 32 + 21,
+ .vdisplay = 800,
+ .vsync_start = 800 + 4,
+ .vsync_end = 800 + 4 + 20,
+ .vtotal = 800 + 4 + 20 + 8,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc auo_b101ean01 = {
+ .modes = &auo_b101ean01_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 217,
+ .height = 136,
+ },
+};
+
static const struct drm_display_mode auo_b101xtn01_mode = {
.clock = 72000,
.hdisplay = 1366,
@@ -615,24 +705,25 @@ static const struct panel_desc giantplus_gpg482739qs5 = {
.width = 95,
.height = 54,
},
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
-static const struct drm_display_mode hannstar_hsd070pww1_mode = {
- .clock = 71100,
- .hdisplay = 1280,
- .hsync_start = 1280 + 1,
- .hsync_end = 1280 + 1 + 158,
- .htotal = 1280 + 1 + 158 + 1,
- .vdisplay = 800,
- .vsync_start = 800 + 1,
- .vsync_end = 800 + 1 + 21,
- .vtotal = 800 + 1 + 21 + 1,
- .vrefresh = 60,
+static const struct display_timing hannstar_hsd070pww1_timing = {
+ .pixelclock = { 64300000, 71100000, 82000000 },
+ .hactive = { 1280, 1280, 1280 },
+ .hfront_porch = { 1, 1, 10 },
+ .hback_porch = { 1, 1, 10 },
+ .hsync_len = { 52, 158, 661 },
+ .vactive = { 800, 800, 800 },
+ .vfront_porch = { 1, 1, 10 },
+ .vback_porch = { 1, 1, 10 },
+ .vsync_len = { 1, 21, 203 },
+ .flags = DISPLAY_FLAGS_DE_HIGH,
};
static const struct panel_desc hannstar_hsd070pww1 = {
- .modes = &hannstar_hsd070pww1_mode,
- .num_modes = 1,
+ .timings = &hannstar_hsd070pww1_timing,
+ .num_timings = 1,
.bpc = 6,
.size = {
.width = 151,
@@ -663,6 +754,31 @@ static const struct panel_desc hitachi_tx23d38vm0caa = {
},
};
+static const struct drm_display_mode innolux_at043tn24_mode = {
+ .clock = 9000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 2,
+ .hsync_end = 480 + 2 + 41,
+ .htotal = 480 + 2 + 41 + 2,
+ .vdisplay = 272,
+ .vsync_start = 272 + 2,
+ .vsync_end = 272 + 2 + 11,
+ .vtotal = 272 + 2 + 11 + 2,
+ .vrefresh = 60,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct panel_desc innolux_at043tn24 = {
+ .modes = &innolux_at043tn24_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 95,
+ .height = 54,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+};
+
static const struct drm_display_mode innolux_g121i1_l01_mode = {
.clock = 71000,
.hdisplay = 1280,
@@ -733,6 +849,29 @@ static const struct panel_desc innolux_n156bge_l21 = {
},
};
+static const struct drm_display_mode innolux_zj070na_01p_mode = {
+ .clock = 51501,
+ .hdisplay = 1024,
+ .hsync_start = 1024 + 128,
+ .hsync_end = 1024 + 128 + 64,
+ .htotal = 1024 + 128 + 64 + 128,
+ .vdisplay = 600,
+ .vsync_start = 600 + 16,
+ .vsync_end = 600 + 16 + 4,
+ .vtotal = 600 + 16 + 4 + 16,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc innolux_zj070na_01p = {
+ .modes = &innolux_zj070na_01p_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 1024,
+ .height = 600,
+ },
+};
+
static const struct drm_display_mode lg_lp129qe_mode = {
.clock = 285250,
.hdisplay = 2560,
@@ -756,6 +895,30 @@ static const struct panel_desc lg_lp129qe = {
},
};
+static const struct drm_display_mode ortustech_com43h4m85ulc_mode = {
+ .clock = 25000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 10,
+ .hsync_end = 480 + 10 + 10,
+ .htotal = 480 + 10 + 10 + 15,
+ .vdisplay = 800,
+ .vsync_start = 800 + 3,
+ .vsync_end = 800 + 3 + 3,
+ .vtotal = 800 + 3 + 3 + 3,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc ortustech_com43h4m85ulc = {
+ .modes = &ortustech_com43h4m85ulc_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 56,
+ .height = 93,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+};
+
static const struct drm_display_mode samsung_ltn101nt05_mode = {
.clock = 54030,
.hdisplay = 1024,
@@ -779,11 +942,63 @@ static const struct panel_desc samsung_ltn101nt05 = {
},
};
+static const struct drm_display_mode samsung_ltn140at29_301_mode = {
+ .clock = 76300,
+ .hdisplay = 1366,
+ .hsync_start = 1366 + 64,
+ .hsync_end = 1366 + 64 + 48,
+ .htotal = 1366 + 64 + 48 + 128,
+ .vdisplay = 768,
+ .vsync_start = 768 + 2,
+ .vsync_end = 768 + 2 + 5,
+ .vtotal = 768 + 2 + 5 + 17,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc samsung_ltn140at29_301 = {
+ .modes = &samsung_ltn140at29_301_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 320,
+ .height = 187,
+ },
+};
+
+static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
+ .clock = 33300,
+ .hdisplay = 800,
+ .hsync_start = 800 + 1,
+ .hsync_end = 800 + 1 + 64,
+ .htotal = 800 + 1 + 64 + 64,
+ .vdisplay = 480,
+ .vsync_start = 480 + 1,
+ .vsync_end = 480 + 1 + 23,
+ .vtotal = 480 + 1 + 23 + 22,
+ .vrefresh = 60,
+};
+
+static const struct panel_desc shelly_sca07010_bfn_lnn = {
+ .modes = &shelly_sca07010_bfn_lnn_mode,
+ .num_modes = 1,
+ .size = {
+ .width = 152,
+ .height = 91,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+};
+
static const struct of_device_id platform_of_match[] = {
{
+ .compatible = "ampire,am800480r3tmqwa1h",
+ .data = &ampire_am800480r3tmqwa1h,
+ }, {
.compatible = "auo,b101aw03",
.data = &auo_b101aw03,
}, {
+ .compatible = "auo,b101ean01",
+ .data = &auo_b101ean01,
+ }, {
.compatible = "auo,b101xtn01",
.data = &auo_b101xtn01,
}, {
@@ -826,6 +1041,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "hit,tx23d38vm0caa",
.data = &hitachi_tx23d38vm0caa
}, {
+ .compatible = "innolux,at043tn24",
+ .data = &innolux_at043tn24,
+ }, {
.compatible ="innolux,g121i1-l01",
.data = &innolux_g121i1_l01
}, {
@@ -835,12 +1053,24 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "innolux,n156bge-l21",
.data = &innolux_n156bge_l21,
}, {
+ .compatible = "innolux,zj070na-01p",
+ .data = &innolux_zj070na_01p,
+ }, {
.compatible = "lg,lp129qe",
.data = &lg_lp129qe,
}, {
+ .compatible = "ortustech,com43h4m85ulc",
+ .data = &ortustech_com43h4m85ulc,
+ }, {
.compatible = "samsung,ltn101nt05",
.data = &samsung_ltn101nt05,
}, {
+ .compatible = "samsung,ltn140at29-301",
+ .data = &samsung_ltn140at29_301,
+ }, {
+ .compatible = "shelly,sca07010-bfn-lnn",
+ .data = &shelly_sca07010_bfn_lnn,
+ }, {
/* sentinel */
}
};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 21a481b224eb..30da7813d53e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -129,6 +129,7 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
struct rockchip_drm_private *private;
struct dma_iommu_mapping *mapping;
struct device *dev = drm_dev->dev;
+ struct drm_connector *connector;
int ret;
private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
@@ -171,6 +172,23 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
if (ret)
goto err_detach_device;
+ /*
+ * All components are now added, we can publish the connector sysfs
+ * entries to userspace. This will generate hotplug events and so
+ * userspace will expect to be able to access DRM at this point.
+ */
+ list_for_each_entry(connector, &drm_dev->mode_config.connector_list,
+ head) {
+ ret = drm_connector_register(connector);
+ if (ret) {
+ dev_err(drm_dev->dev,
+ "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n",
+ connector->base.id,
+ connector->name, ret);
+ goto err_unbind;
+ }
+ }
+
/* init kms poll for handling hpd */
drm_kms_helper_poll_init(drm_dev);
@@ -200,6 +218,7 @@ err_vblank_cleanup:
drm_vblank_cleanup(drm_dev);
err_kms_helper_poll_fini:
drm_kms_helper_poll_fini(drm_dev);
+err_unbind:
component_unbind_all(dev, drm_dev);
err_detach_device:
arm_iommu_detach_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
index d5c1248916b2..5b0dc0f6fd94 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -119,6 +119,9 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
fb->width, fb->height, fb->depth, rk_obj->kvaddr,
offset, size);
+
+ fbi->skip_vt_switch = true;
+
return 0;
err_drm_framebuffer_unref:
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index d041921b3bb9..ccb0ce073ef2 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -421,6 +421,12 @@ static void vop_enable(struct drm_crtc *crtc)
if (vop->is_enabled)
return;
+ ret = pm_runtime_get_sync(vop->dev);
+ if (ret < 0) {
+ dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+ return;
+ }
+
ret = clk_enable(vop->hclk);
if (ret < 0) {
dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
@@ -517,6 +523,7 @@ static void vop_disable(struct drm_crtc *crtc)
clk_disable(vop->dclk);
clk_disable(vop->aclk);
clk_disable(vop->hclk);
+ pm_runtime_put(vop->dev);
}
/*
@@ -893,7 +900,7 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
u16 vact_end = vact_st + vdisplay;
- int ret;
+ int ret, ret_clk;
uint32_t val;
/*
@@ -915,7 +922,8 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
default:
DRM_ERROR("unsupport connector_type[%d]\n",
vop->connector_type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
};
VOP_CTRL_SET(vop, out_mode, vop->connector_out_mode);
@@ -938,7 +946,7 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
ret = vop_crtc_mode_set_base(crtc, x, y, fb);
if (ret)
- return ret;
+ goto out;
/*
* reset dclk, take all mode config affect, so the clk would run in
@@ -949,13 +957,14 @@ static int vop_crtc_mode_set(struct drm_crtc *crtc,
reset_control_deassert(vop->dclk_rst);
clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
- ret = clk_enable(vop->dclk);
- if (ret < 0) {
- dev_err(vop->dev, "failed to enable dclk - %d\n", ret);
- return ret;
+out:
+ ret_clk = clk_enable(vop->dclk);
+ if (ret_clk < 0) {
+ dev_err(vop->dev, "failed to enable dclk - %d\n", ret_clk);
+ return ret_clk;
}
- return 0;
+ return ret;
}
static void vop_crtc_commit(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index b7f781573b15..a287e4fec865 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -425,8 +425,8 @@ static void tegra_plane_reset(struct drm_plane *plane)
{
struct tegra_plane_state *state;
- if (plane->state && plane->state->fb)
- drm_framebuffer_unreference(plane->state->fb);
+ if (plane->state)
+ __drm_atomic_helper_plane_destroy_state(plane, plane->state);
kfree(plane->state);
plane->state = NULL;
@@ -443,12 +443,14 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla
struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
struct tegra_plane_state *copy;
- copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+ copy = kmalloc(sizeof(*copy), GFP_KERNEL);
if (!copy)
return NULL;
- if (copy->base.fb)
- drm_framebuffer_reference(copy->base.fb);
+ __drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
+ copy->tiling = state->tiling;
+ copy->format = state->format;
+ copy->swap = state->swap;
return &copy->base;
}
@@ -456,9 +458,7 @@ static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_pla
static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
{
- if (state->fb)
- drm_framebuffer_unreference(state->fb);
-
+ __drm_atomic_helper_plane_destroy_state(plane, state);
kfree(state);
}
@@ -908,6 +908,15 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc)
return 0;
}
+u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc)
+{
+ if (dc->syncpt)
+ return host1x_syncpt_read(dc->syncpt);
+
+ /* fallback to software emulated VBLANK counter */
+ return drm_crtc_vblank_count(&dc->base);
+}
+
void tegra_dc_enable_vblank(struct tegra_dc *dc)
{
unsigned long value, flags;
@@ -995,6 +1004,9 @@ static void tegra_crtc_reset(struct drm_crtc *crtc)
{
struct tegra_dc_state *state;
+ if (crtc->state)
+ __drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);
+
kfree(crtc->state);
crtc->state = NULL;
@@ -1011,14 +1023,15 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
struct tegra_dc_state *state = to_dc_state(crtc->state);
struct tegra_dc_state *copy;
- copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+ copy = kmalloc(sizeof(*copy), GFP_KERNEL);
if (!copy)
return NULL;
- copy->base.mode_changed = false;
- copy->base.active_changed = false;
- copy->base.planes_changed = false;
- copy->base.event = NULL;
+ __drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
+ copy->clk = state->clk;
+ copy->pclk = state->pclk;
+ copy->div = state->div;
+ copy->planes = state->planes;
return &copy->base;
}
@@ -1026,6 +1039,7 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
+ __drm_atomic_helper_crtc_destroy_state(crtc, state);
kfree(state);
}
@@ -1152,26 +1166,18 @@ static int tegra_dc_set_timings(struct tegra_dc *dc,
return 0;
}
-int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
- unsigned long pclk, unsigned int div)
-{
- u32 value;
- int err;
-
- err = clk_set_parent(dc->clk, parent);
- if (err < 0) {
- dev_err(dc->dev, "failed to set parent clock: %d\n", err);
- return err;
- }
-
- DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div);
-
- value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1;
- tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
-
- return 0;
-}
-
+/**
+ * tegra_dc_state_setup_clock - check clock settings and store them in atomic
+ * state
+ * @dc: display controller
+ * @crtc_state: CRTC atomic state
+ * @clk: parent clock for display controller
+ * @pclk: pixel clock
+ * @div: shift clock divider
+ *
+ * Returns:
+ * 0 on success or a negative error-code on failure.
+ */
int tegra_dc_state_setup_clock(struct tegra_dc *dc,
struct drm_crtc_state *crtc_state,
struct clk *clk, unsigned long pclk,
@@ -1179,6 +1185,9 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
{
struct tegra_dc_state *state = to_dc_state(crtc_state);
+ if (!clk_has_parent(dc->clk, clk))
+ return -EINVAL;
+
state->clk = clk;
state->pclk = pclk;
state->div = div;
@@ -1294,9 +1303,7 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
.disable = tegra_crtc_disable,
.mode_fixup = tegra_crtc_mode_fixup,
- .mode_set = drm_helper_crtc_mode_set,
.mode_set_nofb = tegra_crtc_mode_set_nofb,
- .mode_set_base = drm_helper_crtc_mode_set_base,
.prepare = tegra_crtc_prepare,
.commit = tegra_crtc_commit,
.atomic_check = tegra_crtc_atomic_check,
@@ -1631,7 +1638,6 @@ static int tegra_dc_init(struct host1x_client *client)
struct tegra_drm *tegra = drm->dev_private;
struct drm_plane *primary = NULL;
struct drm_plane *cursor = NULL;
- unsigned int syncpt;
u32 value;
int err;
@@ -1700,13 +1706,15 @@ static int tegra_dc_init(struct host1x_client *client)
}
/* initialize display controller */
- if (dc->pipe)
- syncpt = SYNCPT_VBLANK1;
- else
- syncpt = SYNCPT_VBLANK0;
+ if (dc->syncpt) {
+ u32 syncpt = host1x_syncpt_id(dc->syncpt);
- tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
- tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
+ value = SYNCPT_CNTRL_NO_STALL;
+ tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
+
+ value = SYNCPT_VSYNC_ENABLE | syncpt;
+ tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
+ }
value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT;
tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
@@ -1874,6 +1882,7 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc)
static int tegra_dc_probe(struct platform_device *pdev)
{
+ unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
const struct of_device_id *id;
struct resource *regs;
struct tegra_dc *dc;
@@ -1965,6 +1974,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
return err;
}
+ dc->syncpt = host1x_syncpt_request(&pdev->dev, flags);
+ if (!dc->syncpt)
+ dev_warn(&pdev->dev, "failed to allocate syncpoint\n");
+
platform_set_drvdata(pdev, dc);
return 0;
@@ -1975,6 +1988,8 @@ static int tegra_dc_remove(struct platform_device *pdev)
struct tegra_dc *dc = platform_get_drvdata(pdev);
int err;
+ host1x_syncpt_free(dc->syncpt);
+
err = host1x_client_unregister(&dc->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 705c93b00794..55792daabbb5 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -12,6 +12,8 @@
#define DC_CMD_GENERAL_INCR_SYNCPT 0x000
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x001
+#define SYNCPT_CNTRL_NO_STALL (1 << 8)
+#define SYNCPT_CNTRL_SOFT_RESET (1 << 0)
#define DC_CMD_GENERAL_INCR_SYNCPT_ERROR 0x002
#define DC_CMD_WIN_A_INCR_SYNCPT 0x008
#define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL 0x009
@@ -23,6 +25,7 @@
#define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL 0x019
#define DC_CMD_WIN_C_INCR_SYNCPT_ERROR 0x01a
#define DC_CMD_CONT_SYNCPT_VSYNC 0x028
+#define SYNCPT_VSYNC_ENABLE (1 << 8)
#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
#define DC_CMD_DISPLAY_COMMAND 0x032
#define DISP_CTRL_MODE_STOP (0 << 5)
@@ -438,8 +441,4 @@
#define DC_WINBUF_BD_UFLOW_STATUS 0xdca
#define DC_WINBUF_CD_UFLOW_STATUS 0xfca
-/* synchronization points */
-#define SYNCPT_VBLANK0 26
-#define SYNCPT_VBLANK1 27
-
#endif /* TEGRA_DC_H */
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 5f1880766110..1833abd7d3aa 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -172,6 +172,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
*/
drm->irq_enabled = true;
+ /* syncpoints are used for full 32-bit hardware VBLANK counters */
+ drm->vblank_disable_immediate = true;
+ drm->max_vblank_count = 0xffffffff;
+
err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0)
goto device;
@@ -813,12 +817,12 @@ static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm,
static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe)
{
struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
+ struct tegra_dc *dc = to_tegra_dc(crtc);
if (!crtc)
return 0;
- /* TODO: implement real hardware counter using syncpoints */
- return drm_crtc_vblank_count(crtc);
+ return tegra_dc_get_vblank_counter(dc);
}
static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
@@ -879,8 +883,18 @@ static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
return 0;
}
+static int tegra_debugfs_iova(struct seq_file *s, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *)s->private;
+ struct drm_device *drm = node->minor->dev;
+ struct tegra_drm *tegra = drm->dev_private;
+
+ return drm_mm_dump_table(s, &tegra->mm);
+}
+
static struct drm_info_list tegra_debugfs_list[] = {
{ "framebuffers", tegra_debugfs_framebuffers, 0 },
+ { "iova", tegra_debugfs_iova, 0 },
};
static int tegra_debugfs_init(struct drm_minor *minor)
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 8cb2dfeaa957..659b2fcc986d 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -106,6 +106,7 @@ struct tegra_output;
struct tegra_dc {
struct host1x_client client;
+ struct host1x_syncpt *syncpt;
struct device *dev;
spinlock_t lock;
@@ -180,12 +181,11 @@ struct tegra_dc_window {
};
/* from dc.c */
+u32 tegra_dc_get_vblank_counter(struct tegra_dc *dc);
void tegra_dc_enable_vblank(struct tegra_dc *dc);
void tegra_dc_disable_vblank(struct tegra_dc *dc);
void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
void tegra_dc_commit(struct tegra_dc *dc);
-int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent,
- unsigned long pclk, unsigned int div);
int tegra_dc_state_setup_clock(struct tegra_dc *dc,
struct drm_crtc_state *crtc_state,
struct clk *clk, unsigned long pclk,
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 7eaaee74a039..06ab1783bba1 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -952,7 +952,7 @@ static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder,
}
tegra_hdmi_writel(hdmi,
- SOR_SEQ_CTL_PU_PC(0) |
+ SOR_SEQ_PU_PC(0) |
SOR_SEQ_PU_PC_ALT(0) |
SOR_SEQ_PD_PC(8) |
SOR_SEQ_PD_PC_ALT(8),
@@ -1394,8 +1394,8 @@ static int tegra_hdmi_exit(struct host1x_client *client)
tegra_output_exit(&hdmi->output);
- clk_disable_unprepare(hdmi->clk);
reset_control_assert(hdmi->rst);
+ clk_disable_unprepare(hdmi->clk);
regulator_disable(hdmi->vdd);
regulator_disable(hdmi->pll);
diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h
index 919a19df4e1b..a882514389cd 100644
--- a/drivers/gpu/drm/tegra/hdmi.h
+++ b/drivers/gpu/drm/tegra/hdmi.h
@@ -201,7 +201,7 @@
#define HDMI_NV_PDISP_SOR_CRCB 0x5d
#define HDMI_NV_PDISP_SOR_BLANK 0x5e
#define HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f
-#define SOR_SEQ_CTL_PU_PC(x) (((x) & 0xf) << 0)
+#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0)
#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4)
#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8)
#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12)
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 2afe478ded3b..7591d8901f9a 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -41,6 +41,8 @@ struct tegra_sor {
struct mutex lock;
bool enabled;
+ struct drm_info_list *debugfs_files;
+ struct drm_minor *minor;
struct dentry *debugfs;
};
@@ -68,13 +70,12 @@ static inline struct tegra_sor *to_sor(struct tegra_output *output)
return container_of(output, struct tegra_sor, output);
}
-static inline unsigned long tegra_sor_readl(struct tegra_sor *sor,
- unsigned long offset)
+static inline u32 tegra_sor_readl(struct tegra_sor *sor, unsigned long offset)
{
return readl(sor->regs + (offset << 2));
}
-static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value,
+static inline void tegra_sor_writel(struct tegra_sor *sor, u32 value,
unsigned long offset)
{
writel(value, sor->regs + (offset << 2));
@@ -83,9 +84,9 @@ static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value,
static int tegra_sor_dp_train_fast(struct tegra_sor *sor,
struct drm_dp_link *link)
{
- unsigned long value;
unsigned int i;
u8 pattern;
+ u32 value;
int err;
/* setup lane parameters */
@@ -202,7 +203,7 @@ static void tegra_sor_update(struct tegra_sor *sor)
static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout)
{
- unsigned long value;
+ u32 value;
value = tegra_sor_readl(sor, SOR_PWM_DIV);
value &= ~SOR_PWM_DIV_MASK;
@@ -281,7 +282,7 @@ static int tegra_sor_wakeup(struct tegra_sor *sor)
static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout)
{
- unsigned long value;
+ u32 value;
value = tegra_sor_readl(sor, SOR_PWR);
value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU;
@@ -674,38 +675,195 @@ static const struct file_operations tegra_sor_crc_fops = {
.release = tegra_sor_crc_release,
};
+static int tegra_sor_show_regs(struct seq_file *s, void *data)
+{
+ struct drm_info_node *node = s->private;
+ struct tegra_sor *sor = node->info_ent->data;
+
+#define DUMP_REG(name) \
+ seq_printf(s, "%-38s %#05x %08x\n", #name, name, \
+ tegra_sor_readl(sor, name))
+
+ DUMP_REG(SOR_CTXSW);
+ DUMP_REG(SOR_SUPER_STATE_0);
+ DUMP_REG(SOR_SUPER_STATE_1);
+ DUMP_REG(SOR_STATE_0);
+ DUMP_REG(SOR_STATE_1);
+ DUMP_REG(SOR_HEAD_STATE_0(0));
+ DUMP_REG(SOR_HEAD_STATE_0(1));
+ DUMP_REG(SOR_HEAD_STATE_1(0));
+ DUMP_REG(SOR_HEAD_STATE_1(1));
+ DUMP_REG(SOR_HEAD_STATE_2(0));
+ DUMP_REG(SOR_HEAD_STATE_2(1));
+ DUMP_REG(SOR_HEAD_STATE_3(0));
+ DUMP_REG(SOR_HEAD_STATE_3(1));
+ DUMP_REG(SOR_HEAD_STATE_4(0));
+ DUMP_REG(SOR_HEAD_STATE_4(1));
+ DUMP_REG(SOR_HEAD_STATE_5(0));
+ DUMP_REG(SOR_HEAD_STATE_5(1));
+ DUMP_REG(SOR_CRC_CNTRL);
+ DUMP_REG(SOR_DP_DEBUG_MVID);
+ DUMP_REG(SOR_CLK_CNTRL);
+ DUMP_REG(SOR_CAP);
+ DUMP_REG(SOR_PWR);
+ DUMP_REG(SOR_TEST);
+ DUMP_REG(SOR_PLL_0);
+ DUMP_REG(SOR_PLL_1);
+ DUMP_REG(SOR_PLL_2);
+ DUMP_REG(SOR_PLL_3);
+ DUMP_REG(SOR_CSTM);
+ DUMP_REG(SOR_LVDS);
+ DUMP_REG(SOR_CRC_A);
+ DUMP_REG(SOR_CRC_B);
+ DUMP_REG(SOR_BLANK);
+ DUMP_REG(SOR_SEQ_CTL);
+ DUMP_REG(SOR_LANE_SEQ_CTL);
+ DUMP_REG(SOR_SEQ_INST(0));
+ DUMP_REG(SOR_SEQ_INST(1));
+ DUMP_REG(SOR_SEQ_INST(2));
+ DUMP_REG(SOR_SEQ_INST(3));
+ DUMP_REG(SOR_SEQ_INST(4));
+ DUMP_REG(SOR_SEQ_INST(5));
+ DUMP_REG(SOR_SEQ_INST(6));
+ DUMP_REG(SOR_SEQ_INST(7));
+ DUMP_REG(SOR_SEQ_INST(8));
+ DUMP_REG(SOR_SEQ_INST(9));
+ DUMP_REG(SOR_SEQ_INST(10));
+ DUMP_REG(SOR_SEQ_INST(11));
+ DUMP_REG(SOR_SEQ_INST(12));
+ DUMP_REG(SOR_SEQ_INST(13));
+ DUMP_REG(SOR_SEQ_INST(14));
+ DUMP_REG(SOR_SEQ_INST(15));
+ DUMP_REG(SOR_PWM_DIV);
+ DUMP_REG(SOR_PWM_CTL);
+ DUMP_REG(SOR_VCRC_A_0);
+ DUMP_REG(SOR_VCRC_A_1);
+ DUMP_REG(SOR_VCRC_B_0);
+ DUMP_REG(SOR_VCRC_B_1);
+ DUMP_REG(SOR_CCRC_A_0);
+ DUMP_REG(SOR_CCRC_A_1);
+ DUMP_REG(SOR_CCRC_B_0);
+ DUMP_REG(SOR_CCRC_B_1);
+ DUMP_REG(SOR_EDATA_A_0);
+ DUMP_REG(SOR_EDATA_A_1);
+ DUMP_REG(SOR_EDATA_B_0);
+ DUMP_REG(SOR_EDATA_B_1);
+ DUMP_REG(SOR_COUNT_A_0);
+ DUMP_REG(SOR_COUNT_A_1);
+ DUMP_REG(SOR_COUNT_B_0);
+ DUMP_REG(SOR_COUNT_B_1);
+ DUMP_REG(SOR_DEBUG_A_0);
+ DUMP_REG(SOR_DEBUG_A_1);
+ DUMP_REG(SOR_DEBUG_B_0);
+ DUMP_REG(SOR_DEBUG_B_1);
+ DUMP_REG(SOR_TRIG);
+ DUMP_REG(SOR_MSCHECK);
+ DUMP_REG(SOR_XBAR_CTRL);
+ DUMP_REG(SOR_XBAR_POL);
+ DUMP_REG(SOR_DP_LINKCTL_0);
+ DUMP_REG(SOR_DP_LINKCTL_1);
+ DUMP_REG(SOR_LANE_DRIVE_CURRENT_0);
+ DUMP_REG(SOR_LANE_DRIVE_CURRENT_1);
+ DUMP_REG(SOR_LANE4_DRIVE_CURRENT_0);
+ DUMP_REG(SOR_LANE4_DRIVE_CURRENT_1);
+ DUMP_REG(SOR_LANE_PREEMPHASIS_0);
+ DUMP_REG(SOR_LANE_PREEMPHASIS_1);
+ DUMP_REG(SOR_LANE4_PREEMPHASIS_0);
+ DUMP_REG(SOR_LANE4_PREEMPHASIS_1);
+ DUMP_REG(SOR_LANE_POST_CURSOR_0);
+ DUMP_REG(SOR_LANE_POST_CURSOR_1);
+ DUMP_REG(SOR_DP_CONFIG_0);
+ DUMP_REG(SOR_DP_CONFIG_1);
+ DUMP_REG(SOR_DP_MN_0);
+ DUMP_REG(SOR_DP_MN_1);
+ DUMP_REG(SOR_DP_PADCTL_0);
+ DUMP_REG(SOR_DP_PADCTL_1);
+ DUMP_REG(SOR_DP_DEBUG_0);
+ DUMP_REG(SOR_DP_DEBUG_1);
+ DUMP_REG(SOR_DP_SPARE_0);
+ DUMP_REG(SOR_DP_SPARE_1);
+ DUMP_REG(SOR_DP_AUDIO_CTRL);
+ DUMP_REG(SOR_DP_AUDIO_HBLANK_SYMBOLS);
+ DUMP_REG(SOR_DP_AUDIO_VBLANK_SYMBOLS);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_HEADER);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_0);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_1);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_2);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_3);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_4);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_5);
+ DUMP_REG(SOR_DP_GENERIC_INFOFRAME_SUBPACK_6);
+ DUMP_REG(SOR_DP_TPG);
+ DUMP_REG(SOR_DP_TPG_CONFIG);
+ DUMP_REG(SOR_DP_LQ_CSTM_0);
+ DUMP_REG(SOR_DP_LQ_CSTM_1);
+ DUMP_REG(SOR_DP_LQ_CSTM_2);
+
+#undef DUMP_REG
+
+ return 0;
+}
+
+static const struct drm_info_list debugfs_files[] = {
+ { "regs", tegra_sor_show_regs, 0, NULL },
+};
+
static int tegra_sor_debugfs_init(struct tegra_sor *sor,
struct drm_minor *minor)
{
struct dentry *entry;
+ unsigned int i;
int err = 0;
sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root);
if (!sor->debugfs)
return -ENOMEM;
+ sor->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
+ GFP_KERNEL);
+ if (!sor->debugfs_files) {
+ err = -ENOMEM;
+ goto remove;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_files); i++)
+ sor->debugfs_files[i].data = sor;
+
+ err = drm_debugfs_create_files(sor->debugfs_files,
+ ARRAY_SIZE(debugfs_files),
+ sor->debugfs, minor);
+ if (err < 0)
+ goto free;
+
entry = debugfs_create_file("crc", 0644, sor->debugfs, sor,
&tegra_sor_crc_fops);
if (!entry) {
- dev_err(sor->dev,
- "cannot create /sys/kernel/debug/dri/%s/sor/crc\n",
- minor->debugfs_root->d_name.name);
err = -ENOMEM;
- goto remove;
+ goto free;
}
return err;
+free:
+ kfree(sor->debugfs_files);
+ sor->debugfs_files = NULL;
remove:
- debugfs_remove(sor->debugfs);
+ debugfs_remove_recursive(sor->debugfs);
sor->debugfs = NULL;
return err;
}
static void tegra_sor_debugfs_exit(struct tegra_sor *sor)
{
- debugfs_remove_recursive(sor->debugfs);
+ drm_debugfs_remove_files(sor->debugfs_files, ARRAY_SIZE(debugfs_files),
+ sor->minor);
+ sor->minor = NULL;
+
+ kfree(sor->debugfs_files);
sor->debugfs = NULL;
+
+ debugfs_remove_recursive(sor->debugfs);
+ sor->debugfs_files = NULL;
}
static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode)
@@ -791,8 +949,8 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder,
struct tegra_sor_config config;
struct drm_dp_link link;
struct drm_dp_aux *aux;
- unsigned long value;
int err = 0;
+ u32 value;
mutex_lock(&sor->lock);
@@ -1354,12 +1512,30 @@ static int tegra_sor_init(struct host1x_client *client)
}
}
+ /*
+ * XXX: Remove this reset once proper hand-over from firmware to
+ * kernel is possible.
+ */
+ err = reset_control_assert(sor->rst);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to assert SOR reset: %d\n", err);
+ return err;
+ }
+
err = clk_prepare_enable(sor->clk);
if (err < 0) {
dev_err(sor->dev, "failed to enable clock: %d\n", err);
return err;
}
+ usleep_range(1000, 3000);
+
+ err = reset_control_deassert(sor->rst);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to deassert SOR reset: %d\n", err);
+ return err;
+ }
+
err = clk_prepare_enable(sor->clk_safe);
if (err < 0)
return err;
diff --git a/drivers/gpu/drm/vgem/Makefile b/drivers/gpu/drm/vgem/Makefile
new file mode 100644
index 000000000000..1055cb79096c
--- /dev/null
+++ b/drivers/gpu/drm/vgem/Makefile
@@ -0,0 +1,4 @@
+ccflags-y := -Iinclude/drm
+vgem-y := vgem_drv.o vgem_dma_buf.o
+
+obj-$(CONFIG_DRM_VGEM) += vgem.o
diff --git a/drivers/gpu/drm/vgem/vgem_dma_buf.c b/drivers/gpu/drm/vgem/vgem_dma_buf.c
new file mode 100644
index 000000000000..0254438ad1a6
--- /dev/null
+++ b/drivers/gpu/drm/vgem/vgem_dma_buf.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2014 The Chromium OS Authors
+ *
+ * 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, sublicense,
+ * 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 NONINFRINGEMENT. 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.
+ *
+ * Authors:
+ * Ben Widawsky <ben@bwidawsk.net>
+ *
+ */
+
+#include <linux/dma-buf.h>
+#include "vgem_drv.h"
+
+struct sg_table *vgem_gem_prime_get_sg_table(struct drm_gem_object *gobj)
+{
+ struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
+ BUG_ON(obj->pages == NULL);
+
+ return drm_prime_pages_to_sg(obj->pages, obj->base.size / PAGE_SIZE);
+}
+
+int vgem_gem_prime_pin(struct drm_gem_object *gobj)
+{
+ struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
+ return vgem_gem_get_pages(obj);
+}
+
+void vgem_gem_prime_unpin(struct drm_gem_object *gobj)
+{
+ struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
+ vgem_gem_put_pages(obj);
+}
+
+void *vgem_gem_prime_vmap(struct drm_gem_object *gobj)
+{
+ struct drm_vgem_gem_object *obj = to_vgem_bo(gobj);
+ BUG_ON(obj->pages == NULL);
+
+ return vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL);
+}
+
+void vgem_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
+{
+ vunmap(vaddr);
+}
+
+struct drm_gem_object *vgem_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct drm_vgem_gem_object *obj = NULL;
+ int ret;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (obj == NULL) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = drm_gem_object_init(dev, &obj->base, dma_buf->size);
+ if (ret) {
+ ret = -ENOMEM;
+ goto fail_free;
+ }
+
+ get_dma_buf(dma_buf);
+
+ obj->base.dma_buf = dma_buf;
+ obj->use_dma_buf = true;
+
+ return &obj->base;
+
+fail_free:
+ kfree(obj);
+fail:
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/vgem/vgem_drv.c b/drivers/gpu/drm/vgem/vgem_drv.c
new file mode 100644
index 000000000000..cb3b43525b2d
--- /dev/null
+++ b/drivers/gpu/drm/vgem/vgem_drv.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ * Copyright © 2014 The Chromium OS Authors
+ *
+ * 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
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * them 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 MERCHANTIBILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS 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.
+ *
+ * Authors:
+ * Adam Jackson <ajax@redhat.com>
+ * Ben Widawsky <ben@bwidawsk.net>
+ */
+
+/**
+ * This is vgem, a (non-hardware-backed) GEM service. This is used by Mesa's
+ * software renderer and the X server for efficient buffer sharing.
+ */
+
+#include <linux/module.h>
+#include <linux/ramfs.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include "vgem_drv.h"
+
+#define DRIVER_NAME "vgem"
+#define DRIVER_DESC "Virtual GEM provider"
+#define DRIVER_DATE "20120112"
+#define DRIVER_MAJOR 1
+#define DRIVER_MINOR 0
+
+void vgem_gem_put_pages(struct drm_vgem_gem_object *obj)
+{
+ drm_gem_put_pages(&obj->base, obj->pages, false, false);
+ obj->pages = NULL;
+}
+
+static void vgem_gem_free_object(struct drm_gem_object *obj)
+{
+ struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
+
+ drm_gem_free_mmap_offset(obj);
+
+ if (vgem_obj->use_dma_buf && obj->dma_buf) {
+ dma_buf_put(obj->dma_buf);
+ obj->dma_buf = NULL;
+ }
+
+ drm_gem_object_release(obj);
+
+ if (vgem_obj->pages)
+ vgem_gem_put_pages(vgem_obj);
+
+ vgem_obj->pages = NULL;
+
+ kfree(vgem_obj);
+}
+
+int vgem_gem_get_pages(struct drm_vgem_gem_object *obj)
+{
+ struct page **pages;
+
+ if (obj->pages || obj->use_dma_buf)
+ return 0;
+
+ pages = drm_gem_get_pages(&obj->base);
+ if (IS_ERR(pages)) {
+ return PTR_ERR(pages);
+ }
+
+ obj->pages = pages;
+
+ return 0;
+}
+
+static int vgem_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct drm_vgem_gem_object *obj = vma->vm_private_data;
+ struct drm_device *dev = obj->base.dev;
+ loff_t num_pages;
+ pgoff_t page_offset;
+ int ret;
+
+ /* We don't use vmf->pgoff since that has the fake offset */
+ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
+ PAGE_SHIFT;
+
+ num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE);
+
+ if (page_offset > num_pages)
+ return VM_FAULT_SIGBUS;
+
+ mutex_lock(&dev->struct_mutex);
+
+ ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address,
+ obj->pages[page_offset]);
+
+ mutex_unlock(&dev->struct_mutex);
+ switch (ret) {
+ case 0:
+ return VM_FAULT_NOPAGE;
+ case -ENOMEM:
+ return VM_FAULT_OOM;
+ case -EBUSY:
+ return VM_FAULT_RETRY;
+ case -EFAULT:
+ case -EINVAL:
+ return VM_FAULT_SIGBUS;
+ default:
+ WARN_ON(1);
+ return VM_FAULT_SIGBUS;
+ }
+}
+
+static struct vm_operations_struct vgem_gem_vm_ops = {
+ .fault = vgem_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+/* ioctls */
+
+static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
+ struct drm_file *file,
+ unsigned int *handle,
+ unsigned long size)
+{
+ struct drm_vgem_gem_object *obj;
+ struct drm_gem_object *gem_object;
+ int err;
+
+ size = roundup(size, PAGE_SIZE);
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ gem_object = &obj->base;
+
+ err = drm_gem_object_init(dev, gem_object, size);
+ if (err)
+ goto out;
+
+ err = drm_gem_handle_create(file, gem_object, handle);
+ if (err)
+ goto handle_out;
+
+ drm_gem_object_unreference_unlocked(gem_object);
+
+ return gem_object;
+
+handle_out:
+ drm_gem_object_release(gem_object);
+out:
+ kfree(obj);
+ return ERR_PTR(err);
+}
+
+static int vgem_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct drm_gem_object *gem_object;
+ uint64_t size;
+ uint64_t pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
+
+ size = args->height * pitch;
+ if (size == 0)
+ return -EINVAL;
+
+ gem_object = vgem_gem_create(dev, file, &args->handle, size);
+
+ if (IS_ERR(gem_object)) {
+ DRM_DEBUG_DRIVER("object creation failed\n");
+ return PTR_ERR(gem_object);
+ }
+
+ args->size = gem_object->size;
+ args->pitch = pitch;
+
+ DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
+
+ return 0;
+}
+
+int vgem_gem_dumb_map(struct drm_file *file, struct drm_device *dev,
+ uint32_t handle, uint64_t *offset)
+{
+ int ret = 0;
+ struct drm_gem_object *obj;
+
+ mutex_lock(&dev->struct_mutex);
+ obj = drm_gem_object_lookup(dev, file, handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+
+ if (!drm_vma_node_has_offset(&obj->vma_node)) {
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto unref;
+ }
+
+ BUG_ON(!obj->filp);
+
+ obj->filp->private_data = obj;
+
+ ret = vgem_gem_get_pages(to_vgem_bo(obj));
+ if (ret)
+ goto fail_get_pages;
+
+ *offset = drm_vma_node_offset_addr(&obj->vma_node);
+
+ goto unref;
+
+fail_get_pages:
+ drm_gem_free_mmap_offset(obj);
+unref:
+ drm_gem_object_unreference(obj);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
+
+int vgem_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct drm_file *priv = filp->private_data;
+ struct drm_device *dev = priv->minor->dev;
+ struct drm_vma_offset_node *node;
+ struct drm_gem_object *obj;
+ struct drm_vgem_gem_object *vgem_obj;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+
+ node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
+ vma->vm_pgoff,
+ vma_pages(vma));
+ if (!node) {
+ ret = -EINVAL;
+ goto out_unlock;
+ } else if (!drm_vma_node_is_allowed(node, filp)) {
+ ret = -EACCES;
+ goto out_unlock;
+ }
+
+ obj = container_of(node, struct drm_gem_object, vma_node);
+
+ vgem_obj = to_vgem_bo(obj);
+
+ if (obj->dma_buf && vgem_obj->use_dma_buf) {
+ ret = dma_buf_mmap(obj->dma_buf, vma, 0);
+ goto out_unlock;
+ }
+
+ if (!obj->dev->driver->gem_vm_ops) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP;
+ vma->vm_ops = obj->dev->driver->gem_vm_ops;
+ vma->vm_private_data = vgem_obj;
+ vma->vm_page_prot =
+ pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
+
+ mutex_unlock(&dev->struct_mutex);
+ drm_gem_vm_open(vma);
+ return ret;
+
+out_unlock:
+ mutex_unlock(&dev->struct_mutex);
+
+ return ret;
+}
+
+
+static struct drm_ioctl_desc vgem_ioctls[] = {
+};
+
+static const struct file_operations vgem_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = vgem_drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+};
+
+static struct drm_driver vgem_driver = {
+ .driver_features = DRIVER_GEM | DRIVER_PRIME,
+ .gem_free_object = vgem_gem_free_object,
+ .gem_vm_ops = &vgem_gem_vm_ops,
+ .ioctls = vgem_ioctls,
+ .fops = &vgem_driver_fops,
+ .dumb_create = vgem_gem_dumb_create,
+ .dumb_map_offset = vgem_gem_dumb_map,
+ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+ .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = vgem_gem_prime_import,
+ .gem_prime_pin = vgem_gem_prime_pin,
+ .gem_prime_unpin = vgem_gem_prime_unpin,
+ .gem_prime_get_sg_table = vgem_gem_prime_get_sg_table,
+ .gem_prime_vmap = vgem_gem_prime_vmap,
+ .gem_prime_vunmap = vgem_gem_prime_vunmap,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+};
+
+struct drm_device *vgem_device;
+
+static int __init vgem_init(void)
+{
+ int ret;
+
+ vgem_device = drm_dev_alloc(&vgem_driver, NULL);
+ if (!vgem_device) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = drm_dev_register(vgem_device, 0);
+
+ if (ret)
+ goto out_unref;
+
+ return 0;
+
+out_unref:
+ drm_dev_unref(vgem_device);
+out:
+ return ret;
+}
+
+static void __exit vgem_exit(void)
+{
+ drm_dev_unregister(vgem_device);
+ drm_dev_unref(vgem_device);
+}
+
+module_init(vgem_init);
+module_exit(vgem_exit);
+
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/vgem/vgem_drv.h b/drivers/gpu/drm/vgem/vgem_drv.h
new file mode 100644
index 000000000000..57ab4d8f41f9
--- /dev/null
+++ b/drivers/gpu/drm/vgem/vgem_drv.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2014 The Chromium OS Authors
+ *
+ * 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, sublicense,
+ * 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 NONINFRINGEMENT. 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.
+ *
+ * Authors:
+ * Ben Widawsky <ben@bwidawsk.net>
+ *
+ */
+
+#ifndef _VGEM_DRV_H_
+#define _VGEM_DRV_H_
+
+#include <drm/drmP.h>
+#include <drm/drm_gem.h>
+
+#define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base)
+struct drm_vgem_gem_object {
+ struct drm_gem_object base;
+ struct page **pages;
+ bool use_dma_buf;
+};
+
+/* vgem_drv.c */
+extern void vgem_gem_put_pages(struct drm_vgem_gem_object *obj);
+extern int vgem_gem_get_pages(struct drm_vgem_gem_object *obj);
+
+/* vgem_dma_buf.c */
+extern struct sg_table *vgem_gem_prime_get_sg_table(
+ struct drm_gem_object *gobj);
+extern int vgem_gem_prime_pin(struct drm_gem_object *gobj);
+extern void vgem_gem_prime_unpin(struct drm_gem_object *gobj);
+extern void *vgem_gem_prime_vmap(struct drm_gem_object *gobj);
+extern void vgem_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+extern struct drm_gem_object *vgem_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf);
+
+
+#endif
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index b10550ee1d89..6b7fdc1e2ed0 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -425,6 +425,12 @@ u32 host1x_syncpt_read_min(struct host1x_syncpt *sp)
}
EXPORT_SYMBOL(host1x_syncpt_read_min);
+u32 host1x_syncpt_read(struct host1x_syncpt *sp)
+{
+ return host1x_syncpt_load(sp);
+}
+EXPORT_SYMBOL(host1x_syncpt_read);
+
int host1x_syncpt_nb_pts(struct host1x *host)
{
return host->info->nb_pts;
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 829280b56874..d665781eb542 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -87,20 +87,34 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector,
/* default implementations for state handling */
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc);
+void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state);
struct drm_crtc_state *
drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc);
+void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state);
void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
void drm_atomic_helper_plane_reset(struct drm_plane *plane);
+void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
+ struct drm_plane_state *state);
struct drm_plane_state *
drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane);
+void __drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state);
void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state);
void drm_atomic_helper_connector_reset(struct drm_connector *connector);
+void
+__drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,
+ struct drm_connector_state *state);
struct drm_connector_state *
drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector);
+void
+__drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
+ struct drm_connector_state *state);
void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 7b5c661b37d8..2e80ad1aea84 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -53,7 +53,6 @@ struct fence;
#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
-#define DRM_MODE_OBJECT_BRIDGE 0xbdbdbdbd
#define DRM_MODE_OBJECT_ANY 0
struct drm_mode_object {
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 1fbcc96063a7..13ff44b28893 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -29,6 +29,7 @@
struct drm_connector;
struct drm_device;
struct drm_panel;
+struct display_timing;
/**
* struct drm_panel_funcs - perform operations on a given panel
@@ -38,6 +39,8 @@ struct drm_panel;
* @enable: enable panel (turn on back light, etc.)
* @get_modes: add modes to the connector that the panel is attached to and
* return the number of modes added
+ * @get_timings: copy display timings into the provided array and return
+ * the number of display timings available
*
* The .prepare() function is typically called before the display controller
* starts to transmit video data. Panel drivers can use this to turn the panel
@@ -68,6 +71,8 @@ struct drm_panel_funcs {
int (*prepare)(struct drm_panel *panel);
int (*enable)(struct drm_panel *panel);
int (*get_modes)(struct drm_panel *panel);
+ int (*get_timings)(struct drm_panel *panel, unsigned int num_timings,
+ struct display_timing *timings);
};
struct drm_panel {
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 464f33814a94..d2ba7d334039 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -135,6 +135,7 @@ 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);
+u32 host1x_syncpt_read(struct host1x_syncpt *sp);
int host1x_syncpt_incr(struct host1x_syncpt *sp);
u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs);
int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h
index c15d781ecc0f..5391780c2b05 100644
--- a/include/uapi/drm/tegra_drm.h
+++ b/include/uapi/drm/tegra_drm.h
@@ -36,7 +36,8 @@ struct drm_tegra_gem_create {
struct drm_tegra_gem_mmap {
__u32 handle;
- __u32 offset;
+ __u32 pad;
+ __u64 offset;
};
struct drm_tegra_syncpt_read {