/*-
* Copyright (c) 2006 Itronix Inc.
* All rights reserved.
*
* Written by Garrett D'Amore for Itronix Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of Itronix Inc. may not be used to endorse
* or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
x = EDID_STD_TIMING_HRES(data);
switch (EDID_STD_TIMING_RATIO(data)) {
case EDID_STD_TIMING_RATIO_16_10:
y = x * 10 / 16;
break;
case EDID_STD_TIMING_RATIO_4_3:
y = x * 3 / 4;
break;
case EDID_STD_TIMING_RATIO_5_4:
y = x * 4 / 5;
break;
case EDID_STD_TIMING_RATIO_16_9:
default:
y = x * 9 / 16;
break;
}
f = EDID_STD_TIMING_VFREQ(data);
/* first try to lookup the mode as a DMT timing */
snprintf(name, sizeof(name), "%dx%dx%d", x, y, f);
if ((lookup = edid_mode_lookup_list(name)) != NULL) {
*vmp = *lookup;
} else {
/* failing that, calculate it using gtf */
/*
* Hmm. I'm not using alternate GTF timings, which
* could, in theory, be present.
*/
vesagtf_mode(x, y, f, vmp);
}
return 1;
}
static void bump_preferred_mode(struct edid_info *edid, struct videomode *m)
{
/*
* XXX
* Iiyama 4800 series monitors may have their native resolution in the
* 2nd detailed timing descriptor instead of the 1st. Try to detect
* that here and pick the native mode anyway.
*/
if (edid->edid_preferred_mode == NULL) {
edid->edid_preferred_mode = m;
} else if ((strncmp((char*)edid->edid_vendor, "IVM", 3) == 0) &&
(edid->edid_product == 0x4800) &&
(edid->edid_preferred_mode->dot_clock < m->dot_clock))
edid->edid_preferred_mode = m;
}
case EDID_DESC_BLOCK_TYPE_NAME:
/* copy the product name into place */
memcpy(edid->edid_productname,
data + EDID_DESC_ASCII_DATA_OFFSET,
EDID_DESC_ASCII_DATA_LEN);
break;
case EDID_DESC_BLOCK_TYPE_STD_TIMING:
data += EDID_DESC_STD_TIMING_START;
for (i = 0; i < EDID_DESC_STD_TIMING_COUNT; i++) {
if (edid_std_timing(data, &mode)) {
/* Does this mode already exist? */
exist_mode = edid_search_mode(edid, &mode);
if (exist_mode == NULL) {
edid->edid_modes[edid->edid_nmodes] =
mode;
edid->edid_nmodes++;
}
}
data += 2;
}
break;
case EDID_DESC_BLOCK_TYPE_COLOR_POINT:
/* XXX: not implemented yet */
break;
}
}
/*
* Gets EDID version in BCD, e.g. EDID v1.3 returned as 0x0103
*/
int
edid_parse(uint8_t *data, struct edid_info *edid)
{
uint16_t manfid, estmodes;
const struct videomode *vmp;
int i;
const char *name;
int max_dotclock = 0;
int mhz;
/* lookup established modes */
edid->edid_nmodes = 0;
edid->edid_preferred_mode = NULL;
estmodes = EDID_EST_TIMING(data);
/* Iterate in esztablished timing order */
for (i = 15; i >= 0; i--) {
if (estmodes & (1 << i)) {
vmp = edid_mode_lookup_list(_edid_modes[i]);
if (vmp != NULL) {
edid->edid_modes[edid->edid_nmodes] = *vmp;
edid->edid_nmodes++;
}
#ifdef DIAGNOSTIC
else
printf("no data for est. mode %s\n",
_edid_modes[i]);
#endif
}
}
/* do standard timing section */
for (i = 0; i < EDID_STD_TIMING_COUNT; i++) {
struct videomode mode, *exist_mode;
if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2,
&mode)) {
/* Does this mode already exist? */
exist_mode = edid_search_mode(edid, &mode);
if (exist_mode == NULL) {
edid->edid_modes[edid->edid_nmodes] = mode;
edid->edid_nmodes++;
}
}
}
/* do detailed timings and descriptors */
for (i = 0; i < EDID_BLOCK_COUNT; i++) {
edid_block(edid, data + EDID_OFFSET_DESC_BLOCK +
i * EDID_BLOCK_SIZE);
}
/*
* XXX
* some monitors lie about their maximum supported dot clock
* by claiming to support modes which need a higher dot clock
* than the stated maximum.
* For sanity's sake we bump it to the highest dot clock we find
* in the list of supported modes
*/
for (i = 0; i < edid->edid_nmodes; i++)
if (edid->edid_modes[i].dot_clock > max_dotclock)
max_dotclock = edid->edid_modes[i].dot_clock;
#ifdef _KERNEL
aprint_debug("max_dotclock according to supported modes: %d\n",
max_dotclock);
#endif
mhz = (max_dotclock + 999) / 1000;
if (edid->edid_have_range) {
if (mhz > edid->edid_range.er_max_clock)
edid->edid_range.er_max_clock = mhz;
} else
edid->edid_range.er_max_clock = mhz;