2007-12-28 19:55:39 +01:00
|
|
|
/*
|
2003-11-14 17:48:57 +01:00
|
|
|
* Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
|
2007-12-28 19:55:39 +01:00
|
|
|
* Copyright 2007 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
2009-05-14 12:53:08 +02:00
|
|
|
* 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, sublicense,
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
2007-12-28 19:55:39 +01:00
|
|
|
*
|
|
|
|
* 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
|
2009-05-14 12:53:08 +02:00
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 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.
|
2007-12-28 19:55:39 +01:00
|
|
|
*
|
|
|
|
* interpret_edid.c: interpret a primary EDID block
|
2003-11-14 17:48:57 +01:00
|
|
|
*/
|
2007-12-28 19:55:39 +01:00
|
|
|
|
2005-07-03 09:02:09 +02:00
|
|
|
#ifdef HAVE_XORG_CONFIG_H
|
|
|
|
#include <xorg-config.h>
|
|
|
|
#endif
|
|
|
|
|
2005-07-03 10:53:54 +02:00
|
|
|
#include "misc.h"
|
2003-11-14 17:48:57 +01:00
|
|
|
#include "xf86.h"
|
|
|
|
#include "xf86_OSproc.h"
|
|
|
|
#define _PARSE_EDID_
|
|
|
|
#include "xf86DDC.h"
|
2006-02-10 23:00:30 +01:00
|
|
|
#include <string.h>
|
2003-11-14 17:48:57 +01:00
|
|
|
|
|
|
|
static void get_vendor_section(Uchar*, struct vendor *);
|
|
|
|
static void get_version_section(Uchar*, struct edid_version *);
|
2004-11-02 09:54:53 +01:00
|
|
|
static void get_display_section(Uchar*, struct disp_features *,
|
|
|
|
struct edid_version *);
|
2003-11-14 17:48:57 +01:00
|
|
|
static void get_established_timing_section(Uchar*, struct established_timings *);
|
2004-11-02 09:54:53 +01:00
|
|
|
static void get_std_timing_section(Uchar*, struct std_timings *,
|
|
|
|
struct edid_version *);
|
2009-02-18 10:41:26 +01:00
|
|
|
static void fetch_detailed_block(Uchar *c, struct edid_version *ver,
|
|
|
|
struct detailed_monitor_section *det_mon);
|
2003-11-14 17:48:57 +01:00
|
|
|
static void get_dt_md_section(Uchar *, struct edid_version *,
|
|
|
|
struct detailed_monitor_section *det_mon);
|
|
|
|
static void copy_string(Uchar *, Uchar *);
|
2004-11-02 09:54:53 +01:00
|
|
|
static void get_dst_timing_section(Uchar *, struct std_timings *,
|
|
|
|
struct edid_version *);
|
2003-11-14 17:48:57 +01:00
|
|
|
static void get_monitor_ranges(Uchar *, struct monitor_ranges *);
|
|
|
|
static void get_whitepoint_section(Uchar *, struct whitePoints *);
|
|
|
|
static void get_detailed_timing_section(Uchar*, struct detailed_timings *);
|
2004-11-02 09:54:53 +01:00
|
|
|
static Bool validate_version(int scrnIndex, struct edid_version *);
|
|
|
|
|
2009-02-18 10:41:26 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-09-15 00:56:34 +02:00
|
|
|
static void
|
|
|
|
handle_edid_quirks(xf86MonPtr m)
|
|
|
|
{
|
2009-02-18 10:41:26 +01:00
|
|
|
struct monitor_ranges *ranges = NULL;
|
2006-09-15 00:56:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* max_clock is only encoded in EDID in tens of MHz, so occasionally we
|
|
|
|
* find a monitor claiming a max of 160 with a mode requiring 162, or
|
|
|
|
* similar. Strictly we should refuse to round up too far, but let's
|
|
|
|
* see how well this works.
|
|
|
|
*/
|
2009-02-18 10:41:26 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2006-09-15 00:56:34 +02:00
|
|
|
}
|
2009-02-18 10:41:26 +01:00
|
|
|
}
|
2008-10-10 19:41:50 +02:00
|
|
|
|
2009-02-18 10:41:26 +01:00
|
|
|
static void encode_aspect_ratio(xf86MonPtr m)
|
|
|
|
{
|
2008-10-10 19:41:50 +02:00
|
|
|
/*
|
|
|
|
* some monitors encode the aspect ratio instead of the physical size.
|
|
|
|
* try to find the largest detailed timing that matches that aspect
|
|
|
|
* ratio and use that to fill in the feature section.
|
|
|
|
*/
|
|
|
|
if ((m->features.hsize == 16 && m->features.vsize == 9) ||
|
|
|
|
(m->features.hsize == 16 && m->features.vsize == 10) ||
|
|
|
|
(m->features.hsize == 4 && m->features.vsize == 3) ||
|
|
|
|
(m->features.hsize == 5 && m->features.vsize == 4)) {
|
|
|
|
|
2009-02-18 10:41:26 +01:00
|
|
|
struct det_hv_parameter p;
|
|
|
|
p.real_hsize = 0;
|
|
|
|
p.real_vsize = 0;
|
|
|
|
p.target_aspect = (float)m->features.hsize /m->features.vsize;
|
|
|
|
|
|
|
|
xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p);
|
|
|
|
|
|
|
|
if (!p.real_hsize || !p.real_vsize) {
|
2008-10-14 20:04:01 +02:00
|
|
|
m->features.hsize = m->features.vsize = 0;
|
2009-02-18 10:41:26 +01:00
|
|
|
} else if ((m->features.hsize * 10 == p.real_hsize) &&
|
|
|
|
(m->features.vsize * 10 == p.real_vsize)) {
|
2008-10-14 20:04:01 +02:00
|
|
|
/* exact match is just unlikely, should do a better check though */
|
|
|
|
m->features.hsize = m->features.vsize = 0;
|
|
|
|
} else {
|
2008-10-10 19:41:50 +02:00
|
|
|
/* convert mm to cm */
|
2009-02-18 10:41:26 +01:00
|
|
|
m->features.hsize = (p.real_hsize + 5) / 10;
|
|
|
|
m->features.vsize = (p.real_vsize + 5) / 10;
|
2008-10-10 19:41:50 +02:00
|
|
|
}
|
2009-02-18 10:41:26 +01:00
|
|
|
|
2008-10-10 19:41:50 +02:00
|
|
|
xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n",
|
|
|
|
m->features.hsize, m->features.vsize);
|
|
|
|
}
|
2006-09-15 00:56:34 +02:00
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
xf86MonPtr
|
2003-11-14 17:48:57 +01:00
|
|
|
xf86InterpretEDID(int scrnIndex, Uchar *block)
|
|
|
|
{
|
|
|
|
xf86MonPtr m;
|
|
|
|
|
|
|
|
if (!block) return NULL;
|
|
|
|
if (! (m = xnfcalloc(sizeof(xf86Monitor),1))) return NULL;
|
|
|
|
m->scrnIndex = scrnIndex;
|
|
|
|
m->rawData = block;
|
2004-11-02 09:54:53 +01:00
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
get_vendor_section(SECTION(VENDOR_SECTION,block),&m->vendor);
|
|
|
|
get_version_section(SECTION(VERSION_SECTION,block),&m->ver);
|
2004-11-02 09:54:53 +01:00
|
|
|
if (!validate_version(scrnIndex, &m->ver)) goto error;
|
|
|
|
get_display_section(SECTION(DISPLAY_SECTION,block),&m->features,
|
|
|
|
&m->ver);
|
2003-11-14 17:48:57 +01:00
|
|
|
get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION,block),
|
|
|
|
&m->timings1);
|
2004-11-02 09:54:53 +01:00
|
|
|
get_std_timing_section(SECTION(STD_TIMING_SECTION,block),m->timings2,
|
|
|
|
&m->ver);
|
2003-11-14 17:48:57 +01:00
|
|
|
get_dt_md_section(SECTION(DET_TIMING_SECTION,block),&m->ver, m->det_mon);
|
|
|
|
m->no_sections = (int)*(char *)SECTION(NO_EDID,block);
|
2006-09-15 00:56:34 +02:00
|
|
|
|
|
|
|
handle_edid_quirks(m);
|
2009-02-18 10:41:26 +01:00
|
|
|
encode_aspect_ratio(m);
|
2006-09-15 00:56:34 +02:00
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
return (m);
|
2004-11-02 09:54:53 +01:00
|
|
|
|
|
|
|
error:
|
2010-05-05 20:44:06 +02:00
|
|
|
free(m);
|
2004-11-02 09:54:53 +01:00
|
|
|
return NULL;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
2009-02-18 10:41:26 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
xf86MonPtr
|
2008-06-21 22:00:05 +02:00
|
|
|
xf86InterpretEEDID(int scrnIndex, Uchar *block)
|
|
|
|
{
|
|
|
|
xf86MonPtr m;
|
|
|
|
|
|
|
|
m = xf86InterpretEDID(scrnIndex, block);
|
|
|
|
if (!m)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* extension parse */
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
static void
|
|
|
|
get_vendor_section(Uchar *c, struct vendor *r)
|
|
|
|
{
|
|
|
|
r->name[0] = L1;
|
|
|
|
r->name[1] = L2;
|
|
|
|
r->name[2] = L3;
|
|
|
|
r->name[3] = '\0';
|
|
|
|
|
|
|
|
r->prod_id = PROD_ID;
|
|
|
|
r->serial = SERIAL_NO;
|
|
|
|
r->week = WEEK;
|
|
|
|
r->year = YEAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_version_section(Uchar *c, struct edid_version *r)
|
|
|
|
{
|
|
|
|
r->version = VERSION;
|
|
|
|
r->revision = REVISION;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2004-11-02 09:54:53 +01:00
|
|
|
get_display_section(Uchar *c, struct disp_features *r,
|
|
|
|
struct edid_version *v)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
|
|
|
r->input_type = INPUT_TYPE;
|
2004-11-02 09:54:53 +01:00
|
|
|
if (!DIGITAL(r->input_type)) {
|
|
|
|
r->input_voltage = INPUT_VOLTAGE;
|
|
|
|
r->input_setup = SETUP;
|
|
|
|
r->input_sync = SYNC;
|
2007-12-28 21:52:42 +01:00
|
|
|
} else if (v->revision == 2 || v->revision == 3) {
|
2004-11-02 09:54:53 +01:00
|
|
|
r->input_dfp = DFP;
|
2007-12-28 21:52:42 +01:00
|
|
|
} else if (v->revision >= 4) {
|
|
|
|
r->input_bpc = BPC;
|
|
|
|
r->input_interface = DIGITAL_INTERFACE;
|
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
r->hsize = HSIZE_MAX;
|
|
|
|
r->vsize = VSIZE_MAX;
|
|
|
|
r->gamma = GAMMA;
|
|
|
|
r->dpms = DPMS;
|
|
|
|
r->display_type = DISPLAY_TYPE;
|
|
|
|
r->msc = MSC;
|
|
|
|
r->redx = REDX;
|
|
|
|
r->redy = REDY;
|
|
|
|
r->greenx = GREENX;
|
|
|
|
r->greeny = GREENY;
|
|
|
|
r->bluex = BLUEX;
|
|
|
|
r->bluey = BLUEY;
|
|
|
|
r->whitex = WHITEX;
|
|
|
|
r->whitey = WHITEY;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_established_timing_section(Uchar *c, struct established_timings *r)
|
|
|
|
{
|
|
|
|
r->t1 = T1;
|
|
|
|
r->t2 = T2;
|
|
|
|
r->t_manu = T_MANU;
|
|
|
|
}
|
|
|
|
|
2007-12-28 21:00:41 +01:00
|
|
|
static void
|
|
|
|
get_cvt_timing_section(Uchar *c, struct cvt_timings *r)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
if (c[0] && c[1] && c[2]) {
|
|
|
|
r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2;
|
|
|
|
switch (c[1] & 0xc0) {
|
|
|
|
case 0x00: r[i].width = r[i].height * 4 / 3; break;
|
|
|
|
case 0x40: r[i].width = r[i].height * 16 / 9; break;
|
|
|
|
case 0x80: r[i].width = r[i].height * 16 / 10; break;
|
|
|
|
case 0xc0: r[i].width = r[i].height * 15 / 9; break;
|
|
|
|
}
|
|
|
|
switch (c[2] & 0x60) {
|
|
|
|
case 0x00: r[i].rate = 50; break;
|
|
|
|
case 0x20: r[i].rate = 60; break;
|
|
|
|
case 0x40: r[i].rate = 75; break;
|
|
|
|
case 0x60: r[i].rate = 85; break;
|
|
|
|
}
|
|
|
|
r[i].rates = c[2] & 0x1f;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
c += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
static void
|
2004-11-02 09:54:53 +01:00
|
|
|
get_std_timing_section(Uchar *c, struct std_timings *r,
|
|
|
|
struct edid_version *v)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0;i<STD_TIMINGS;i++){
|
2004-11-02 09:54:53 +01:00
|
|
|
if (VALID_TIMING) {
|
|
|
|
r[i].hsize = HSIZE1;
|
|
|
|
VSIZE1(r[i].vsize);
|
|
|
|
r[i].refresh = REFRESH_R;
|
|
|
|
r[i].id = STD_TIMING_ID;
|
|
|
|
} else {
|
|
|
|
r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0;
|
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
NEXT_STD_TIMING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-21 16:20:48 +02:00
|
|
|
static const unsigned char empty_block[18];
|
|
|
|
|
2003-11-14 17:48:57 +01:00
|
|
|
static void
|
2009-02-18 10:41:26 +01:00
|
|
|
fetch_detailed_block(Uchar *c, struct edid_version *ver,
|
|
|
|
struct detailed_monitor_section *det_mon)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
|
|
|
if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) {
|
2009-02-18 10:41:26 +01:00
|
|
|
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];
|
|
|
|
}
|
2009-05-21 16:20:48 +02:00
|
|
|
} else {
|
2009-02-18 10:41:26 +01:00
|
|
|
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++) {
|
|
|
|
fetch_detailed_block(c, ver, det_mon + i);
|
|
|
|
NEXT_DT_MD_SECTION;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
copy_string(Uchar *c, Uchar *s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
c = c + 5;
|
|
|
|
for (i = 0; (i < 13 && *c != 0x0A); i++)
|
|
|
|
*(s++) = *(c++);
|
|
|
|
*s = 0;
|
|
|
|
while (i-- && (*--s == 0x20)) *s = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2004-11-02 09:54:53 +01:00
|
|
|
get_dst_timing_section(Uchar *c, struct std_timings *t,
|
|
|
|
struct edid_version *v)
|
2003-11-14 17:48:57 +01:00
|
|
|
{
|
|
|
|
int j;
|
|
|
|
c = c + 5;
|
|
|
|
for (j = 0; j < 5; j++) {
|
|
|
|
t[j].hsize = HSIZE1;
|
|
|
|
VSIZE1(t[j].vsize);
|
|
|
|
t[j].refresh = REFRESH_R;
|
|
|
|
t[j].id = STD_TIMING_ID;
|
|
|
|
NEXT_STD_TIMING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_monitor_ranges(Uchar *c, struct monitor_ranges *r)
|
|
|
|
{
|
|
|
|
r->min_v = MIN_V;
|
|
|
|
r->max_v = MAX_V;
|
|
|
|
r->min_h = MIN_H;
|
|
|
|
r->max_h = MAX_H;
|
2004-08-27 14:32:14 +02:00
|
|
|
r->max_clock = 0;
|
|
|
|
if(MAX_CLOCK != 0xff) /* is specified? */
|
|
|
|
r->max_clock = MAX_CLOCK * 10;
|
2004-11-02 09:54:53 +01:00
|
|
|
if (HAVE_2ND_GTF) {
|
|
|
|
r->gtf_2nd_f = F_2ND_GTF;
|
|
|
|
r->gtf_2nd_c = C_2ND_GTF;
|
|
|
|
r->gtf_2nd_m = M_2ND_GTF;
|
|
|
|
r->gtf_2nd_k = K_2ND_GTF;
|
|
|
|
r->gtf_2nd_j = J_2ND_GTF;
|
2008-01-12 07:03:44 +01:00
|
|
|
} else {
|
2004-11-02 09:54:53 +01:00
|
|
|
r->gtf_2nd_f = 0;
|
2008-01-12 07:03:44 +01:00
|
|
|
}
|
|
|
|
if (HAVE_CVT) {
|
|
|
|
r->max_clock_khz = MAX_CLOCK_KHZ;
|
|
|
|
r->max_clock = r->max_clock_khz / 1000;
|
|
|
|
r->maxwidth = MAXWIDTH;
|
|
|
|
r->supported_aspect = SUPPORTED_ASPECT;
|
|
|
|
r->preferred_aspect = PREFERRED_ASPECT;
|
|
|
|
r->supported_blanking = SUPPORTED_BLANKING;
|
|
|
|
r->supported_scaling = SUPPORTED_SCALING;
|
|
|
|
r->preferred_refresh = PREFERRED_REFRESH;
|
|
|
|
} else {
|
|
|
|
r->max_clock_khz = 0;
|
|
|
|
}
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_whitepoint_section(Uchar *c, struct whitePoints *wp)
|
|
|
|
{
|
2008-05-27 13:31:26 +02:00
|
|
|
wp[0].white_x = WHITEX1;
|
|
|
|
wp[0].white_y = WHITEY1;
|
|
|
|
wp[1].white_x = WHITEX2;
|
|
|
|
wp[1].white_y = WHITEY2;
|
|
|
|
wp[0].index = WHITE_INDEX1;
|
|
|
|
wp[1].index = WHITE_INDEX2;
|
|
|
|
wp[0].white_gamma = WHITE_GAMMA1;
|
|
|
|
wp[1].white_gamma = WHITE_GAMMA2;
|
2003-11-14 17:48:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_detailed_timing_section(Uchar *c, struct detailed_timings *r)
|
|
|
|
{
|
|
|
|
r->clock = PIXEL_CLOCK;
|
|
|
|
r->h_active = H_ACTIVE;
|
|
|
|
r->h_blanking = H_BLANK;
|
|
|
|
r->v_active = V_ACTIVE;
|
|
|
|
r->v_blanking = V_BLANK;
|
|
|
|
r->h_sync_off = H_SYNC_OFF;
|
|
|
|
r->h_sync_width = H_SYNC_WIDTH;
|
|
|
|
r->v_sync_off = V_SYNC_OFF;
|
|
|
|
r->v_sync_width = V_SYNC_WIDTH;
|
|
|
|
r->h_size = H_SIZE;
|
|
|
|
r->v_size = V_SIZE;
|
|
|
|
r->h_border = H_BORDER;
|
|
|
|
r->v_border = V_BORDER;
|
|
|
|
r->interlaced = INTERLACED;
|
|
|
|
r->stereo = STEREO;
|
2004-11-02 09:54:53 +01:00
|
|
|
r->stereo_1 = STEREO1;
|
2003-11-14 17:48:57 +01:00
|
|
|
r->sync = SYNC_T;
|
|
|
|
r->misc = MISC;
|
|
|
|
}
|
|
|
|
|
2007-12-28 22:50:18 +01:00
|
|
|
#define MAX_EDID_MINOR 4
|
2003-11-14 17:48:57 +01:00
|
|
|
|
2004-11-02 09:54:53 +01:00
|
|
|
static Bool
|
|
|
|
validate_version(int scrnIndex, struct edid_version *r)
|
|
|
|
{
|
2007-12-28 22:50:18 +01:00
|
|
|
if (r->version != 1) {
|
|
|
|
xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n",
|
|
|
|
r->version);
|
2004-11-02 09:54:53 +01:00
|
|
|
return FALSE;
|
2007-12-28 22:50:18 +01:00
|
|
|
}
|
2006-12-10 17:24:05 +01:00
|
|
|
|
|
|
|
if (r->revision > MAX_EDID_MINOR)
|
|
|
|
xf86DrvMsg(scrnIndex, X_WARNING,
|
|
|
|
"Assuming version 1.%d is compatible with 1.%d\n",
|
|
|
|
r->revision, MAX_EDID_MINOR);
|
|
|
|
|
2004-11-02 09:54:53 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
2008-11-03 18:50:28 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns true if HDMI, false if definitely not or unknown.
|
|
|
|
*/
|
Rework symbol visibility for easier maintenance
Save in a few special cases, _X_EXPORT should not be used in C source
files. Instead, it should be used in headers, and the proper C source
include that header. Some special cases are symbols that need to be
shared between modules, but not expected to be used by external drivers,
and symbols that are accessible via LoaderSymbol/dlopen.
This patch also adds conditionally some new sdk header files, depending
on extensions enabled. These files were added to match pattern for
other extensions/modules, that is, have the headers "deciding" symbol
visibility in the sdk. These headers are:
o Xext/panoramiXsrv.h, Xext/panoramiX.h
o fbpict.h (unconditionally)
o vidmodeproc.h
o mioverlay.h (unconditionally, used only by xaa)
o xfixes.h (unconditionally, symbols required by dri2)
LoaderSymbol and similar functions now don't have different prototypes,
in loaderProcs.h and xf86Module.h, so that both headers can be included,
without the need of defining IN_LOADER.
xf86NewInputDevice() device prototype readded to xf86Xinput.h, but
not exported (and with a comment about it).
2008-12-03 08:43:34 +01:00
|
|
|
Bool
|
2008-11-03 18:50:28 +01:00
|
|
|
xf86MonitorIsHDMI(xf86MonPtr mon)
|
|
|
|
{
|
|
|
|
int i = 0, version, offset;
|
|
|
|
char *edid = NULL;
|
|
|
|
|
|
|
|
if (!mon)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!(mon->flags & EDID_COMPLETE_RAWDATA))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!mon->no_sections)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
edid = (char *)mon->rawData;
|
|
|
|
if (!edid)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* find the CEA extension block */
|
|
|
|
for (i = 1; i <= mon->no_sections; i++)
|
|
|
|
if (edid[i * 128] == 0x02)
|
|
|
|
break;
|
|
|
|
if (i == mon->no_sections + 1)
|
|
|
|
return FALSE;
|
|
|
|
edid += (i * 128);
|
|
|
|
|
|
|
|
version = edid[1];
|
|
|
|
offset = edid[2];
|
|
|
|
if (version < 3 || offset < 4)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/* walk the cea data blocks */
|
|
|
|
for (i = 4; i < offset; i += (edid[i] & 0x1f) + 1) {
|
|
|
|
char *x = edid + i;
|
|
|
|
|
|
|
|
/* find a vendor specific block */
|
|
|
|
if ((x[0] & 0xe0) >> 5 == 0x03) {
|
|
|
|
int oui = (x[3] << 16) + (x[2] << 8) + x[1];
|
|
|
|
|
|
|
|
/* find the HDMI vendor OUI */
|
|
|
|
if (oui == 0x000c03)
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* guess it's not HDMI after all */
|
|
|
|
return FALSE;
|
|
|
|
}
|