From 4a27d62243942c4be181c7129fb532a709a15a8e Mon Sep 17 00:00:00 2001 From: aszlig Date: Thu, 15 Dec 2016 23:18:58 +0100 Subject: hardware/t100ha: Add Linux 4.9 fixes for intel DRM This is essentially a cherry-pick of the three commits mentioned in: https://bugs.freedesktop.org/show_bug.cgi?id=97529#c33 Plus a backport against Linux 4.9 of the following patch: https://bugs.freedesktop.org/attachment.cgi?id=128410 (essentially https://patchwork.freedesktop.org/series/16242/) And also the backlight.patch (a PMIC hack) we had before all combined into one patch, which now should fix the following bug: https://bugs.freedesktop.org/show_bug.cgi?id=97529 Signed-off-by: aszlig --- modules/hardware/t100ha/backlight.patch | 224 ---------- modules/hardware/t100ha/default.nix | 4 +- modules/hardware/t100ha/drm.patch | 767 ++++++++++++++++++++++++++++++++ 3 files changed, 769 insertions(+), 226 deletions(-) delete mode 100644 modules/hardware/t100ha/backlight.patch create mode 100644 modules/hardware/t100ha/drm.patch (limited to 'modules/hardware') diff --git a/modules/hardware/t100ha/backlight.patch b/modules/hardware/t100ha/backlight.patch deleted file mode 100644 index 41d7b019..00000000 --- a/modules/hardware/t100ha/backlight.patch +++ /dev/null @@ -1,224 +0,0 @@ -diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c -index 3562bf3..4ae5430 100644 ---- a/drivers/gpu/drm/i915/intel_dsi.c -+++ b/drivers/gpu/drm/i915/intel_dsi.c -@@ -551,6 +551,8 @@ static void intel_dsi_disable(struct intel_encoder *encoder) - struct drm_device *dev = encoder->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); -+ struct intel_connector *intel_connector = -+ intel_dsi->attached_connector; - enum port port; - u32 temp; - -@@ -564,6 +566,8 @@ static void intel_dsi_disable(struct intel_encoder *encoder) - msleep(2); - } - -+ intel_panel_disable_backlight(intel_connector); -+ - for_each_dsi_port(port, intel_dsi->ports) { - /* Panel commands can be sent when clock is in LP11 */ - I915_WRITE(MIPI_DEVICE_READY(port), 0x0); -@@ -1093,6 +1097,7 @@ static void intel_dsi_connector_destroy(struct drm_connector *connector) - - DRM_DEBUG_KMS("\n"); - intel_panel_fini(&intel_connector->panel); -+ intel_panel_destroy_backlight(connector); - drm_connector_cleanup(connector); - kfree(connector); - } -diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c -index 8c8996f..4c8678a 100644 ---- a/drivers/gpu/drm/i915/intel_panel.c -+++ b/drivers/gpu/drm/i915/intel_panel.c -@@ -32,6 +32,7 @@ - - #include - #include -+#include - #include - #include "intel_drv.h" - -@@ -544,6 +545,11 @@ static u32 pwm_get_backlight(struct intel_connector *connector) - return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); - } - -+static u32 vlv_pmic_get_backlight(struct intel_connector *connector) -+{ -+ return intel_soc_pmic_readb(0x4E); -+} -+ - static u32 intel_panel_get_backlight(struct intel_connector *connector) - { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); -@@ -635,6 +641,11 @@ static void pwm_set_backlight(struct intel_connector *connector, u32 level) - pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS); - } - -+static void vlv_pmic_set_backlight(struct intel_connector *connector, u32 level) -+{ -+ intel_soc_pmic_writeb(0x4E, level); -+} -+ - static void - intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level) - { -@@ -808,6 +819,14 @@ static void pwm_disable_backlight(struct intel_connector *connector) - pwm_disable(panel->backlight.pwm); - } - -+static void vlv_pmic_disable_backlight(struct intel_connector *connector) -+{ -+ intel_panel_actually_set_backlight(connector, 0); -+ -+ intel_soc_pmic_writeb(0x51, 0x00); -+ intel_soc_pmic_writeb(0x4B, 0x7F); -+} -+ - void intel_panel_disable_backlight(struct intel_connector *connector) - { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); -@@ -1073,6 +1092,17 @@ static void pwm_enable_backlight(struct intel_connector *connector) - intel_panel_actually_set_backlight(connector, panel->backlight.level); - } - -+static void vlv_pmic_enable_backlight(struct intel_connector *connector) -+{ -+ struct intel_panel *panel = &connector->panel; -+ -+ intel_soc_pmic_writeb(0x4B, 0xFF); -+ intel_soc_pmic_writeb(0x4E, 0xFF); -+ intel_soc_pmic_writeb(0x51, 0x01); -+ -+ intel_panel_actually_set_backlight(connector, panel->backlight.level); -+} -+ - void intel_panel_enable_backlight(struct intel_connector *connector) - { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); -@@ -1657,6 +1687,20 @@ static int pwm_setup_backlight(struct intel_connector *connector, - return 0; - } - -+static int vlv_pmic_setup_backlight(struct intel_connector *connector, -+ enum pipe unused) -+{ -+ struct intel_panel *panel = &connector->panel; -+ -+ panel->backlight.present = 1; -+ panel->backlight.min = 0x00; -+ panel->backlight.max = 0xFF; -+ panel->backlight.level = 0x5A; -+ panel->backlight.enabled = 1; -+ -+ return 0; -+} -+ - int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe) - { - struct drm_i915_private *dev_priv = to_i915(connector->dev); -@@ -1664,6 +1708,8 @@ int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe) - struct intel_panel *panel = &intel_connector->panel; - int ret; - -+ intel_backlight_device_register(intel_connector); -+ - if (!dev_priv->vbt.backlight.present) { - if (dev_priv->quirks & QUIRK_BACKLIGHT_PRESENT) { - DRM_DEBUG_KMS("no backlight present per VBT, but present per quirk\n"); -@@ -1744,18 +1790,17 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel) - panel->backlight.hz_to_pwm = pch_hz_to_pwm; - } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { - if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) { -- panel->backlight.setup = pwm_setup_backlight; -- panel->backlight.enable = pwm_enable_backlight; -- panel->backlight.disable = pwm_disable_backlight; -- panel->backlight.set = pwm_set_backlight; -- panel->backlight.get = pwm_get_backlight; -+ panel->backlight.setup = vlv_pmic_setup_backlight; -+ panel->backlight.enable = vlv_pmic_enable_backlight; -+ panel->backlight.disable = vlv_pmic_disable_backlight; -+ panel->backlight.set = vlv_pmic_set_backlight; -+ panel->backlight.get = vlv_pmic_get_backlight; - } else { - panel->backlight.setup = vlv_setup_backlight; - panel->backlight.enable = vlv_enable_backlight; - panel->backlight.disable = vlv_disable_backlight; - panel->backlight.set = vlv_set_backlight; - panel->backlight.get = vlv_get_backlight; -- panel->backlight.hz_to_pwm = vlv_hz_to_pwm; - } - } else if (IS_GEN4(dev_priv)) { - panel->backlight.setup = i965_setup_backlight; -diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c -index d9e15cf..8af5afd 100644 ---- a/drivers/mfd/intel_soc_pmic_core.c -+++ b/drivers/mfd/intel_soc_pmic_core.c -@@ -43,6 +43,8 @@ static struct pwm_lookup crc_pwm_lookup[] = { - PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_backlight", 0, PWM_POLARITY_NORMAL), - }; - -+static struct intel_soc_pmic *pmic_hack = NULL; -+ - static int intel_soc_pmic_find_gpio_irq(struct device *dev) - { - struct gpio_desc *desc; -@@ -76,6 +78,7 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, - config = (struct intel_soc_pmic_config *)id->driver_data; - - pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); -+ pmic_hack = pmic; - if (!pmic) - return -ENOMEM; - -@@ -167,6 +170,37 @@ static int intel_soc_pmic_resume(struct device *dev) - } - #endif - -+int intel_soc_pmic_readb(int reg) -+{ -+ int ret; -+ unsigned int val; -+ -+ if (!pmic_hack) { -+ ret = -EIO; -+ } else { -+ ret = regmap_read(pmic_hack->regmap, reg, &val); -+ if (!ret) { -+ ret = val; -+ } -+ } -+ -+ return ret; -+} -+EXPORT_SYMBOL(intel_soc_pmic_readb); -+ -+int intel_soc_pmic_writeb(int reg, u8 val) -+{ -+ int ret; -+ -+ if (!pmic_hack) { -+ ret = -EIO; -+ } else { -+ ret = regmap_write(pmic_hack->regmap, reg, val); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(intel_soc_pmic_writeb); -+ - static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend, - intel_soc_pmic_resume); - -diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h -index cf619db..52ad034 100644 ---- a/include/linux/mfd/intel_soc_pmic.h -+++ b/include/linux/mfd/intel_soc_pmic.h -@@ -29,4 +29,7 @@ struct intel_soc_pmic { - struct device *dev; - }; - -+int intel_soc_pmic_readb(int reg); -+int intel_soc_pmic_writeb(int reg, u8 val); -+ - #endif /* __INTEL_SOC_PMIC_H__ */ diff --git a/modules/hardware/t100ha/default.nix b/modules/hardware/t100ha/default.nix index ac0a0be4..00a5855d 100644 --- a/modules/hardware/t100ha/default.nix +++ b/modules/hardware/t100ha/default.nix @@ -18,8 +18,8 @@ in { ''); boot.kernelPatches = [ - { name = "backlight"; - patch = ./backlight.patch; + { name = "drm"; + patch = ./drm.patch; } { name = "meta-keys"; patch = ./meta-keys.patch; diff --git a/modules/hardware/t100ha/drm.patch b/modules/hardware/t100ha/drm.patch new file mode 100644 index 00000000..b33d4b03 --- /dev/null +++ b/modules/hardware/t100ha/drm.patch @@ -0,0 +1,767 @@ +diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h +index 8405b5a367d7..7e3545f65257 100644 +--- a/drivers/gpu/drm/i915/intel_bios.h ++++ b/drivers/gpu/drm/i915/intel_bios.h +@@ -46,14 +46,20 @@ struct edp_power_seq { + u16 t11_t12; + } __packed; + +-/* MIPI Sequence Block definitions */ ++/* ++ * MIPI Sequence Block definitions ++ * ++ * Note the VBT spec has AssertReset / DeassertReset swapped from their ++ * usual naming, we use the proper names here to avoid confusion when ++ * reading the code. ++ */ + enum mipi_seq { + MIPI_SEQ_END = 0, +- MIPI_SEQ_ASSERT_RESET, ++ MIPI_SEQ_DEASSERT_RESET, /* Spec says MipiAssertResetPin */ + MIPI_SEQ_INIT_OTP, + MIPI_SEQ_DISPLAY_ON, + MIPI_SEQ_DISPLAY_OFF, +- MIPI_SEQ_DEASSERT_RESET, ++ MIPI_SEQ_ASSERT_RESET, /* Spec says MipiDeassertResetPin */ + MIPI_SEQ_BACKLIGHT_ON, /* sequence block v2+ */ + MIPI_SEQ_BACKLIGHT_OFF, /* sequence block v2+ */ + MIPI_SEQ_TEAR_ON, /* sequence block v2+ */ +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index 3cb70d73239b..0ca7a684267c 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -13775,6 +13775,13 @@ static void update_scanline_offset(struct intel_crtc *crtc) + * type. For DP ports it behaves like most other platforms, but on HDMI + * there's an extra 1 line difference. So we need to add two instead of + * one to the value. ++ * ++ * On VLV/CHV DSI the scanline counter would appear to increment ++ * approx. 1/3 of a scanline before start of vblank. Unfortunately ++ * that we can't tell whether we're in vblank or not while we're ++ * on that particular line. We must set scanline_offset to 1 so ++ * that the vblank timestamps come out correct when we query ++ * the scanline counter from the vblank interrupt handler. + */ + if (IS_GEN2(dev)) { + const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; +diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c +index b2e3d3a334f7..c07ef5e9de95 100644 +--- a/drivers/gpu/drm/i915/intel_dsi.c ++++ b/drivers/gpu/drm/i915/intel_dsi.c +@@ -80,7 +80,7 @@ enum mipi_dsi_pixel_format pixel_format_from_register_bits(u32 fmt) + } + } + +-static void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port) ++void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port) + { + struct drm_encoder *encoder = &intel_dsi->base.base; + struct drm_device *dev = encoder->dev; +@@ -505,37 +505,83 @@ static void intel_dsi_port_disable(struct intel_encoder *encoder) + } + } + +-static void intel_dsi_enable(struct intel_encoder *encoder) +-{ +- struct drm_device *dev = encoder->base.dev; +- struct drm_i915_private *dev_priv = to_i915(dev); +- struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); +- enum port port; +- +- DRM_DEBUG_KMS("\n"); +- +- if (is_cmd_mode(intel_dsi)) { +- for_each_dsi_port(port, intel_dsi->ports) +- I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(port), 8 * 4); +- } else { +- msleep(20); /* XXX */ +- for_each_dsi_port(port, intel_dsi->ports) +- dpi_send_cmd(intel_dsi, TURN_ON, false, port); +- msleep(100); +- +- drm_panel_enable(intel_dsi->panel); ++static void intel_dsi_prepare(struct intel_encoder *intel_encoder, ++ struct intel_crtc_state *pipe_config); + +- for_each_dsi_port(port, intel_dsi->ports) +- wait_for_dsi_fifo_empty(intel_dsi, port); ++static void intel_dsi_msleep(struct intel_dsi *intel_dsi, int msec) ++{ ++ struct drm_i915_private *dev_priv = to_i915(intel_dsi->base.base.dev); + +- intel_dsi_port_enable(encoder); +- } ++ /* For v3 VBTs in vid-mode the delays are part of the VBT sequences */ ++ if (is_vid_mode(intel_dsi) && dev_priv->vbt.dsi.seq_version >= 3) ++ return; + +- intel_panel_enable_backlight(intel_dsi->attached_connector); ++ msleep(msec); + } + +-static void intel_dsi_prepare(struct intel_encoder *intel_encoder, +- struct intel_crtc_state *pipe_config); ++/* ++ * Panel enable/disable sequences from the spec: ++ * ++ * v2 sequence for video mode: ++ * - power on ++ * - wait t1+t2 ++ * - MIPIDeassertResetPin ++ * - clk/data lines to lp-11 ++ * - MIPISendInitialDcsCmds ++ * - turn on DPI ++ * - MIPIDisplayOn ++ * - wait t5 ++ * - backlight on ++ * ... ++ * - backlight off ++ * - wait t6 ++ * - MIPIDisplayOff ++ * - turn off DPI ++ * - clk/data lines to lp-00 ++ * - MIPIAssertResetPin ++ * - wait t3 ++ * - power off ++ * - wait t4 ++ * ++ * v3 sequence for video mode: ++ * - MIPIPanelPowerOn ++ * - MIPIDeassertResetPin ++ * - set clk/data lines to lp-11 ++ * - MIPISendInitialDcsCmds (LP) ++ * - turn on DPI ++ * - MIPITearOn (command mode only) + MIPIDisplayOn (LP and HS) ++ * - MIPIBacklightOn ++ * ... ++ * - MIPIBacklightOff ++ * - turn off DPI ++ * - MIPITearOff + MIPIDisplayOff (LP) ++ * - clk/data lines to lp-00 ++ * - MIPIAssertResetPin ++ * - MIPIPanelPowerOff ++ * ++ * sequence for command mode: ++ * - power on ++ * - wait t1+t2 ++ * - MIPIDeassertResetPin ++ * - clk/data lines to lp-11 ++ * - MIPISendInitialDcsCmds ++ * - MIPITearOn ++ * - MIPIDisplayOn ++ * - set pipe to dsr mode ++ * - wait t5 ++ * - backlight on ++ * ... issue write_mem_start/write_mem_continue commands ... ++ * - backlight off ++ * - wait t6 ++ * - disable pipe dsr mode ++ * - MIPITearOff ++ * - MIPIDisplayOff ++ * - clk/data lines to lp-00 ++ * - MIPIAssertResetPin ++ * - wait t3 ++ * - power off ++ * - wait t4 ++ */ + + static void intel_dsi_pre_enable(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config, +@@ -554,14 +600,6 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder, + intel_disable_dsi_pll(encoder); + intel_enable_dsi_pll(encoder, pipe_config); + +- intel_dsi_prepare(encoder, pipe_config); +- +- /* Panel Enable over CRC PMIC */ +- if (intel_dsi->gpio_panel) +- gpiod_set_value_cansleep(intel_dsi->gpio_panel, 1); +- +- msleep(intel_dsi->panel_on_delay); +- + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + u32 val; + +@@ -571,17 +609,44 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder, + I915_WRITE(DSPCLK_GATE_D, val); + } + +- /* put device in ready state */ +- intel_dsi_device_ready(encoder); ++ intel_dsi_prepare(encoder, pipe_config); ++ ++ /* Power on, try both CRC pmic gpio and VBT */ ++ if (intel_dsi->gpio_panel) ++ gpiod_set_value_cansleep(intel_dsi->gpio_panel, 1); ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_POWER_ON); ++ intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay); + +- drm_panel_prepare(intel_dsi->panel); ++ /* Deassert reset */ ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET); + +- for_each_dsi_port(port, intel_dsi->ports) +- wait_for_dsi_fifo_empty(intel_dsi, port); ++ /* Put device in ready state (LP-11) */ ++ intel_dsi_device_ready(encoder); ++ ++ /* Send initialization commands in LP mode */ ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_INIT_OTP); + + /* Enable port in pre-enable phase itself because as per hw team + * recommendation, port should be enabled befor plane & pipe */ +- intel_dsi_enable(encoder); ++ if (is_cmd_mode(intel_dsi)) { ++ for_each_dsi_port(port, intel_dsi->ports) ++ I915_WRITE(MIPI_MAX_RETURN_PKT_SIZE(port), 8 * 4); ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_TEAR_ON); ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON); ++ } else { ++ msleep(20); /* XXX */ ++ for_each_dsi_port(port, intel_dsi->ports) ++ dpi_send_cmd(intel_dsi, TURN_ON, false, port); ++ intel_dsi_msleep(intel_dsi, 100); ++ ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DISPLAY_ON); ++ ++ intel_dsi_port_enable(encoder); ++ } ++ ++ /* Enable backlight, both pwm and VBT */ ++ intel_panel_enable_backlight(intel_dsi->attached_connector); ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_ON); + } + + static void intel_dsi_enable_nop(struct intel_encoder *encoder, +@@ -605,8 +670,15 @@ static void intel_dsi_pre_disable(struct intel_encoder *encoder, + + DRM_DEBUG_KMS("\n"); + ++ /* Disable backlight, both VBT and pwm */ ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_BACKLIGHT_OFF); + intel_panel_disable_backlight(intel_dsi->attached_connector); + ++ /* ++ * XXX: According to the spec we should send SHUTDOWN before ++ * MIPI_SEQ_DISPLAY_OFF only for v3+ VBTs, but testing in the field ++ * has shown that we should do this for v2 VBTs too? ++ */ + if (is_vid_mode(intel_dsi)) { + /* Send Shutdown command to the panel in LP mode */ + for_each_dsi_port(port, intel_dsi->ports) +@@ -615,45 +687,6 @@ static void intel_dsi_pre_disable(struct intel_encoder *encoder, + } + } + +-static void intel_dsi_disable(struct intel_encoder *encoder) +-{ +- struct drm_device *dev = encoder->base.dev; +- struct drm_i915_private *dev_priv = to_i915(dev); +- struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); +- enum port port; +- u32 temp; +- +- DRM_DEBUG_KMS("\n"); +- +- if (is_vid_mode(intel_dsi)) { +- for_each_dsi_port(port, intel_dsi->ports) +- wait_for_dsi_fifo_empty(intel_dsi, port); +- +- intel_dsi_port_disable(encoder); +- msleep(2); +- } +- +- for_each_dsi_port(port, intel_dsi->ports) { +- /* Panel commands can be sent when clock is in LP11 */ +- I915_WRITE(MIPI_DEVICE_READY(port), 0x0); +- +- intel_dsi_reset_clocks(encoder, port); +- I915_WRITE(MIPI_EOT_DISABLE(port), CLOCKSTOP); +- +- temp = I915_READ(MIPI_DSI_FUNC_PRG(port)); +- temp &= ~VID_MODE_FORMAT_MASK; +- I915_WRITE(MIPI_DSI_FUNC_PRG(port), temp); +- +- I915_WRITE(MIPI_DEVICE_READY(port), 0x1); +- } +- /* if disable packets are sent before sending shutdown packet then in +- * some next enable sequence send turn on packet error is observed */ +- drm_panel_disable(intel_dsi->panel); +- +- for_each_dsi_port(port, intel_dsi->ports) +- wait_for_dsi_fifo_empty(intel_dsi, port); +-} +- + static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) + { + struct drm_device *dev = encoder->base.dev; +@@ -696,8 +729,6 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder) + I915_WRITE(MIPI_DEVICE_READY(port), 0x00); + usleep_range(2000, 2500); + } +- +- intel_disable_dsi_pll(encoder); + } + + static void intel_dsi_post_disable(struct intel_encoder *encoder, +@@ -706,13 +737,50 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder, + { + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); ++ struct intel_connector *intel_connector = intel_dsi->attached_connector; ++ enum port port; ++ u32 temp; + + DRM_DEBUG_KMS("\n"); + +- intel_dsi_disable(encoder); ++ if (is_vid_mode(intel_dsi)) { ++ for_each_dsi_port(port, intel_dsi->ports) ++ wait_for_dsi_fifo_empty(intel_dsi, port); ++ ++ intel_dsi_port_disable(encoder); ++ usleep_range(2000, 5000); ++ } ++ ++ intel_panel_disable_backlight(intel_connector); ++ ++ for_each_dsi_port(port, intel_dsi->ports) { ++ /* Panel commands can be sent when clock is in LP11 */ ++ I915_WRITE(MIPI_DEVICE_READY(port), 0x0); + ++ intel_dsi_reset_clocks(encoder, port); ++ I915_WRITE(MIPI_EOT_DISABLE(port), CLOCKSTOP); ++ ++ temp = I915_READ(MIPI_DSI_FUNC_PRG(port)); ++ temp &= ~VID_MODE_FORMAT_MASK; ++ I915_WRITE(MIPI_DSI_FUNC_PRG(port), temp); ++ ++ I915_WRITE(MIPI_DEVICE_READY(port), 0x1); ++ } ++ ++ /* ++ * if disable packets are sent before sending shutdown packet then in ++ * some next enable sequence send turn on packet error is observed ++ * XXX spec specifies SHUTDOWN before MIPI_SEQ_DISPLAY_OFF for ++ * v3 VBTs, but not for v2 VBTs? ++ */ ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_TEAR_OFF); ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_DISPLAY_OFF); ++ ++ /* Transition to LP-00 */ + intel_dsi_clear_device_ready(encoder); + ++ intel_disable_dsi_pll(encoder); ++ + if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + u32 val; + +@@ -721,11 +789,12 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder, + I915_WRITE(DSPCLK_GATE_D, val); + } + +- drm_panel_unprepare(intel_dsi->panel); +- +- msleep(intel_dsi->panel_off_delay); ++ /* Assert reset */ ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_ASSERT_RESET); + +- /* Panel Disable over CRC PMIC */ ++ /* Power off, try both CRC pmic gpio and VBT */ ++ intel_dsi_msleep(intel_dsi, intel_dsi->panel_off_delay); ++ intel_dsi_exec_vbt_sequence(intel_dsi, MIPI_SEQ_POWER_OFF); + if (intel_dsi->gpio_panel) + gpiod_set_value_cansleep(intel_dsi->gpio_panel, 0); + +@@ -733,7 +802,7 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder, + * FIXME As we do with eDP, just make a note of the time here + * and perform the wait before the next panel power on. + */ +- msleep(intel_dsi->panel_pwr_cycle_delay); ++ intel_dsi_msleep(intel_dsi, intel_dsi->panel_pwr_cycle_delay); + } + + static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, +@@ -1376,6 +1445,7 @@ static void intel_dsi_connector_destroy(struct drm_connector *connector) + + DRM_DEBUG_KMS("\n"); + intel_panel_fini(&intel_connector->panel); ++ intel_panel_destroy_backlight(connector); + drm_connector_cleanup(connector); + kfree(connector); + } +diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h +index 5967ea6d6045..548649158abd 100644 +--- a/drivers/gpu/drm/i915/intel_dsi.h ++++ b/drivers/gpu/drm/i915/intel_dsi.h +@@ -130,6 +130,11 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder) + return container_of(encoder, struct intel_dsi, base.base); + } + ++void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port); ++ ++void intel_dsi_exec_vbt_sequence(struct intel_dsi *intel_dsi, ++ enum mipi_seq seq_id); ++ + bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv); + int intel_compute_dsi_pll(struct intel_encoder *encoder, + struct intel_crtc_state *config); +diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +index cd154ce6b6c1..a4e3c642b39a 100644 +--- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c ++++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +@@ -189,6 +189,8 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi, + break; + } + ++ wait_for_dsi_fifo_empty(intel_dsi, port); ++ + out: + data += len; + +@@ -296,7 +298,8 @@ static void chv_exec_gpio(struct drm_i915_private *dev_priv, + mutex_lock(&dev_priv->sb_lock); + vlv_iosf_sb_write(dev_priv, port, cfg1, 0); + vlv_iosf_sb_write(dev_priv, port, cfg0, +- CHV_GPIO_GPIOCFG_GPO | CHV_GPIO_GPIOTXSTATE(value)); ++ CHV_GPIO_GPIOEN | CHV_GPIO_GPIOCFG_GPO | ++ CHV_GPIO_GPIOTXSTATE(value)); + mutex_unlock(&dev_priv->sb_lock); + } + +@@ -352,11 +355,11 @@ static const fn_mipi_elem_exec exec_elem[] = { + */ + + static const char * const seq_name[] = { +- [MIPI_SEQ_ASSERT_RESET] = "MIPI_SEQ_ASSERT_RESET", ++ [MIPI_SEQ_DEASSERT_RESET] = "MIPI_SEQ_DEASSERT_RESET", + [MIPI_SEQ_INIT_OTP] = "MIPI_SEQ_INIT_OTP", + [MIPI_SEQ_DISPLAY_ON] = "MIPI_SEQ_DISPLAY_ON", + [MIPI_SEQ_DISPLAY_OFF] = "MIPI_SEQ_DISPLAY_OFF", +- [MIPI_SEQ_DEASSERT_RESET] = "MIPI_SEQ_DEASSERT_RESET", ++ [MIPI_SEQ_ASSERT_RESET] = "MIPI_SEQ_ASSERT_RESET", + [MIPI_SEQ_BACKLIGHT_ON] = "MIPI_SEQ_BACKLIGHT_ON", + [MIPI_SEQ_BACKLIGHT_OFF] = "MIPI_SEQ_BACKLIGHT_OFF", + [MIPI_SEQ_TEAR_ON] = "MIPI_SEQ_TEAR_ON", +@@ -373,10 +376,9 @@ static const char *sequence_name(enum mipi_seq seq_id) + return "(unknown)"; + } + +-static void generic_exec_sequence(struct drm_panel *panel, enum mipi_seq seq_id) ++void intel_dsi_exec_vbt_sequence(struct intel_dsi *intel_dsi, ++ enum mipi_seq seq_id) + { +- struct vbt_panel *vbt_panel = to_vbt_panel(panel); +- struct intel_dsi *intel_dsi = vbt_panel->intel_dsi; + struct drm_i915_private *dev_priv = to_i915(intel_dsi->base.base.dev); + const u8 *data; + fn_mipi_elem_exec mipi_elem_exec; +@@ -435,35 +437,6 @@ static void generic_exec_sequence(struct drm_panel *panel, enum mipi_seq seq_id) + } + } + +-static int vbt_panel_prepare(struct drm_panel *panel) +-{ +- generic_exec_sequence(panel, MIPI_SEQ_ASSERT_RESET); +- generic_exec_sequence(panel, MIPI_SEQ_INIT_OTP); +- +- return 0; +-} +- +-static int vbt_panel_unprepare(struct drm_panel *panel) +-{ +- generic_exec_sequence(panel, MIPI_SEQ_DEASSERT_RESET); +- +- return 0; +-} +- +-static int vbt_panel_enable(struct drm_panel *panel) +-{ +- generic_exec_sequence(panel, MIPI_SEQ_DISPLAY_ON); +- +- return 0; +-} +- +-static int vbt_panel_disable(struct drm_panel *panel) +-{ +- generic_exec_sequence(panel, MIPI_SEQ_DISPLAY_OFF); +- +- return 0; +-} +- + static int vbt_panel_get_modes(struct drm_panel *panel) + { + struct vbt_panel *vbt_panel = to_vbt_panel(panel); +@@ -487,10 +460,6 @@ static int vbt_panel_get_modes(struct drm_panel *panel) + } + + static const struct drm_panel_funcs vbt_panel_funcs = { +- .disable = vbt_panel_disable, +- .unprepare = vbt_panel_unprepare, +- .prepare = vbt_panel_prepare, +- .enable = vbt_panel_enable, + .get_modes = vbt_panel_get_modes, + }; + +diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c +index be4b4d546fd9..5aac10c3239f 100644 +--- a/drivers/gpu/drm/i915/intel_panel.c ++++ b/drivers/gpu/drm/i915/intel_panel.c +@@ -32,6 +32,7 @@ + + #include + #include ++#include + #include + #include "intel_drv.h" + +@@ -544,6 +545,11 @@ static u32 pwm_get_backlight(struct intel_connector *connector) + return DIV_ROUND_UP(duty_ns * 100, CRC_PMIC_PWM_PERIOD_NS); + } + ++static u32 vlv_pmic_get_backlight(struct intel_connector *connector) ++{ ++ return intel_soc_pmic_readb(0x4E); ++} ++ + static u32 intel_panel_get_backlight(struct intel_connector *connector) + { + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); +@@ -635,6 +641,11 @@ static void pwm_set_backlight(struct intel_connector *connector, u32 level) + pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS); + } + ++static void vlv_pmic_set_backlight(struct intel_connector *connector, u32 level) ++{ ++ intel_soc_pmic_writeb(0x4E, level); ++} ++ + static void + intel_panel_actually_set_backlight(struct intel_connector *connector, u32 level) + { +@@ -808,6 +819,14 @@ static void pwm_disable_backlight(struct intel_connector *connector) + pwm_disable(panel->backlight.pwm); + } + ++static void vlv_pmic_disable_backlight(struct intel_connector *connector) ++{ ++ intel_panel_actually_set_backlight(connector, 0); ++ ++ intel_soc_pmic_writeb(0x51, 0x00); ++ intel_soc_pmic_writeb(0x4B, 0x7F); ++} ++ + void intel_panel_disable_backlight(struct intel_connector *connector) + { + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); +@@ -1089,6 +1108,17 @@ static void pwm_enable_backlight(struct intel_connector *connector) + intel_panel_actually_set_backlight(connector, panel->backlight.level); + } + ++static void vlv_pmic_enable_backlight(struct intel_connector *connector) ++{ ++ struct intel_panel *panel = &connector->panel; ++ ++ intel_soc_pmic_writeb(0x4B, 0xFF); ++ intel_soc_pmic_writeb(0x4E, 0xFF); ++ intel_soc_pmic_writeb(0x51, 0x01); ++ ++ intel_panel_actually_set_backlight(connector, panel->backlight.level); ++} ++ + void intel_panel_enable_backlight(struct intel_connector *connector) + { + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); +@@ -1687,6 +1717,20 @@ static int pwm_setup_backlight(struct intel_connector *connector, + return 0; + } + ++static int vlv_pmic_setup_backlight(struct intel_connector *connector, ++ enum pipe unused) ++{ ++ struct intel_panel *panel = &connector->panel; ++ ++ panel->backlight.present = 1; ++ panel->backlight.min = 0x00; ++ panel->backlight.max = 0xFF; ++ panel->backlight.level = 0x5A; ++ panel->backlight.enabled = 1; ++ ++ return 0; ++} ++ + int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe) + { + struct drm_i915_private *dev_priv = to_i915(connector->dev); +@@ -1694,6 +1738,8 @@ int intel_panel_setup_backlight(struct drm_connector *connector, enum pipe pipe) + struct intel_panel *panel = &intel_connector->panel; + int ret; + ++ intel_backlight_device_register(intel_connector); ++ + if (!dev_priv->vbt.backlight.present) { + if (dev_priv->quirks & QUIRK_BACKLIGHT_PRESENT) { + DRM_DEBUG_KMS("no backlight present per VBT, but present per quirk\n"); +@@ -1783,18 +1829,17 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel) + panel->backlight.hz_to_pwm = pch_hz_to_pwm; + } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) { + if (connector->base.connector_type == DRM_MODE_CONNECTOR_DSI) { +- panel->backlight.setup = pwm_setup_backlight; +- panel->backlight.enable = pwm_enable_backlight; +- panel->backlight.disable = pwm_disable_backlight; +- panel->backlight.set = pwm_set_backlight; +- panel->backlight.get = pwm_get_backlight; ++ panel->backlight.setup = vlv_pmic_setup_backlight; ++ panel->backlight.enable = vlv_pmic_enable_backlight; ++ panel->backlight.disable = vlv_pmic_disable_backlight; ++ panel->backlight.set = vlv_pmic_set_backlight; ++ panel->backlight.get = vlv_pmic_get_backlight; + } else { + panel->backlight.setup = vlv_setup_backlight; + panel->backlight.enable = vlv_enable_backlight; + panel->backlight.disable = vlv_disable_backlight; + panel->backlight.set = vlv_set_backlight; + panel->backlight.get = vlv_get_backlight; +- panel->backlight.hz_to_pwm = vlv_hz_to_pwm; + } + } else if (IS_GEN4(dev_priv)) { + panel->backlight.setup = i965_setup_backlight; +diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c +index a38c2fefe85a..23ed3f5972fa 100644 +--- a/drivers/gpu/drm/i915/intel_runtime_pm.c ++++ b/drivers/gpu/drm/i915/intel_runtime_pm.c +@@ -1065,7 +1065,18 @@ static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv, + + static void vlv_init_display_clock_gating(struct drm_i915_private *dev_priv) + { +- I915_WRITE(DSPCLK_GATE_D, VRHUNIT_CLOCK_GATE_DISABLE); ++ u32 val; ++ ++ /* ++ * On driver load, a pipe may be active and driving a DSI display. ++ * Preserve DPOUNIT_CLOCK_GATE_DISABLE to avoid the pipe getting stuck ++ * (and never recovering) in this case. intel_dsi_post_disable() will ++ * clear it when we turn off the display. ++ */ ++ val = I915_READ(DSPCLK_GATE_D); ++ val &= DPOUNIT_CLOCK_GATE_DISABLE; ++ val |= VRHUNIT_CLOCK_GATE_DISABLE; ++ I915_WRITE(DSPCLK_GATE_D, val); + + /* + * Disable trickle feed and enable pnd deadline calculation +diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c +index dbed12c484c9..a2f9a4671193 100644 +--- a/drivers/gpu/drm/i915/intel_sprite.c ++++ b/drivers/gpu/drm/i915/intel_sprite.c +@@ -81,10 +81,13 @@ int intel_usecs_to_scanlines(const struct drm_display_mode *adjusted_mode, + */ + void intel_pipe_update_start(struct intel_crtc *crtc) + { ++ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; + long timeout = msecs_to_jiffies_timeout(1); + int scanline, min, max, vblank_start; + wait_queue_head_t *wq = drm_crtc_vblank_waitqueue(&crtc->base); ++ bool need_vlv_dsi_wa = (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && ++ intel_crtc_has_type(crtc->config, INTEL_OUTPUT_DSI); + DEFINE_WAIT(wait); + + vblank_start = adjusted_mode->crtc_vblank_start; +@@ -136,6 +139,25 @@ void intel_pipe_update_start(struct intel_crtc *crtc) + + drm_crtc_vblank_put(&crtc->base); + ++ /* ++ * On VLV/CHV DSI the scanline counter would appear to ++ * increment approx. 1/3 of a scanline before start of vblank. ++ * The registers still get latched at start of vblank however. ++ * This means we must not write any registers on the first ++ * line of vblank (since not the whole line is actually in ++ * vblank). And unfortunately we can't use the interrupt to ++ * wait here since it may have already fired before we check ++ * the scanline. We could use the frame start interrupt instead ++ * since it will fire after the critical scanline, but that would ++ * require more changes in the interrupt code. So for now we'll ++ * just do the nasty thing and poll for the bad scanline to ++ * pass us by. ++ * ++ * FIXME figure out if BXT+ DSI suffers from this as well ++ */ ++ while (need_vlv_dsi_wa && scanline == vblank_start) ++ scanline = intel_get_crtc_scanline(crtc); ++ + crtc->debug.scanline_start = scanline; + crtc->debug.start_vbl_time = ktime_get(); + crtc->debug.start_vbl_count = intel_crtc_get_vblank_counter(crtc); +diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c +index 12d6ebb4ae5d..8583e77deae0 100644 +--- a/drivers/mfd/intel_soc_pmic_core.c ++++ b/drivers/mfd/intel_soc_pmic_core.c +@@ -44,6 +44,8 @@ static struct pwm_lookup crc_pwm_lookup[] = { + PWM_LOOKUP("crystal_cove_pwm", 0, "0000:00:02.0", "pwm_backlight", 0, PWM_POLARITY_NORMAL), + }; + ++static struct intel_soc_pmic *pmic_hack = NULL; ++ + static int intel_soc_pmic_find_gpio_irq(struct device *dev) + { + struct gpio_desc *desc; +@@ -77,6 +79,7 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, + config = (struct intel_soc_pmic_config *)id->driver_data; + + pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL); ++ pmic_hack = pmic; + if (!pmic) + return -ENOMEM; + +@@ -168,6 +171,37 @@ static int intel_soc_pmic_resume(struct device *dev) + } + #endif + ++int intel_soc_pmic_readb(int reg) ++{ ++ int ret; ++ unsigned int val; ++ ++ if (!pmic_hack) { ++ ret = -EIO; ++ } else { ++ ret = regmap_read(pmic_hack->regmap, reg, &val); ++ if (!ret) { ++ ret = val; ++ } ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(intel_soc_pmic_readb); ++ ++int intel_soc_pmic_writeb(int reg, u8 val) ++{ ++ int ret; ++ ++ if (!pmic_hack) { ++ ret = -EIO; ++ } else { ++ ret = regmap_write(pmic_hack->regmap, reg, val); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(intel_soc_pmic_writeb); ++ + static SIMPLE_DEV_PM_OPS(intel_soc_pmic_pm_ops, intel_soc_pmic_suspend, + intel_soc_pmic_resume); + +diff --git a/include/linux/mfd/intel_soc_pmic.h b/include/linux/mfd/intel_soc_pmic.h +index cf619dbeace2..52ad034f606a 100644 +--- a/include/linux/mfd/intel_soc_pmic.h ++++ b/include/linux/mfd/intel_soc_pmic.h +@@ -29,4 +29,7 @@ struct intel_soc_pmic { + struct device *dev; + }; + ++int intel_soc_pmic_readb(int reg); ++int intel_soc_pmic_writeb(int reg, u8 val); ++ + #endif /* __INTEL_SOC_PMIC_H__ */ -- cgit 1.4.1