/*
*/
/*
* Copyright (c) 2006 Luc Verhaegen (quirks list)
* Copyright (c) 2007-2008, 2013, Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
* FB layer.
* Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sub license,
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drm.h"
#include "drmP.h"
#include "drm_edid.h"
#include "drm_sun_i2c.h"
#include "drm_crtc.h"
/*
* EDID blocks out in the wild have a variety of bugs, try to collect
* them here (note that userspace may work around broken monitors first,
* but fixes should make their way here so that the kernel "just works"
* on as many displays as possible).
*/
/* First detailed mode wrong, use largest 60Hz mode */
/* Reported 135MHz pixel clock is too high, needs adjustment */
/* Prefer the largest mode at 75 Hz */
/* Detail timing is in cm not mm */
/* Detailed timing descriptors have bogus size values, so just take the
* maximum size and use that.
*/
/* Monitor forgot to set the first detailed is preferred bit. */
/* use +hsync +vsync for detailed mode */
/* Force reduced-blanking timings for detailed modes */
struct detailed_mode_closure {
bool preferred;
int modes;
};
#define LEVEL_DMT 0
static struct edid_quirk {
int product_id;
} edid_quirk_list[] = {
/* Acer AL1706 */
/* Acer F51 */
/* Unknown Acer */
/* Belinea 10 15 55 */
/* Envision Peripherals, Inc. EN-7100e */
/* Envision EN2028 */
/* Funai Electronics PM36B */
/* LG Philips LCD LP154W01-A5 */
{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
/* Philips 107p5 CRT */
/* Proview AY765C */
/* Samsung SyncMaster 205BW. Note: irony */
/* Samsung SyncMaster 22[5-6]BW */
/* ViewSonic VA2026w */
};
/*
* Autogenerated from the DMT spec.
* This table is copied from xfree86/modes/xf86EdidModes.c.
*/
/* 640x350@85Hz */
736, 832, 0, 350, 382, 385, 445, 0,
/* 640x400@85Hz */
736, 832, 0, 400, 401, 404, 445, 0,
/* 720x400@85Hz */
828, 936, 0, 400, 401, 404, 446, 0,
/* 640x480@60Hz */
752, 800, 0, 480, 489, 492, 525, 0,
/* 640x480@72Hz */
704, 832, 0, 480, 489, 492, 520, 0,
/* 640x480@75Hz */
720, 840, 0, 480, 481, 484, 500, 0,
/* 640x480@85Hz */
752, 832, 0, 480, 481, 484, 509, 0,
/* 800x600@56Hz */
896, 1024, 0, 600, 601, 603, 625, 0,
/* 800x600@60Hz */
968, 1056, 0, 600, 601, 605, 628, 0,
/* 800x600@72Hz */
976, 1040, 0, 600, 637, 643, 666, 0,
/* 800x600@75Hz */
896, 1056, 0, 600, 601, 604, 625, 0,
/* 800x600@85Hz */
896, 1048, 0, 600, 601, 604, 631, 0,
/* 800x600@120Hz RB */
880, 960, 0, 600, 603, 607, 636, 0,
/* 848x480@60Hz */
976, 1088, 0, 480, 486, 494, 517, 0,
/* 1024x768@43Hz, interlace */
1208, 1264, 0, 768, 768, 772, 817, 0,
/* 1024x768@60Hz */
1184, 1344, 0, 768, 771, 777, 806, 0,
/* 1024x768@70Hz */
1184, 1328, 0, 768, 771, 777, 806, 0,
/* 1024x768@75Hz */
1136, 1312, 0, 768, 769, 772, 800, 0,
/* 1024x768@85Hz */
1168, 1376, 0, 768, 769, 772, 808, 0,
/* 1024x768@120Hz RB */
1104, 1184, 0, 768, 771, 775, 813, 0,
/* 1152x864@75Hz */
1344, 1600, 0, 864, 865, 868, 900, 0,
/* 1280x768@60Hz RB */
1360, 1440, 0, 768, 771, 778, 790, 0,
/* 1280x768@60Hz */
1472, 1664, 0, 768, 771, 778, 798, 0,
/* 1280x768@75Hz */
1488, 1696, 0, 768, 771, 778, 805, 0,
/* 1280x768@85Hz */
1496, 1712, 0, 768, 771, 778, 809, 0,
/* 1280x768@120Hz RB */
1360, 1440, 0, 768, 771, 778, 813, 0,
/* 1280x800@60Hz RB */
1360, 1440, 0, 800, 803, 809, 823, 0,
/* 1280x800@60Hz */
1480, 1680, 0, 800, 803, 809, 831, 0,
/* 1280x800@75Hz */
1488, 1696, 0, 800, 803, 809, 838, 0,
/* 1280x800@85Hz */
1496, 1712, 0, 800, 803, 809, 843, 0,
/* 1280x800@120Hz RB */
1360, 1440, 0, 800, 803, 809, 847, 0,
/* 1280x960@60Hz */
1488, 1800, 0, 960, 961, 964, 1000, 0,
/* 1280x960@85Hz */
1504, 1728, 0, 960, 961, 964, 1011, 0,
/* 1280x960@120Hz RB */
1360, 1440, 0, 960, 963, 967, 1017, 0,
/* 1280x1024@60Hz */
1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
/* 1280x1024@75Hz */
1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
/* 1280x1024@85Hz */
1504, 1728, 0, 1024, 1025, 1028, 1072, 0,
/* 1280x1024@120Hz RB */
1360, 1440, 0, 1024, 1027, 1034, 1084, 0,
/* 1360x768@60Hz */
1536, 1792, 0, 768, 771, 777, 795, 0,
/* 1360x768@120Hz RB */
1440, 1520, 0, 768, 771, 776, 813, 0,
/* 1400x1050@60Hz RB */
1480, 1560, 0, 1050, 1053, 1057, 1080, 0,
/* 1400x1050@60Hz */
1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
/* 1400x1050@75Hz */
1648, 1896, 0, 1050, 1053, 1057, 1099, 0,
/* 1400x1050@85Hz */
1656, 1912, 0, 1050, 1053, 1057, 1105, 0,
/* 1400x1050@120Hz RB */
1480, 1560, 0, 1050, 1053, 1057, 1112, 0,
/* 1440x900@60Hz RB */
1520, 1600, 0, 900, 903, 909, 926, 0,
/* 1440x900@60Hz */
1672, 1904, 0, 900, 903, 909, 934, 0,
/* 1440x900@75Hz */
1688, 1936, 0, 900, 903, 909, 942, 0,
/* 1440x900@85Hz */
1696, 1952, 0, 900, 903, 909, 948, 0,
/* 1440x900@120Hz RB */
1520, 1600, 0, 900, 903, 909, 953, 0,
/* 1600x1200@60Hz */
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
/* 1600x1200@65Hz */
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
/* 1600x1200@70Hz */
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
/* 1600x1200@75Hz */
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
/* 1600x1200@85Hz */
1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
/* 1600x1200@120Hz RB */
1680, 1760, 0, 1200, 1203, 1207, 1271, 0,
/* 1680x1050@60Hz RB */
1760, 1840, 0, 1050, 1053, 1059, 1080, 0,
/* 1680x1050@60Hz */
1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
/* 1680x1050@75Hz */
1976, 2272, 0, 1050, 1053, 1059, 1099, 0,
/* 1680x1050@85Hz */
1984, 2288, 0, 1050, 1053, 1059, 1105, 0,
/* 1680x1050@120Hz RB */
1760, 1840, 0, 1050, 1053, 1059, 1112, 0,
/* 1792x1344@60Hz */
2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
/* 1792x1344@75Hz */
2104, 2456, 0, 1344, 1345, 1348, 1417, 0,
/* 1792x1344@120Hz RB */
1872, 1952, 0, 1344, 1347, 1351, 1423, 0,
/* 1856x1392@60Hz */
2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
/* 1856x1392@75Hz */
2208, 2560, 0, 1392, 1395, 1399, 1500, 0,
/* 1856x1392@120Hz RB */
1936, 2016, 0, 1392, 1395, 1399, 1474, 0,
/* 1920x1200@60Hz RB */
2000, 2080, 0, 1200, 1203, 1209, 1235, 0,
/* 1920x1200@60Hz */
2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
/* 1920x1200@75Hz */
2264, 2608, 0, 1200, 1203, 1209, 1255, 0,
/* 1920x1200@85Hz */
2272, 2624, 0, 1200, 1203, 1209, 1262, 0,
/* 1920x1200@120Hz RB */
2000, 2080, 0, 1200, 1203, 1209, 1271, 0,
/* 1920x1440@60Hz */
2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
/* 1920x1440@75Hz */
2288, 2640, 0, 1440, 1441, 1444, 1500, 0,
/* 1920x1440@120Hz RB */
2000, 2080, 0, 1440, 1443, 1447, 1525, 0,
/* 2560x1600@60Hz RB */
2640, 2720, 0, 1600, 1603, 1609, 1646, 0,
/* 2560x1600@60Hz */
3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
/* 2560x1600@75HZ */
3048, 3536, 0, 1600, 1603, 1609, 1672, 0,
/* 2560x1600@85HZ */
3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
/* 2560x1600@120Hz RB */
2640, 2720, 0, 1600, 1603, 1609, 1694, 0,
};
968, 1056, 0, 600, 601, 605, 628, 0,
896, 1024, 0, 600, 601, 603, 625, 0,
720, 840, 0, 480, 481, 484, 500, 0,
704, 832, 0, 480, 489, 491, 520, 0,
768, 864, 0, 480, 483, 486, 525, 0,
752, 800, 0, 480, 490, 492, 525, 0,
846, 900, 0, 400, 421, 423, 449, 0,
846, 900, 0, 400, 412, 414, 449, 0,
1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
1136, 1312, 0, 768, 769, 772, 800, 0,
1184, 1328, 0, 768, 771, 777, 806, 0,
1184, 1344, 0, 768, 771, 777, 806, 0,
1208, 1264, 0, 768, 768, 776, 817, 0,
928, 1152, 0, 624, 625, 628, 667, 0,
896, 1056, 0, 600, 601, 604, 625, 0,
976, 1040, 0, 600, 637, 643, 666, 0,
1344, 1600, 0, 864, 865, 868, 900, 0,
};
struct minimode {
short w;
short h;
short r;
short rb;
};
/* byte 6 */
{ 640, 350, 85, 0 },
{ 640, 400, 85, 0 },
{ 720, 400, 85, 0 },
{ 640, 480, 85, 0 },
{ 848, 480, 60, 0 },
{ 800, 600, 85, 0 },
{ 1024, 768, 85, 0 },
{ 1152, 864, 75, 0 },
/* byte 7 */
{ 1280, 768, 60, 1 },
{ 1280, 768, 60, 0 },
{ 1280, 768, 75, 0 },
{ 1280, 768, 85, 0 },
{ 1280, 960, 60, 0 },
{ 1280, 960, 85, 0 },
{ 1280, 1024, 60, 0 },
{ 1280, 1024, 85, 0 },
/* byte 8 */
{ 1360, 768, 60, 0 },
{ 1440, 900, 60, 1 },
{ 1440, 900, 60, 0 },
{ 1440, 900, 75, 0 },
{ 1440, 900, 85, 0 },
{ 1400, 1050, 60, 1 },
{ 1400, 1050, 60, 0 },
{ 1400, 1050, 75, 0 },
/* byte 9 */
{ 1400, 1050, 85, 0 },
{ 1680, 1050, 60, 1 },
{ 1680, 1050, 60, 0 },
{ 1680, 1050, 75, 0 },
{ 1680, 1050, 85, 0 },
{ 1600, 1200, 60, 0 },
{ 1600, 1200, 65, 0 },
{ 1600, 1200, 70, 0 },
/* byte 10 */
{ 1600, 1200, 75, 0 },
{ 1600, 1200, 85, 0 },
{ 1792, 1344, 60, 0 },
{ 1792, 1344, 85, 0 },
{ 1856, 1392, 60, 0 },
{ 1856, 1392, 75, 0 },
{ 1920, 1200, 60, 1 },
{ 1920, 1200, 60, 0 },
/* byte 11 */
{ 1920, 1200, 75, 0 },
{ 1920, 1200, 85, 0 },
{ 1920, 1440, 60, 0 },
{ 1920, 1440, 75, 0 },
};
{ 1024, 576, 60, 0 },
{ 1366, 768, 60, 0 },
{ 1600, 900, 60, 0 },
{ 1680, 945, 60, 0 },
{ 1920, 1080, 60, 0 },
{ 2048, 1152, 60, 0 },
{ 2048, 1536, 60, 0 },
};
/*
* Probably taken from CEA-861 spec.
* This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c.
*/
/* 1 - 640x480@60Hz */
752, 800, 0, 480, 490, 492, 525, 0,
/* 2 - 720x480@60Hz */
798, 858, 0, 480, 489, 495, 525, 0,
/* 3 - 720x480@60Hz */
798, 858, 0, 480, 489, 495, 525, 0,
/* 4 - 1280x720@60Hz */
1430, 1650, 0, 720, 725, 730, 750, 0,
/* 5 - 1920x1080i@60Hz */
2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
/* 6 - 1440x480i@60Hz */
1602, 1716, 0, 480, 488, 494, 525, 0,
/* 7 - 1440x480i@60Hz */
1602, 1716, 0, 480, 488, 494, 525, 0,
/* 8 - 1440x240@60Hz */
1602, 1716, 0, 240, 244, 247, 262, 0,
/* 9 - 1440x240@60Hz */
1602, 1716, 0, 240, 244, 247, 262, 0,
/* 10 - 2880x480i@60Hz */
3204, 3432, 0, 480, 488, 494, 525, 0,
/* 11 - 2880x480i@60Hz */
3204, 3432, 0, 480, 488, 494, 525, 0,
/* 12 - 2880x240@60Hz */
3204, 3432, 0, 240, 244, 247, 262, 0,
/* 13 - 2880x240@60Hz */
3204, 3432, 0, 240, 244, 247, 262, 0,
/* 14 - 1440x480@60Hz */
1596, 1716, 0, 480, 489, 495, 525, 0,
/* 15 - 1440x480@60Hz */
1596, 1716, 0, 480, 489, 495, 525, 0,
/* 16 - 1920x1080@60Hz */
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
/* 17 - 720x576@50Hz */
796, 864, 0, 576, 581, 586, 625, 0,
/* 18 - 720x576@50Hz */
796, 864, 0, 576, 581, 586, 625, 0,
/* 19 - 1280x720@50Hz */
1760, 1980, 0, 720, 725, 730, 750, 0,
/* 20 - 1920x1080i@50Hz */
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
/* 21 - 1440x576i@50Hz */
1590, 1728, 0, 576, 580, 586, 625, 0,
/* 22 - 1440x576i@50Hz */
1590, 1728, 0, 576, 580, 586, 625, 0,
/* 23 - 1440x288@50Hz */
1590, 1728, 0, 288, 290, 293, 312, 0,
/* 24 - 1440x288@50Hz */
1590, 1728, 0, 288, 290, 293, 312, 0,
/* 25 - 2880x576i@50Hz */
3180, 3456, 0, 576, 580, 586, 625, 0,
/* 26 - 2880x576i@50Hz */
3180, 3456, 0, 576, 580, 586, 625, 0,
/* 27 - 2880x288@50Hz */
3180, 3456, 0, 288, 290, 293, 312, 0,
/* 28 - 2880x288@50Hz */
3180, 3456, 0, 288, 290, 293, 312, 0,
/* 29 - 1440x576@50Hz */
1592, 1728, 0, 576, 581, 586, 625, 0,
/* 30 - 1440x576@50Hz */
1592, 1728, 0, 576, 581, 586, 625, 0,
/* 31 - 1920x1080@50Hz */
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
/* 32 - 1920x1080@24Hz */
2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
/* 33 - 1920x1080@25Hz */
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
/* 34 - 1920x1080@30Hz */
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
/* 35 - 2880x480@60Hz */
3192, 3432, 0, 480, 489, 495, 525, 0,
/* 36 - 2880x480@60Hz */
3192, 3432, 0, 480, 489, 495, 525, 0,
/* 37 - 2880x576@50Hz */
3184, 3456, 0, 576, 581, 586, 625, 0,
/* 38 - 2880x576@50Hz */
3184, 3456, 0, 576, 581, 586, 625, 0,
/* 39 - 1920x1080i@50Hz */
2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
/* 40 - 1920x1080i@100Hz */
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
/* 41 - 1280x720@100Hz */
1760, 1980, 0, 720, 725, 730, 750, 0,
/* 42 - 720x576@100Hz */
796, 864, 0, 576, 581, 586, 625, 0,
/* 43 - 720x576@100Hz */
796, 864, 0, 576, 581, 586, 625, 0,
/* 44 - 1440x576i@100Hz */
1590, 1728, 0, 576, 580, 586, 625, 0,
/* 45 - 1440x576i@100Hz */
1590, 1728, 0, 576, 580, 586, 625, 0,
/* 46 - 1920x1080i@120Hz */
2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
/* 47 - 1280x720@120Hz */
1430, 1650, 0, 720, 725, 730, 750, 0,
/* 48 - 720x480@120Hz */
798, 858, 0, 480, 489, 495, 525, 0,
/* 49 - 720x480@120Hz */
798, 858, 0, 480, 489, 495, 525, 0,
/* 50 - 1440x480i@120Hz */
1602, 1716, 0, 480, 488, 494, 525, 0,
/* 51 - 1440x480i@120Hz */
1602, 1716, 0, 480, 488, 494, 525, 0,
/* 52 - 720x576@200Hz */
796, 864, 0, 576, 581, 586, 625, 0,
/* 53 - 720x576@200Hz */
796, 864, 0, 576, 581, 586, 625, 0,
/* 54 - 1440x576i@200Hz */
1590, 1728, 0, 576, 580, 586, 625, 0,
/* 55 - 1440x576i@200Hz */
1590, 1728, 0, 576, 580, 586, 625, 0,
/* 56 - 720x480@240Hz */
798, 858, 0, 480, 489, 495, 525, 0,
/* 57 - 720x480@240Hz */
798, 858, 0, 480, 489, 495, 525, 0,
/* 58 - 1440x480i@240 */
1602, 1716, 0, 480, 488, 494, 525, 0,
/* 59 - 1440x480i@240 */
1602, 1716, 0, 480, 488, 494, 525, 0,
/* 60 - 1280x720@24Hz */
3080, 3300, 0, 720, 725, 730, 750, 0,
/* 61 - 1280x720@25Hz */
3740, 3960, 0, 720, 725, 730, 750, 0,
/* 62 - 1280x720@30Hz */
3080, 3300, 0, 720, 725, 730, 750, 0,
/* 63 - 1920x1080@120Hz */
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
/* 64 - 1920x1080@100Hz */
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
};
/*** DDC fetch and block validation ***/
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.
*/
{
int i, score = 0;
for (i = 0; i < sizeof(edid_header); i++)
if (raw_edid[i] == edid_header[i])
score++;
return score;
}
/*
* Sanity check the EDID block (base or extension). Return 0 if the block
* doesn't check out, or 1 if it's valid.
*/
{
int i;
if (!raw_edid) {
WARN_ON(1);
return false;
}
edid_fixup = 6;
if (block == 0) {
if (score == 8)
DRM_DEBUG("edid header is perfect");
else if (score >= edid_fixup) {
DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
} else {
goto bad;
}
}
for (i = 0; i < EDID_LENGTH; i++)
if (csum) {
if (print_bad_edid) {
}
/* allow CEA to slide through, switches mangle this */
if (raw_edid[0] != 0x02)
goto bad;
}
/* per-block-type checks */
switch (raw_edid[0]) {
case 0: /* base */
goto bad;
}
DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
break;
default:
break;
}
return true;
bad:
if (print_bad_edid) {
DRM_DEBUG_KMS("Raw EDID:\n");
// print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH);
}
return false;
}
/**
* drm_edid_is_valid - sanity check EDID data
* @edid: EDID data
*
* Sanity-check an entire EDID record (including extensions)
*/
{
int i;
if (!edid)
return false;
for (i = 0; i <= edid->extensions; i++)
return false;
return true;
}
/**
* Get EDID information via I2C.
*
* \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 function.
*/
static int
{
/* 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
* of the individual block a few times seems to overcome this.
*/
do {
{
.addr = DDC_SEGMENT_ADDR,
.flags = 0,
.len = 1,
}, {
.flags = 0,
.len = 1,
}, {
}
};
/*
* Avoid sending the segment addr to not upset non-compliant ddc
* monitors.
*/
DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
break;
}
}
{
int i;
for (i = 0; i < length / 4; i++)
if (*(raw_edid + i) != 0)
return false;
return true;
}
static struct edid *
{
int i, j = 0;
/* try to allock max memory at first time */
return NULL;
/* base block fetch */
for (i = 0; i < 4; i++) {
goto out;
break;
goto carp;
}
}
if (i == 4)
goto carp;
/* if there's no extensions, we're done */
if (block[0x7e] == 0)
for (i = 0; i < 4; i++) {
j, EDID_LENGTH))
goto out;
break;
}
}
if (i == 4)
DRM_ERROR("%s: Ignoring invalid EDID block %d.\n",
}
}
carp:
if (print_bad_edid) {
DRM_DEBUG_KMS("%s: EDID block %d invalid.\n",
}
out:
return NULL;
}
/**
* Probe DDC presence.
*
* \param adapter : i2c device adaptor
* \return 1 on success
*/
bool
{
unsigned char out;
}
/**
* drm_get_edid - get EDID data, if available
* @connector: connector we're probing
* @adapter: i2c adapter to use for DDC
*
* 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.
*/
struct i2c_adapter *adapter)
{
if (drm_probe_ddc(adapter))
return edid;
}
/*** EDID parsing ***/
/**
* edid_vendor - match a string against EDID's obfuscated vendor field
* @edid: EDID to match
* @vendor: vendor string
*
* Returns true if @vendor is in @edid, false otherwise
*/
{
}
/**
* edid_get_quirks - return quirk flags for a given EDID
* @edid: EDID to process
*
* This tells subsequent routines what fixes they need to apply.
*/
{
int i;
for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
quirk = &edid_quirk_list[i];
}
return 0;
}
/**
* edid_fixup_preferred - set preferred modes based on quirk list
* @connector: has mode list to fix up
* @quirks: quirks list
*
* Walk the mode list for @connector, clearing the preferred status
* on existing modes and setting it anew for the right mode ala @quirks.
*/
{
int target_refresh = 0;
return;
if (quirks & EDID_QUIRK_PREFER_LARGE_60)
target_refresh = 60;
if (quirks & EDID_QUIRK_PREFER_LARGE_75)
target_refresh = 75;
struct drm_display_mode, head);
if (cur_mode == preferred_mode)
continue;
/* Largest mode is preferred */
/* At a given size, try to get closest to target refresh */
}
}
}
static bool
{
}
/*
* drm_mode_find_dmt - Create a copy of a mode if present in DMT
* @dev: Device to duplicate against
* @hsize: Mode width
* @vsize: Mode height
* @fresh: Mode refresh rate
* @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.
*/
bool rb)
{
int i;
for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
continue;
continue;
continue;
continue;
}
return NULL;
}
static void
{
int i, n = 0;
n = (127 - d) / 18;
for (i = 0; i < n; i++)
}
static void
{
return; /* unknown version */
for (i = 0; i < n; i++)
}
static void
{
int i;
return;
for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
switch (*ext) {
case CEA_EXT:
break;
case VTB_EXT:
break;
default:
break;
}
}
}
static void
{
if (r[3] == EDID_DETAIL_MONITOR_RANGE)
if (r[15] & 0x10)
*(bool *)data = true;
}
/* EDID 1.4 defines this explicitly. For EDID 1.3, we guess, badly. */
static bool
{
bool ret = false;
return ret;
}
}
static void
{
}
/* Secondary GTF curve kicks in above some break frequency */
static int
{
return r ? (r[12] * 2) : 0;
}
static int
{
return r ? r[13] : 0;
}
static int
{
return r ? (r[15] << 8) + r[14] : 0;
}
static int
{
return r ? r[16] : 0;
}
static int
{
return r ? r[17] : 0;
}
/**
* @edid: EDID block to scan
*/
{
return LEVEL_CVT;
if (drm_gtf2_hbreak(edid))
return LEVEL_GTF2;
return LEVEL_GTF;
}
return LEVEL_DMT;
}
/*
* 0 is reserved. The spec says 0x01 fill for unused timings. Some old
* monitors fill with ascii space (0x20) instead.
*/
static int
{
return (a == 0x00 && b == 0x00) ||
(a == 0x01 && b == 0x01) ||
(a == 0x20 && b == 0x20);
}
/**
* drm_mode_std - convert standard mode info (width, height, refresh) into mode
* @t: standard timing params
* @timing_level: standard timing level
*
* Take the standard timing params (in this case width, aspect, and refresh)
*/
static struct drm_display_mode *
struct std_timing *t, int revision)
{
int vrefresh_rate;
return NULL;
/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
/* vrefresh_rate = vfreq + 60 */
/* the vdisplay is calculated based on the aspect ratio */
if (aspect_ratio == 0) {
if (revision < 3)
else
} else if (aspect_ratio == 1)
else if (aspect_ratio == 2)
else
/* HDTV hack, part 1 */
if (vrefresh_rate == 60 &&
hsize = 1366;
vsize = 768;
}
/*
* If this connector already has a mode for this size and refresh
* rate (because it came from detailed or CVT info), use that
* instead. This way we don't have to guess at interlace or
* reduced blanking.
*/
drm_mode_vrefresh(m) == vrefresh_rate)
return NULL;
/* HDTV hack, part 2 */
false);
return mode;
}
/* check whether it can be found in default mode table */
if (drm_monitor_supports_rb(edid)) {
true);
if (mode)
return mode;
}
if (mode)
return mode;
/* okay, generate it */
switch (timing_level) {
case LEVEL_DMT:
break;
case LEVEL_GTF:
break;
case LEVEL_GTF2:
/*
* This is potentially wrong if there's ever a monitor with
* more than one ranges section, each claiming a different
* secondary GTF curve. Please don't do that.
*/
if (!mode)
return NULL;
vrefresh_rate, 0, 0,
drm_gtf2_2j(edid));
}
break;
case LEVEL_CVT:
false);
break;
}
return mode;
}
/*
* EDID is delightfully ambiguous about how interlaced modes are to be
* encoded. Our internal representation is of frame height, but some
* HDTV detailed timings are encoded as field height.
*
* The format list here is from CEA, in frame size. Technically we
* should be checking refresh rate too. Whatever.
*/
static void
struct detailed_pixel_timing *pt)
{
int i;
static const struct {
int w, h;
} cea_interlaced[] = {
{ 1920, 1080 },
{ 720, 480 },
{ 1440, 480 },
{ 2880, 480 },
{ 720, 576 },
{ 1440, 576 },
{ 2880, 576 },
};
return;
for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
}
}
}
/**
* drm_mode_detailed - create a new mode from an EDID detailed timing section
* @dev: DRM device (needed to create new mode)
* @edid: EDID block
* @timing: EDID detailed timing info
* @quirks: quirks to apply
*
* An EDID detailed timing block contains enough info for us to create and
* return a new struct drm_display_mode.
*/
struct detailed_timing *timing,
{
unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo;
unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4;
unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);
/* ignore tiny modes */
return NULL;
DRM_ERROR("stereo mode not supported\n");
return NULL;
}
DRM_ERROR("integrated sync not supported\n");
}
if (!hsync_pulse_width || !vsync_pulse_width) {
DRM_DEBUG_KMS("Incorrect Detailed timing. "
return NULL;
}
if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
if (!mode)
return NULL;
goto set_size;
}
if (!mode)
return NULL;
if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
}
if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
}
}
return mode;
}
static bool
{
hmin = t[7];
hmax = t[8];
}
static bool
{
vmin = t[5];
vmax = t[6];
}
static u32
{
/* unspecified */
if (t[9] == 0 || t[9] == 255)
return 0;
/* 1.4 with CVT support gives us real precision, yay */
return (t[9] * 10000) - ((t[12] >> 2) * 250);
/* 1.3 is pathetic, so fuzz up a bit */
return t[9] * 10000 + 5001;
}
static bool
struct detailed_timing *timing)
{
return false;
return false;
return false;
/* 1.4 max horizontal check */
return false;
return false;
return true;
}
const struct drm_display_mode *mode)
{
struct drm_display_mode *m;
bool ok = false;
return false; /* duplicated */
ok = true;
}
return ok;
}
static int
struct detailed_timing *timing)
{
int i, modes = 0;
for (i = 0; i < ARRAY_SIZE(drm_dmt_modes); i++) {
if (newmode) {
modes++;
}
}
}
return modes;
}
/* fix up 1366x768 mode from 1368x768;
*/
{
mode->hsync_start--;
}
}
static int
struct detailed_timing *timing)
{
int i, modes = 0;
for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
const struct minimode *m = &extra_modes[i];
if (!newmode)
return modes;
continue;
}
modes++;
}
return modes;
}
static int
struct detailed_timing *timing)
{
int i, modes = 0;
for (i = 0; i < ARRAY_SIZE(extra_modes); i++) {
const struct minimode *m = &extra_modes[i];
if (!newmode)
return modes;
continue;
}
modes++;
}
return modes;
}
static void
{
return;
timing);
return; /* GTF not defined yet */
case 0x02: /* secondary gtf, XXX could do more */
case 0x00: /* default gtf */
timing);
break;
case 0x04: /* cvt, only in 1.4+ */
break;
timing);
break;
case 0x01: /* just the ranges, no formula */
default:
break;
}
}
static int
{
};
&closure);
}
static int
{
int i, j, m, modes = 0;
for (i = 0; i < 6; i++) {
for (j = 7; j > 0; j--) {
m = (i * 8) + (7 - j);
if (m >= ARRAY_SIZE(est3_modes))
break;
if (est[i] & (1 << j)) {
est3_modes[m].w,
est3_modes[m].h,
est3_modes[m].r,
est3_modes[m].rb);
if (mode) {
modes++;
}
}
}
}
return modes;
}
static void
{
}
/**
* add_established_modes - get est. modes from EDID and add them
* @edid: EDID block to scan
*
* Each EDID block contains a bitmap of the supported "established modes" list
* (defined above). Tease them out and add them to the global modes list.
*/
static int
{
int i, modes = 0;
};
for (i = 0; i <= EDID_EST_TIMINGS; i++) {
if (est_bits & (1<<i)) {
if (newmode) {
modes++;
}
}
}
}
static void
{
int i;
for (i = 0; i < 6; i++) {
if (newmode) {
}
}
}
}
/**
* add_standard_modes - get std. modes from EDID and add them
* @edid: EDID block to scan
*
* Standard modes can be calculated using the appropriate standard (DMT,
* GTF or CVT. Grab them from @edid and add them to the list.
*/
static int
{
int i, modes = 0;
};
for (i = 0; i < EDID_STD_TIMINGS; i++) {
&edid->standard_timings[i],
if (newmode) {
modes++;
}
}
&closure);
/* XXX should also look for standard codes in VTB blocks */
}
struct detailed_timing *timing)
{
int i, j, modes = 0;
for (i = 0; i < 4; i++) {
continue;
case 0x00:
break;
case 0x04:
break;
case 0x08:
break;
case 0x0c:
break;
}
for (j = 1; j < 5; j++) {
rates[j], j == 0,
false, false);
if (newmode) {
modes++;
}
}
}
}
return modes;
}
static void
{
}
static int
{
};
/* XXX should also look for CVT codes in VTB blocks */
}
static void
{
if (timing->pixel_clock) {
if (!newmode)
return;
}
}
/*
* add_detailed_modes - Add modes from detailed timings
* @connector: attached connector
* @edid: EDID block to scan
* @quirks: quirks to apply
*/
static int
{
edid,
1,
0
};
}
/**
* Search EDID for CEA extension block.
*/
{
int i;
/* No EDID or EDID extensions */
return NULL;
/* Find CEA extension */
for (i = 0; i < edid->extensions; i++) {
break;
}
if (i == edid->extensions)
return NULL;
return edid_ext;
}
/*
* Calculate the alternate clock for the CEA mode
* (60Hz vs. 59.94Hz etc.)
*/
static unsigned int
{
return clock;
/*
* edid_cea_modes contains the 59.94Hz
* variant for 240 and 480 line modes,
* and the 60Hz variant otherwise.
*/
else
return clock;
}
/**
* 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
* mode.
*/
{
return 0;
/* Check both 60Hz and 59.94Hz */
return mode + 1;
}
return 0;
}
static int
{
int modes = 0;
/* Don't add CEA modes if the CEA extension block is missing */
if (!drm_find_cea_extension(edid))
return 0;
/*
* Go through all probed modes and create a new mode
* with the alternate clock for certain CEA modes.
*/
continue;
continue;
continue;
if (!newmode)
continue;
/*
* The current mode could be either variant. Make
* sure to pick the "other" clock for the new mode.
*/
else
}
modes++;
}
return modes;
}
static int
{
int modes = 0;
if (newmode) {
modes++;
}
}
}
return modes;
}
static int
{
return db[0] & 0x1f;
}
static int
{
return db[0] >> 5;
}
static int
{
return cea[1];
}
static int
{
/* Data block offset in CEA extension block */
*start = 4;
if (*end == 0)
*end = 127;
return -ERANGE;
return 0;
}
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
static int
{
int modes = 0;
return 0;
}
}
return modes;
}
static void
{
if (len >= 6) {
}
if (len >= 7)
if (len >= 8) {
}
if (len >= 9)
if (len >= 10)
if (len >= 11)
if (len >= 12)
DRM_LOG_KMS("HDMI: DVI dual %d, "
"max TMDS clock %d, "
"latency present %d %d, "
"video latency %d %d, "
"audio latency %d %d\n",
(int) connector->latency_present[0],
connector->video_latency[0],
connector->audio_latency[0],
}
static void
{
}
{
int hdmi_id;
return false;
return false;
return hdmi_id == HDMI_IDENTIFIER;
}
/**
* drm_edid_to_eld - build ELD from EDID
* @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
*/
{
int sad_count = 0;
int mnl;
int dbl;
if (!cea) {
DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
return;
}
break;
}
start = 0;
end = 0;
}
switch (cea_db_tag(db)) {
case AUDIO_BLOCK:
/* Audio Data Block, contains SADs */
if (dbl >= 1)
break;
case SPEAKER_BLOCK:
/* Speaker Allocation Data Block */
if (dbl >= 1)
break;
case VENDOR_BLOCK:
/* HDMI Vendor-Specific Data Block */
if (cea_db_is_hdmi_vsdb(db))
break;
default:
break;
}
}
}
}
/**
* drm_edid_to_sad - extracts SADs from EDID
* @edid: EDID to parse
* @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.
*/
{
int count = 0;
if (!cea) {
DRM_DEBUG_KMS("SAD: no CEA Extension found\n");
return -ENOENT;
}
DRM_DEBUG_KMS("SAD: wrong CEA revision\n");
return -ENOTSUP;
}
DRM_DEBUG_KMS("SAD: invalid data block offsets\n");
return -EPROTO;
}
int j;
if (!*sads)
return -ENOMEM;
for (j = 0; j < count; j++) {
}
break;
}
}
return count;
}
/**
* @mode: the display mode
*/
struct drm_display_mode *mode)
{
int a, v;
if (!connector->latency_present[0])
return 0;
i = 0;
a = connector->audio_latency[i];
v = connector->video_latency[i];
/*
*/
if (a == 255 || v == 255)
return 0;
/*
* Convert raw EDID values to millisecond.
* Treat unknown latency as 0ms.
*/
if (a)
if (v)
return max(v - a, 0);
}
/**
* @encoder: the encoder just changed display mode
* @mode: the adjusted display mode
*
*/
/* LINTED E_FUNC_ARG_UNUSED */
struct drm_display_mode *mode)
{
return connector;
return NULL;
}
/**
* 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.
*/
{
int i;
if (!edid_ext)
return false;
return false;
/*
* Because HDMI identifier is in Vendor Specific Block,
* search it from all data blocks of CEA extension.
*/
if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
return true;
}
return false;
}
/**
* drm_detect_monitor_audio - check monitor audio capability
*
* Monitor should have CEA extension block.
* If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
* audio' only. If there is any audio extension block and supported
* audio format, assume at least 'basic audio' support, even if 'basic
* audio' is not defined in EDID.
*
*/
{
int i, j;
bool has_audio = false;
if (!edid_ext)
goto end;
if (has_audio) {
DRM_DEBUG_KMS("Monitor has basic audio support\n");
goto end;
}
goto end;
has_audio = true;
DRM_DEBUG_KMS("CEA audio format %d\n",
goto end;
}
}
end:
return has_audio;
}
/**
* drm_rgb_quant_range_selectable - is RGB quantization range selectable?
*
* 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.
*/
{
if (!edid_ext)
return false;
return false;
}
}
return false;
}
/**
* drm_add_display_info - pull display info out if present
* @edid: EDID data
* @info: display info (attached to connector)
*
* 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.
*/
struct drm_display_info *info)
{
/* driver figures it out in this case */
info->color_formats = 0;
return;
return;
/* Get data from CEA blocks if present */
if (edid_ext) {
/* The existence of a CEA block should imply RGB support */
}
/* Only defined for 1.4 with digital displays */
return;
case DRM_EDID_DIGITAL_DEPTH_6:
break;
case DRM_EDID_DIGITAL_DEPTH_8:
break;
break;
break;
break;
break;
default:
break;
}
}
/**
* drm_add_edid_modes - add modes from EDID data, if available
* @connector: connector we're probing
* @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.
*/
{
int num_modes = 0;
return 0;
}
if (!drm_edid_is_valid(edid)) {
DRM_ERROR("%s: EDID invalid.\n",
return 0;
}
/*
* EDID spec says modes should be preferred in this order:
* - preferred detailed mode
* - other detailed modes from base block
* - detailed modes from extension blocks
* - CVT 3-byte code modes
* - standard timing codes
* - established timing codes
* - modes inferred from GTF or CVT range information
*
* We get this pretty much right.
*
* XXX order for additional mode types in extension blocks?
*/
return num_modes;
}
/**
* drm_add_modes_noedid - add modes for the connectors without EDID
* @connector: connector we're probing
* @hdisplay: the horizontal display limit
* @vdisplay: the vertical display limit
*
* Add the specified modes to the connector's mode list. Only when the
*
* Return number of modes added or 0 if we couldn't find any.
*/
{
if (hdisplay < 0)
hdisplay = 0;
if (vdisplay < 0)
vdisplay = 0;
for (i = 0; i < count; i++) {
/*
* Only when two are valid, they will be used to check
* whether the mode should be added to the mode list of
* the connector.
*/
continue;
}
continue;
if (mode) {
num_modes++;
}
}
return num_modes;
}