diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_dsi.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_dsi.c | 327 |
1 files changed, 263 insertions, 64 deletions
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 01b8e9f4c272..2b22bb9bb86f 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -46,6 +46,24 @@ static const struct { }, }; +enum mipi_dsi_pixel_format pixel_format_from_register_bits(u32 fmt) +{ + /* It just so happens the VBT matches register contents. */ + switch (fmt) { + case VID_MODE_FORMAT_RGB888: + return MIPI_DSI_FMT_RGB888; + case VID_MODE_FORMAT_RGB666: + return MIPI_DSI_FMT_RGB666; + case VID_MODE_FORMAT_RGB666_PACKED: + return MIPI_DSI_FMT_RGB666_PACKED; + case VID_MODE_FORMAT_RGB565: + return MIPI_DSI_FMT_RGB565; + default: + MISSING_CASE(fmt); + return MIPI_DSI_FMT_RGB666; + } +} + static void wait_for_dsi_fifo_empty(struct intel_dsi *intel_dsi, enum port port) { struct drm_encoder *encoder = &intel_dsi->base.base; @@ -268,22 +286,47 @@ static inline bool is_cmd_mode(struct intel_dsi *intel_dsi) static bool intel_dsi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi, base); struct intel_connector *intel_connector = intel_dsi->attached_connector; - struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc); + const struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode; + int ret; DRM_DEBUG_KMS("\n"); pipe_config->has_dsi_encoder = true; - if (fixed_mode) + if (fixed_mode) { intel_fixed_panel_mode(fixed_mode, adjusted_mode); + if (HAS_GMCH_DISPLAY(dev_priv)) + intel_gmch_panel_fitting(crtc, pipe_config, + intel_connector->panel.fitting_mode); + else + intel_pch_panel_fitting(crtc, pipe_config, + intel_connector->panel.fitting_mode); + } + /* DSI uses short packets for sync events, so clear mode flags for DSI */ adjusted_mode->flags = 0; + if (IS_BROXTON(dev_priv)) { + /* Dual link goes to DSI transcoder A. */ + if (intel_dsi->ports == BIT(PORT_C)) + pipe_config->cpu_transcoder = TRANSCODER_DSI_C; + else + pipe_config->cpu_transcoder = TRANSCODER_DSI_A; + } + + ret = intel_compute_dsi_pll(encoder, pipe_config); + if (ret) + return false; + + pipe_config->clock_set = true; + return true; } @@ -403,7 +446,7 @@ static void intel_dsi_port_enable(struct intel_encoder *encoder) temp &= ~LANE_CONFIGURATION_MASK; temp &= ~DUAL_LINK_MODE_MASK; - if (intel_dsi->ports == ((1 << PORT_A) | (1 << PORT_C))) { + if (intel_dsi->ports == (BIT(PORT_A) | BIT(PORT_C))) { temp |= (intel_dsi->dual_link - 1) << DUAL_LINK_MODE_SHIFT; temp |= intel_crtc->pipe ? @@ -471,14 +514,19 @@ static void intel_dsi_pre_enable(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_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); - enum pipe pipe = intel_crtc->pipe; + struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); enum port port; u32 tmp; DRM_DEBUG_KMS("\n"); - intel_enable_dsi_pll(encoder); + /* + * The BIOS may leave the PLL in a wonky state where it doesn't + * lock. It needs to be fully powered down to fix it. + */ + intel_disable_dsi_pll(encoder); + intel_enable_dsi_pll(encoder, crtc->config); + intel_dsi_prepare(encoder); /* Panel Enable over CRC PMIC */ @@ -488,19 +536,7 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) msleep(intel_dsi->panel_on_delay); if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { - /* - * Disable DPOunit clock gating, can stall pipe - * and we need DPLL REFA always enabled - */ - tmp = I915_READ(DPLL(pipe)); - tmp |= DPLL_REF_CLK_ENABLE_VLV; - I915_WRITE(DPLL(pipe), tmp); - - /* update the hw state for DPLL */ - intel_crtc->config->dpll_hw_state.dpll = - DPLL_INTEGRATED_REF_CLK_VLV | - DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; - + /* Disable DPOunit clock gating, can stall pipe */ tmp = I915_READ(DSPCLK_GATE_D); tmp |= DPOUNIT_CLOCK_GATE_DISABLE; I915_WRITE(DSPCLK_GATE_D, tmp); @@ -652,11 +688,16 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder) drm_panel_unprepare(intel_dsi->panel); msleep(intel_dsi->panel_off_delay); - msleep(intel_dsi->panel_pwr_cycle_delay); /* Panel Disable over CRC PMIC */ if (intel_dsi->gpio_panel) gpiod_set_value_cansleep(intel_dsi->gpio_panel, 0); + + /* + * 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); } static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, @@ -667,7 +708,7 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; enum intel_display_power_domain power_domain; enum port port; - bool ret; + bool active = false; DRM_DEBUG_KMS("\n"); @@ -675,55 +716,133 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; - ret = false; + /* + * On Broxton the PLL needs to be enabled with a valid divider + * configuration, otherwise accessing DSI registers will hang the + * machine. See BSpec North Display Engine registers/MIPI[BXT]. + */ + if (IS_BROXTON(dev_priv) && !intel_dsi_pll_is_enabled(dev_priv)) + goto out_put_power; /* XXX: this only works for one DSI output */ for_each_dsi_port(port, intel_dsi->ports) { i915_reg_t ctrl_reg = IS_BROXTON(dev) ? BXT_MIPI_PORT_CTRL(port) : MIPI_PORT_CTRL(port); - u32 dpi_enabled, func; - - func = I915_READ(MIPI_DSI_FUNC_PRG(port)); - dpi_enabled = I915_READ(ctrl_reg) & DPI_ENABLE; + bool enabled = I915_READ(ctrl_reg) & DPI_ENABLE; - /* Due to some hardware limitations on BYT, MIPI Port C DPI - * Enable bit does not get set. To check whether DSI Port C - * was enabled in BIOS, check the Pipe B enable bit + /* + * Due to some hardware limitations on VLV/CHV, the DPI enable + * bit in port C control register does not get set. As a + * workaround, check pipe B conf instead. */ - if (IS_VALLEYVIEW(dev) && port == PORT_C) - dpi_enabled = I915_READ(PIPECONF(PIPE_B)) & - PIPECONF_ENABLE; + if ((IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) && port == PORT_C) + enabled = I915_READ(PIPECONF(PIPE_B)) & PIPECONF_ENABLE; + + /* Try command mode if video mode not enabled */ + if (!enabled) { + u32 tmp = I915_READ(MIPI_DSI_FUNC_PRG(port)); + enabled = tmp & CMD_MODE_DATA_WIDTH_MASK; + } - if (dpi_enabled || (func & CMD_MODE_DATA_WIDTH_MASK)) { - if (I915_READ(MIPI_DEVICE_READY(port)) & DEVICE_READY) { - *pipe = port == PORT_A ? PIPE_A : PIPE_B; - ret = true; + if (!enabled) + continue; - goto out; - } + if (!(I915_READ(MIPI_DEVICE_READY(port)) & DEVICE_READY)) + continue; + + if (IS_BROXTON(dev_priv)) { + u32 tmp = I915_READ(MIPI_CTRL(port)); + tmp &= BXT_PIPE_SELECT_MASK; + tmp >>= BXT_PIPE_SELECT_SHIFT; + + if (WARN_ON(tmp > PIPE_C)) + continue; + + *pipe = tmp; + } else { + *pipe = port == PORT_A ? PIPE_A : PIPE_B; } + + active = true; + break; } -out: + +out_put_power: intel_display_power_put(dev_priv, power_domain); - return ret; + return active; } +static void bxt_dsi_get_pipe_config(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *adjusted_mode = + &pipe_config->base.adjusted_mode; + struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); + unsigned int bpp, fmt; + enum port port; + u16 vfp, vsync, vbp; + + /* + * Atleast one port is active as encoder->get_config called only if + * encoder->get_hw_state() returns true. + */ + for_each_dsi_port(port, intel_dsi->ports) { + if (I915_READ(BXT_MIPI_PORT_CTRL(port)) & DPI_ENABLE) + break; + } + + fmt = I915_READ(MIPI_DSI_FUNC_PRG(port)) & VID_MODE_FORMAT_MASK; + pipe_config->pipe_bpp = + mipi_dsi_pixel_format_to_bpp( + pixel_format_from_register_bits(fmt)); + bpp = pipe_config->pipe_bpp; + + /* In terms of pixels */ + adjusted_mode->crtc_hdisplay = + I915_READ(BXT_MIPI_TRANS_HACTIVE(port)); + adjusted_mode->crtc_vdisplay = + I915_READ(BXT_MIPI_TRANS_VACTIVE(port)); + adjusted_mode->crtc_vtotal = + I915_READ(BXT_MIPI_TRANS_VTOTAL(port)); + + /* + * TODO: Retrieve hfp, hsync and hbp. Adjust them for dual link and + * calculate hsync_start, hsync_end, htotal and hblank_end + */ + + /* vertical values are in terms of lines */ + vfp = I915_READ(MIPI_VFP_COUNT(port)); + vsync = I915_READ(MIPI_VSYNC_PADDING_COUNT(port)); + vbp = I915_READ(MIPI_VBP_COUNT(port)); + + adjusted_mode->crtc_hblank_start = adjusted_mode->crtc_hdisplay; + + adjusted_mode->crtc_vsync_start = + vfp + adjusted_mode->crtc_vdisplay; + adjusted_mode->crtc_vsync_end = + vsync + adjusted_mode->crtc_vsync_start; + adjusted_mode->crtc_vblank_start = adjusted_mode->crtc_vdisplay; + adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal; +} + + static void intel_dsi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config) { + struct drm_device *dev = encoder->base.dev; u32 pclk; DRM_DEBUG_KMS("\n"); pipe_config->has_dsi_encoder = true; - /* - * DPLL_MD is not used in case of DSI, reading will get some default value - * set dpll_md = 0 - */ - pipe_config->dpll_hw_state.dpll_md = 0; + if (IS_BROXTON(dev)) + bxt_dsi_get_pipe_config(encoder, pipe_config); - pclk = intel_dsi_get_pclk(encoder, pipe_config->pipe_bpp); + pclk = intel_dsi_get_pclk(encoder, pipe_config->pipe_bpp, + pipe_config); if (!pclk) return; @@ -736,7 +855,7 @@ intel_dsi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_connector *intel_connector = to_intel_connector(connector); - struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; + const struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; DRM_DEBUG_KMS("\n"); @@ -787,7 +906,7 @@ static void set_dsi_timings(struct drm_encoder *encoder, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); enum port port; - unsigned int bpp = dsi_pixel_format_bpp(intel_dsi->pixel_format); + unsigned int bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); unsigned int lane_count = intel_dsi->lane_count; u16 hactive, hfp, hsync, hbp, vfp, vsync, vbp; @@ -849,6 +968,23 @@ static void set_dsi_timings(struct drm_encoder *encoder, } } +static u32 pixel_format_to_reg(enum mipi_dsi_pixel_format fmt) +{ + switch (fmt) { + case MIPI_DSI_FMT_RGB888: + return VID_MODE_FORMAT_RGB888; + case MIPI_DSI_FMT_RGB666: + return VID_MODE_FORMAT_RGB666; + case MIPI_DSI_FMT_RGB666_PACKED: + return VID_MODE_FORMAT_RGB666_PACKED; + case MIPI_DSI_FMT_RGB565: + return VID_MODE_FORMAT_RGB565; + default: + MISSING_CASE(fmt); + return VID_MODE_FORMAT_RGB666; + } +} + static void intel_dsi_prepare(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; @@ -858,7 +994,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); const struct drm_display_mode *adjusted_mode = &intel_crtc->config->base.adjusted_mode; enum port port; - unsigned int bpp = dsi_pixel_format_bpp(intel_dsi->pixel_format); + unsigned int bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); u32 val, tmp; u16 mode_hdisplay; @@ -917,9 +1053,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) val |= CMD_MODE_DATA_WIDTH_8_BIT; /* XXX */ } else { val |= intel_dsi->channel << VID_MODE_CHANNEL_NUMBER_SHIFT; - - /* XXX: cross-check bpp vs. pixel format? */ - val |= intel_dsi->pixel_format; + val |= pixel_format_to_reg(intel_dsi->pixel_format); } tmp = 0; @@ -1059,6 +1193,48 @@ static int intel_dsi_get_modes(struct drm_connector *connector) return 1; } +static int intel_dsi_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_crtc *crtc; + int ret; + + ret = drm_object_property_set_value(&connector->base, property, val); + if (ret) + return ret; + + if (property == dev->mode_config.scaling_mode_property) { + if (val == DRM_MODE_SCALE_NONE) { + DRM_DEBUG_KMS("no scaling not supported\n"); + return -EINVAL; + } + if (HAS_GMCH_DISPLAY(dev) && + val == DRM_MODE_SCALE_CENTER) { + DRM_DEBUG_KMS("centering not supported\n"); + return -EINVAL; + } + + if (intel_connector->panel.fitting_mode == val) + return 0; + + intel_connector->panel.fitting_mode = val; + } + + crtc = intel_attached_encoder(connector)->base.crtc; + if (crtc && crtc->state->enable) { + /* + * If the CRTC is enabled, the display will be changed + * according to the new panel fitting mode. + */ + intel_crtc_restore_mode(crtc); + } + + return 0; +} + static void intel_dsi_connector_destroy(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); @@ -1101,11 +1277,25 @@ static const struct drm_connector_funcs intel_dsi_connector_funcs = { .detect = intel_dsi_detect, .destroy = intel_dsi_connector_destroy, .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = intel_dsi_set_property, .atomic_get_property = intel_connector_atomic_get_property, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, }; +static void intel_dsi_add_properties(struct intel_connector *connector) +{ + struct drm_device *dev = connector->base.dev; + + if (connector->panel.fixed_mode) { + drm_mode_create_scaling_mode_property(dev); + drm_object_attach_property(&connector->base.base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_ASPECT); + connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT; + } +} + void intel_dsi_init(struct drm_device *dev) { struct intel_dsi *intel_dsi; @@ -1121,11 +1311,13 @@ void intel_dsi_init(struct drm_device *dev) DRM_DEBUG_KMS("\n"); /* There is no detection method for MIPI so rely on VBT */ - if (!dev_priv->vbt.has_mipi) + if (!intel_bios_is_dsi_present(dev_priv, &port)) return; if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { dev_priv->mipi_mmio_base = VLV_MIPI_BASE; + } else if (IS_BROXTON(dev)) { + dev_priv->mipi_mmio_base = BXT_MIPI_BASE; } else { DRM_ERROR("Unsupported Mipi device to reg base"); return; @@ -1161,17 +1353,21 @@ void intel_dsi_init(struct drm_device *dev) intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector->unregister = intel_connector_unregister; - /* Pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI port C */ - if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { - intel_encoder->crtc_mask = (1 << PIPE_A); - intel_dsi->ports = (1 << PORT_A); - } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) { - intel_encoder->crtc_mask = (1 << PIPE_B); - intel_dsi->ports = (1 << PORT_C); - } + /* + * On BYT/CHV, pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI + * port C. BXT isn't limited like this. + */ + if (IS_BROXTON(dev_priv)) + intel_encoder->crtc_mask = BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C); + else if (port == PORT_A) + intel_encoder->crtc_mask = BIT(PIPE_A); + else + intel_encoder->crtc_mask = BIT(PIPE_B); if (dev_priv->vbt.dsi.config->dual_link) - intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C)); + intel_dsi->ports = BIT(PORT_A) | BIT(PORT_C); + else + intel_dsi->ports = BIT(port); /* Create a DSI host (and a device) for each port. */ for_each_dsi_port(port, intel_dsi->ports) { @@ -1223,8 +1419,6 @@ void intel_dsi_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); - drm_connector_register(connector); - drm_panel_attach(intel_dsi->panel, connector); mutex_lock(&dev->mode_config.mutex); @@ -1243,6 +1437,11 @@ void intel_dsi_init(struct drm_device *dev) } intel_panel_init(&intel_connector->panel, fixed_mode, NULL); + + intel_dsi_add_properties(intel_connector); + + drm_connector_register(connector); + intel_panel_setup_backlight(connector, INVALID_PIPE); return; |