EDID: CEA extension support

Reviewed-by: Adam Jackson <ajax@redhat.com>
This commit is contained in:
Ma Ling 2009-02-18 17:41:26 +08:00 committed by Adam Jackson
parent fab74d1081
commit fc2ec95664
7 changed files with 935 additions and 429 deletions

View File

@ -544,6 +544,36 @@ configureMonitorSection (int screennum)
return ptr;
}
/* Initialize Configure Monitor from Detailed Timing Block */
static void handle_detailed_input(struct detailed_monitor_section *det_mon,
void *data)
{
XF86ConfMonitorPtr ptr = (XF86ConfMonitorPtr) data;
switch (det_mon->type) {
case DS_NAME:
ptr->mon_modelname = realloc(ptr->mon_modelname,
strlen((char*)(det_mon->section.name)) +
1);
strcpy(ptr->mon_modelname,
(char*)(det_mon->section.name));
break;
case DS_RANGES:
ptr->mon_hsync[ptr->mon_n_hsync].lo =
det_mon->section.ranges.min_h;
ptr->mon_hsync[ptr->mon_n_hsync].hi =
det_mon->section.ranges.max_h;
ptr->mon_n_vrefresh = 1;
ptr->mon_vrefresh[ptr->mon_n_hsync].lo =
det_mon->section.ranges.min_v;
ptr->mon_vrefresh[ptr->mon_n_hsync].hi =
det_mon->section.ranges.max_v;
ptr->mon_n_hsync++;
default:
break;
}
}
static XF86ConfMonitorPtr
configureDDCMonitorSection (int screennum)
{
@ -590,30 +620,8 @@ configureDDCMonitorSection (int screennum)
}
#endif /* def CONFIGURE_DISPLAYSIZE */
for (i=0;i<4;i++) {
switch (ConfiguredMonitor->det_mon[i].type) {
case DS_NAME:
ptr->mon_modelname = realloc(ptr->mon_modelname,
strlen((char*)(ConfiguredMonitor->det_mon[i].section.name))
+ 1);
strcpy(ptr->mon_modelname,
(char*)(ConfiguredMonitor->det_mon[i].section.name));
break;
case DS_RANGES:
ptr->mon_hsync[ptr->mon_n_hsync].lo =
ConfiguredMonitor->det_mon[i].section.ranges.min_h;
ptr->mon_hsync[ptr->mon_n_hsync].hi =
ConfiguredMonitor->det_mon[i].section.ranges.max_h;
ptr->mon_n_vrefresh = 1;
ptr->mon_vrefresh[ptr->mon_n_hsync].lo =
ConfiguredMonitor->det_mon[i].section.ranges.min_v;
ptr->mon_vrefresh[ptr->mon_n_hsync].hi =
ConfiguredMonitor->det_mon[i].section.ranges.max_v;
ptr->mon_n_hsync++;
default:
break;
}
}
xf86ForEachDetailedBlock(ConfiguredMonitor, handle_detailed_input,
ptr);
if (ConfiguredMonitor->features.dpms) {
ptr->mon_option_lst = xf86addNewOption(ptr->mon_option_lst, xstrdup("DPMS"), NULL);

View File

@ -562,4 +562,101 @@ typedef struct {
extern _X_EXPORT xf86MonPtr ConfiguredMonitor;
#define EXT_TAG 0
#define EXT_REV 1
#define CEA_EXT 0x02
#define VTB_EXT 0x10
#define DI_EXT 0x40
#define LS_EXT 0x50
#define MI_EXT 0x60
#define CEA_EXT_MIN_DATA_OFFSET 4
#define CEA_EXT_MAX_DATA_OFFSET 127
#define CEA_EXT_DET_TIMING_NUM 6
#define IEEE_ID_HDMI 0x000C03
#define CEA_AUDIO_BLK 1
#define CEA_VIDEO_BLK 2
#define CEA_VENDOR_BLK 3
#define CEA_SPEAKER_ALLOC_BLK 4
#define CEA_VESA_DTC_BLK 5
#define VENDOR_SUPPORT_AI(x) ((x) >> 7)
#define VENDOR_SUPPORT_DC_48bit(x) ( ( (x) >> 6) & 0x01)
#define VENDOR_SUPPORT_DC_36bit(x) ( ( (x) >> 5) & 0x01)
#define VENDOR_SUPPORT_DC_30bit(x) ( ( (x) >> 4) & 0x01)
#define VENDOR_SUPPORT_DC_Y444(x) ( ( (x) >> 3) & 0x01)
#define VENDOR_LATENCY_PRESENT(x) ( (x) >> 7)
#define VENDOR_LATENCY_PRESENT_I(x) ( ( (x) >> 6) & 0x01)
#define HDMI_MAX_TMDS_UNIT (5000)
struct cea_video_block {
Uchar video_code;
};
struct cea_audio_block_descriptor {
Uchar audio_code[3];
};
struct cea_audio_block {
struct cea_audio_block_descriptor descriptor[10];
};
struct cea_vendor_block_hdmi {
Uchar portB:4;
Uchar portA:4;
Uchar portD:4;
Uchar portC:4;
Uchar support_flags;
Uchar max_tmds_clock;
Uchar latency_present;
Uchar video_latency;
Uchar audio_latency;
Uchar interlaced_video_latency;
Uchar interlaced_audio_latency;
};
struct cea_vendor_block {
unsigned char ieee_id[3];
union {
struct cea_vendor_block_hdmi hdmi;
/* any other vendor blocks we know about */
};
};
struct cea_speaker_block
{
Uchar FLR:1;
Uchar LFE:1;
Uchar FC:1;
Uchar RLR:1;
Uchar RC:1;
Uchar FLRC:1;
Uchar RLRC:1;
Uchar FLRW:1;
Uchar FLRH:1;
Uchar TC:1;
Uchar FCH:1;
Uchar Resv:5;
Uchar ResvByte;
};
struct cea_data_block {
Uchar len:5;
Uchar tag:3;
union{
struct cea_video_block video;
struct cea_audio_block audio;
struct cea_vendor_block vendor;
struct cea_speaker_block speaker;
}u;
};
struct cea_ext_body {
Uchar tag;
Uchar rev;
Uchar dt_offset;
Uchar flags;
struct cea_data_block data_collection;
};
#endif /* _EDID_H_ */

View File

@ -42,6 +42,8 @@ static void get_display_section(Uchar*, struct disp_features *,
static void get_established_timing_section(Uchar*, struct established_timings *);
static void get_std_timing_section(Uchar*, struct std_timings *,
struct edid_version *);
static void fetch_detailed_block(Uchar *c, struct edid_version *ver,
struct detailed_monitor_section *det_mon);
static void get_dt_md_section(Uchar *, struct edid_version *,
struct detailed_monitor_section *det_mon);
static void copy_string(Uchar *, Uchar *);
@ -52,12 +54,26 @@ static void get_whitepoint_section(Uchar *, struct whitePoints *);
static void get_detailed_timing_section(Uchar*, struct detailed_timings *);
static Bool validate_version(int scrnIndex, struct edid_version *);
static void
find_ranges_section(struct detailed_monitor_section *det, void *ranges)
{
if (det->type == DS_RANGES && det->section.ranges.max_clock)
*(struct monitor_ranges **)ranges = &det->section.ranges;
}
static void
find_max_detailed_clock(struct detailed_monitor_section *det, void *ret)
{
if (det->type == DT) {
*(int *)ret = max(*((int *)ret),
det->section.d_timings.clock);
}
}
static void
handle_edid_quirks(xf86MonPtr m)
{
int i, j;
struct detailed_timings *preferred_timing;
struct monitor_ranges *ranges;
struct monitor_ranges *ranges = NULL;
/*
* max_clock is only encoded in EDID in tens of MHz, so occasionally we
@ -65,28 +81,49 @@ handle_edid_quirks(xf86MonPtr m)
* similar. Strictly we should refuse to round up too far, but let's
* see how well this works.
*/
for (i = 0; i < 4; i++) {
if (m->det_mon[i].type == DS_RANGES) {
ranges = &m->det_mon[i].section.ranges;
for (j = 0; j < 4; j++) {
if (m->det_mon[j].type == DT) {
preferred_timing = &m->det_mon[j].section.d_timings;
if (!ranges->max_clock) continue; /* zero is legal */
if (ranges->max_clock * 1000000 < preferred_timing->clock) {
xf86Msg(X_WARNING,
"EDID preferred timing clock %.2fMHz exceeds "
"claimed max %dMHz, fixing\n",
preferred_timing->clock / 1.0e6,
ranges->max_clock);
ranges->max_clock =
(preferred_timing->clock+999999)/1000000;
return;
}
}
}
}
}
/* Try to find Monitor Range and max clock, then re-set range value*/
xf86ForEachDetailedBlock(m, find_ranges_section, &ranges);
if (ranges && ranges->max_clock) {
int clock = 0;
xf86ForEachDetailedBlock(m, find_max_detailed_clock, &clock);
if (clock && (ranges->max_clock * 1e6 < clock)) {
xf86Msg(X_WARNING, "EDID timing clock %.2f exceeds claimed max "
"%dMHz, fixing\n", clock / 1.0e6, ranges->max_clock);
ranges->max_clock = (clock+999999)/1e6;
}
}
}
struct det_hv_parameter {
int real_hsize;
int real_vsize;
float target_aspect;
};
static void handle_detailed_hvsize(struct detailed_monitor_section *det_mon,
void *data)
{
struct det_hv_parameter *p = (struct det_hv_parameter *)data;
float timing_aspect;
if (det_mon->type == DT) {
struct detailed_timings *timing;
timing = &det_mon->section.d_timings;
if (!timing->v_size)
return;
timing_aspect = (float)timing->h_size / timing->v_size;
if (fabs(1 - (timing_aspect / p->target_aspect)) < 0.05) {
p->real_hsize = max(p->real_hsize, timing->h_size);
p->real_vsize = max(p->real_vsize, timing->v_size);
}
}
}
static void encode_aspect_ratio(xf86MonPtr m)
{
/*
* some monitors encode the aspect ratio instead of the physical size.
* try to find the largest detailed timing that matches that aspect
@ -96,38 +133,26 @@ handle_edid_quirks(xf86MonPtr m)
(m->features.hsize == 16 && m->features.vsize == 10) ||
(m->features.hsize == 4 && m->features.vsize == 3) ||
(m->features.hsize == 5 && m->features.vsize == 4)) {
int real_hsize = 0, real_vsize = 0;
float target_aspect, timing_aspect;
target_aspect = (float)m->features.hsize / (float)m->features.vsize;
for (i = 0; i < 4; i++) {
if (m->det_mon[i].type == DT) {
struct detailed_timings *timing;
timing = &m->det_mon[i].section.d_timings;
if (!timing->v_size)
continue;
struct det_hv_parameter p;
p.real_hsize = 0;
p.real_vsize = 0;
p.target_aspect = (float)m->features.hsize /m->features.vsize;
timing_aspect = (float)timing->h_size / (float)timing->v_size;
if (fabs(1 - (timing_aspect / target_aspect)) < 0.05) {
real_hsize = max(real_hsize, timing->h_size);
real_vsize = max(real_vsize, timing->v_size);
}
}
}
xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p);
if (!real_hsize || !real_vsize) {
if (!p.real_hsize || !p.real_vsize) {
m->features.hsize = m->features.vsize = 0;
} else if ((m->features.hsize * 10 == real_hsize) &&
(m->features.vsize * 10 == real_vsize)) {
} else if ((m->features.hsize * 10 == p.real_hsize) &&
(m->features.vsize * 10 == p.real_vsize)) {
/* exact match is just unlikely, should do a better check though */
m->features.hsize = m->features.vsize = 0;
} else {
/* convert mm to cm */
m->features.hsize = (real_hsize + 5) / 10;
m->features.vsize = (real_vsize + 5) / 10;
m->features.hsize = (p.real_hsize + 5) / 10;
m->features.vsize = (p.real_vsize + 5) / 10;
}
xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n",
m->features.hsize, m->features.vsize);
}
@ -156,6 +181,7 @@ xf86InterpretEDID(int scrnIndex, Uchar *block)
m->no_sections = (int)*(char *)SECTION(NO_EDID,block);
handle_edid_quirks(m);
encode_aspect_ratio(m);
return (m);
@ -164,6 +190,141 @@ xf86InterpretEDID(int scrnIndex, Uchar *block)
return NULL;
}
static int get_cea_detail_timing(Uchar *blk, xf86MonPtr mon,
struct detailed_monitor_section *det_mon)
{
int dt_num;
int dt_offset = ((struct cea_ext_body *)blk)->dt_offset;
dt_num = 0;
if (dt_offset < CEA_EXT_MIN_DATA_OFFSET)
return dt_num;
for (; dt_offset < (CEA_EXT_MAX_DATA_OFFSET - DET_TIMING_INFO_LEN) &&
dt_num < CEA_EXT_DET_TIMING_NUM;
_NEXT_DT_MD_SECTION(dt_offset)) {
fetch_detailed_block(blk + dt_offset, &mon->ver, det_mon + dt_num);
dt_num = dt_num + 1 ;
}
return dt_num;
}
static void handle_cea_detail_block(Uchar *ext, xf86MonPtr mon,
handle_detailed_fn fn,
void *data)
{
int i;
struct detailed_monitor_section det_mon[CEA_EXT_DET_TIMING_NUM];
int det_mon_num;
det_mon_num = get_cea_detail_timing(ext, mon, det_mon);
for (i = 0; i < det_mon_num; i++)
fn(det_mon + i, data);
}
void xf86ForEachDetailedBlock(xf86MonPtr mon,
handle_detailed_fn fn,
void *data)
{
int i;
Uchar *ext;
if (mon == NULL)
return;
for (i = 0; i < DET_TIMINGS; i++)
fn(mon->det_mon + i, data);
for (i = 0; i < mon->no_sections; i++) {
ext = mon->rawData + EDID1_LEN * (i + 1);
switch (ext[EXT_TAG]){
case CEA_EXT:
handle_cea_detail_block(ext, mon, fn, data);
break;
case VTB_EXT:
case DI_EXT:
case LS_EXT:
case MI_EXT:
break;
}
}
}
static struct cea_data_block *
extract_cea_data_block(Uchar *ext, int data_type)
{
struct cea_ext_body *cea;
struct cea_data_block *data_collection;
struct cea_data_block *data_end;
cea = (struct cea_ext_body *)ext;
if (cea->dt_offset <= CEA_EXT_MIN_DATA_OFFSET)
return NULL;
data_collection = &cea->data_collection;
data_end = (struct cea_data_block *)(cea->dt_offset + ext);
for ( ;data_collection < data_end;) {
if (data_type == data_collection->tag) {
return data_collection;
}
data_collection = (void *)((unsigned char *)data_collection +
data_collection->len + 1);
}
return NULL;
}
static void handle_cea_video_block(Uchar *ext, handle_video_fn fn, void *data)
{
struct cea_video_block *video;
struct cea_video_block *video_end;
struct cea_data_block *data_collection;
data_collection = extract_cea_data_block(ext, CEA_VIDEO_BLK);
if (data_collection == NULL)
return;
video = &data_collection->u.video;
video_end = (struct cea_video_block *)
((Uchar *)video + data_collection->len);
for (; video < video_end; video = video + 1) {
fn(video, data);
}
}
void xf86ForEachVideoBlock(xf86MonPtr mon,
handle_video_fn fn,
void *data)
{
int i;
Uchar *ext;
if (mon == NULL)
return;
for (i = 0; i < mon->no_sections; i++) {
ext = mon->rawData + EDID1_LEN * (i + 1);
switch (ext[EXT_TAG]) {
case CEA_EXT:
handle_cea_video_block(ext, fn, data);
break;
case VTB_EXT:
case DI_EXT:
case LS_EXT:
case MI_EXT:
break;
}
}
}
xf86MonPtr
xf86InterpretEEDID(int scrnIndex, Uchar *block)
{
@ -288,66 +449,72 @@ get_std_timing_section(Uchar *c, struct std_timings *r,
static const unsigned char empty_block[18];
static void
get_dt_md_section(Uchar *c, struct edid_version *ver,
fetch_detailed_block(Uchar *c, struct edid_version *ver,
struct detailed_monitor_section *det_mon)
{
if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) {
switch (MONITOR_DESC_TYPE) {
case SERIAL_NUMBER:
det_mon->type = DS_SERIAL;
copy_string(c,det_mon->section.serial);
break;
case ASCII_STR:
det_mon->type = DS_ASCII_STR;
copy_string(c,det_mon->section.ascii_data);
break;
case MONITOR_RANGES:
det_mon->type = DS_RANGES;
get_monitor_ranges(c,&det_mon->section.ranges);
break;
case MONITOR_NAME:
det_mon->type = DS_NAME;
copy_string(c,det_mon->section.name);
break;
case ADD_COLOR_POINT:
det_mon->type = DS_WHITE_P;
get_whitepoint_section(c,det_mon->section.wp);
break;
case ADD_STD_TIMINGS:
det_mon->type = DS_STD_TIMINGS;
get_dst_timing_section(c,det_mon->section.std_t, ver);
break;
case COLOR_MANAGEMENT_DATA:
det_mon->type = DS_CMD;
break;
case CVT_3BYTE_DATA:
det_mon->type = DS_CVT;
get_cvt_timing_section(c, det_mon->section.cvt);
break;
case ADD_EST_TIMINGS:
det_mon->type = DS_EST_III;
memcpy(det_mon->section.est_iii, c + 6, 6);
break;
case ADD_DUMMY:
det_mon->type = DS_DUMMY;
break;
default:
det_mon->type = DS_UNKOWN;
break;
}
if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) {
det_mon->type = DS_VENDOR + c[3];
}
} else {
det_mon->type = DT;
get_detailed_timing_section(c, &det_mon->section.d_timings);
}
}
static void
get_dt_md_section(Uchar *c, struct edid_version *ver,
struct detailed_monitor_section *det_mon)
{
int i;
for (i=0;i<DET_TIMINGS;i++) {
if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) {
int i;
switch (MONITOR_DESC_TYPE) {
case SERIAL_NUMBER:
det_mon[i].type = DS_SERIAL;
copy_string(c,det_mon[i].section.serial);
break;
case ASCII_STR:
det_mon[i].type = DS_ASCII_STR;
copy_string(c,det_mon[i].section.ascii_data);
break;
case MONITOR_RANGES:
det_mon[i].type = DS_RANGES;
get_monitor_ranges(c,&det_mon[i].section.ranges);
break;
case MONITOR_NAME:
det_mon[i].type = DS_NAME;
copy_string(c,det_mon[i].section.name);
break;
case ADD_COLOR_POINT:
det_mon[i].type = DS_WHITE_P;
get_whitepoint_section(c,det_mon[i].section.wp);
break;
case ADD_STD_TIMINGS:
det_mon[i].type = DS_STD_TIMINGS;
get_dst_timing_section(c,det_mon[i].section.std_t, ver);
break;
case COLOR_MANAGEMENT_DATA:
det_mon[i].type = DS_CMD;
break;
case CVT_3BYTE_DATA:
det_mon[i].type = DS_CVT;
get_cvt_timing_section(c, det_mon[i].section.cvt);
break;
case ADD_EST_TIMINGS:
det_mon[i].type = DS_EST_III;
memcpy(det_mon[i].section.est_iii, c + 6, 6);
break;
case ADD_DUMMY:
det_mon[i].type = DS_DUMMY;
break;
default:
det_mon[i].type = DS_UNKOWN;
break;
}
if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) {
det_mon[i].type = DS_VENDOR + c[3];
}
} else {
det_mon[i].type = DT;
get_detailed_timing_section(c,&det_mon[i].section.d_timings);
for (i=0; i < DET_TIMINGS; i++) {
fetch_detailed_block(c, ver, det_mon + i);
NEXT_DT_MD_SECTION;
}
NEXT_DT_MD_SECTION;
}
}
static void

View File

@ -334,129 +334,147 @@ print_detailed_timings(int scrnIndex, struct detailed_timings *t)
}
}
/* This function handle all detailed patchs,
* including EDID and EDID-extension
*/
struct det_print_parameter{
xf86MonPtr m;
int index;
ddc_quirk_t quirks;
};
static void
print_detailed_monitor_section(int scrnIndex,
struct detailed_monitor_section *m)
handle_detailed_print(struct detailed_monitor_section *det_mon,
void *data)
{
int i,j;
for (i=0;i<DET_TIMINGS;i++) {
switch (m[i].type) {
case DT:
print_detailed_timings(scrnIndex,&m[i].section.d_timings);
break;
case DS_SERIAL:
xf86DrvMsg(scrnIndex,X_INFO,"Serial No: %s\n",m[i].section.serial);
break;
case DS_ASCII_STR:
xf86DrvMsg(scrnIndex,X_INFO," %s\n",m[i].section.ascii_data);
break;
case DS_NAME:
xf86DrvMsg(scrnIndex,X_INFO,"Monitor name: %s\n",m[i].section.name);
break;
case DS_RANGES:
{
struct monitor_ranges *r = &m[i].section.ranges;
xf86DrvMsg(scrnIndex,X_INFO,
"Ranges: V min: %i V max: %i Hz, H min: %i H max: %i kHz,",
r->min_v, r->max_v, r->min_h, r->max_h);
if (r->max_clock_khz != 0) {
xf86ErrorF(" PixClock max %i kHz\n", r->max_clock_khz);
if (r->maxwidth)
xf86DrvMsg(scrnIndex, X_INFO, "Maximum pixel width: %d\n",
r->maxwidth);
xf86DrvMsg(scrnIndex, X_INFO, "Supported aspect ratios:");
if (r->supported_aspect & SUPPORTED_ASPECT_4_3)
xf86ErrorF(" 4:3%s",
r->preferred_aspect == PREFERRED_ASPECT_4_3?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_16_9)
xf86ErrorF(" 16:9%s",
r->preferred_aspect == PREFERRED_ASPECT_16_9?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_16_10)
xf86ErrorF(" 16:10%s",
r->preferred_aspect == PREFERRED_ASPECT_16_10?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_5_4)
xf86ErrorF(" 5:4%s",
r->preferred_aspect == PREFERRED_ASPECT_5_4?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_15_9)
xf86ErrorF(" 15:9%s",
r->preferred_aspect == PREFERRED_ASPECT_15_9?"*":"");
xf86ErrorF("\n");
xf86DrvMsg(scrnIndex, X_INFO, "Supported blankings:");
if (r->supported_blanking & CVT_STANDARD)
xf86ErrorF(" standard");
if (r->supported_blanking & CVT_REDUCED)
xf86ErrorF(" reduced");
xf86ErrorF("\n");
xf86DrvMsg(scrnIndex, X_INFO, "Supported scalings:");
if (r->supported_scaling & SCALING_HSHRINK)
xf86ErrorF(" hshrink");
if (r->supported_scaling & SCALING_HSTRETCH)
xf86ErrorF(" hstretch");
if (r->supported_scaling & SCALING_VSHRINK)
xf86ErrorF(" vshrink");
if (r->supported_scaling & SCALING_VSTRETCH)
xf86ErrorF(" vstretch");
xf86ErrorF("\n");
if (r->preferred_refresh)
xf86DrvMsg(scrnIndex, X_INFO, "Preferred refresh rate: %d\n",
r->preferred_refresh);
else
xf86DrvMsg(scrnIndex, X_INFO, "Buggy monitor, no preferred "
"refresh rate given\n");
} else if (r->max_clock != 0) {
xf86ErrorF(" PixClock max %i MHz\n", r->max_clock);
} else {
xf86ErrorF("\n");
}
if (r->gtf_2nd_f > 0)
xf86DrvMsg(scrnIndex,X_INFO," 2nd GTF parameters: f: %i kHz "
"c: %i m: %i k %i j %i\n", r->gtf_2nd_f,
r->gtf_2nd_c, r->gtf_2nd_m, r->gtf_2nd_k,
r->gtf_2nd_j);
break;
}
case DS_STD_TIMINGS:
for (j = 0; j<5; j++)
xf86DrvMsg(scrnIndex,X_INFO,"#%i: hsize: %i vsize %i refresh: %i "
"vid: %i\n",i,m[i].section.std_t[i].hsize,
m[i].section.std_t[j].vsize,m[i].section.std_t[j].refresh,
m[i].section.std_t[j].id);
break;
case DS_WHITE_P:
for (j = 0; j<2; j++)
if (m[i].section.wp[j].index != 0)
xf86DrvMsg(scrnIndex,X_INFO,
"White point %i: whiteX: %f, whiteY: %f; gamma: %f\n",
m[i].section.wp[j].index,m[i].section.wp[j].white_x,
m[i].section.wp[j].white_y,
m[i].section.wp[j].white_gamma);
break;
case DS_CMD:
xf86DrvMsg(scrnIndex, X_INFO,
"Color management data: (not decoded)\n");
break;
case DS_CVT:
xf86DrvMsg(scrnIndex, X_INFO,
"CVT 3-byte-code modes:\n");
print_cvt_timings(scrnIndex, m[i].section.cvt);
break;
case DS_EST_III:
xf86DrvMsg(scrnIndex, X_INFO,
"Established timings III: (not decoded)\n");
break;
case DS_DUMMY:
default:
break;
}
if (m[i].type >= DS_VENDOR && m[i].type <= DS_VENDOR_MAX) {
xf86DrvMsg(scrnIndex, X_INFO,
"Unknown vendor-specific block %hx\n",
m[i].type - DS_VENDOR);
}
int j, scrnIndex;
struct det_print_parameter *p;
p = (struct det_print_parameter *)data;
scrnIndex = p->m->scrnIndex;
xf86DetTimingApplyQuirks(det_mon,p->quirks,
p->m->features.hsize,
p->m->features.vsize);
switch (det_mon->type) {
case DT:
print_detailed_timings(scrnIndex,&det_mon->section.d_timings);
break;
case DS_SERIAL:
xf86DrvMsg(scrnIndex,X_INFO,"Serial No: %s\n",det_mon->section.serial);
break;
case DS_ASCII_STR:
xf86DrvMsg(scrnIndex,X_INFO," %s\n",det_mon->section.ascii_data);
break;
case DS_NAME:
xf86DrvMsg(scrnIndex,X_INFO,"Monitor name: %s\n",det_mon->section.name);
break;
case DS_RANGES:
{
struct monitor_ranges *r = &det_mon->section.ranges;
xf86DrvMsg(scrnIndex,X_INFO,
"Ranges: V min: %i V max: %i Hz, H min: %i H max: %i kHz,",
r->min_v, r->max_v, r->min_h, r->max_h);
if (r->max_clock_khz != 0) {
xf86ErrorF(" PixClock max %i kHz\n", r->max_clock_khz);
if (r->maxwidth)
xf86DrvMsg(scrnIndex, X_INFO, "Maximum pixel width: %d\n",
r->maxwidth);
xf86DrvMsg(scrnIndex, X_INFO, "Supported aspect ratios:");
if (r->supported_aspect & SUPPORTED_ASPECT_4_3)
xf86ErrorF(" 4:3%s",
r->preferred_aspect == PREFERRED_ASPECT_4_3?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_16_9)
xf86ErrorF(" 16:9%s",
r->preferred_aspect == PREFERRED_ASPECT_16_9?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_16_10)
xf86ErrorF(" 16:10%s",
r->preferred_aspect == PREFERRED_ASPECT_16_10?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_5_4)
xf86ErrorF(" 5:4%s",
r->preferred_aspect == PREFERRED_ASPECT_5_4?"*":"");
if (r->supported_aspect & SUPPORTED_ASPECT_15_9)
xf86ErrorF(" 15:9%s",
r->preferred_aspect == PREFERRED_ASPECT_15_9?"*":"");
xf86ErrorF("\n");
xf86DrvMsg(scrnIndex, X_INFO, "Supported blankings:");
if (r->supported_blanking & CVT_STANDARD)
xf86ErrorF(" standard");
if (r->supported_blanking & CVT_REDUCED)
xf86ErrorF(" reduced");
xf86ErrorF("\n");
xf86DrvMsg(scrnIndex, X_INFO, "Supported scalings:");
if (r->supported_scaling & SCALING_HSHRINK)
xf86ErrorF(" hshrink");
if (r->supported_scaling & SCALING_HSTRETCH)
xf86ErrorF(" hstretch");
if (r->supported_scaling & SCALING_VSHRINK)
xf86ErrorF(" vshrink");
if (r->supported_scaling & SCALING_VSTRETCH)
xf86ErrorF(" vstretch");
xf86ErrorF("\n");
if (r->preferred_refresh)
xf86DrvMsg(scrnIndex, X_INFO, "Preferred refresh rate: %d\n",
r->preferred_refresh);
else
xf86DrvMsg(scrnIndex, X_INFO, "Buggy monitor, no preferred "
"refresh rate given\n");
} else if (r->max_clock != 0) {
xf86ErrorF(" PixClock max %i MHz\n", r->max_clock);
} else {
xf86ErrorF("\n");
}
if (r->gtf_2nd_f > 0)
xf86DrvMsg(scrnIndex,X_INFO," 2nd GTF parameters: f: %i kHz "
"c: %i m: %i k %i j %i\n", r->gtf_2nd_f,
r->gtf_2nd_c, r->gtf_2nd_m, r->gtf_2nd_k,
r->gtf_2nd_j);
break;
}
case DS_STD_TIMINGS:
for (j = 0; j<5; j++)
xf86DrvMsg(scrnIndex,X_INFO,
"#%i: hsize: %i vsize %i refresh: %i "
"vid: %i\n",p->index ,det_mon->section.std_t[j].hsize,
det_mon->section.std_t[j].vsize,
det_mon->section.std_t[j].refresh,
det_mon->section.std_t[j].id);
break;
case DS_WHITE_P:
for (j = 0; j<2; j++)
if (det_mon->section.wp[j].index != 0)
xf86DrvMsg(scrnIndex,X_INFO,
"White point %i: whiteX: %f, whiteY: %f; gamma: %f\n",
det_mon->section.wp[j].index,det_mon->section.wp[j].white_x,
det_mon->section.wp[j].white_y,
det_mon->section.wp[j].white_gamma);
break;
case DS_CMD:
xf86DrvMsg(scrnIndex, X_INFO,
"Color management data: (not decoded)\n");
break;
case DS_CVT:
xf86DrvMsg(scrnIndex, X_INFO,
"CVT 3-byte-code modes:\n");
print_cvt_timings(scrnIndex, det_mon->section.cvt);
break;
case DS_EST_III:
xf86DrvMsg(scrnIndex, X_INFO,
"Established timings III: (not decoded)\n");
break;
case DS_DUMMY:
default:
break;
}
if (det_mon->type >= DS_VENDOR && det_mon->type <= DS_VENDOR_MAX) {
xf86DrvMsg(scrnIndex, X_INFO,
"Unknown vendor-specific block %hx\n",
det_mon->type - DS_VENDOR);
}
p->index = p->index + 1;
}
static void
print_number_sections(int scrnIndex, int num)
{
@ -470,6 +488,7 @@ xf86PrintEDID(xf86MonPtr m)
{
CARD16 i, j, n;
char buf[EDID_WIDTH * 2 + 1];
struct det_print_parameter p;
if (!m) return NULL;
@ -478,7 +497,12 @@ xf86PrintEDID(xf86MonPtr m)
print_display(m->scrnIndex, &m->features, &m->ver);
print_established_timings(m->scrnIndex, &m->timings1);
print_std_timings(m->scrnIndex, m->timings2);
print_detailed_monitor_section(m->scrnIndex, m->det_mon);
p.m = m;
p.index = 0;
p.quirks = xf86DDCDetectQuirks(m->scrnIndex, m, FALSE);
xf86ForEachDetailedBlock(m,
handle_detailed_print ,
&p);
print_number_sections(m->scrnIndex, m->no_sections);
/* extension block section stuff */
@ -495,6 +519,6 @@ xf86PrintEDID(xf86MonPtr m)
}
xf86DrvMsg(m->scrnIndex, X_INFO, "\t%s\n", buf);
}
return m;
}

View File

@ -73,4 +73,54 @@ FindDMTMode(int hsize, int vsize, int refresh, Bool rb);
extern _X_EXPORT const DisplayModeRec DMTModes[];
/*
* Quirks to work around broken EDID data from various monitors.
*/
typedef enum {
DDC_QUIRK_NONE = 0,
/* First detailed mode is bogus, prefer largest mode at 60hz */
DDC_QUIRK_PREFER_LARGE_60 = 1 << 0,
/* 135MHz clock is too high, drop a bit */
DDC_QUIRK_135_CLOCK_TOO_HIGH = 1 << 1,
/* Prefer the largest mode at 75 Hz */
DDC_QUIRK_PREFER_LARGE_75 = 1 << 2,
/* Convert detailed timing's horizontal from units of cm to mm */
DDC_QUIRK_DETAILED_H_IN_CM = 1 << 3,
/* Convert detailed timing's vertical from units of cm to mm */
DDC_QUIRK_DETAILED_V_IN_CM = 1 << 4,
/* Detailed timing descriptors have bogus size values, so just take the
* maximum size and use that.
*/
DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE = 1 << 5,
/* Monitor forgot to set the first detailed is preferred bit. */
DDC_QUIRK_FIRST_DETAILED_PREFERRED = 1 << 6,
/* use +hsync +vsync for detailed mode */
DDC_QUIRK_DETAILED_SYNC_PP = 1 << 7,
/* Force single-link DVI bandwidth limit */
DDC_QUIRK_DVI_SINGLE_LINK = 1 << 8,
} ddc_quirk_t;
DisplayModePtr xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC);
extern Bool
xf86MonitorIsHDMI(xf86MonPtr mon);
typedef void (* handle_detailed_fn)(struct detailed_monitor_section *,void *);
void xf86ForEachDetailedBlock(xf86MonPtr mon,
handle_detailed_fn,
void *data);
ddc_quirk_t
xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC, Bool verbose);
void xf86DetTimingApplyQuirks(struct detailed_monitor_section *det_mon,
ddc_quirk_t quirks, int hsize, int vsize);
typedef void (* handle_video_fn)(struct cea_video_block *, void *);
void xf86ForEachVideoBlock(xf86MonPtr,
handle_video_fn,
void *);
#endif

View File

@ -1523,6 +1523,42 @@ GuessRangeFromModes(MonPtr mon, DisplayModePtr mode)
mon->vrefresh[0].lo = 58.0;
}
struct det_monrec_parameter {
MonRec *mon_rec;
int *max_clock;
Bool set_hsync;
Bool set_vrefresh;
enum { sync_config, sync_edid, sync_default } *sync_source;
};
static void handle_detailed_monrec(struct detailed_monitor_section *det_mon,
void *data)
{
enum { sync_config, sync_edid, sync_default };
struct det_monrec_parameter *p;
p = (struct det_monrec_parameter *)data;
if (det_mon->type == DS_RANGES) {
struct monitor_ranges *ranges = &det_mon->section.ranges;
if (p->set_hsync && ranges->max_h) {
p->mon_rec->hsync[p->mon_rec->nHsync].lo = ranges->min_h;
p->mon_rec->hsync[p->mon_rec->nHsync].hi = ranges->max_h;
p->mon_rec->nHsync++;
if (*p->sync_source == sync_default)
*p->sync_source = sync_edid;
}
if (p->set_vrefresh && ranges->max_v) {
p->mon_rec->vrefresh[p->mon_rec->nVrefresh].lo = ranges->min_v;
p->mon_rec->vrefresh[p->mon_rec->nVrefresh].hi = ranges->max_v;
p->mon_rec->nVrefresh++;
if (*p->sync_source == sync_default)
*p->sync_source = sync_edid;
}
if (ranges->max_clock * 1000 > *p->max_clock)
*p->max_clock = ranges->max_clock * 1000;
}
}
void
xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
{
@ -1601,42 +1637,24 @@ xf86ProbeOutputModes (ScrnInfoPtr scrn, int maxX, int maxY)
edid_monitor = output->MonInfo;
if (edid_monitor)
{
int i;
Bool set_hsync = mon_rec.nHsync == 0;
Bool set_vrefresh = mon_rec.nVrefresh == 0;
struct disp_features *features = &edid_monitor->features;
if (edid_monitor)
{
struct det_monrec_parameter p;
struct disp_features *features = &edid_monitor->features;
/* if display is not continuous-frequency, don't add default modes */
if (!GTF_SUPPORTED(features->msc))
add_default_modes = FALSE;
for (i = 0; i < sizeof (edid_monitor->det_mon) / sizeof (edid_monitor->det_mon[0]); i++)
{
if (edid_monitor->det_mon[i].type == DS_RANGES)
{
struct monitor_ranges *ranges = &edid_monitor->det_mon[i].section.ranges;
if (set_hsync && ranges->max_h)
{
mon_rec.hsync[mon_rec.nHsync].lo = ranges->min_h;
mon_rec.hsync[mon_rec.nHsync].hi = ranges->max_h;
mon_rec.nHsync++;
if (sync_source == sync_default)
sync_source = sync_edid;
}
if (set_vrefresh && ranges->max_v)
{
mon_rec.vrefresh[mon_rec.nVrefresh].lo = ranges->min_v;
mon_rec.vrefresh[mon_rec.nVrefresh].hi = ranges->max_v;
mon_rec.nVrefresh++;
if (sync_source == sync_default)
sync_source = sync_edid;
}
if (ranges->max_clock * 1000 > max_clock)
max_clock = ranges->max_clock * 1000;
}
}
p.mon_rec = &mon_rec;
p.max_clock = &max_clock;
p.set_hsync = mon_rec.nHsync == 0;
p.set_vrefresh = mon_rec.nVrefresh == 0;
p.sync_source = &sync_source;
xf86ForEachDetailedBlock(edid_monitor,
handle_detailed_monrec,
&p);
}
if (xf86GetOptValFreq (output->options, OPTION_MIN_CLOCK,
@ -2900,6 +2918,35 @@ xf86OutputSetEDIDProperty (xf86OutputPtr output, void *data, int data_len)
#endif
/* Pull out a phyiscal size from a detailed timing if available. */
struct det_phySize_parameter {
xf86OutputPtr output;
ddc_quirk_t quirks;
Bool ret;
};
static void handle_detailed_physical_size(struct detailed_monitor_section
*det_mon, void *data)
{
struct det_phySize_parameter *p;
p = (struct det_phySize_parameter *)data;
if (p->ret == TRUE )
return ;
xf86DetTimingApplyQuirks(det_mon, p->quirks,
p->output->MonInfo->features.hsize,
p->output->MonInfo->features.vsize);
if (det_mon->type == DT &&
det_mon->section.d_timings.h_size != 0 &&
det_mon->section.d_timings.v_size != 0) {
p->output->mm_width = det_mon->section.d_timings.h_size;
p->output->mm_height = det_mon->section.d_timings.v_size;
p->ret = TRUE;
}
}
/**
* Set the EDID information for the specified output
*/
@ -2908,7 +2955,6 @@ xf86OutputSetEDID (xf86OutputPtr output, xf86MonPtr edid_mon)
{
ScrnInfoPtr scrn = output->scrn;
xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
int i;
#ifdef RANDR_12_INTERFACE
int size;
#endif
@ -2943,20 +2989,15 @@ xf86OutputSetEDID (xf86OutputPtr output, xf86MonPtr edid_mon)
xf86OutputSetEDIDProperty (output, edid_mon ? edid_mon->rawData : NULL, size);
#endif
if (edid_mon)
{
/* Pull out a phyiscal size from a detailed timing if available. */
for (i = 0; i < 4; i++) {
if (edid_mon->det_mon[i].type == DT &&
edid_mon->det_mon[i].section.d_timings.h_size != 0 &&
edid_mon->det_mon[i].section.d_timings.v_size != 0)
{
output->mm_width = edid_mon->det_mon[i].section.d_timings.h_size;
output->mm_height = edid_mon->det_mon[i].section.d_timings.v_size;
break;
}
}
if (edid_mon) {
struct det_phySize_parameter p;
p.output = output;
p.quirks = xf86DDCDetectQuirks(scrn->scrnIndex,edid_mon, FALSE);
p.ret = FALSE;
xf86ForEachDetailedBlock(edid_mon,
handle_detailed_physical_size, &p);
/* if no mm size is available from a detailed timing, check the max size field */
if ((!output->mm_width || !output->mm_height) &&
(edid_mon->features.hsize && edid_mon->features.vsize))

View File

@ -45,20 +45,23 @@
#include <string.h>
#include <math.h>
static void handle_detailed_rblank(struct detailed_monitor_section *det_mon,
void *data)
{
if (det_mon->type == DS_RANGES)
if (det_mon->section.ranges.supported_blanking & CVT_REDUCED)
*(Bool*)data = TRUE;
}
static Bool
xf86MonitorSupportsReducedBlanking(xf86MonPtr DDC)
{
/* EDID 1.4 explicitly defines RB support */
if (DDC->ver.revision >= 4) {
int i;
for (i = 0; i < DET_TIMINGS; i++) {
struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
if (det_mon->type == DS_RANGES)
if (det_mon->section.ranges.supported_blanking & CVT_REDUCED)
return TRUE;
}
return FALSE;
Bool ret = FALSE;
xf86ForEachDetailedBlock(DDC, handle_detailed_rblank, &ret);
return ret;
}
/* For anything older, assume digital means RB support. Boo. */
@ -68,34 +71,6 @@ xf86MonitorSupportsReducedBlanking(xf86MonPtr DDC)
return FALSE;
}
/*
* Quirks to work around broken EDID data from various monitors.
*/
typedef enum {
DDC_QUIRK_NONE = 0,
/* First detailed mode is bogus, prefer largest mode at 60hz */
DDC_QUIRK_PREFER_LARGE_60 = 1 << 0,
/* 135MHz clock is too high, drop a bit */
DDC_QUIRK_135_CLOCK_TOO_HIGH = 1 << 1,
/* Prefer the largest mode at 75 Hz */
DDC_QUIRK_PREFER_LARGE_75 = 1 << 2,
/* Convert detailed timing's horizontal from units of cm to mm */
DDC_QUIRK_DETAILED_H_IN_CM = 1 << 3,
/* Convert detailed timing's vertical from units of cm to mm */
DDC_QUIRK_DETAILED_V_IN_CM = 1 << 4,
/* Detailed timing descriptors have bogus size values, so just take the
* maximum size and use that.
*/
DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE = 1 << 5,
/* Monitor forgot to set the first detailed is preferred bit. */
DDC_QUIRK_FIRST_DETAILED_PREFERRED = 1 << 6,
/* use +hsync +vsync for detailed mode */
DDC_QUIRK_DETAILED_SYNC_PP = 1 << 7,
/* Force single-link DVI bandwidth limit */
DDC_QUIRK_DVI_SINGLE_LINK = 1 << 8,
} ddc_quirk_t;
static Bool quirk_prefer_large_60 (int scrnIndex, xf86MonPtr DDC)
{
/* Belinea 10 15 55 */
@ -774,7 +749,7 @@ DDCGuessRangesFromModes(int scrnIndex, MonPtr Monitor, DisplayModePtr Modes)
}
}
static ddc_quirk_t
ddc_quirk_t
xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC, Bool verbose)
{
ddc_quirk_t quirks;
@ -794,6 +769,25 @@ xf86DDCDetectQuirks(int scrnIndex, xf86MonPtr DDC, Bool verbose)
return quirks;
}
void xf86DetTimingApplyQuirks(struct detailed_monitor_section *det_mon,
ddc_quirk_t quirks,
int hsize, int vsize)
{
if (det_mon->type != DT)
return;
if (quirks & DDC_QUIRK_DETAILED_H_IN_CM)
det_mon->section.d_timings.h_size *= 10;
if (quirks & DDC_QUIRK_DETAILED_V_IN_CM)
det_mon->section.d_timings.v_size *= 10;
if (quirks & DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
det_mon->section.d_timings.h_size = 10 * hsize;
det_mon->section.d_timings.v_size = 10 * vsize;
}
}
/**
* Applies monitor-specific quirks to the decoded EDID information.
*
@ -807,21 +801,9 @@ xf86DDCApplyQuirks(int scrnIndex, xf86MonPtr DDC)
int i;
for (i = 0; i < DET_TIMINGS; i++) {
struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
if (det_mon->type != DT)
continue;
if (quirks & DDC_QUIRK_DETAILED_H_IN_CM)
det_mon->section.d_timings.h_size *= 10;
if (quirks & DDC_QUIRK_DETAILED_V_IN_CM)
det_mon->section.d_timings.v_size *= 10;
if (quirks & DDC_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
det_mon->section.d_timings.h_size = 10 * DDC->features.hsize;
det_mon->section.d_timings.v_size = 10 * DDC->features.vsize;
}
xf86DetTimingApplyQuirks(DDC->det_mon + i, quirks,
DDC->features.hsize,
DDC->features.vsize);
}
}
@ -866,14 +848,156 @@ xf86DDCSetPreferredRefresh(int scrnIndex, DisplayModePtr modes,
best->type |= M_T_PREFERRED;
}
#define CEA_VIDEO_MODES_NUM 64
static const DisplayModeRec CEAVideoModes[CEA_VIDEO_MODES_NUM] = {
{ MODEPREFIX, 25175, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 1:640x480@60Hz */
{ MODEPREFIX, 27000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 2:720x480@60Hz */
{ MODEPREFIX, 27000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 3:720x480@60Hz */
{ MODEPREFIX, 74250, 1280, 1390, 1430, 1650, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 4: 1280x720@60Hz */
{ MODEPREFIX, 74250, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 5:1920x1080i@60Hz */
{ MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 6:1440x480i@60Hz */
{ MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 7:1440x480i@60Hz */
{ MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 8:1440x240@60Hz */
{ MODEPREFIX, 27000, 1440, 1478, 1602, 1716, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 9:1440x240@60Hz */
{ MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 10:2880x480i@60Hz */
{ MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 11:2880x480i@60Hz */
{ MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 12:2880x240@60Hz */
{ MODEPREFIX, 54000, 2880, 2956, 3204, 3432, 0, 240, 244, 247, 262, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 13:2880x240@60Hz */
{ MODEPREFIX, 54000, 1440, 1472, 1596, 1716, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 14:1440x480@60Hz */
{ MODEPREFIX, 54000, 1440, 1472, 1596, 1716, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 15:1440x480@60Hz */
{ MODEPREFIX, 148500, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 16:1920x1080@60Hz */
{ MODEPREFIX, 27000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 17:720x576@50Hz */
{ MODEPREFIX, 27000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 18:720x576@50Hz */
{ MODEPREFIX, 74250, 1280, 1720, 1760, 1980, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 19: 1280x720@50Hz */
{ MODEPREFIX, 74250, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 20:1920x1080i@50Hz */
{ MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 21:1440x576i@50Hz */
{ MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 22:1440x576i@50Hz */
{ MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 23:1440x288@50Hz */
{ MODEPREFIX, 27000, 1440, 1464, 1590, 1728, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 24:1440x288@50Hz */
{ MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 25:2880x576i@50Hz */
{ MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 26:2880x576i@50Hz */
{ MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 27:2880x288@50Hz */
{ MODEPREFIX, 54000, 2880, 2928, 3180, 3456, 0, 288, 290, 293, 312, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 28:2880x288@50Hz */
{ MODEPREFIX, 54000, 1440, 1464, 1592, 1728, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 29:1440x576@50Hz */
{ MODEPREFIX, 54000, 1440, 1464, 1592, 1728, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 30:1440x576@50Hz */
{ MODEPREFIX, 148500, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 31:1920x1080@50Hz */
{ MODEPREFIX, 74250, 1920, 2558, 2602, 2750, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 32:1920x1080@24Hz */
{ MODEPREFIX, 74250, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 33:1920x1080@25Hz */
{ MODEPREFIX, 74250, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 34:1920x1080@30Hz */
{ MODEPREFIX, 108000, 2880, 2944, 3192, 3432, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 35:2880x480@60Hz */
{ MODEPREFIX, 108000, 2880, 2944, 3192, 3432, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 36:2880x480@60Hz */
{ MODEPREFIX, 108000, 2880, 2928, 3184, 3456, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 37:2880x576@50Hz */
{ MODEPREFIX, 108000, 2880, 2928, 3184, 3456, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 38:2880x576@50Hz */
{ MODEPREFIX, 72000, 1920, 1952, 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, V_PHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 39:1920x1080i@50Hz */
{ MODEPREFIX, 148500, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 40:1920x1080i@100Hz */
{ MODEPREFIX, 148500, 1280, 1720, 1760, 1980, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 41:1280x720@100Hz */
{ MODEPREFIX, 54000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 42:720x576@100Hz */
{ MODEPREFIX, 54000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 43:720x576@100Hz */
{ MODEPREFIX, 54000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 44:1440x576i@100Hz */
{ MODEPREFIX, 54000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 45:1440x576i@100Hz */
{ MODEPREFIX, 148500, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC | V_INTERLACE, MODESUFFIX }, /* VIC 46:1920x1080i@120Hz */
{ MODEPREFIX, 148500, 1280, 1390, 1430, 1650, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 47:1280x720@120Hz */
{ MODEPREFIX, 54000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 48:720x480@120Hz */
{ MODEPREFIX, 54000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 49:720x480@120Hz */
{ MODEPREFIX, 54000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 50:1440x480i@120Hz */
{ MODEPREFIX, 54000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 51:1440x480i@120Hz */
{ MODEPREFIX, 108000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 52:720x576@200Hz */
{ MODEPREFIX, 108000, 720, 732, 796, 864, 0, 576, 581, 586, 625, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 53:720x576@200Hz */
{ MODEPREFIX, 108000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 54:1440x576i@200Hz */
{ MODEPREFIX, 108000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 55:1440x576i@200Hz */
{ MODEPREFIX, 108000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 56:720x480@240Hz */
{ MODEPREFIX, 108000, 720, 736, 798, 858, 0, 480, 489, 495, 525, 0, V_NHSYNC | V_NVSYNC, MODESUFFIX }, /* VIC 57:720x480@240Hz */
{ MODEPREFIX, 108000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 58:1440x480i@240 */
{ MODEPREFIX, 108000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, V_NHSYNC | V_NVSYNC | V_INTERLACE, MODESUFFIX },/* VIC 59:1440x480i@240 */
{ MODEPREFIX, 59400, 1280, 3040, 3080, 3300, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 60: 1280x720@24Hz */
{ MODEPREFIX, 74250, 3700, 3740, 1430, 3960, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 61: 1280x720@25Hz */
{ MODEPREFIX, 74250, 1280, 3040, 3080, 3300, 0, 720, 725, 730, 750, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 62: 1280x720@30Hz */
{ MODEPREFIX, 297000, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 63: 1920x1080@120Hz */
{ MODEPREFIX, 297000, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, V_PHSYNC | V_PVSYNC, MODESUFFIX }, /* VIC 64:1920x1080@100Hz */
};
/* chose mode line by cea short video descriptor*/
static void handle_cea_svd(struct cea_video_block *video, void *data)
{
DisplayModePtr Mode;
DisplayModePtr *Modes = (DisplayModePtr *) data;
int vid;
vid = video ->video_code & 0x7f;
if (vid < CEA_VIDEO_MODES_NUM) {
Mode = xf86DuplicateMode(CEAVideoModes + vid);
*Modes = xf86ModesAdd(*Modes, Mode);
}
}
static DisplayModePtr
DDCModesFromCEAExtension(int scrnIndex, xf86MonPtr MonPtr)
{
DisplayModePtr Modes = NULL;
xf86ForEachVideoBlock(MonPtr,
handle_cea_svd,
&Modes);
return Modes;
}
struct det_modes_parameter {
xf86MonPtr DDC;
ddc_quirk_t quirks;
DisplayModePtr Modes;
Bool rb;
Bool preferred;
int timing_level;
};
static void handle_detailed_modes(struct detailed_monitor_section *det_mon,
void *data)
{
DisplayModePtr Mode;
struct det_modes_parameter *p = (struct det_modes_parameter *)data;
xf86DetTimingApplyQuirks(det_mon,p->quirks,
p->DDC->features.hsize,
p->DDC->features.vsize);
switch (det_mon->type) {
case DT:
Mode = DDCModeFromDetailedTiming(p->DDC->scrnIndex,
&det_mon->section.d_timings,
p->preferred,
p->quirks);
p->preferred = FALSE;
p->Modes = xf86ModesAdd(p->Modes, Mode);
break;
case DS_STD_TIMINGS:
Mode = DDCModesFromStandardTiming(det_mon->section.std_t,
p->quirks, p->timing_level,p->rb);
p->Modes = xf86ModesAdd(p->Modes, Mode);
break;
#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(7,0,0,0,0)
case DS_CVT:
Mode = DDCModesFromCVT(p->DDC->scrnIndex, det_mon->section.cvt);
p->Modes = xf86ModesAdd(p->Modes, Mode);
break;
#endif
case DS_EST_III:
Mode = DDCModesFromEstIII(det_mon->section.est_iii);
p->Modes = xf86ModesAdd(p->Modes, Mode);
break;
default:
break;
}
}
DisplayModePtr
xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
{
int i;
DisplayModePtr Modes = NULL, Mode;
ddc_quirk_t quirks;
Bool preferred, rb;
int timing_level;
struct det_modes_parameter p;
xf86DrvMsg (scrnIndex, X_INFO, "EDID vendor \"%s\", prod id %d\n",
DDC->vendor.name, DDC->vendor.prod_id);
@ -892,35 +1016,14 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
timing_level = MonitorStandardTimingLevel(DDC);
for (i = 0; i < DET_TIMINGS; i++) {
struct detailed_monitor_section *det_mon = &DDC->det_mon[i];
Mode = NULL;
switch (det_mon->type) {
case DT:
Mode = DDCModeFromDetailedTiming(scrnIndex,
&det_mon->section.d_timings,
preferred,
quirks);
preferred = FALSE;
break;
case DS_STD_TIMINGS:
Mode = DDCModesFromStandardTiming(det_mon->section.std_t,
quirks, timing_level, rb);
break;
#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(7,0,0,0,0)
case DS_CVT:
Mode = DDCModesFromCVT(scrnIndex, det_mon->section.cvt);
break;
#endif
case DS_EST_III:
Mode = DDCModesFromEstIII(det_mon->section.est_iii);
break;
default:
break;
}
Modes = xf86ModesAdd(Modes, Mode);
}
p.quirks = quirks;
p.DDC = DDC;
p.Modes = Modes;
p.rb = rb;
p.preferred = preferred;
p.timing_level = timing_level;
xf86ForEachDetailedBlock(DDC, handle_detailed_modes, &p);
Modes = p.Modes;
/* Add established timings */
Mode = DDCModesFromEstablished(scrnIndex, &DDC->timings1, quirks);
@ -930,6 +1033,10 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
Mode = DDCModesFromStandardTiming(DDC->timings2, quirks, timing_level, rb);
Modes = xf86ModesAdd(Modes, Mode);
/* Add cea-extension mode timings */
Mode = DDCModesFromCEAExtension(scrnIndex,DDC);
Modes = xf86ModesAdd(Modes, Mode);
if (quirks & DDC_QUIRK_PREFER_LARGE_60)
xf86DDCSetPreferredRefresh(scrnIndex, Modes, 60);
@ -939,6 +1046,63 @@ xf86DDCGetModes(int scrnIndex, xf86MonPtr DDC)
return Modes;
}
struct det_mon_parameter {
MonPtr Monitor;
ddc_quirk_t quirks;
Bool have_hsync;
Bool have_vrefresh;
Bool have_maxpixclock;
};
static void handle_detailed_monset(struct detailed_monitor_section *det_mon,
void *data)
{
int clock;
struct det_mon_parameter *p = (struct det_mon_parameter *)data;
int scrnIndex = ((xf86MonPtr)(p->Monitor->DDC))->scrnIndex;
switch (det_mon->type) {
case DS_RANGES:
if (!p->have_hsync) {
if (!p->Monitor->nHsync)
xf86DrvMsg(scrnIndex, X_INFO,
"Using EDID range info for horizontal sync\n");
p->Monitor->hsync[p->Monitor->nHsync].lo =
det_mon->section.ranges.min_h;
p->Monitor->hsync[p->Monitor->nHsync].hi =
det_mon->section.ranges.max_h;
p->Monitor->nHsync++;
} else {
xf86DrvMsg(scrnIndex, X_INFO,
"Using hsync ranges from config file\n");
}
if (!p->have_vrefresh) {
if (!p->Monitor->nVrefresh)
xf86DrvMsg(scrnIndex, X_INFO,
"Using EDID range info for vertical refresh\n");
p->Monitor->vrefresh[p->Monitor->nVrefresh].lo =
det_mon->section.ranges.min_v;
p->Monitor->vrefresh[p->Monitor->nVrefresh].hi =
det_mon->section.ranges.max_v;
p->Monitor->nVrefresh++;
} else {
xf86DrvMsg(scrnIndex, X_INFO,
"Using vrefresh ranges from config file\n");
}
clock = det_mon->section.ranges.max_clock * 1000;
if (p->quirks & DDC_QUIRK_DVI_SINGLE_LINK)
clock = min(clock, 165000);
if (!p->have_maxpixclock && clock > p->Monitor->maxPixClock)
p->Monitor->maxPixClock = clock;
break;
default:
break;
}
}
/*
* Fill out MonPtr with xf86MonPtr information.
*/
@ -946,17 +1110,13 @@ void
xf86EdidMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC)
{
DisplayModePtr Modes = NULL, Mode;
int i, clock;
Bool have_hsync = FALSE, have_vrefresh = FALSE, have_maxpixclock = FALSE;
ddc_quirk_t quirks;
struct det_mon_parameter p;
if (!Monitor || !DDC)
return;
Monitor->DDC = DDC;
quirks = xf86DDCDetectQuirks(scrnIndex, DDC, FALSE);
if (Monitor->widthmm <= 0 || Monitor->heightmm <= 0) {
Monitor->widthmm = 10 * DDC->features.hsize;
Monitor->heightmm = 10 * DDC->features.vsize;
@ -966,54 +1126,13 @@ xf86EdidMonitorSet(int scrnIndex, MonPtr Monitor, xf86MonPtr DDC)
Modes = xf86DDCGetModes(scrnIndex, DDC);
/* Skip EDID ranges if they were specified in the config file */
have_hsync = (Monitor->nHsync != 0);
have_vrefresh = (Monitor->nVrefresh != 0);
have_maxpixclock = (Monitor->maxPixClock != 0);
/* Go through the detailed monitor sections */
for (i = 0; i < DET_TIMINGS; i++) {
switch (DDC->det_mon[i].type) {
case DS_RANGES:
if (!have_hsync) {
if (!Monitor->nHsync)
xf86DrvMsg(scrnIndex, X_INFO,
"Using EDID range info for horizontal sync\n");
Monitor->hsync[Monitor->nHsync].lo =
DDC->det_mon[i].section.ranges.min_h;
Monitor->hsync[Monitor->nHsync].hi =
DDC->det_mon[i].section.ranges.max_h;
Monitor->nHsync++;
} else {
xf86DrvMsg(scrnIndex, X_INFO,
"Using hsync ranges from config file\n");
}
if (!have_vrefresh) {
if (!Monitor->nVrefresh)
xf86DrvMsg(scrnIndex, X_INFO,
"Using EDID range info for vertical refresh\n");
Monitor->vrefresh[Monitor->nVrefresh].lo =
DDC->det_mon[i].section.ranges.min_v;
Monitor->vrefresh[Monitor->nVrefresh].hi =
DDC->det_mon[i].section.ranges.max_v;
Monitor->nVrefresh++;
} else {
xf86DrvMsg(scrnIndex, X_INFO,
"Using vrefresh ranges from config file\n");
}
clock = DDC->det_mon[i].section.ranges.max_clock * 1000;
if (quirks & DDC_QUIRK_DVI_SINGLE_LINK)
clock = min(clock, 165000);
if (!have_maxpixclock && clock > Monitor->maxPixClock)
Monitor->maxPixClock = clock;
break;
default:
break;
}
}
p.Monitor = Monitor;
p.quirks = xf86DDCDetectQuirks(scrnIndex, Monitor->DDC, FALSE);
p.have_hsync = (Monitor->nHsync != 0);
p.have_vrefresh = (Monitor->nVrefresh != 0);
p.have_maxpixclock = (Monitor->maxPixClock != 0);
xf86ForEachDetailedBlock(DDC, handle_detailed_monset, &p);
if (Modes) {
/* Print Modes */