diff options
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
| -rw-r--r-- | drivers/gpu/drm/drm_edid.c | 834 | 
1 files changed, 669 insertions, 165 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1688ff50051..dfa9769b26b 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -68,6 +68,10 @@  #define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)  /* Force reduced-blanking timings for detailed modes */  #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; @@ -123,11 +127,17 @@ 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 },  	/* Medion MD 30217 PG */  	{ "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, + +	/* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ +	{ "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC },  };  /* @@ -458,6 +468,15 @@ static const struct drm_display_mode drm_dmt_modes[] = {  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },  }; +/* + * These more or less come from the DMT spec.  The 720x400 modes are + * inferred from historical 80x25 practice.  The 640x480@67 and 832x624@75 + * modes are old-school Mac modes.  The EDID spec says the 1152x864@75 mode + * should be 1152x870, again for the Mac, but instead we use the x864 DMT + * mode. + * + * The DMT modes have been fact-checked; the rest are mild guesses. + */  static const struct drm_display_mode edid_est_modes[] = {  	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,  		   968, 1056, 0, 600, 601, 605, 628, 0, @@ -560,7 +579,7 @@ static const struct minimode est3_modes[] = {  	{ 1600, 1200, 75, 0 },  	{ 1600, 1200, 85, 0 },  	{ 1792, 1344, 60, 0 }, -	{ 1792, 1344, 85, 0 }, +	{ 1792, 1344, 75, 0 },  	{ 1856, 1392, 60, 0 },  	{ 1856, 1392, 75, 0 },  	{ 1920, 1200, 60, 1 }, @@ -591,347 +610,347 @@ static const struct drm_display_mode edid_cea_modes[] = {  	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,  		   752, 800, 0, 480, 490, 492, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 2 - 720x480@60Hz */  	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,  		   798, 858, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 3 - 720x480@60Hz */  	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,  		   798, 858, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 4 - 1280x720@60Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,  		   1430, 1650, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 5 - 1920x1080i@60Hz */  	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,  		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 6 - 1440x480i@60Hz */  	{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,  		   1602, 1716, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 7 - 1440x480i@60Hz */  	{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,  		   1602, 1716, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 8 - 1440x240@60Hz */  	{ DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,  		   1602, 1716, 0, 240, 244, 247, 262, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 9 - 1440x240@60Hz */  	{ DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,  		   1602, 1716, 0, 240, 244, 247, 262, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 10 - 2880x480i@60Hz */  	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,  		   3204, 3432, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 11 - 2880x480i@60Hz */  	{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,  		   3204, 3432, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 12 - 2880x240@60Hz */  	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,  		   3204, 3432, 0, 240, 244, 247, 262, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 13 - 2880x240@60Hz */  	{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,  		   3204, 3432, 0, 240, 244, 247, 262, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 14 - 1440x480@60Hz */  	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,  		   1596, 1716, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 15 - 1440x480@60Hz */  	{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,  		   1596, 1716, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 16 - 1920x1080@60Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,  		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 17 - 720x576@50Hz */  	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,  		   796, 864, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 18 - 720x576@50Hz */  	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,  		   796, 864, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 19 - 1280x720@50Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,  		   1760, 1980, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 20 - 1920x1080i@50Hz */  	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,  		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 21 - 1440x576i@50Hz */  	{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,  		   1590, 1728, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 22 - 1440x576i@50Hz */  	{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,  		   1590, 1728, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 23 - 1440x288@50Hz */  	{ DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,  		   1590, 1728, 0, 288, 290, 293, 312, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 24 - 1440x288@50Hz */  	{ DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,  		   1590, 1728, 0, 288, 290, 293, 312, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 25 - 2880x576i@50Hz */  	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,  		   3180, 3456, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 26 - 2880x576i@50Hz */  	{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,  		   3180, 3456, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 27 - 2880x288@50Hz */  	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,  		   3180, 3456, 0, 288, 290, 293, 312, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 28 - 2880x288@50Hz */  	{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,  		   3180, 3456, 0, 288, 290, 293, 312, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 29 - 1440x576@50Hz */  	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,  		   1592, 1728, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 30 - 1440x576@50Hz */  	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,  		   1592, 1728, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 31 - 1920x1080@50Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,  		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 32 - 1920x1080@24Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,  		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 24, }, +	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 33 - 1920x1080@25Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,  		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 25, }, +	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 34 - 1920x1080@30Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,  		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 30, }, +	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 35 - 2880x480@60Hz */  	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,  		   3192, 3432, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 36 - 2880x480@60Hz */  	{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,  		   3192, 3432, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 60, }, +	  .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 37 - 2880x576@50Hz */  	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,  		   3184, 3456, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 38 - 2880x576@50Hz */  	{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,  		   3184, 3456, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 39 - 1920x1080i@50Hz */  	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,  		   2120, 2304, 0, 1080, 1126, 1136, 1250, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 50, }, +	  .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 40 - 1920x1080i@100Hz */  	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,  		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 100, }, +	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 41 - 1280x720@100Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,  		   1760, 1980, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 100, }, +	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 42 - 720x576@100Hz */  	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,  		   796, 864, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 100, }, +	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 43 - 720x576@100Hz */  	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,  		   796, 864, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 100, }, +	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 44 - 1440x576i@100Hz */  	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,  		   1590, 1728, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 100, }, +	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 45 - 1440x576i@100Hz */  	{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,  		   1590, 1728, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 100, }, +	  .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 46 - 1920x1080i@120Hz */  	{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,  		   2052, 2200, 0, 1080, 1084, 1094, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |  			DRM_MODE_FLAG_INTERLACE), -	  .vrefresh = 120, }, +	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 47 - 1280x720@120Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,  		   1430, 1650, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 120, }, +	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 48 - 720x480@120Hz */  	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,  		   798, 858, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 120, }, +	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 49 - 720x480@120Hz */  	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,  		   798, 858, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 120, }, +	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 50 - 1440x480i@120Hz */  	{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,  		   1602, 1716, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 120, }, +	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 51 - 1440x480i@120Hz */  	{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,  		   1602, 1716, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 120, }, +	  .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 52 - 720x576@200Hz */  	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,  		   796, 864, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 200, }, +	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 53 - 720x576@200Hz */  	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,  		   796, 864, 0, 576, 581, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 200, }, +	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 54 - 1440x576i@200Hz */  	{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,  		   1590, 1728, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 200, }, +	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 55 - 1440x576i@200Hz */  	{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,  		   1590, 1728, 0, 576, 580, 586, 625, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 200, }, +	  .vrefresh = 200, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 56 - 720x480@240Hz */  	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,  		   798, 858, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 240, }, +	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 57 - 720x480@240Hz */  	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,  		   798, 858, 0, 480, 489, 495, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC), -	  .vrefresh = 240, }, +	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 58 - 1440x480i@240 */  	{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,  		   1602, 1716, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 240, }, +	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },  	/* 59 - 1440x480i@240 */  	{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,  		   1602, 1716, 0, 480, 488, 494, 525, 0,  		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |  			DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK), -	  .vrefresh = 240, }, +	  .vrefresh = 240, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 60 - 1280x720@24Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,  		   3080, 3300, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 24, }, +	  .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 61 - 1280x720@25Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,  		   3740, 3960, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 25, }, +	  .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 62 - 1280x720@30Hz */  	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,  		   3080, 3300, 0, 720, 725, 730, 750, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	  .vrefresh = 30, }, +	  .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 63 - 1920x1080@120Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,  		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	 .vrefresh = 120, }, +	 .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  	/* 64 - 1920x1080@100Hz */  	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,  		   2492, 2640, 0, 1080, 1084, 1094, 1125, 0,  		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), -	 .vrefresh = 100, }, +	 .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },  };  /* @@ -970,9 +989,13 @@ static const u8 edid_header[] = {  	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00  }; - /* - * Sanity check the header of the base EDID block.  Return 8 if the header - * is perfect, down to 0 if it's totally wrong. +/** + * drm_edid_header_is_valid - sanity check the header of the base EDID block + * @raw_edid: pointer to raw base EDID block + * + * Sanity check the header of the base EDID block. + * + * Return: 8 if the header is perfect, down to 0 if it's totally wrong.   */  int drm_edid_header_is_valid(const u8 *raw_edid)  { @@ -991,9 +1014,16 @@ module_param_named(edid_fixup, edid_fixup, int, 0400);  MODULE_PARM_DESC(edid_fixup,  		 "Minimum number of valid EDID header bytes (0-8, default 6)"); -/* - * Sanity check the EDID block (base or extension).  Return 0 if the block - * doesn't check out, or 1 if it's valid. +/** + * drm_edid_block_valid - Sanity check the EDID block (base or extension) + * @raw_edid: pointer to raw EDID block + * @block: type of block to validate (0 for base, extension otherwise) + * @print_bad_edid: if true, dump bad EDID blocks to the console + * + * Validate a base or extension EDID block and optionally dump bad blocks to + * the console. + * + * Return: True if the block is valid, false otherwise.   */  bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)  { @@ -1063,6 +1093,8 @@ EXPORT_SYMBOL(drm_edid_block_valid);   * @edid: EDID data   *   * Sanity-check an entire EDID record (including extensions) + * + * Return: True if the EDID data is valid, false otherwise.   */  bool drm_edid_is_valid(struct edid *edid)  { @@ -1082,14 +1114,15 @@ EXPORT_SYMBOL(drm_edid_is_valid);  #define DDC_SEGMENT_ADDR 0x30  /** - * Get EDID information via I2C. + * drm_do_probe_ddc_edid() - get EDID information via I2C + * @adapter: I2C device adaptor + * @buf: EDID data buffer to be filled + * @block: 128 byte EDID block to start fetching from + * @len: EDID data buffer length to fetch   * - * \param adapter : i2c device adaptor - * \param buf     : EDID data buffer to be filled - * \param len     : EDID data buffer length - * \return 0 on success or -1 on failure. + * Try to fetch EDID information by calling I2C driver functions.   * - * Try to fetch EDID information by calling i2c driver function. + * Return: 0 on success or -1 on failure.   */  static int  drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, @@ -1100,7 +1133,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,  	unsigned char xfers = segment ? 3 : 2;  	int ret, retries = 5; -	/* The core i2c driver will automatically retry the transfer if the +	/* +	 * The core I2C driver will automatically retry the transfer if the  	 * adapter reports EAGAIN. However, we find that bit-banging transfers  	 * are susceptible to errors under a heavily loaded machine and  	 * generate spurious NAKs and timeouts. Retrying the transfer @@ -1126,10 +1160,10 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,  			}  		}; -	/* -	 * Avoid sending the segment addr to not upset non-compliant ddc -	 * monitors. -	 */ +		/* +		 * Avoid sending the segment addr to not upset non-compliant +		 * DDC monitors. +		 */  		ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);  		if (ret == -ENXIO) { @@ -1198,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++;  		} @@ -1218,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++; @@ -1228,10 +1262,10 @@ out:  }  /** - * Probe DDC presence. + * drm_probe_ddc() - probe DDC presence + * @adapter: I2C adapter to probe   * - * \param adapter : i2c device adaptor - * \return 1 on success + * Return: True on success, false on failure.   */  bool  drm_probe_ddc(struct i2c_adapter *adapter) @@ -1245,12 +1279,12 @@ EXPORT_SYMBOL(drm_probe_ddc);  /**   * drm_get_edid - get EDID data, if available   * @connector: connector we're probing - * @adapter: i2c adapter to use for DDC + * @adapter: I2C adapter to use for DDC   * - * Poke the given i2c channel to grab EDID data if possible.  If found, + * Poke the given I2C channel to grab EDID data if possible.  If found,   * attach it to the connector.   * - * Return edid data or NULL if we couldn't find any. + * Return: Pointer to valid EDID or NULL if we couldn't find any.   */  struct edid *drm_get_edid(struct drm_connector *connector,  			  struct i2c_adapter *adapter) @@ -1264,6 +1298,18 @@ struct edid *drm_get_edid(struct drm_connector *connector,  }  EXPORT_SYMBOL(drm_get_edid); +/** + * drm_edid_duplicate - duplicate an EDID and the extensions + * @edid: EDID to duplicate + * + * Return: Pointer to duplicated EDID or NULL on allocation failure. + */ +struct edid *drm_edid_duplicate(const struct edid *edid) +{ +	return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); +} +EXPORT_SYMBOL(drm_edid_duplicate); +  /*** EDID parsing ***/  /** @@ -1308,7 +1354,7 @@ static u32 edid_get_quirks(struct edid *edid)  }  #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) -#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) +#define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))  /**   * edid_fixup_preferred - set preferred modes based on quirk list @@ -1323,6 +1369,7 @@ static void edid_fixup_preferred(struct drm_connector *connector,  {  	struct drm_display_mode *t, *cur_mode, *preferred_mode;  	int target_refresh = 0; +	int cur_vrefresh, preferred_vrefresh;  	if (list_empty(&connector->probed_modes))  		return; @@ -1345,10 +1392,14 @@ static void edid_fixup_preferred(struct drm_connector *connector,  		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))  			preferred_mode = cur_mode; +		cur_vrefresh = cur_mode->vrefresh ? +			cur_mode->vrefresh : drm_mode_vrefresh(cur_mode); +		preferred_vrefresh = preferred_mode->vrefresh ? +			preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode);  		/* At a given size, try to get closest to target refresh */  		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && -		    MODE_REFRESH_DIFF(cur_mode, target_refresh) < -		    MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { +		    MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) < +		    MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) {  			preferred_mode = cur_mode;  		}  	} @@ -1374,7 +1425,8 @@ mode_is_rb(const struct drm_display_mode *mode)   * @rb: Mode reduced-blanking-ness   *   * Walk the DMT mode list looking for a match for the given parameters. - * Return a newly allocated copy of the mode, or NULL if not found. + * + * Return: A newly allocated copy of the mode, or NULL if not found.   */  struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,  					   int hsize, int vsize, int fresh, @@ -1555,15 +1607,16 @@ bad_std_timing(u8 a, u8 b)  /**   * drm_mode_std - convert standard mode info (width, height, refresh) into mode + * @connector: connector of for the EDID block + * @edid: EDID block to scan   * @t: standard timing params - * @timing_level: standard timing level   *   * Take the standard timing params (in this case width, aspect, and refresh)   * and convert them into a real mode using CVT/GTF/DMT.   */  static struct drm_display_mode *  drm_mode_std(struct drm_connector *connector, struct edid *edid, -	     struct std_timing *t, int revision) +	     struct std_timing *t)  {  	struct drm_device *dev = connector->dev;  	struct drm_display_mode *m, *mode = NULL; @@ -1584,7 +1637,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,  	vrefresh_rate = vfreq + 60;  	/* the vdisplay is calculated based on the aspect ratio */  	if (aspect_ratio == 0) { -		if (revision < 3) +		if (edid->revision < 3)  			vsize = hsize;  		else  			vsize = (hsize * 10) / 16; @@ -2068,7 +2121,7 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)  	u8 *est = ((u8 *)timing) + 5;  	for (i = 0; i < 6; i++) { -		for (j = 7; j > 0; j--) { +		for (j = 7; j >= 0; j--) {  			m = (i * 8) + (7 - j);  			if (m >= ARRAY_SIZE(est3_modes))  				break; @@ -2101,6 +2154,7 @@ do_established_modes(struct detailed_timing *timing, void *c)  /**   * add_established_modes - get est. modes from EDID and add them + * @connector: connector to add mode(s) to   * @edid: EDID block to scan   *   * Each EDID block contains a bitmap of the supported "established modes" list @@ -2151,8 +2205,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)  			struct drm_display_mode *newmode;  			std = &data->data.timings[i]; -			newmode = drm_mode_std(connector, edid, std, -					       edid->revision); +			newmode = drm_mode_std(connector, edid, std);  			if (newmode) {  				drm_mode_probed_add(connector, newmode);  				closure->modes++; @@ -2163,6 +2216,7 @@ do_standard_modes(struct detailed_timing *timing, void *c)  /**   * add_standard_modes - get std. modes from EDID and add them + * @connector: connector to add mode(s) to   * @edid: EDID block to scan   *   * Standard modes can be calculated using the appropriate standard (DMT, @@ -2180,8 +2234,7 @@ add_standard_modes(struct drm_connector *connector, struct edid *edid)  		struct drm_display_mode *newmode;  		newmode = drm_mode_std(connector, edid, -				       &edid->standard_timings[i], -				       edid->revision); +				       &edid->standard_timings[i]);  		if (newmode) {  			drm_mode_probed_add(connector, newmode);  			modes++; @@ -2384,7 +2437,7 @@ cea_mode_alternate_clock(const struct drm_display_mode *cea_mode)   * drm_match_cea_mode - look for a CEA mode matching given mode   * @to_match: display mode   * - * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 + * Return: The CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861   * mode.   */  u8 drm_match_cea_mode(const struct drm_display_mode *to_match) @@ -2404,13 +2457,29 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match)  		if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||  		     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && -		    drm_mode_equal_no_clocks(to_match, cea_mode)) +		    drm_mode_equal_no_clocks_no_stereo(to_match, cea_mode))  			return mode + 1;  	}  	return 0;  }  EXPORT_SYMBOL(drm_match_cea_mode); +/** + * drm_get_cea_aspect_ratio - get the picture aspect ratio corresponding to + * the input VIC from the CEA mode list + * @video_code: ID given to each of the CEA modes + * + * Returns picture aspect ratio + */ +enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code) +{ +	/* return picture aspect ratio for video_code - 1 to access the +	 * right array element +	*/ +	return edid_cea_modes[video_code-1].picture_aspect_ratio; +} +EXPORT_SYMBOL(drm_get_cea_aspect_ratio); +  /*   * Calculate the alternate clock for HDMI modes (those from the HDMI vendor   * specific block). @@ -2453,7 +2522,7 @@ static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match)  		if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) ||  		     KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && -		    drm_mode_equal_no_clocks(to_match, hdmi_mode)) +		    drm_mode_equal_no_clocks_no_stereo(to_match, hdmi_mode))  			return mode + 1;  	}  	return 0; @@ -2507,6 +2576,9 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)  		if (!newmode)  			continue; +		/* Carry over the stereo flags */ +		newmode->flags |= mode->flags & DRM_MODE_FLAG_3D_MASK; +  		/*  		 * The current mode could be either variant. Make  		 * sure to pick the "other" clock for the new mode. @@ -2528,25 +2600,167 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid)  	return modes;  } +static struct drm_display_mode * +drm_display_mode_from_vic_index(struct drm_connector *connector, +				const u8 *video_db, u8 video_len, +				u8 video_index) +{ +	struct drm_device *dev = connector->dev; +	struct drm_display_mode *newmode; +	u8 cea_mode; + +	if (video_db == NULL || video_index >= video_len) +		return NULL; + +	/* CEA modes are numbered 1..127 */ +	cea_mode = (video_db[video_index] & 127) - 1; +	if (cea_mode >= ARRAY_SIZE(edid_cea_modes)) +		return NULL; + +	newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]); +	if (!newmode) +		return NULL; + +	newmode->vrefresh = 0; + +	return newmode; +} +  static int  do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)  { +	int i, modes = 0; + +	for (i = 0; i < len; i++) { +		struct drm_display_mode *mode; +		mode = drm_display_mode_from_vic_index(connector, db, len, i); +		if (mode) { +			drm_mode_probed_add(connector, mode); +			modes++; +		} +	} + +	return modes; +} + +struct stereo_mandatory_mode { +	int width, height, vrefresh; +	unsigned int flags; +}; + +static const struct stereo_mandatory_mode stereo_mandatory_modes[] = { +	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, +	{ 1920, 1080, 24, DRM_MODE_FLAG_3D_FRAME_PACKING }, +	{ 1920, 1080, 50, +	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, +	{ 1920, 1080, 60, +	  DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF }, +	{ 1280, 720,  50, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, +	{ 1280, 720,  50, DRM_MODE_FLAG_3D_FRAME_PACKING }, +	{ 1280, 720,  60, DRM_MODE_FLAG_3D_TOP_AND_BOTTOM }, +	{ 1280, 720,  60, DRM_MODE_FLAG_3D_FRAME_PACKING } +}; + +static bool +stereo_match_mandatory(const struct drm_display_mode *mode, +		       const struct stereo_mandatory_mode *stereo_mode) +{ +	unsigned int interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; + +	return mode->hdisplay == stereo_mode->width && +	       mode->vdisplay == stereo_mode->height && +	       interlaced == (stereo_mode->flags & DRM_MODE_FLAG_INTERLACE) && +	       drm_mode_vrefresh(mode) == stereo_mode->vrefresh; +} + +static int add_hdmi_mandatory_stereo_modes(struct drm_connector *connector) +{  	struct drm_device *dev = connector->dev; -	const u8 *mode; -	u8 cea_mode; +	const struct drm_display_mode *mode; +	struct list_head stereo_modes; +	int modes = 0, i; + +	INIT_LIST_HEAD(&stereo_modes); + +	list_for_each_entry(mode, &connector->probed_modes, head) { +		for (i = 0; i < ARRAY_SIZE(stereo_mandatory_modes); i++) { +			const struct stereo_mandatory_mode *mandatory; +			struct drm_display_mode *new_mode; + +			if (!stereo_match_mandatory(mode, +						    &stereo_mandatory_modes[i])) +				continue; + +			mandatory = &stereo_mandatory_modes[i]; +			new_mode = drm_mode_duplicate(dev, mode); +			if (!new_mode) +				continue; + +			new_mode->flags |= mandatory->flags; +			list_add_tail(&new_mode->head, &stereo_modes); +			modes++; +		} +	} + +	list_splice_tail(&stereo_modes, &connector->probed_modes); + +	return modes; +} + +static int add_hdmi_mode(struct drm_connector *connector, u8 vic) +{ +	struct drm_device *dev = connector->dev; +	struct drm_display_mode *newmode; + +	vic--; /* VICs start at 1 */ +	if (vic >= ARRAY_SIZE(edid_4k_modes)) { +		DRM_ERROR("Unknown HDMI VIC: %d\n", vic); +		return 0; +	} + +	newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); +	if (!newmode) +		return 0; + +	drm_mode_probed_add(connector, newmode); + +	return 1; +} + +static int add_3d_struct_modes(struct drm_connector *connector, u16 structure, +			       const u8 *video_db, u8 video_len, u8 video_index) +{ +	struct drm_display_mode *newmode;  	int modes = 0; -	for (mode = db; mode < db + len; mode++) { -		cea_mode = (*mode & 127) - 1; /* CEA modes are numbered 1..127 */ -		if (cea_mode < ARRAY_SIZE(edid_cea_modes)) { -			struct drm_display_mode *newmode; -			newmode = drm_mode_duplicate(dev, -						     &edid_cea_modes[cea_mode]); -			if (newmode) { -				newmode->vrefresh = 0; -				drm_mode_probed_add(connector, newmode); -				modes++; -			} +	if (structure & (1 << 0)) { +		newmode = drm_display_mode_from_vic_index(connector, video_db, +							  video_len, +							  video_index); +		if (newmode) { +			newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING; +			drm_mode_probed_add(connector, newmode); +			modes++; +		} +	} +	if (structure & (1 << 6)) { +		newmode = drm_display_mode_from_vic_index(connector, video_db, +							  video_len, +							  video_index); +		if (newmode) { +			newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; +			drm_mode_probed_add(connector, newmode); +			modes++; +		} +	} +	if (structure & (1 << 8)) { +		newmode = drm_display_mode_from_vic_index(connector, video_db, +							  video_len, +							  video_index); +		if (newmode) { +			newmode->flags |= DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; +			drm_mode_probed_add(connector, newmode); +			modes++;  		}  	} @@ -2559,14 +2773,17 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len)   * @db: start of the CEA vendor specific block   * @len: length of the CEA block payload, ie. one can access up to db[len]   * - * Parses the HDMI VSDB looking for modes to add to @connector. + * Parses the HDMI VSDB looking for modes to add to @connector. This function + * also adds the stereo 3d modes when applicable.   */  static int -do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) +do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len, +		   const u8 *video_db, u8 video_len)  { -	struct drm_device *dev = connector->dev; -	int modes = 0, offset = 0, i; -	u8 vic_len; +	int modes = 0, offset = 0, i, multi_present = 0, multi_len; +	u8 vic_len, hdmi_3d_len = 0; +	u16 mask; +	u16 structure_all;  	if (len < 8)  		goto out; @@ -2585,30 +2802,108 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)  	/* the declared length is not long enough for the 2 first bytes  	 * of additional video format capabilities */ -	offset += 2; -	if (len < (8 + offset)) +	if (len < (8 + offset + 2))  		goto out; +	/* 3D_Present */ +	offset++; +	if (db[8 + offset] & (1 << 7)) { +		modes += add_hdmi_mandatory_stereo_modes(connector); + +		/* 3D_Multi_present */ +		multi_present = (db[8 + offset] & 0x60) >> 5; +	} + +	offset++;  	vic_len = db[8 + offset] >> 5; +	hdmi_3d_len = db[8 + offset] & 0x1f;  	for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { -		struct drm_display_mode *newmode;  		u8 vic;  		vic = db[9 + offset + i]; +		modes += add_hdmi_mode(connector, vic); +	} +	offset += 1 + vic_len; -		vic--; /* VICs start at 1 */ -		if (vic >= ARRAY_SIZE(edid_4k_modes)) { -			DRM_ERROR("Unknown HDMI VIC: %d\n", vic); -			continue; +	if (multi_present == 1) +		multi_len = 2; +	else if (multi_present == 2) +		multi_len = 4; +	else +		multi_len = 0; + +	if (len < (8 + offset + hdmi_3d_len - 1)) +		goto out; + +	if (hdmi_3d_len < multi_len) +		goto out; + +	if (multi_present == 1 || multi_present == 2) { +		/* 3D_Structure_ALL */ +		structure_all = (db[8 + offset] << 8) | db[9 + offset]; + +		/* check if 3D_MASK is present */ +		if (multi_present == 2) +			mask = (db[10 + offset] << 8) | db[11 + offset]; +		else +			mask = 0xffff; + +		for (i = 0; i < 16; i++) { +			if (mask & (1 << i)) +				modes += add_3d_struct_modes(connector, +						structure_all, +						video_db, +						video_len, i);  		} +	} -		newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); -		if (!newmode) -			continue; +	offset += multi_len; -		drm_mode_probed_add(connector, newmode); -		modes++; +	for (i = 0; i < (hdmi_3d_len - multi_len); i++) { +		int vic_index; +		struct drm_display_mode *newmode = NULL; +		unsigned int newflag = 0; +		bool detail_present; + +		detail_present = ((db[8 + offset + i] & 0x0f) > 7); + +		if (detail_present && (i + 1 == hdmi_3d_len - multi_len)) +			break; + +		/* 2D_VIC_order_X */ +		vic_index = db[8 + offset + i] >> 4; + +		/* 3D_Structure_X */ +		switch (db[8 + offset + i] & 0x0f) { +		case 0: +			newflag = DRM_MODE_FLAG_3D_FRAME_PACKING; +			break; +		case 6: +			newflag = DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; +			break; +		case 8: +			/* 3D_Detail_X */ +			if ((db[9 + offset + i] >> 4) == 1) +				newflag = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; +			break; +		} + +		if (newflag != 0) { +			newmode = drm_display_mode_from_vic_index(connector, +								  video_db, +								  video_len, +								  vic_index); + +			if (newmode) { +				newmode->flags |= newflag; +				drm_mode_probed_add(connector, newmode); +				modes++; +			} +		} + +		if (detail_present) +			i++;  	}  out: @@ -2668,8 +2963,8 @@ static int  add_cea_modes(struct drm_connector *connector, struct edid *edid)  {  	const u8 *cea = drm_find_cea_extension(edid); -	const u8 *db; -	u8 dbl; +	const u8 *db, *hdmi = NULL, *video = NULL; +	u8 dbl, hdmi_len, video_len = 0;  	int modes = 0;  	if (cea && cea_revision(cea) >= 3) { @@ -2682,13 +2977,26 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)  			db = &cea[i];  			dbl = cea_db_payload_len(db); -			if (cea_db_tag(db) == VIDEO_BLOCK) -				modes += do_cea_modes(connector, db + 1, dbl); -			else if (cea_db_is_hdmi_vsdb(db)) -				modes += do_hdmi_vsdb_modes(connector, db, dbl); +			if (cea_db_tag(db) == VIDEO_BLOCK) { +				video = db + 1; +				video_len = dbl; +				modes += do_cea_modes(connector, video, dbl); +			} +			else if (cea_db_is_hdmi_vsdb(db)) { +				hdmi = db; +				hdmi_len = dbl; +			}  		}  	} +	/* +	 * We parse the HDMI VSDB after having added the cea modes as we will +	 * be patching their flags when the sink supports stereo 3D. +	 */ +	if (hdmi) +		modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video, +					    video_len); +  	return modes;  } @@ -2743,11 +3051,9 @@ monitor_name(struct detailed_timing *t, void *data)   * @connector: connector corresponding to the HDMI/DP sink   * @edid: EDID to parse   * - * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. - * Some ELD fields are left to the graphics driver caller: - * - Conn_Type - * - HDCP - * - Port_ID + * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver. The + * Conn_Type, HDCP and Port_ID ELD fields are left for the graphics driver to + * fill in.   */  void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)  { @@ -2831,9 +3137,10 @@ EXPORT_SYMBOL(drm_edid_to_eld);   * @sads: pointer that will be set to the extracted SADs   *   * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from it. - * Note: returned pointer needs to be kfreed   * - * Return number of found SADs or negative number on error. + * Note: The returned pointer needs to be freed using kfree(). + * + * Return: The number of found SADs or negative number on error.   */  int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads)  { @@ -2890,9 +3197,11 @@ EXPORT_SYMBOL(drm_edid_to_sad);   * @sadb: pointer to the speaker block   *   * Looks for CEA EDID block and extracts the Speaker Allocation Data Block from it. - * Note: returned pointer needs to be kfreed   * - * Return number of found Speaker Allocation Blocks or negative number on error. + * Note: The returned pointer needs to be freed using kfree(). + * + * Return: The number of found Speaker Allocation Blocks or negative number on + * error.   */  int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)  { @@ -2924,8 +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); -				memcpy(*sadb, &db[1], dbl); +				*sadb = kmemdup(&db[1], dbl, GFP_KERNEL); +				if (!*sadb) +					return -ENOMEM;  				count = dbl;  				break;  			} @@ -2937,9 +3247,12 @@ int drm_edid_to_speaker_allocation(struct edid *edid, u8 **sadb)  EXPORT_SYMBOL(drm_edid_to_speaker_allocation);  /** - * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond + * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay   * @connector: connector associated with the HDMI/DP sink   * @mode: the display mode + * + * Return: The HDMI/DP sink's audio-video sync delay in milliseconds or 0 if + * the sink doesn't support audio or video.   */  int drm_av_sync_delay(struct drm_connector *connector,  		      struct drm_display_mode *mode) @@ -2981,6 +3294,9 @@ EXPORT_SYMBOL(drm_av_sync_delay);   *   * It's possible for one encoder to be associated with multiple HDMI/DP sinks.   * The policy is now hard coded to simply use the first HDMI/DP sink's ELD. + * + * Return: The connector associated with the first HDMI/DP sink that has ELD + * attached to it.   */  struct drm_connector *drm_select_eld(struct drm_encoder *encoder,  				     struct drm_display_mode *mode) @@ -2988,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; @@ -2997,11 +3315,12 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,  EXPORT_SYMBOL(drm_select_eld);  /** - * drm_detect_hdmi_monitor - detect whether monitor is hdmi. + * drm_detect_hdmi_monitor - detect whether monitor is HDMI   * @edid: monitor EDID information   *   * Parse the CEA extension according to CEA-861-B. - * Return true if HDMI, false if not or unknown. + * + * Return: True if the monitor is HDMI, false if not or unknown.   */  bool drm_detect_hdmi_monitor(struct edid *edid)  { @@ -3031,6 +3350,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);  /**   * drm_detect_monitor_audio - check monitor audio capability + * @edid: EDID block to scan   *   * Monitor should have CEA extension block.   * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic @@ -3038,6 +3358,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor);   * audio format, assume at least 'basic audio' support, even if 'basic   * audio' is not defined in EDID.   * + * Return: True if the monitor supports audio, false otherwise.   */  bool drm_detect_monitor_audio(struct edid *edid)  { @@ -3076,10 +3397,13 @@ EXPORT_SYMBOL(drm_detect_monitor_audio);  /**   * drm_rgb_quant_range_selectable - is RGB quantization range selectable? + * @edid: EDID block to scan   *   * Check whether the monitor reports the RGB quantization range selection   * as supported. The AVI infoframe can then be used to inform the monitor   * which quantization range (full or limited) is used. + * + * Return: True if the RGB quantization range is selectable, false otherwise.   */  bool drm_rgb_quant_range_selectable(struct edid *edid)  { @@ -3106,16 +3430,119 @@ 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; @@ -3145,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; @@ -3174,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; @@ -3184,11 +3617,11 @@ static void drm_add_display_info(struct edid *edid,  /**   * drm_add_edid_modes - add modes from EDID data, if available   * @connector: connector we're probing - * @edid: edid data + * @edid: EDID data   *   * Add the specified modes to the connector's mode list.   * - * Return number of modes added or 0 if we couldn't find any. + * Return: The number of modes added or 0 if we couldn't find any.   */  int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)  { @@ -3200,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;  	} @@ -3232,7 +3665,13 @@ 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;  } @@ -3247,7 +3686,7 @@ EXPORT_SYMBOL(drm_add_edid_modes);   * Add the specified modes to the connector's mode list. Only when the   * hdisplay/vdisplay is not beyond the given limit, it will be added.   * - * Return number of modes added or 0 if we couldn't find any. + * Return: The number of modes added or 0 if we couldn't find any.   */  int drm_add_modes_noedid(struct drm_connector *connector,  			int hdisplay, int vdisplay) @@ -3287,12 +3726,34 @@ int drm_add_modes_noedid(struct drm_connector *connector,  EXPORT_SYMBOL(drm_add_modes_noedid);  /** + * drm_set_preferred_mode - Sets the preferred mode of a connector + * @connector: connector whose mode list should be processed + * @hpref: horizontal resolution of preferred mode + * @vpref: vertical resolution of preferred mode + * + * Marks a mode as preferred if it matches the resolution specified by @hpref + * and @vpref. + */ +void drm_set_preferred_mode(struct drm_connector *connector, +			   int hpref, int vpref) +{ +	struct drm_display_mode *mode; + +	list_for_each_entry(mode, &connector->probed_modes, head) { +		if (mode->hdisplay == hpref && +		    mode->vdisplay == vpref) +			mode->type |= DRM_MODE_TYPE_PREFERRED; +	} +} +EXPORT_SYMBOL(drm_set_preferred_mode); + +/**   * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with   *                                              data from a DRM display mode   * @frame: HDMI AVI infoframe   * @mode: DRM display mode   * - * Returns 0 on success or a negative error code on failure. + * Return: 0 on success or a negative error code on failure.   */  int  drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, @@ -3313,12 +3774,46 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame,  	frame->video_code = drm_match_cea_mode(mode);  	frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; + +	/* Populate picture aspect ratio from CEA mode list */ +	if (frame->video_code > 0) +		frame->picture_aspect = drm_get_cea_aspect_ratio( +						frame->video_code); +  	frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; +	frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN;  	return 0;  }  EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); +static enum hdmi_3d_structure +s3d_structure_from_display_mode(const struct drm_display_mode *mode) +{ +	u32 layout = mode->flags & DRM_MODE_FLAG_3D_MASK; + +	switch (layout) { +	case DRM_MODE_FLAG_3D_FRAME_PACKING: +		return HDMI_3D_STRUCTURE_FRAME_PACKING; +	case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE: +		return HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE; +	case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE: +		return HDMI_3D_STRUCTURE_LINE_ALTERNATIVE; +	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL: +		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL; +	case DRM_MODE_FLAG_3D_L_DEPTH: +		return HDMI_3D_STRUCTURE_L_DEPTH; +	case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH: +		return HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH; +	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM: +		return HDMI_3D_STRUCTURE_TOP_AND_BOTTOM; +	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF: +		return HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF; +	default: +		return HDMI_3D_STRUCTURE_INVALID; +	} +} +  /**   * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with   * data from a DRM display mode @@ -3329,27 +3824,36 @@ EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode);   * 4k or stereoscopic 3D mode. So when giving any other mode as input this   * function will return -EINVAL, error that can be safely ignored.   * - * Returns 0 on success or a negative error code on failure. + * Return: 0 on success or a negative error code on failure.   */  int  drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_vendor_infoframe *frame,  					    const struct drm_display_mode *mode)  {  	int err; +	u32 s3d_flags;  	u8 vic;  	if (!frame || !mode)  		return -EINVAL;  	vic = drm_match_hdmi_mode(mode); -	if (!vic) +	s3d_flags = mode->flags & DRM_MODE_FLAG_3D_MASK; + +	if (!vic && !s3d_flags) +		return -EINVAL; + +	if (vic && s3d_flags)  		return -EINVAL;  	err = hdmi_vendor_infoframe_init(frame);  	if (err < 0)  		return err; -	frame->vic = vic; +	if (vic) +		frame->vic = vic; +	else +		frame->s3d_struct = s3d_structure_from_display_mode(mode);  	return 0;  }  | 
