diff --git a/src/mesa/drivers/dri/i965/brw_clip.c b/src/mesa/drivers/dri/i965/brw_clip.c
index d411208..b4a2e0a 100644
--- a/src/mesa/drivers/dri/i965/brw_clip.c
+++ b/src/mesa/drivers/dri/i965/brw_clip.c
@@ -47,6 +47,86 @@
 #define FRONT_UNFILLED_BIT  0x1
 #define BACK_UNFILLED_BIT   0x2
 
+/**
+ * Lookup the interpolation mode information for every element in the
+ * vue.
+ */
+static void
+brw_lookup_interpolation(struct brw_context *brw)
+{
+   /* pprog means "previous program", i.e. the last program before the
+    * fragment shader.  It can only be the vertex shader for now, but
+    * it may be a geometry shader in the future.
+    */
+   const struct gl_program *pprog = &brw->vertex_program->Base;
+   const struct gl_fragment_program *fprog = brw->fragment_program;
+   struct brw_vue_map *vue_map = &brw->vs.prog_data->vue_map;
+
+   /* Default everything to INTERP_QUALIFIER_NONE */
+   memset(brw->interpolation_mode, INTERP_QUALIFIER_NONE, BRW_VERT_RESULT_MAX);
+
+   /* If there is no fragment shader, interpolation won't be needed,
+    * so defaulting to none is good.
+    */
+   if (!fprog)
+      return;
+
+   for (int i = 0; i < vue_map->num_slots; i++) {
+      /* First lookup the vert result, skip if there isn't one */
+      int vert_result = vue_map->slot_to_vert_result[i];
+      if (vert_result == BRW_VERT_RESULT_MAX)
+         continue;
+
+      /* HPOS is special.  In the clipper, it is handled specifically,
+       * so its value is irrelevant.  In the sf, it's forced to
+       * linear.  In the wm, it's special cased, irrelevant again.  So
+       * force linear to remove the sf special case.
+       */
+      if (vert_result == VERT_RESULT_HPOS) {
+         brw->interpolation_mode[i] = INTERP_QUALIFIER_NOPERSPECTIVE;
+         continue;
+      }
+
+      /* There is a 1-1 mapping of vert result to frag attrib except
+       * for BackColor and vars
+       */
+      int frag_attrib = vert_result;
+      if (vert_result >= VERT_RESULT_BFC0 && vert_result <= VERT_RESULT_BFC1)
+         frag_attrib = vert_result - VERT_RESULT_BFC0 + FRAG_ATTRIB_COL0;
+      else if(vert_result >= VERT_RESULT_VAR0)
+         frag_attrib = vert_result - VERT_RESULT_VAR0 + FRAG_ATTRIB_VAR0;
+
+      /* If the output is not used by the fragment shader, skip it. */
+      if (!(fprog->Base.InputsRead & BITFIELD64_BIT(frag_attrib)))
+         continue;
+
+      /* Lookup the interpolation mode */
+      enum glsl_interp_qualifier interpolation_mode = fprog->InterpQualifier[frag_attrib];
+
+      /* If the mode is not specified, then the default varies.  Color
+       * values follow the shader model, while all the rest uses
+       * smooth.
+       */
+      if (interpolation_mode == INTERP_QUALIFIER_NONE) {
+         if (frag_attrib >= FRAG_ATTRIB_COL0 && frag_attrib <= FRAG_ATTRIB_COL1)
+            interpolation_mode = brw->intel.ctx.Light.ShadeModel == GL_FLAT ? INTERP_QUALIFIER_FLAT : INTERP_QUALIFIER_SMOOTH;
+         else
+            interpolation_mode = INTERP_QUALIFIER_SMOOTH;
+      }
+
+      /* Finally, if we have both a front color and a back color for
+       * the same channel, the selection will be done before
+       * interpolation and the back color copied over the front color
+       * if necessary.  So interpolating the back color is
+       * unnecessary.
+       */
+      if (vert_result >= VERT_RESULT_BFC0 && vert_result <= VERT_RESULT_BFC1)
+         if (pprog->OutputsWritten & BITFIELD64_BIT(vert_result - VERT_RESULT_BFC0 + VERT_RESULT_COL0))
+            interpolation_mode = INTERP_QUALIFIER_NONE;
+
+      brw->interpolation_mode[i] = interpolation_mode;
+   }
+}
 
 static void compile_clip_prog( struct brw_context *brw,
 			     struct brw_clip_prog_key *key )
@@ -143,6 +223,10 @@ brw_upload_clip_prog(struct brw_context *brw)
 
    /* Populate the key:
     */
+
+   /* BRW_NEW_FRAGMENT_PROGRAM, _NEW_LIGHT */
+   brw_lookup_interpolation(brw);
+
    /* BRW_NEW_REDUCED_PRIMITIVE */
    key.primitive = brw->intel.reduced_primitive;
    /* CACHE_NEW_VS_PROG (also part of VUE map) */
@@ -150,6 +234,10 @@ brw_upload_clip_prog(struct brw_context *brw)
    /* _NEW_LIGHT */
    key.do_flat_shading = (ctx->Light.ShadeModel == GL_FLAT);
    key.pv_first = (ctx->Light.ProvokingVertex == GL_FIRST_VERTEX_CONVENTION);
+
+   /* BRW_NEW_FRAGMENT_PROGRAM, _NEW_LIGHT */
+   memcpy(key.interpolation_mode, brw->interpolation_mode, BRW_VERT_RESULT_MAX);
+
    /* _NEW_TRANSFORM (also part of VUE map)*/
    key.nr_userclip = _mesa_bitcount_64(ctx->Transform.ClipPlanesEnabled);
 
@@ -258,7 +346,7 @@ const struct brw_tracked_state brw_clip_prog = {
 		_NEW_TRANSFORM |
 		_NEW_POLYGON | 
 		_NEW_BUFFERS),
-      .brw   = (BRW_NEW_REDUCED_PRIMITIVE),
+      .brw   = (BRW_NEW_FRAGMENT_PROGRAM|BRW_NEW_REDUCED_PRIMITIVE),
       .cache = CACHE_NEW_VS_PROG
    },
    .emit = brw_upload_clip_prog
diff --git a/src/mesa/drivers/dri/i965/brw_clip.h b/src/mesa/drivers/dri/i965/brw_clip.h
index 9185651..e78d074 100644
--- a/src/mesa/drivers/dri/i965/brw_clip.h
+++ b/src/mesa/drivers/dri/i965/brw_clip.h
@@ -43,6 +43,7 @@
  */
 struct brw_clip_prog_key {
    GLbitfield64 attrs;
+   unsigned char interpolation_mode[BRW_VERT_RESULT_MAX]; /* copy of the main context */
    GLuint primitive:4;
    GLuint nr_userclip:4;
    GLuint do_flat_shading:1;
diff --git a/src/mesa/drivers/dri/i965/brw_context.h b/src/mesa/drivers/dri/i965/brw_context.h
index b4868fe..afafa47 100644
--- a/src/mesa/drivers/dri/i965/brw_context.h
+++ b/src/mesa/drivers/dri/i965/brw_context.h
@@ -1054,6 +1054,17 @@ struct brw_context
    uint32_t render_target_format[MESA_FORMAT_COUNT];
    bool format_supported_as_render_target[MESA_FORMAT_COUNT];
 
+   /* Interpolation modes, one byte per vue slot, values equal to
+    * glsl_interp_qualifier.
+    *
+    * Used on gen4/5 by the clipper, sf and wm stages.  Given the
+    * update order, the clipper is responsible to update it.
+    *
+    * Ignored on gen 6+
+    */
+
+   unsigned char interpolation_mode[BRW_VERT_RESULT_MAX];
+
    /* PrimitiveRestart */
    struct {
       bool in_progress;
diff --git a/src/mesa/drivers/dri/i965/brw_sf.c b/src/mesa/drivers/dri/i965/brw_sf.c
index 791210f..26cbaf7 100644
--- a/src/mesa/drivers/dri/i965/brw_sf.c
+++ b/src/mesa/drivers/dri/i965/brw_sf.c
@@ -194,6 +194,9 @@ brw_upload_sf_prog(struct brw_context *brw)
    key.do_flat_shading = (ctx->Light.ShadeModel == GL_FLAT);
    key.do_twoside_color = ctx->VertexProgram._TwoSideEnabled;
 
+   /* BRW_NEW_FRAGMENT_PROGRAM, _NEW_LIGHT */
+   memcpy(key.interpolation_mode, brw->interpolation_mode, BRW_VERT_RESULT_MAX);
+
    /* _NEW_POLYGON */
    if (key.do_twoside_color) {
       /* If we're rendering to a FBO, we have to invert the polygon
@@ -215,7 +218,7 @@ const struct brw_tracked_state brw_sf_prog = {
    .dirty = {
       .mesa  = (_NEW_HINT | _NEW_LIGHT | _NEW_POLYGON | _NEW_POINT |
                 _NEW_TRANSFORM | _NEW_BUFFERS),
-      .brw   = (BRW_NEW_REDUCED_PRIMITIVE),
+      .brw   = (BRW_NEW_FRAGMENT_PROGRAM|BRW_NEW_REDUCED_PRIMITIVE),
       .cache = CACHE_NEW_VS_PROG
    },
    .emit = brw_upload_sf_prog
diff --git a/src/mesa/drivers/dri/i965/brw_sf.h b/src/mesa/drivers/dri/i965/brw_sf.h
index f908fc0..5e261fb 100644
--- a/src/mesa/drivers/dri/i965/brw_sf.h
+++ b/src/mesa/drivers/dri/i965/brw_sf.h
@@ -46,6 +46,7 @@
 
 struct brw_sf_prog_key {
    GLbitfield64 attrs;
+   unsigned char interpolation_mode[BRW_VERT_RESULT_MAX]; /* copy of the main context */
    uint8_t point_sprite_coord_replace;
    GLuint primitive:2;
    GLuint do_twoside_color:1;
diff --git a/src/mesa/drivers/dri/i965/brw_wm.c b/src/mesa/drivers/dri/i965/brw_wm.c
index 587cc35..b54f4b1 100644
--- a/src/mesa/drivers/dri/i965/brw_wm.c
+++ b/src/mesa/drivers/dri/i965/brw_wm.c
@@ -513,6 +513,8 @@ static void brw_wm_populate_key( struct brw_context *brw,
 
    /* _NEW_LIGHT */
    key->flat_shade = (ctx->Light.ShadeModel == GL_FLAT);
+   if (intel->gen < 6)
+      memcpy(key->interpolation_mode, brw->interpolation_mode, BRW_VERT_RESULT_MAX);
 
    /* _NEW_FRAG_CLAMP | _NEW_BUFFERS */
    key->clamp_fragment_color = ctx->Color._ClampFragmentColor;
diff --git a/src/mesa/drivers/dri/i965/brw_wm.h b/src/mesa/drivers/dri/i965/brw_wm.h
index b976a60..add9dd6 100644
--- a/src/mesa/drivers/dri/i965/brw_wm.h
+++ b/src/mesa/drivers/dri/i965/brw_wm.h
@@ -60,6 +60,7 @@
 #define AA_ALWAYS    2
 
 struct brw_wm_prog_key {
+   unsigned char interpolation_mode[BRW_VERT_RESULT_MAX]; /* copy of the main context */
    uint8_t iz_lookup;
    GLuint stats_wm:1;
    GLuint flat_shade:1;
