Merge tag 'drm-intel-next-2014-04-16' of git://anongit.freedesktop.org/drm-intel...
[linux-2.6-block.git] / drivers / gpu / drm / i915 / intel_bios.c
index fa486c5fbb0250650b558632c6f63aa0ebf5bd63..fba9efd09e8765e014484cd75c2b091052993adf 100644 (file)
@@ -206,7 +206,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
        const struct lvds_dvo_timing *panel_dvo_timing;
        const struct lvds_fp_timing *fp_timing;
        struct drm_display_mode *panel_fixed_mode;
-       int i, downclock;
+       int i, downclock, drrs_mode;
 
        lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
        if (!lvds_options)
@@ -218,6 +218,28 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
 
        panel_type = lvds_options->panel_type;
 
+       drrs_mode = (lvds_options->dps_panel_type_bits
+                               >> (panel_type * 2)) & MODE_MASK;
+       /*
+        * VBT has static DRRS = 0 and seamless DRRS = 2.
+        * The below piece of code is required to adjust vbt.drrs_type
+        * to match the enum drrs_support_type.
+        */
+       switch (drrs_mode) {
+       case 0:
+               dev_priv->vbt.drrs_type = STATIC_DRRS_SUPPORT;
+               DRM_DEBUG_KMS("DRRS supported mode is static\n");
+               break;
+       case 2:
+               dev_priv->vbt.drrs_type = SEAMLESS_DRRS_SUPPORT;
+               DRM_DEBUG_KMS("DRRS supported mode is seamless\n");
+               break;
+       default:
+               dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
+               DRM_DEBUG_KMS("DRRS not supported (VBT input)\n");
+               break;
+       }
+
        lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
        if (!lvds_lfp_data)
                return;
@@ -526,6 +548,16 @@ parse_driver_features(struct drm_i915_private *dev_priv,
 
        if (driver->dual_frequency)
                dev_priv->render_reclock_avail = true;
+
+       DRM_DEBUG_KMS("DRRS State Enabled:%d\n", driver->drrs_enabled);
+       /*
+        * If DRRS is not supported, drrs_type has to be set to 0.
+        * This is because, VBT is configured in such a way that
+        * static DRRS is 0 and DRRS not supported is represented by
+        * driver->drrs_enabled=false
+        */
+       if (!driver->drrs_enabled)
+               dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
 }
 
 static void
@@ -604,19 +636,217 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
        }
 }
 
+static u8 *goto_next_sequence(u8 *data, int *size)
+{
+       u16 len;
+       int tmp = *size;
+
+       if (--tmp < 0)
+               return NULL;
+
+       /* goto first element */
+       data++;
+       while (1) {
+               switch (*data) {
+               case MIPI_SEQ_ELEM_SEND_PKT:
+                       /*
+                        * skip by this element payload size
+                        * skip elem id, command flag and data type
+                        */
+                       tmp -= 5;
+                       if (tmp < 0)
+                               return NULL;
+
+                       data += 3;
+                       len = *((u16 *)data);
+
+                       tmp -= len;
+                       if (tmp < 0)
+                               return NULL;
+
+                       /* skip by len */
+                       data = data + 2 + len;
+                       break;
+               case MIPI_SEQ_ELEM_DELAY:
+                       /* skip by elem id, and delay is 4 bytes */
+                       tmp -= 5;
+                       if (tmp < 0)
+                               return NULL;
+
+                       data += 5;
+                       break;
+               case MIPI_SEQ_ELEM_GPIO:
+                       tmp -= 3;
+                       if (tmp < 0)
+                               return NULL;
+
+                       data += 3;
+                       break;
+               default:
+                       DRM_ERROR("Unknown element\n");
+                       return NULL;
+               }
+
+               /* end of sequence ? */
+               if (*data == 0)
+                       break;
+       }
+
+       /* goto next sequence or end of block byte */
+       if (--tmp < 0)
+               return NULL;
+
+       data++;
+
+       /* update amount of data left for the sequence block to be parsed */
+       *size = tmp;
+       return data;
+}
+
 static void
 parse_mipi(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
 {
-       struct bdb_mipi *mipi;
+       struct bdb_mipi_config *start;
+       struct bdb_mipi_sequence *sequence;
+       struct mipi_config *config;
+       struct mipi_pps_data *pps;
+       u8 *data, *seq_data;
+       int i, panel_id, seq_size;
+       u16 block_size;
+
+       /* Initialize this to undefined indicating no generic MIPI support */
+       dev_priv->vbt.dsi.panel_id = MIPI_DSI_UNDEFINED_PANEL_ID;
+
+       /* Block #40 is already parsed and panel_fixed_mode is
+        * stored in dev_priv->lfp_lvds_vbt_mode
+        * resuse this when needed
+        */
 
-       mipi = find_section(bdb, BDB_MIPI_CONFIG);
-       if (!mipi) {
-               DRM_DEBUG_KMS("No MIPI BDB found");
+       /* Parse #52 for panel index used from panel_type already
+        * parsed
+        */
+       start = find_section(bdb, BDB_MIPI_CONFIG);
+       if (!start) {
+               DRM_DEBUG_KMS("No MIPI config BDB found");
                return;
        }
 
-       /* XXX: add more info */
+       DRM_DEBUG_DRIVER("Found MIPI Config block, panel index = %d\n",
+                                                               panel_type);
+
+       /*
+        * get hold of the correct configuration block and pps data as per
+        * the panel_type as index
+        */
+       config = &start->config[panel_type];
+       pps = &start->pps[panel_type];
+
+       /* store as of now full data. Trim when we realise all is not needed */
+       dev_priv->vbt.dsi.config = kmemdup(config, sizeof(struct mipi_config), GFP_KERNEL);
+       if (!dev_priv->vbt.dsi.config)
+               return;
+
+       dev_priv->vbt.dsi.pps = kmemdup(pps, sizeof(struct mipi_pps_data), GFP_KERNEL);
+       if (!dev_priv->vbt.dsi.pps) {
+               kfree(dev_priv->vbt.dsi.config);
+               return;
+       }
+
+       /* We have mandatory mipi config blocks. Initialize as generic panel */
        dev_priv->vbt.dsi.panel_id = MIPI_DSI_GENERIC_PANEL_ID;
+
+       /* Check if we have sequence block as well */
+       sequence = find_section(bdb, BDB_MIPI_SEQUENCE);
+       if (!sequence) {
+               DRM_DEBUG_KMS("No MIPI Sequence found, parsing complete\n");
+               return;
+       }
+
+       DRM_DEBUG_DRIVER("Found MIPI sequence block\n");
+
+       block_size = get_blocksize(sequence);
+
+       /*
+        * parse the sequence block for individual sequences
+        */
+       dev_priv->vbt.dsi.seq_version = sequence->version;
+
+       seq_data = &sequence->data[0];
+
+       /*
+        * sequence block is variable length and hence we need to parse and
+        * get the sequence data for specific panel id
+        */
+       for (i = 0; i < MAX_MIPI_CONFIGURATIONS; i++) {
+               panel_id = *seq_data;
+               seq_size = *((u16 *) (seq_data + 1));
+               if (panel_id == panel_type)
+                       break;
+
+               /* skip the sequence including seq header of 3 bytes */
+               seq_data = seq_data + 3 + seq_size;
+               if ((seq_data - &sequence->data[0]) > block_size) {
+                       DRM_ERROR("Sequence start is beyond sequence block size, corrupted sequence block\n");
+                       return;
+               }
+       }
+
+       if (i == MAX_MIPI_CONFIGURATIONS) {
+               DRM_ERROR("Sequence block detected but no valid configuration\n");
+               return;
+       }
+
+       /* check if found sequence is completely within the sequence block
+        * just being paranoid */
+       if (seq_size > block_size) {
+               DRM_ERROR("Corrupted sequence/size, bailing out\n");
+               return;
+       }
+
+       /* skip the panel id(1 byte) and seq size(2 bytes) */
+       dev_priv->vbt.dsi.data = kmemdup(seq_data + 3, seq_size, GFP_KERNEL);
+       if (!dev_priv->vbt.dsi.data)
+               return;
+
+       /*
+        * loop into the sequence data and split into multiple sequneces
+        * There are only 5 types of sequences as of now
+        */
+       data = dev_priv->vbt.dsi.data;
+       dev_priv->vbt.dsi.size = seq_size;
+
+       /* two consecutive 0x00 indicate end of all sequences */
+       while (1) {
+               int seq_id = *data;
+               if (MIPI_SEQ_MAX > seq_id && seq_id > MIPI_SEQ_UNDEFINED) {
+                       dev_priv->vbt.dsi.sequence[seq_id] = data;
+                       DRM_DEBUG_DRIVER("Found mipi sequence - %d\n", seq_id);
+               } else {
+                       DRM_ERROR("undefined sequence\n");
+                       goto err;
+               }
+
+               /* partial parsing to skip elements */
+               data = goto_next_sequence(data, &seq_size);
+
+               if (data == NULL) {
+                       DRM_ERROR("Sequence elements going beyond block itself. Sequence block parsing failed\n");
+                       goto err;
+               }
+
+               if (*data == 0)
+                       break; /* end of sequence reached */
+       }
+
+       DRM_DEBUG_DRIVER("MIPI related vbt parsing complete\n");
+       return;
+err:
+       kfree(dev_priv->vbt.dsi.data);
+       dev_priv->vbt.dsi.data = NULL;
+
+       /* error during parsing so set all pointers to null
+        * because of partial parsing */
+       memset(dev_priv->vbt.dsi.sequence, 0, MIPI_SEQ_MAX);
 }
 
 static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port,