1159 lines
32 KiB
Diff
1159 lines
32 KiB
Diff
|
From f70f270793e34ae99ab777f6cb27cb1e72054d78 Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?Filip=20Matijevi=C4=87?= <filip.matijevic.pz@gmail.com>
|
||
|
Date: Sat, 28 Oct 2017 14:28:52 +0200
|
||
|
Subject: [PATCH 01/12] SEB
|
||
|
|
||
|
---
|
||
|
arch/arm/boot/dts/omap3-n950.dts | 88 ++++++++
|
||
|
arch/arm/boot/dts/omap4-droid4-xt894.dts | 6 +-
|
||
|
drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c | 289 +++++++++++++++++-------
|
||
|
drivers/gpu/drm/omapdrm/dss/dispc.c | 16 ++
|
||
|
drivers/gpu/drm/omapdrm/dss/omapdss.h | 5 +-
|
||
|
drivers/gpu/drm/omapdrm/omap_connector.c | 14 ++
|
||
|
drivers/gpu/drm/omapdrm/omap_crtc.c | 158 ++++++++++++-
|
||
|
drivers/gpu/drm/omapdrm/omap_drv.h | 4 +
|
||
|
drivers/gpu/drm/omapdrm/omap_fb.c | 20 ++
|
||
|
drivers/gpu/drm/omapdrm/omap_fbdev.c | 3 -
|
||
|
drivers/gpu/drm/omapdrm/omap_irq.c | 24 ++
|
||
|
11 files changed, 529 insertions(+), 98 deletions(-)
|
||
|
|
||
|
diff --git a/arch/arm/boot/dts/omap3-n950.dts b/arch/arm/boot/dts/omap3-n950.dts
|
||
|
index c354a1ed1e70..2354e09343f3 100644
|
||
|
--- a/arch/arm/boot/dts/omap3-n950.dts
|
||
|
+++ b/arch/arm/boot/dts/omap3-n950.dts
|
||
|
@@ -51,6 +51,26 @@
|
||
|
};
|
||
|
};
|
||
|
|
||
|
+&omap3_pmx_core {
|
||
|
+ dsi_pins: pinmux_dsi_pins {
|
||
|
+ pinctrl-single,pins = <
|
||
|
+ OMAP3_CORE1_IOPAD(0x20dc, PIN_OUTPUT | MUX_MODE1) /* dsi_dx0 - data0+ */
|
||
|
+ OMAP3_CORE1_IOPAD(0x20de, PIN_OUTPUT | MUX_MODE1) /* dsi_dy0 - data0- */
|
||
|
+ OMAP3_CORE1_IOPAD(0x20e0, PIN_OUTPUT | MUX_MODE1) /* dsi_dx1 - clk+ */
|
||
|
+ OMAP3_CORE1_IOPAD(0x20e2, PIN_OUTPUT | MUX_MODE1) /* dsi_dy1 - clk- */
|
||
|
+ OMAP3_CORE1_IOPAD(0x20e4, PIN_OUTPUT | MUX_MODE1) /* dsi_dx2 - data1+ */
|
||
|
+ OMAP3_CORE1_IOPAD(0x20e6, PIN_OUTPUT | MUX_MODE1) /* dsi_dy2 - data1- */
|
||
|
+ >;
|
||
|
+ };
|
||
|
+
|
||
|
+ display_pins: pinmux_display_pins {
|
||
|
+ pinctrl-single,pins = <
|
||
|
+ OMAP3_CORE1_IOPAD(0x20ca, PIN_INPUT | MUX_MODE4) /* gpio 62 - display te */
|
||
|
+ OMAP3_CORE1_IOPAD(0x20fe, PIN_OUTPUT | MUX_MODE4) /* gpio 87 - display reset */
|
||
|
+ >;
|
||
|
+ };
|
||
|
+};
|
||
|
+
|
||
|
&i2c2 {
|
||
|
smia_1: camera@10 {
|
||
|
compatible = "nokia,smia";
|
||
|
@@ -186,3 +206,71 @@
|
||
|
st,max-limit-y = <32>;
|
||
|
st,max-limit-z = <32>;
|
||
|
};
|
||
|
+
|
||
|
+&dss {
|
||
|
+ status = "ok";
|
||
|
+
|
||
|
+ vdda_video-supply = <&vdac>;
|
||
|
+};
|
||
|
+
|
||
|
+&dsi {
|
||
|
+ status = "ok";
|
||
|
+
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&dsi_pins>;
|
||
|
+
|
||
|
+ vdd-supply = <&vpll2>;
|
||
|
+
|
||
|
+ port {
|
||
|
+ dsi_out_ep: endpoint {
|
||
|
+ remote-endpoint = <&lcd0_in>;
|
||
|
+ lanes = <2 3 0 1 4 5>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+
|
||
|
+ lcd0: display {
|
||
|
+ compatible = "nokia,himalaya", "panel-dsi-cm";
|
||
|
+ label = "lcd0";
|
||
|
+
|
||
|
+ pinctrl-names = "default";
|
||
|
+ pinctrl-0 = <&display_pins>;
|
||
|
+
|
||
|
+ vpnl-supply = <&vmmc2>;
|
||
|
+ vddi-supply = <&vio>;
|
||
|
+
|
||
|
+ reset-gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>; /* 87 */
|
||
|
+ te-gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>; /* 62 */
|
||
|
+
|
||
|
+ width-mm = <49>; /* 48.960 mm */
|
||
|
+ height-mm = <88>; /* 88.128 mm */
|
||
|
+
|
||
|
+ /* TODO:
|
||
|
+ * - panel is upside-down
|
||
|
+ * - top + bottom 5px are not visible
|
||
|
+ */
|
||
|
+ panel-timing {
|
||
|
+ clock-frequency = <0>; /* Calculated by dsi */
|
||
|
+
|
||
|
+ hback-porch = <2>;
|
||
|
+ hactive = <480>;
|
||
|
+ hfront-porch = <0>;
|
||
|
+ hsync-len = <2>;
|
||
|
+
|
||
|
+ vback-porch = <1>;
|
||
|
+ vactive = <864>;
|
||
|
+ vfront-porch = <0>;
|
||
|
+ vsync-len = <1>;
|
||
|
+
|
||
|
+ hsync-active = <0>;
|
||
|
+ vsync-active = <0>;
|
||
|
+ de-active = <1>;
|
||
|
+ pixelclk-active = <1>;
|
||
|
+ };
|
||
|
+
|
||
|
+ port {
|
||
|
+ lcd0_in: endpoint {
|
||
|
+ remote-endpoint = <&dsi_out_ep>;
|
||
|
+ };
|
||
|
+ };
|
||
|
+ };
|
||
|
+};
|
||
|
diff --git a/arch/arm/boot/dts/omap4-droid4-xt894.dts b/arch/arm/boot/dts/omap4-droid4-xt894.dts
|
||
|
index 24a463f8641f..b21084da490b 100644
|
||
|
--- a/arch/arm/boot/dts/omap4-droid4-xt894.dts
|
||
|
+++ b/arch/arm/boot/dts/omap4-droid4-xt894.dts
|
||
|
@@ -177,6 +177,10 @@
|
||
|
vddi-supply = <&lcd_regulator>;
|
||
|
reset-gpios = <&gpio4 5 GPIO_ACTIVE_HIGH>; /* gpio101 */
|
||
|
|
||
|
+ width-mm = <50>;
|
||
|
+ height-mm = <89>;
|
||
|
+ backlight = <&lcd_backlight>;
|
||
|
+
|
||
|
panel-timing {
|
||
|
clock-frequency = <0>; /* Calculated by dsi */
|
||
|
|
||
|
@@ -346,7 +350,7 @@
|
||
|
|
||
|
enable-gpios = <&gpio6 12 GPIO_ACTIVE_HIGH>;
|
||
|
|
||
|
- backlight {
|
||
|
+ lcd_backlight: backlight {
|
||
|
compatible = "ti,lm3532-backlight";
|
||
|
|
||
|
lcd {
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
|
||
|
index 92c556ac22c7..996991de674b 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
|
||
|
@@ -22,9 +22,10 @@
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/of_device.h>
|
||
|
-#include <linux/of_gpio.h>
|
||
|
+#include <linux/regulator/consumer.h>
|
||
|
|
||
|
#include <video/mipi_display.h>
|
||
|
+#include <video/of_display_timing.h>
|
||
|
|
||
|
#include "../dss/omapdss.h"
|
||
|
|
||
|
@@ -49,6 +50,7 @@ struct panel_drv_data {
|
||
|
struct mutex lock;
|
||
|
|
||
|
struct backlight_device *bldev;
|
||
|
+ struct backlight_device *extbldev;
|
||
|
|
||
|
unsigned long hw_guard_end; /* next value of jiffies when we can
|
||
|
* issue the next sleep in/out command
|
||
|
@@ -56,11 +58,17 @@ struct panel_drv_data {
|
||
|
unsigned long hw_guard_wait; /* max guard time in jiffies */
|
||
|
|
||
|
/* panel HW configuration from DT or platform data */
|
||
|
- int reset_gpio;
|
||
|
- int ext_te_gpio;
|
||
|
+ struct gpio_desc *reset_gpio;
|
||
|
+ struct gpio_desc *ext_te_gpio;
|
||
|
+
|
||
|
+ struct regulator *vpnl;
|
||
|
+ struct regulator *vddi;
|
||
|
|
||
|
bool use_dsi_backlight;
|
||
|
|
||
|
+ int width_mm;
|
||
|
+ int height_mm;
|
||
|
+
|
||
|
struct omap_dsi_pin_config pin_config;
|
||
|
|
||
|
/* runtime variables */
|
||
|
@@ -92,6 +100,30 @@ static int dsicm_panel_reset(struct panel_drv_data *ddata);
|
||
|
|
||
|
static void dsicm_ulps_work(struct work_struct *work);
|
||
|
|
||
|
+static void dsicm_bl_power(struct panel_drv_data *ddata, bool enable)
|
||
|
+{
|
||
|
+ struct backlight_device *backlight;
|
||
|
+
|
||
|
+ if (ddata->bldev)
|
||
|
+ backlight = ddata->bldev;
|
||
|
+ else if (ddata->extbldev)
|
||
|
+ backlight = ddata->extbldev;
|
||
|
+ else
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (enable) {
|
||
|
+ backlight->props.fb_blank = FB_BLANK_UNBLANK;
|
||
|
+ backlight->props.state = ~(BL_CORE_FBBLANK | BL_CORE_SUSPENDED);
|
||
|
+ backlight->props.power = FB_BLANK_UNBLANK;
|
||
|
+ } else {
|
||
|
+ backlight->props.fb_blank = FB_BLANK_NORMAL;
|
||
|
+ backlight->props.power = FB_BLANK_POWERDOWN;
|
||
|
+ backlight->props.state |= BL_CORE_FBBLANK | BL_CORE_SUSPENDED;
|
||
|
+ }
|
||
|
+
|
||
|
+ backlight_update_status(backlight);
|
||
|
+}
|
||
|
+
|
||
|
static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
|
||
|
{
|
||
|
ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
|
||
|
@@ -255,8 +287,8 @@ static int dsicm_enter_ulps(struct panel_drv_data *ddata)
|
||
|
if (r)
|
||
|
goto err;
|
||
|
|
||
|
- if (gpio_is_valid(ddata->ext_te_gpio))
|
||
|
- disable_irq(gpio_to_irq(ddata->ext_te_gpio));
|
||
|
+ if (ddata->ext_te_gpio)
|
||
|
+ disable_irq(gpiod_to_irq(ddata->ext_te_gpio));
|
||
|
|
||
|
in->ops.dsi->disable(in, false, true);
|
||
|
|
||
|
@@ -297,8 +329,8 @@ static int dsicm_exit_ulps(struct panel_drv_data *ddata)
|
||
|
goto err2;
|
||
|
}
|
||
|
|
||
|
- if (gpio_is_valid(ddata->ext_te_gpio))
|
||
|
- enable_irq(gpio_to_irq(ddata->ext_te_gpio));
|
||
|
+ if (ddata->ext_te_gpio)
|
||
|
+ enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
|
||
|
|
||
|
dsicm_queue_ulps_work(ddata);
|
||
|
|
||
|
@@ -311,8 +343,8 @@ static int dsicm_exit_ulps(struct panel_drv_data *ddata)
|
||
|
|
||
|
r = dsicm_panel_reset(ddata);
|
||
|
if (!r) {
|
||
|
- if (gpio_is_valid(ddata->ext_te_gpio))
|
||
|
- enable_irq(gpio_to_irq(ddata->ext_te_gpio));
|
||
|
+ if (ddata->ext_te_gpio)
|
||
|
+ enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
|
||
|
ddata->ulps_enabled = false;
|
||
|
}
|
||
|
err1:
|
||
|
@@ -335,7 +367,7 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
|
||
|
{
|
||
|
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
|
||
|
struct omap_dss_device *in = ddata->in;
|
||
|
- int r;
|
||
|
+ int r = 0;
|
||
|
int level;
|
||
|
|
||
|
if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
|
||
|
@@ -356,8 +388,6 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
|
||
|
r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);
|
||
|
|
||
|
in->ops.dsi->bus_unlock(in);
|
||
|
- } else {
|
||
|
- r = 0;
|
||
|
}
|
||
|
|
||
|
mutex_unlock(&ddata->lock);
|
||
|
@@ -560,16 +590,13 @@ static const struct attribute_group dsicm_attr_group = {
|
||
|
|
||
|
static void dsicm_hw_reset(struct panel_drv_data *ddata)
|
||
|
{
|
||
|
- if (!gpio_is_valid(ddata->reset_gpio))
|
||
|
- return;
|
||
|
-
|
||
|
- gpio_set_value(ddata->reset_gpio, 1);
|
||
|
+ gpiod_set_value(ddata->reset_gpio, 1);
|
||
|
udelay(10);
|
||
|
/* reset the panel */
|
||
|
- gpio_set_value(ddata->reset_gpio, 0);
|
||
|
+ gpiod_set_value(ddata->reset_gpio, 0);
|
||
|
/* assert reset */
|
||
|
udelay(10);
|
||
|
- gpio_set_value(ddata->reset_gpio, 1);
|
||
|
+ gpiod_set_value(ddata->reset_gpio, 1);
|
||
|
/* wait after releasing reset */
|
||
|
usleep_range(5000, 10000);
|
||
|
}
|
||
|
@@ -589,25 +616,43 @@ static int dsicm_power_on(struct panel_drv_data *ddata)
|
||
|
.lp_clk_max = 10000000,
|
||
|
};
|
||
|
|
||
|
+ if (ddata->vpnl) {
|
||
|
+ r = regulator_enable(ddata->vpnl);
|
||
|
+ if (r) {
|
||
|
+ dev_err(&ddata->pdev->dev,
|
||
|
+ "failed to enable VPNL: %d\n", r);
|
||
|
+ return r;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (ddata->vddi) {
|
||
|
+ r = regulator_enable(ddata->vddi);
|
||
|
+ if (r) {
|
||
|
+ dev_err(&ddata->pdev->dev,
|
||
|
+ "failed to enable VDDI: %d\n", r);
|
||
|
+ goto err_vpnl;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
if (ddata->pin_config.num_pins > 0) {
|
||
|
r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
|
||
|
if (r) {
|
||
|
dev_err(&ddata->pdev->dev,
|
||
|
"failed to configure DSI pins\n");
|
||
|
- goto err0;
|
||
|
+ goto err_vddi;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
r = in->ops.dsi->set_config(in, &dsi_config);
|
||
|
if (r) {
|
||
|
dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
|
||
|
- goto err0;
|
||
|
+ goto err_vddi;
|
||
|
}
|
||
|
|
||
|
r = in->ops.dsi->enable(in);
|
||
|
if (r) {
|
||
|
dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
|
||
|
- goto err0;
|
||
|
+ goto err_vddi;
|
||
|
}
|
||
|
|
||
|
dsicm_hw_reset(ddata);
|
||
|
@@ -665,7 +710,13 @@ static int dsicm_power_on(struct panel_drv_data *ddata)
|
||
|
dsicm_hw_reset(ddata);
|
||
|
|
||
|
in->ops.dsi->disable(in, true, false);
|
||
|
-err0:
|
||
|
+err_vddi:
|
||
|
+ if (ddata->vddi)
|
||
|
+ regulator_disable(ddata->vddi);
|
||
|
+err_vpnl:
|
||
|
+ if (ddata->vpnl)
|
||
|
+ regulator_disable(ddata->vpnl);
|
||
|
+
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
@@ -688,6 +739,11 @@ static void dsicm_power_off(struct panel_drv_data *ddata)
|
||
|
|
||
|
in->ops.dsi->disable(in, true, false);
|
||
|
|
||
|
+ if (ddata->vddi)
|
||
|
+ regulator_disable(ddata->vddi);
|
||
|
+ if (ddata->vpnl)
|
||
|
+ regulator_disable(ddata->vpnl);
|
||
|
+
|
||
|
ddata->enabled = 0;
|
||
|
}
|
||
|
|
||
|
@@ -782,6 +838,8 @@ static int dsicm_enable(struct omap_dss_device *dssdev)
|
||
|
|
||
|
mutex_unlock(&ddata->lock);
|
||
|
|
||
|
+ dsicm_bl_power(ddata, true);
|
||
|
+
|
||
|
return 0;
|
||
|
err:
|
||
|
dev_dbg(&ddata->pdev->dev, "enable failed\n");
|
||
|
@@ -797,6 +855,8 @@ static void dsicm_disable(struct omap_dss_device *dssdev)
|
||
|
|
||
|
dev_dbg(&ddata->pdev->dev, "disable\n");
|
||
|
|
||
|
+ dsicm_bl_power(ddata, false);
|
||
|
+
|
||
|
mutex_lock(&ddata->lock);
|
||
|
|
||
|
dsicm_cancel_ulps_work(ddata);
|
||
|
@@ -890,7 +950,7 @@ static int dsicm_update(struct omap_dss_device *dssdev,
|
||
|
if (r)
|
||
|
goto err;
|
||
|
|
||
|
- if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) {
|
||
|
+ if (ddata->te_enabled && ddata->ext_te_gpio) {
|
||
|
schedule_delayed_work(&ddata->te_timeout_work,
|
||
|
msecs_to_jiffies(250));
|
||
|
atomic_set(&ddata->do_update, 1);
|
||
|
@@ -937,7 +997,7 @@ static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
|
||
|
else
|
||
|
r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
|
||
|
|
||
|
- if (!gpio_is_valid(ddata->ext_te_gpio))
|
||
|
+ if (!ddata->ext_te_gpio)
|
||
|
in->ops.dsi->enable_te(in, enable);
|
||
|
|
||
|
/* possible panel bug */
|
||
|
@@ -1099,6 +1159,45 @@ static void dsicm_ulps_work(struct work_struct *work)
|
||
|
mutex_unlock(&ddata->lock);
|
||
|
}
|
||
|
|
||
|
+static void dsicm_get_timings(struct omap_dss_device *dssdev,
|
||
|
+ struct videomode *vm)
|
||
|
+{
|
||
|
+ struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||
|
+
|
||
|
+ *vm = ddata->vm;
|
||
|
+}
|
||
|
+
|
||
|
+static int dsicm_check_timings(struct omap_dss_device *dssdev,
|
||
|
+ struct videomode *vm)
|
||
|
+{
|
||
|
+ struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||
|
+ int ret = 0;
|
||
|
+
|
||
|
+ if (vm->hactive != ddata->vm.hactive)
|
||
|
+ ret = -EINVAL;
|
||
|
+
|
||
|
+ if (vm->vactive != ddata->vm.vactive)
|
||
|
+ ret = -EINVAL;
|
||
|
+
|
||
|
+ if (ret) {
|
||
|
+ dev_warn(dssdev->dev, "wrong resolution: %d x %d",
|
||
|
+ vm->hactive, vm->vactive);
|
||
|
+ dev_warn(dssdev->dev, "panel resolution: %d x %d",
|
||
|
+ ddata->vm.hactive, ddata->vm.vactive);
|
||
|
+ }
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+static void dsicm_get_size(struct omap_dss_device *dssdev,
|
||
|
+ unsigned int *width, unsigned int *height)
|
||
|
+{
|
||
|
+ struct panel_drv_data *ddata = to_panel_data(dssdev);
|
||
|
+
|
||
|
+ *width = ddata->width_mm;
|
||
|
+ *height = ddata->height_mm;
|
||
|
+}
|
||
|
+
|
||
|
static struct omap_dss_driver dsicm_ops = {
|
||
|
.connect = dsicm_connect,
|
||
|
.disconnect = dsicm_disconnect,
|
||
|
@@ -1109,6 +1208,10 @@ static struct omap_dss_driver dsicm_ops = {
|
||
|
.update = dsicm_update,
|
||
|
.sync = dsicm_sync,
|
||
|
|
||
|
+ .get_timings = dsicm_get_timings,
|
||
|
+ .check_timings = dsicm_check_timings,
|
||
|
+ .get_size = dsicm_get_size,
|
||
|
+
|
||
|
.enable_te = dsicm_enable_te,
|
||
|
.get_te = dsicm_get_te,
|
||
|
|
||
|
@@ -1118,41 +1221,87 @@ static struct omap_dss_driver dsicm_ops = {
|
||
|
static int dsicm_probe_of(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device_node *node = pdev->dev.of_node;
|
||
|
+ struct device_node *backlight;
|
||
|
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||
|
struct omap_dss_device *in;
|
||
|
- int gpio;
|
||
|
+ struct display_timing timing;
|
||
|
+ int err;
|
||
|
+
|
||
|
+ ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||
|
+ if (IS_ERR(ddata->reset_gpio)) {
|
||
|
+ err = PTR_ERR(ddata->reset_gpio);
|
||
|
+ dev_err(&pdev->dev, "reset gpio request failed: %d", err);
|
||
|
+ return err;
|
||
|
+ }
|
||
|
|
||
|
- gpio = of_get_named_gpio(node, "reset-gpios", 0);
|
||
|
- if (!gpio_is_valid(gpio)) {
|
||
|
- dev_err(&pdev->dev, "failed to parse reset gpio\n");
|
||
|
- return gpio;
|
||
|
+ ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
|
||
|
+ GPIOD_IN);
|
||
|
+ if (IS_ERR(ddata->ext_te_gpio)) {
|
||
|
+ err = PTR_ERR(ddata->ext_te_gpio);
|
||
|
+ dev_err(&pdev->dev, "TE gpio request failed: %d", err);
|
||
|
+ return err;
|
||
|
}
|
||
|
- ddata->reset_gpio = gpio;
|
||
|
|
||
|
- gpio = of_get_named_gpio(node, "te-gpios", 0);
|
||
|
- if (gpio_is_valid(gpio) || gpio == -ENOENT) {
|
||
|
- ddata->ext_te_gpio = gpio;
|
||
|
+ err = of_get_display_timing(node, "panel-timing", &timing);
|
||
|
+ if (!err) {
|
||
|
+ videomode_from_timing(&timing, &ddata->vm);
|
||
|
+ if (!ddata->vm.pixelclock)
|
||
|
+ ddata->vm.pixelclock =
|
||
|
+ ddata->vm.hactive * ddata->vm.vactive * 60;
|
||
|
} else {
|
||
|
- dev_err(&pdev->dev, "failed to parse TE gpio\n");
|
||
|
- return gpio;
|
||
|
+ dev_warn(&pdev->dev,
|
||
|
+ "failed to get video timing, using defaults\n");
|
||
|
}
|
||
|
|
||
|
+ ddata->width_mm = 0;
|
||
|
+ of_property_read_u32(node, "width-mm", &ddata->width_mm);
|
||
|
+
|
||
|
+ ddata->height_mm = 0;
|
||
|
+ of_property_read_u32(node, "height-mm", &ddata->height_mm);
|
||
|
+
|
||
|
in = omapdss_of_find_source_for_first_ep(node);
|
||
|
if (IS_ERR(in)) {
|
||
|
dev_err(&pdev->dev, "failed to find video source\n");
|
||
|
return PTR_ERR(in);
|
||
|
}
|
||
|
|
||
|
+ ddata->vpnl = devm_regulator_get_optional(&pdev->dev, "vpnl");
|
||
|
+ if (IS_ERR(ddata->vpnl)) {
|
||
|
+ err = PTR_ERR(ddata->vpnl);
|
||
|
+ if (err == -EPROBE_DEFER)
|
||
|
+ return err;
|
||
|
+ ddata->vpnl = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ ddata->vddi = devm_regulator_get_optional(&pdev->dev, "vddi");
|
||
|
+ if (IS_ERR(ddata->vddi)) {
|
||
|
+ err = PTR_ERR(ddata->vddi);
|
||
|
+ if (err == -EPROBE_DEFER)
|
||
|
+ return err;
|
||
|
+ ddata->vddi = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
ddata->in = in;
|
||
|
|
||
|
- /* TODO: ulps, backlight */
|
||
|
+ backlight = of_parse_phandle(node, "backlight", 0);
|
||
|
+ if (backlight) {
|
||
|
+ ddata->extbldev = of_find_backlight_by_node(backlight);
|
||
|
+ of_node_put(backlight);
|
||
|
+
|
||
|
+ if (!ddata->extbldev)
|
||
|
+ return -EPROBE_DEFER;
|
||
|
+ } else {
|
||
|
+ /* assume native backlight support */
|
||
|
+ ddata->use_dsi_backlight = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* TODO: ulps */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int dsicm_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
- struct backlight_properties props;
|
||
|
struct panel_drv_data *ddata;
|
||
|
struct backlight_device *bldev = NULL;
|
||
|
struct device *dev = &pdev->dev;
|
||
|
@@ -1171,14 +1320,14 @@ static int dsicm_probe(struct platform_device *pdev)
|
||
|
if (!pdev->dev.of_node)
|
||
|
return -ENODEV;
|
||
|
|
||
|
- r = dsicm_probe_of(pdev);
|
||
|
- if (r)
|
||
|
- return r;
|
||
|
-
|
||
|
ddata->vm.hactive = 864;
|
||
|
ddata->vm.vactive = 480;
|
||
|
ddata->vm.pixelclock = 864 * 480 * 60;
|
||
|
|
||
|
+ r = dsicm_probe_of(pdev);
|
||
|
+ if (r)
|
||
|
+ return r;
|
||
|
+
|
||
|
dssdev = &ddata->dssdev;
|
||
|
dssdev->dev = dev;
|
||
|
dssdev->driver = &dsicm_ops;
|
||
|
@@ -1200,31 +1349,15 @@ static int dsicm_probe(struct platform_device *pdev)
|
||
|
|
||
|
atomic_set(&ddata->do_update, 0);
|
||
|
|
||
|
- if (gpio_is_valid(ddata->reset_gpio)) {
|
||
|
- r = devm_gpio_request_one(dev, ddata->reset_gpio,
|
||
|
- GPIOF_OUT_INIT_LOW, "taal rst");
|
||
|
- if (r) {
|
||
|
- dev_err(dev, "failed to request reset gpio\n");
|
||
|
- return r;
|
||
|
- }
|
||
|
- }
|
||
|
-
|
||
|
- if (gpio_is_valid(ddata->ext_te_gpio)) {
|
||
|
- r = devm_gpio_request_one(dev, ddata->ext_te_gpio,
|
||
|
- GPIOF_IN, "taal irq");
|
||
|
- if (r) {
|
||
|
- dev_err(dev, "GPIO request failed\n");
|
||
|
- return r;
|
||
|
- }
|
||
|
-
|
||
|
- r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio),
|
||
|
+ if (ddata->ext_te_gpio) {
|
||
|
+ r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
|
||
|
dsicm_te_isr,
|
||
|
IRQF_TRIGGER_RISING,
|
||
|
"taal vsync", ddata);
|
||
|
|
||
|
if (r) {
|
||
|
dev_err(dev, "IRQ request failed\n");
|
||
|
- return r;
|
||
|
+ goto err_reg;
|
||
|
}
|
||
|
|
||
|
INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
|
||
|
@@ -1234,48 +1367,43 @@ static int dsicm_probe(struct platform_device *pdev)
|
||
|
}
|
||
|
|
||
|
ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
|
||
|
- if (ddata->workqueue == NULL) {
|
||
|
- dev_err(dev, "can't create workqueue\n");
|
||
|
- return -ENOMEM;
|
||
|
+ if (!ddata->workqueue) {
|
||
|
+ r = -ENOMEM;
|
||
|
+ goto err_reg;
|
||
|
}
|
||
|
INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
|
||
|
|
||
|
dsicm_hw_reset(ddata);
|
||
|
|
||
|
if (ddata->use_dsi_backlight) {
|
||
|
- memset(&props, 0, sizeof(props));
|
||
|
+ struct backlight_properties props = { 0 };
|
||
|
props.max_brightness = 255;
|
||
|
-
|
||
|
props.type = BACKLIGHT_RAW;
|
||
|
- bldev = backlight_device_register(dev_name(dev),
|
||
|
- dev, ddata, &dsicm_bl_ops, &props);
|
||
|
+
|
||
|
+ bldev = devm_backlight_device_register(dev, dev_name(dev),
|
||
|
+ dev, ddata, &dsicm_bl_ops, &props);
|
||
|
if (IS_ERR(bldev)) {
|
||
|
r = PTR_ERR(bldev);
|
||
|
goto err_bl;
|
||
|
}
|
||
|
|
||
|
ddata->bldev = bldev;
|
||
|
-
|
||
|
- bldev->props.fb_blank = FB_BLANK_UNBLANK;
|
||
|
- bldev->props.power = FB_BLANK_UNBLANK;
|
||
|
- bldev->props.brightness = 255;
|
||
|
-
|
||
|
- dsicm_bl_update_status(bldev);
|
||
|
}
|
||
|
|
||
|
r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
|
||
|
if (r) {
|
||
|
dev_err(dev, "failed to create sysfs files\n");
|
||
|
- goto err_sysfs_create;
|
||
|
+ goto err_bl;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
-err_sysfs_create:
|
||
|
- backlight_device_unregister(bldev);
|
||
|
err_bl:
|
||
|
destroy_workqueue(ddata->workqueue);
|
||
|
err_reg:
|
||
|
+ if (ddata->extbldev)
|
||
|
+ put_device(&ddata->extbldev->dev);
|
||
|
+
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
@@ -1283,7 +1411,6 @@ static int __exit dsicm_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
struct panel_drv_data *ddata = platform_get_drvdata(pdev);
|
||
|
struct omap_dss_device *dssdev = &ddata->dssdev;
|
||
|
- struct backlight_device *bldev;
|
||
|
|
||
|
dev_dbg(&pdev->dev, "remove\n");
|
||
|
|
||
|
@@ -1294,12 +1421,8 @@ static int __exit dsicm_remove(struct platform_device *pdev)
|
||
|
|
||
|
sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
|
||
|
|
||
|
- bldev = ddata->bldev;
|
||
|
- if (bldev != NULL) {
|
||
|
- bldev->props.power = FB_BLANK_POWERDOWN;
|
||
|
- dsicm_bl_update_status(bldev);
|
||
|
- backlight_device_unregister(bldev);
|
||
|
- }
|
||
|
+ if (ddata->extbldev)
|
||
|
+ put_device(&ddata->extbldev->dev);
|
||
|
|
||
|
omap_dss_put_device(ddata->in);
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c
|
||
|
index 0f4fdb221498..d5d2d3c62dfa 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/dss/dispc.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/dss/dispc.c
|
||
|
@@ -1491,6 +1491,18 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane_id plane,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+void dispc_ovl_set_manual_fifo_threshold(enum omap_plane_id plane)
|
||
|
+{
|
||
|
+ u32 fifo_low, fifo_high;
|
||
|
+ bool use_fifo_merge = false;
|
||
|
+ bool use_manual_update = true;
|
||
|
+
|
||
|
+ dispc_ovl_compute_fifo_thresholds(plane, &fifo_low, &fifo_high,
|
||
|
+ use_fifo_merge, use_manual_update);
|
||
|
+
|
||
|
+ dispc_ovl_set_fifo_threshold(plane, fifo_low, fifo_high);
|
||
|
+}
|
||
|
+
|
||
|
static void dispc_ovl_set_mflag(enum omap_plane_id plane, bool enable)
|
||
|
{
|
||
|
int bit;
|
||
|
@@ -2654,6 +2666,10 @@ static int dispc_ovl_setup(enum omap_plane_id plane,
|
||
|
oi->zorder, oi->pre_mult_alpha, oi->global_alpha,
|
||
|
oi->rotation_type, replication, vm, mem_to_mem);
|
||
|
|
||
|
+ /* manual mode needs other fifo thresholds */
|
||
|
+ if (mgr_fld_read(channel, DISPC_MGR_FLD_STALLMODE))
|
||
|
+ dispc_ovl_set_manual_fifo_threshold(plane);
|
||
|
+
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h
|
||
|
index 990422b35784..ad6eefc511b7 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/dss/omapdss.h
|
||
|
+++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h
|
||
|
@@ -563,6 +563,8 @@ struct omap_dss_driver {
|
||
|
struct videomode *vm);
|
||
|
void (*get_timings)(struct omap_dss_device *dssdev,
|
||
|
struct videomode *vm);
|
||
|
+ void (*get_size)(struct omap_dss_device *dssdev,
|
||
|
+ unsigned int *width, unsigned int *height);
|
||
|
|
||
|
int (*set_wss)(struct omap_dss_device *dssdev, u32 wss);
|
||
|
u32 (*get_wss)(struct omap_dss_device *dssdev);
|
||
|
@@ -585,9 +587,6 @@ struct omap_dss_driver {
|
||
|
|
||
|
bool omapdss_is_initialized(void);
|
||
|
|
||
|
-int omap_dss_register_driver(struct omap_dss_driver *);
|
||
|
-void omap_dss_unregister_driver(struct omap_dss_driver *);
|
||
|
-
|
||
|
int omapdss_register_display(struct omap_dss_device *dssdev);
|
||
|
void omapdss_unregister_display(struct omap_dss_device *dssdev);
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
|
||
|
index aa5ba9ae2191..8874f55259ab 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
|
||
|
@@ -59,6 +59,14 @@ bool omap_connector_get_hdmi_mode(struct drm_connector *connector)
|
||
|
return omap_connector->hdmi_mode;
|
||
|
}
|
||
|
|
||
|
+bool omap_connector_get_manually_updated(struct drm_connector *connector)
|
||
|
+{
|
||
|
+ struct omap_connector *omap_connector = to_omap_connector(connector);
|
||
|
+
|
||
|
+ return !!(omap_connector->dssdev->caps &
|
||
|
+ OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE);
|
||
|
+}
|
||
|
+
|
||
|
static enum drm_connector_status omap_connector_detect(
|
||
|
struct drm_connector *connector, bool force)
|
||
|
{
|
||
|
@@ -149,6 +157,12 @@ static int omap_connector_get_modes(struct drm_connector *connector)
|
||
|
drm_mode_set_name(mode);
|
||
|
drm_mode_probed_add(connector, mode);
|
||
|
|
||
|
+ if (dssdrv->get_size) {
|
||
|
+ dssdrv->get_size(dssdev,
|
||
|
+ &connector->display_info.width_mm,
|
||
|
+ &connector->display_info.height_mm);
|
||
|
+ }
|
||
|
+
|
||
|
n = 1;
|
||
|
}
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
|
||
|
index cc85c16cbc2a..8255241d71fc 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
|
||
|
@@ -52,6 +52,10 @@ struct omap_crtc {
|
||
|
bool pending;
|
||
|
wait_queue_head_t pending_wait;
|
||
|
struct drm_pending_vblank_event *event;
|
||
|
+ struct delayed_work update_work;
|
||
|
+
|
||
|
+ void (*framedone_handler)(void *);
|
||
|
+ void *framedone_handler_data;
|
||
|
};
|
||
|
|
||
|
/* -----------------------------------------------------------------------------
|
||
|
@@ -140,6 +144,28 @@ static void omap_crtc_dss_disconnect(enum omap_channel channel,
|
||
|
|
||
|
static void omap_crtc_dss_start_update(enum omap_channel channel)
|
||
|
{
|
||
|
+ struct omap_crtc *omap_crtc = omap_crtcs[channel];
|
||
|
+ struct omap_drm_private *priv = omap_crtc->base.dev->dev_private;
|
||
|
+
|
||
|
+ priv->dispc_ops->mgr_enable(channel, true);
|
||
|
+}
|
||
|
+
|
||
|
+static bool omap_crtc_is_manually_updated(struct drm_crtc *crtc)
|
||
|
+{
|
||
|
+ struct drm_connector *connector;
|
||
|
+ struct drm_connector_list_iter conn_iter;
|
||
|
+ bool result = false;
|
||
|
+
|
||
|
+ drm_connector_list_iter_begin(crtc->dev, &conn_iter);
|
||
|
+ drm_for_each_connector_iter(connector, &conn_iter) {
|
||
|
+ if (connector->state->crtc != crtc)
|
||
|
+ continue;
|
||
|
+ result = omap_connector_get_manually_updated(connector);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ drm_connector_list_iter_end(&conn_iter);
|
||
|
+
|
||
|
+ return result;
|
||
|
}
|
||
|
|
||
|
/* Called only from the encoder enable/disable and suspend/resume handlers. */
|
||
|
@@ -151,12 +177,17 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
|
||
|
enum omap_channel channel = omap_crtc->channel;
|
||
|
struct omap_irq_wait *wait;
|
||
|
u32 framedone_irq, vsync_irq;
|
||
|
+ bool is_manual = omap_crtc_is_manually_updated(crtc);
|
||
|
+ enum omap_display_type type = omap_crtc_output[channel]->output_type;
|
||
|
int ret;
|
||
|
|
||
|
if (WARN_ON(omap_crtc->enabled == enable))
|
||
|
return;
|
||
|
|
||
|
- if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) {
|
||
|
+ if (is_manual)
|
||
|
+ omap_irq_enable_framedone(crtc, enable);
|
||
|
+
|
||
|
+ if (is_manual || type == OMAP_DISPLAY_TYPE_HDMI) {
|
||
|
priv->dispc_ops->mgr_enable(channel, enable);
|
||
|
omap_crtc->enabled = enable;
|
||
|
return;
|
||
|
@@ -207,7 +238,6 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-
|
||
|
static int omap_crtc_dss_enable(enum omap_channel channel)
|
||
|
{
|
||
|
struct omap_crtc *omap_crtc = omap_crtcs[channel];
|
||
|
@@ -248,6 +278,17 @@ static int omap_crtc_dss_register_framedone(
|
||
|
enum omap_channel channel,
|
||
|
void (*handler)(void *), void *data)
|
||
|
{
|
||
|
+ struct omap_crtc *omap_crtc = omap_crtcs[channel];
|
||
|
+ struct drm_device *dev = omap_crtc->base.dev;
|
||
|
+
|
||
|
+ if (omap_crtc->framedone_handler)
|
||
|
+ return -EBUSY;
|
||
|
+
|
||
|
+ dev_dbg(dev->dev, "register framedone %s", omap_crtc->name);
|
||
|
+
|
||
|
+ omap_crtc->framedone_handler = handler;
|
||
|
+ omap_crtc->framedone_handler_data = data;
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -255,6 +296,16 @@ static void omap_crtc_dss_unregister_framedone(
|
||
|
enum omap_channel channel,
|
||
|
void (*handler)(void *), void *data)
|
||
|
{
|
||
|
+ struct omap_crtc *omap_crtc = omap_crtcs[channel];
|
||
|
+ struct drm_device *dev = omap_crtc->base.dev;
|
||
|
+
|
||
|
+ dev_dbg(dev->dev, "unregister framedone %s", omap_crtc->name);
|
||
|
+
|
||
|
+ WARN_ON(omap_crtc->framedone_handler != handler);
|
||
|
+ WARN_ON(omap_crtc->framedone_handler_data != data);
|
||
|
+
|
||
|
+ omap_crtc->framedone_handler = NULL;
|
||
|
+ omap_crtc->framedone_handler_data = NULL;
|
||
|
}
|
||
|
|
||
|
static const struct dss_mgr_ops mgr_ops = {
|
||
|
@@ -322,6 +373,77 @@ void omap_crtc_vblank_irq(struct drm_crtc *crtc)
|
||
|
DBG("%s: apply done", omap_crtc->name);
|
||
|
}
|
||
|
|
||
|
+void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus)
|
||
|
+{
|
||
|
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||
|
+
|
||
|
+ if (!omap_crtc->framedone_handler) {
|
||
|
+ dev_warn(omap_crtc->base.dev->dev, "no framedone handler?");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ omap_crtc->framedone_handler(omap_crtc->framedone_handler_data);
|
||
|
+
|
||
|
+ spin_lock(&crtc->dev->event_lock);
|
||
|
+ /* Send the vblank event if one has been requested. */
|
||
|
+ if (omap_crtc->event) {
|
||
|
+ drm_crtc_send_vblank_event(crtc, omap_crtc->event);
|
||
|
+ omap_crtc->event = NULL;
|
||
|
+ }
|
||
|
+ omap_crtc->pending = false;
|
||
|
+ spin_unlock(&crtc->dev->event_lock);
|
||
|
+
|
||
|
+ /* Wake up omap_atomic_complete. */
|
||
|
+ wake_up(&omap_crtc->pending_wait);
|
||
|
+}
|
||
|
+
|
||
|
+void omap_crtc_flush(struct drm_crtc *crtc)
|
||
|
+{
|
||
|
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||
|
+
|
||
|
+ if (!omap_crtc_is_manually_updated(crtc))
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (!delayed_work_pending(&omap_crtc->update_work))
|
||
|
+ schedule_delayed_work(&omap_crtc->update_work, 0);
|
||
|
+}
|
||
|
+
|
||
|
+static void omap_crtc_manual_display_update(struct work_struct *data)
|
||
|
+{
|
||
|
+ struct omap_crtc *omap_crtc =
|
||
|
+ container_of(data, struct omap_crtc, update_work.work);
|
||
|
+ struct omap_dss_device *dssdev = omap_crtc_output[omap_crtc->channel];
|
||
|
+ struct drm_device *dev = omap_crtc->base.dev;
|
||
|
+ struct omap_dss_driver *dssdrv;
|
||
|
+ int ret, width, height;
|
||
|
+
|
||
|
+ if (!dssdev || !dssdev->dst) {
|
||
|
+ dev_err_once(dev->dev, "missing dssdev!");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ dssdev = dssdev->dst;
|
||
|
+ dssdrv = dssdev->driver;
|
||
|
+
|
||
|
+ if (!dssdrv || !dssdrv->update) {
|
||
|
+ dev_err_once(dev->dev, "incorrect dssdrv!");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (dssdrv->sync)
|
||
|
+ dssdrv->sync(dssdev);
|
||
|
+
|
||
|
+ width = dssdev->panel.vm.hactive;
|
||
|
+ height = dssdev->panel.vm.vactive;
|
||
|
+ ret = dssdrv->update(dssdev, 0, 0, width, height);
|
||
|
+ if (ret < 0) {
|
||
|
+ spin_lock_irq(&dev->event_lock);
|
||
|
+ omap_crtc->pending = false;
|
||
|
+ spin_unlock_irq(&dev->event_lock);
|
||
|
+ wake_up(&omap_crtc->pending_wait);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc)
|
||
|
{
|
||
|
struct omap_drm_private *priv = crtc->dev->dev_private;
|
||
|
@@ -374,6 +496,10 @@ static void omap_crtc_atomic_enable(struct drm_crtc *crtc,
|
||
|
|
||
|
DBG("%s", omap_crtc->name);
|
||
|
|
||
|
+ /* manual updated display will not trigger vsync irq */
|
||
|
+ if (omap_crtc_is_manually_updated(crtc))
|
||
|
+ return;
|
||
|
+
|
||
|
spin_lock_irq(&crtc->dev->event_lock);
|
||
|
drm_crtc_vblank_on(crtc);
|
||
|
ret = drm_crtc_vblank_get(crtc);
|
||
|
@@ -387,6 +513,7 @@ static void omap_crtc_atomic_disable(struct drm_crtc *crtc,
|
||
|
struct drm_crtc_state *old_state)
|
||
|
{
|
||
|
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
|
||
|
+ struct drm_device *dev = crtc->dev;
|
||
|
|
||
|
DBG("%s", omap_crtc->name);
|
||
|
|
||
|
@@ -397,6 +524,11 @@ static void omap_crtc_atomic_disable(struct drm_crtc *crtc,
|
||
|
}
|
||
|
spin_unlock_irq(&crtc->dev->event_lock);
|
||
|
|
||
|
+ cancel_delayed_work(&omap_crtc->update_work);
|
||
|
+
|
||
|
+ if (!omap_crtc_wait_pending(crtc))
|
||
|
+ dev_warn(dev->dev, "manual display update did not finish!");
|
||
|
+
|
||
|
drm_crtc_vblank_off(crtc);
|
||
|
}
|
||
|
|
||
|
@@ -511,13 +643,20 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
|
||
|
|
||
|
DBG("%s: GO", omap_crtc->name);
|
||
|
|
||
|
- ret = drm_crtc_vblank_get(crtc);
|
||
|
- WARN_ON(ret != 0);
|
||
|
+ if (!omap_crtc_is_manually_updated(crtc)) {
|
||
|
+ ret = drm_crtc_vblank_get(crtc);
|
||
|
+ WARN_ON(ret != 0);
|
||
|
|
||
|
- spin_lock_irq(&crtc->dev->event_lock);
|
||
|
- priv->dispc_ops->mgr_go(omap_crtc->channel);
|
||
|
- omap_crtc_arm_event(crtc);
|
||
|
- spin_unlock_irq(&crtc->dev->event_lock);
|
||
|
+ spin_lock_irq(&crtc->dev->event_lock);
|
||
|
+ priv->dispc_ops->mgr_go(omap_crtc->channel);
|
||
|
+ omap_crtc_arm_event(crtc);
|
||
|
+ spin_unlock_irq(&crtc->dev->event_lock);
|
||
|
+ } else {
|
||
|
+ spin_lock_irq(&crtc->dev->event_lock);
|
||
|
+ omap_crtc_flush(crtc);
|
||
|
+ omap_crtc_arm_event(crtc);
|
||
|
+ spin_unlock_irq(&crtc->dev->event_lock);
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
|
||
|
@@ -678,6 +817,9 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
||
|
omap_crtc->channel = channel;
|
||
|
omap_crtc->name = channel_names[channel];
|
||
|
|
||
|
+ INIT_DELAYED_WORK(&omap_crtc->update_work,
|
||
|
+ omap_crtc_manual_display_update);
|
||
|
+
|
||
|
ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
|
||
|
&omap_crtc_funcs, NULL);
|
||
|
if (ret < 0) {
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h
|
||
|
index 4bd1e9070b31..3cb9f9afba16 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/omap_drv.h
|
||
|
+++ b/drivers/gpu/drm/omapdrm/omap_drv.h
|
||
|
@@ -97,6 +97,7 @@ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
|
||
|
int omap_gem_resume(struct device *dev);
|
||
|
#endif
|
||
|
|
||
|
+int omap_irq_enable_framedone(struct drm_crtc *crtc, bool enable);
|
||
|
int omap_irq_enable_vblank(struct drm_crtc *crtc);
|
||
|
void omap_irq_disable_vblank(struct drm_crtc *crtc);
|
||
|
void omap_drm_irq_uninstall(struct drm_device *dev);
|
||
|
@@ -124,6 +125,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
|
||
|
int omap_crtc_wait_pending(struct drm_crtc *crtc);
|
||
|
void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus);
|
||
|
void omap_crtc_vblank_irq(struct drm_crtc *crtc);
|
||
|
+void omap_crtc_framedone_irq(struct drm_crtc *crtc, uint32_t irqstatus);
|
||
|
+void omap_crtc_flush(struct drm_crtc *crtc);
|
||
|
|
||
|
struct drm_plane *omap_plane_init(struct drm_device *dev,
|
||
|
int idx, enum drm_plane_type type,
|
||
|
@@ -140,6 +143,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||
|
struct drm_encoder *omap_connector_attached_encoder(
|
||
|
struct drm_connector *connector);
|
||
|
bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
|
||
|
+bool omap_connector_get_manually_updated(struct drm_connector *connector);
|
||
|
|
||
|
struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
|
||
|
struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd);
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
|
||
|
index b1a762b70cbf..9492be69d59b 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
|
||
|
@@ -97,8 +97,28 @@ static void omap_framebuffer_destroy(struct drm_framebuffer *fb)
|
||
|
kfree(omap_fb);
|
||
|
}
|
||
|
|
||
|
+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)
|
||
|
+{
|
||
|
+ struct drm_connector *connector = NULL;
|
||
|
+
|
||
|
+ drm_modeset_lock_all(fb->dev);
|
||
|
+
|
||
|
+ while ((connector = omap_framebuffer_get_next_connector(fb, connector)))
|
||
|
+ if (connector->encoder && connector->encoder->crtc)
|
||
|
+ omap_crtc_flush(connector->encoder->crtc);
|
||
|
+
|
||
|
+ drm_modeset_unlock_all(fb->dev);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
|
||
|
.create_handle = omap_framebuffer_create_handle,
|
||
|
+ .dirty = omap_framebuffer_dirty,
|
||
|
.destroy = omap_framebuffer_destroy,
|
||
|
};
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
|
||
|
index 9273118040b7..e0378182bd7c 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
|
||
|
@@ -84,9 +84,6 @@ static struct fb_ops omap_fb_ops = {
|
||
|
.owner = THIS_MODULE,
|
||
|
DRM_FB_HELPER_DEFAULT_OPS,
|
||
|
|
||
|
- /* Note: to properly handle manual update displays, we wrap the
|
||
|
- * basic fbdev ops which write to the framebuffer
|
||
|
- */
|
||
|
.fb_read = drm_fb_helper_sys_read,
|
||
|
.fb_write = drm_fb_helper_sys_write,
|
||
|
.fb_fillrect = drm_fb_helper_sys_fillrect,
|
||
|
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
|
||
|
index 013b0bba712f..301c0e7a70b7 100644
|
||
|
--- a/drivers/gpu/drm/omapdrm/omap_irq.c
|
||
|
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
|
||
|
@@ -87,6 +87,27 @@ int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
|
||
|
return ret == 0 ? -1 : 0;
|
||
|
}
|
||
|
|
||
|
+int omap_irq_enable_framedone(struct drm_crtc *crtc, bool enable)
|
||
|
+{
|
||
|
+ struct drm_device *dev = crtc->dev;
|
||
|
+ struct omap_drm_private *priv = dev->dev_private;
|
||
|
+ unsigned long flags;
|
||
|
+ enum omap_channel channel = omap_crtc_channel(crtc);
|
||
|
+ int framedone_irq = priv->dispc_ops->mgr_get_framedone_irq(channel);
|
||
|
+
|
||
|
+ DBG("dev=%p, crtc=%u, enable=%d", dev, channel, enable);
|
||
|
+
|
||
|
+ spin_lock_irqsave(&priv->wait_lock, flags);
|
||
|
+ if (enable)
|
||
|
+ priv->irq_mask |= framedone_irq;
|
||
|
+ else
|
||
|
+ priv->irq_mask &= ~framedone_irq;
|
||
|
+ omap_irq_update(dev);
|
||
|
+ spin_unlock_irqrestore(&priv->wait_lock, flags);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
/**
|
||
|
* enable_vblank - enable vblank interrupt events
|
||
|
* @dev: DRM device
|
||
|
@@ -217,6 +238,9 @@ static irqreturn_t omap_irq_handler(int irq, void *arg)
|
||
|
|
||
|
if (irqstatus & priv->dispc_ops->mgr_get_sync_lost_irq(channel))
|
||
|
omap_crtc_error_irq(crtc, irqstatus);
|
||
|
+
|
||
|
+ if (irqstatus & priv->dispc_ops->mgr_get_framedone_irq(channel))
|
||
|
+ omap_crtc_framedone_irq(crtc, irqstatus);
|
||
|
}
|
||
|
|
||
|
omap_irq_ocp_error_handler(dev, irqstatus);
|
||
|
--
|
||
|
2.14.1
|