[Intel-gfx] Thinkpad T420 and single/dual channel lvds

Rodrigo Vivi rodrigo.vivi at gmail.com
Fri Mar 16 16:33:13 CET 2012


Reviewed-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>

On Thu, Mar 15, 2012 at 11:42 AM, Takashi Iwai <tiwai at suse.de> wrote:
> At Thu, 15 Mar 2012 14:30:35 +0100,
> Takashi Iwai wrote:
>>
>> At Thu, 15 Mar 2012 13:25:08 +0000,
>> Chris Wilson wrote:
>> >
>> > On Thu, 15 Mar 2012 14:15:54 +0100, Takashi Iwai <tiwai at suse.de> wrote:
>> > > +static bool is_dual_link_lvds(struct drm_i915_private *dev_priv)
>> > > +{
>> > > + /* BIOS should set the proper LVDS register value at boot, but
>> > > +  * in reality, it doesn't set the value when the lid is closed;
>> > > +  * thus when a machine is booted with the lid closed, the LVDS
>> > > +  * reg value can't be trusted.  So we need to check "the value
>> > > +  * to be set" in VBT at first.
>> > > +  */
>> > > + if ((dev_priv->bios_lvds_val & LVDS_CLKB_POWER_MASK) ==
>> > > +     LVDS_CLKB_POWER_UP)
>> > > +         return true;
>> > > + if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
>> >
>> > This is either PCH_LVDS or LVDS depending on the generation.
>>
>> Oh, right.  The revised patch is below.
>
> One more change.  Now it checks the resolution to be sure.
>
>
> Takashi
>
> ---
> From: Takashi Iwai <tiwai at suse.de>
> Subject: [PATCH v3] drm/i915: Check VBIOS value for determining LVDS dual channel mode, too
>
> Currently i915 driver checks [PCH_]LVDS register bits to decide
> whether to set up the dual-link or the single-link mode.  This relies
> implicitly on that BIOS initializes the register properly at boot.
> However, BIOS doesn't initialize it always.  When the machine is
> booted with the closed lid, BIOS skips the LVDS reg initialization.
> This ends up in blank output on a machine with a dual-link LVDS when
> you open the lid after the boot.
>
> This patch adds a workaround for that problem by checking the initial
> LVDS register value in VBT.
>
> Signed-off-by: Takashi Iwai <tiwai at suse.de>
> ---
> v1->v2: Fix the register for gen<=4
> v2->v3: Check the resolution of the entry to be sure
>
>  drivers/gpu/drm/i915/i915_drv.h      |    1 +
>  drivers/gpu/drm/i915/intel_bios.c    |   26 ++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_display.c |   26 ++++++++++++++++++++------
>  3 files changed, 47 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 9689ca3..8c8e488 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -376,6 +376,7 @@ typedef struct drm_i915_private {
>        unsigned int lvds_use_ssc:1;
>        unsigned int display_clock_mode:1;
>        int lvds_ssc_freq;
> +       unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */
>        struct {
>                int rate;
>                int lanes;
> diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
> index 63880e2..6b93f92 100644
> --- a/drivers/gpu/drm/i915/intel_bios.c
> +++ b/drivers/gpu/drm/i915/intel_bios.c
> @@ -173,6 +173,27 @@ get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
>        return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
>  }
>
> +/* read the initial LVDS register value for the given panel mode */
> +static unsigned int get_lvds_reg_val(const struct bdb_header *bdb,
> +                                    const struct bdb_lvds_lfp_data_ptrs *ptrs,
> +                                    int index,
> +                                    struct drm_display_mode *mode)
> +{
> +       unsigned int ofs;
> +       const struct lvds_fp_timing *timing;
> +
> +       if (index >= ARRAY_SIZE(ptrs->ptr))
> +               return 0;
> +       ofs = ptrs->ptr[index].fp_timing_offset;
> +       if (ofs + sizeof(*timing) > bdb->bdb_size)
> +               return 0;
> +       timing = (const struct lvds_fp_timing *)((const u8 *)bdb + ofs);
> +       /* check the resolution, just to be sure */
> +       if (timing->x_res != mode->hdisplay || timing->y_res != mode->vdisplay)
> +               return 0;
> +       return timing->lvds_reg_val;
> +}
> +
>  /* Try to find integrated panel data */
>  static void
>  parse_lfp_panel_data(struct drm_i915_private *dev_priv,
> @@ -243,6 +264,11 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
>                              "Normal Clock %dKHz, downclock %dKHz\n",
>                              panel_fixed_mode->clock, 10*downclock);
>        }
> +
> +       dev_priv->bios_lvds_val = get_lvds_reg_val(bdb, lvds_lfp_data_ptrs,
> +                                                  lvds_options->panel_type,
> +                                                  panel_fixed_mode);
> +       DRM_DEBUG_KMS("VBT initial LVDS value %x\n", dev_priv->bios_lvds_val);
>  }
>
>  /* Try to find sdvo panel data */
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index f851db7..314af26 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -356,6 +356,23 @@ static const intel_limit_t intel_limits_ironlake_display_port = {
>        .find_pll = intel_find_pll_ironlake_dp,
>  };
>
> +static bool is_dual_link_lvds(struct drm_i915_private *dev_priv,
> +                             unsigned int reg)
> +{
> +       /* BIOS should set the proper LVDS register value at boot, but
> +        * in reality, it doesn't set the value when the lid is closed;
> +        * thus when a machine is booted with the lid closed, the LVDS
> +        * reg value can't be trusted.  So we need to check "the value
> +        * to be set" in VBT at first.
> +        */
> +       if ((dev_priv->bios_lvds_val & LVDS_CLKB_POWER_MASK) ==
> +           LVDS_CLKB_POWER_UP)
> +               return true;
> +       if ((I915_READ(reg) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
> +               return true;
> +       return false;
> +}
> +
>  static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
>                                                int refclk)
>  {
> @@ -364,8 +381,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
>        const intel_limit_t *limit;
>
>        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
> -               if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
> -                   LVDS_CLKB_POWER_UP) {
> +               if (is_dual_link_lvds(dev_priv, PCH_LVDS)) {
>                        /* LVDS dual channel */
>                        if (refclk == 100000)
>                                limit = &intel_limits_ironlake_dual_lvds_100m;
> @@ -393,8 +409,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
>        const intel_limit_t *limit;
>
>        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
> -               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
> -                   LVDS_CLKB_POWER_UP)
> +               if (is_dual_link_lvds(dev_priv, LVDS))
>                        /* LVDS with dual channel */
>                        limit = &intel_limits_g4x_dual_channel_lvds;
>                else
> @@ -531,8 +546,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
>                 * reliably set up different single/dual channel state, if we
>                 * even can.
>                 */
> -               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
> -                   LVDS_CLKB_POWER_UP)
> +               if (is_dual_link_lvds(dev_priv, LVDS))
>                        clock.p2 = limit->p2.p2_fast;
>                else
>                        clock.p2 = limit->p2.p2_slow;
> --
> 1.7.9.2
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Rodrigo Vivi
Blog: http://blog.vivi.eng.br
GPG: 0x905BE242 @ wwwkeys.pgp.net



More information about the Intel-gfx mailing list