]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_edid.c
Merge remote-tracking branch 'pci-current/for-linus'
[karo-tx-linux.git] / drivers / gpu / drm / drm_edid.c
index 7a4fd2ed1280b5d23e1ac46a0c70672a873986e2..dfa9769b26b5c57db0731361478639a9600b65b3 100644 (file)
@@ -70,6 +70,8 @@
 #define EDID_QUIRK_FORCE_REDUCED_BLANKING      (1 << 7)
 /* Force 8bpc */
 #define EDID_QUIRK_FORCE_8BPC                  (1 << 8)
+/* Force 12bpc */
+#define EDID_QUIRK_FORCE_12BPC                 (1 << 9)
 
 struct detailed_mode_closure {
        struct drm_connector *connector;
@@ -125,6 +127,9 @@ static struct edid_quirk {
        { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
        { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
 
+       /* Sony PVM-2541A does up to 12 bpc, but only reports max 8 bpc */
+       { "SNY", 0x2541, EDID_QUIRK_FORCE_12BPC },
+
        /* ViewSonic VA2026w */
        { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
 
@@ -1227,7 +1232,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
                if (i == 4 && print_bad_edid) {
                        dev_warn(connector->dev->dev,
                         "%s: Ignoring invalid EDID block %d.\n",
-                        drm_get_connector_name(connector), j);
+                        connector->name, j);
 
                        connector->bad_edid_counter++;
                }
@@ -1247,7 +1252,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
 carp:
        if (print_bad_edid) {
                dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
-                        drm_get_connector_name(connector), j);
+                        connector->name, j);
        }
        connector->bad_edid_counter++;
 
@@ -3228,10 +3233,9 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)
 
                        /* Speaker Allocation Data Block */
                        if (dbl == 3) {
-                               *sadb = kmalloc(dbl, GFP_KERNEL);
+                               *sadb = kmemdup(&db[1], dbl, GFP_KERNEL);
                                if (!*sadb)
                                        return -ENOMEM;
-                               memcpy(*sadb, &db[1], dbl);
                                count = dbl;
                                break;
                        }
@@ -3300,6 +3304,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
        struct drm_connector *connector;
        struct drm_device *dev = encoder->dev;
 
+       WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+
        list_for_each_entry(connector, &dev->mode_config.connector_list, head)
                if (connector->encoder == encoder && connector->eld[0])
                        return connector;
@@ -3423,17 +3429,120 @@ bool drm_rgb_quant_range_selectable(struct edid *edid)
 }
 EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
 
+/**
+ * drm_assign_hdmi_deep_color_info - detect whether monitor supports
+ * hdmi deep color modes and update drm_display_info if so.
+ *
+ * @edid: monitor EDID information
+ * @info: Updated with maximum supported deep color bpc and color format
+ *        if deep color supported.
+ *
+ * Parse the CEA extension according to CEA-861-B.
+ * Return true if HDMI deep color supported, false if not or unknown.
+ */
+static bool drm_assign_hdmi_deep_color_info(struct edid *edid,
+                                            struct drm_display_info *info,
+                                            struct drm_connector *connector)
+{
+       u8 *edid_ext, *hdmi;
+       int i;
+       int start_offset, end_offset;
+       unsigned int dc_bpc = 0;
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               return false;
+
+       if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
+               return false;
+
+       /*
+        * Because HDMI identifier is in Vendor Specific Block,
+        * search it from all data blocks of CEA extension.
+        */
+       for_each_cea_db(edid_ext, i, start_offset, end_offset) {
+               if (cea_db_is_hdmi_vsdb(&edid_ext[i])) {
+                       /* HDMI supports at least 8 bpc */
+                       info->bpc = 8;
+
+                       hdmi = &edid_ext[i];
+                       if (cea_db_payload_len(hdmi) < 6)
+                               return false;
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
+                               dc_bpc = 10;
+                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
+                               DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
+                                                 connector->name);
+                       }
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
+                               dc_bpc = 12;
+                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
+                               DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
+                                                 connector->name);
+                       }
+
+                       if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
+                               dc_bpc = 16;
+                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
+                               DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
+                                                 connector->name);
+                       }
+
+                       if (dc_bpc > 0) {
+                               DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
+                                                 connector->name, dc_bpc);
+                               info->bpc = dc_bpc;
+
+                               /*
+                                * Deep color support mandates RGB444 support for all video
+                                * modes and forbids YCRCB422 support for all video modes per
+                                * HDMI 1.3 spec.
+                                */
+                               info->color_formats = DRM_COLOR_FORMAT_RGB444;
+
+                               /* YCRCB444 is optional according to spec. */
+                               if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
+                                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+                                       DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
+                                                         connector->name);
+                               }
+
+                               /*
+                                * Spec says that if any deep color mode is supported at all,
+                                * then deep color 36 bit must be supported.
+                                */
+                               if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
+                                       DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
+                                                         connector->name);
+                               }
+
+                               return true;
+                       }
+                       else {
+                               DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
+                                                 connector->name);
+                       }
+               }
+       }
+
+       return false;
+}
+
 /**
  * drm_add_display_info - pull display info out if present
  * @edid: EDID data
  * @info: display info (attached to connector)
+ * @connector: connector whose edid is used to build display info
  *
  * Grab any available display info and stuff it into the drm_display_info
  * structure that's part of the connector.  Useful for tracking bpp and
  * color spaces.
  */
 static void drm_add_display_info(struct edid *edid,
-                                struct drm_display_info *info)
+                                 struct drm_display_info *info,
+                                 struct drm_connector *connector)
 {
        u8 *edid_ext;
 
@@ -3463,6 +3572,9 @@ static void drm_add_display_info(struct edid *edid,
                        info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
        }
 
+       /* HDMI deep color modes supported? Assign to info, if so */
+       drm_assign_hdmi_deep_color_info(edid, info, connector);
+
        /* Only defined for 1.4 with digital displays */
        if (edid->revision < 4)
                return;
@@ -3492,6 +3604,9 @@ static void drm_add_display_info(struct edid *edid,
                break;
        }
 
+       DRM_DEBUG("%s: Assigning EDID-1.4 digital sink color depth as %d bpc.\n",
+                         connector->name, info->bpc);
+
        info->color_formats |= DRM_COLOR_FORMAT_RGB444;
        if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
                info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
@@ -3518,7 +3633,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        }
        if (!drm_edid_is_valid(edid)) {
                dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
-                        drm_get_connector_name(connector));
+                        connector->name);
                return 0;
        }
 
@@ -3550,11 +3665,14 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
                edid_fixup_preferred(connector, quirks);
 
-       drm_add_display_info(edid, &connector->display_info);
+       drm_add_display_info(edid, &connector->display_info, connector);
 
        if (quirks & EDID_QUIRK_FORCE_8BPC)
                connector->display_info.bpc = 8;
 
+       if (quirks & EDID_QUIRK_FORCE_12BPC)
+               connector->display_info.bpc = 12;
+
        return num_modes;
 }
 EXPORT_SYMBOL(drm_add_edid_modes);