From 52f9028c84baea81230dc673b756552e8e90aecd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 11:21:06 +1000 Subject: Initial import of modesetting for intel driver in DRM --- linux-core/drm_edid.c | 515 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 linux-core/drm_edid.c (limited to 'linux-core/drm_edid.c') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c new file mode 100644 index 00000000..3c123751 --- /dev/null +++ b/linux-core/drm_edid.c @@ -0,0 +1,515 @@ +#include +#include +#include "drmP.h" +#include "intel_drv.h" + +/* + * DDC/EDID probing rippped off from FB layer + */ + +#include "edid.h" +#define DDC_ADDR 0x50 + +#ifdef BIG_ENDIAN +#error "EDID structure is little endian, need big endian versions" +#endif + +struct est_timings { + u8 t1; + u8 t2; + u8 mfg_rsvd; +} __attribute__((packed)); + +struct std_timing { + u8 hsize; /* need to multiply by 8 then add 248 */ + u8 vfreq:6; /* need to add 60 */ + u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ +} __attribute__((packed)); + +/* If detailed data is pixel timing */ +struct detailed_pixel_timing { + u8 hactive_lo; + u8 hblank_lo; + u8 hblank_hi:4; + u8 hactive_hi:4; + u8 vactive_lo; + u8 vblank_lo; + u8 vblank_hi:4; + u8 vactive_hi:4; + u8 hsync_offset_lo; + u8 hsync_pulse_width_lo; + u8 vsync_pulse_width_lo:4; + u8 vsync_offset_lo:4; + u8 hsync_pulse_width_hi:2; + u8 hsync_offset_hi:2; + u8 vsync_pulse_width_hi:2; + u8 vsync_offset_hi:2; + u8 width_mm_lo; + u8 height_mm_lo; + u8 height_mm_hi:4; + u8 width_mm_hi:4; + u8 hborder; + u8 vborder; + u8 unknown0:1; + u8 vsync_positive:1; + u8 hsync_positive:1; + u8 separate_sync:2; + u8 stereo:1; + u8 unknown6:1; + u8 interlaced:1; +} __attribute__((packed)); + +/* If it's not pixel timing, it'll be one of the below */ +struct detailed_data_string { + u8 str[13]; +} __attribute__((packed)); + +struct detailed_data_monitor_range { + u8 min_vfreq; + u8 max_vfreq; + u8 min_hfreq_khz; + u8 max_hfreq_khz; + u8 pixel_clock_mhz; /* need to multiply by 10 */ + u16 sec_gtf_toggle; /* A000=use above, 20=use below */ + u8 hfreq_start_khz; /* need to multiply by 2 */ + u8 c; /* need to divide by 2 */ + u16 m; + u8 k; + u8 j; /* need to divide by 2 */ +} __attribute__((packed)); + +struct detailed_data_wpindex { + u8 white_y_lo:2; + u8 white_x_lo:2; + u8 pad:4; + u8 white_x_hi; + u8 white_y_hi; + u8 gamma; /* need to divide by 100 then add 1 */ +} __attribute__((packed)); + +struct detailed_data_color_point { + u8 windex1; + u8 wpindex1[3]; + u8 windex2; + u8 wpindex2[3]; +} __attribute__((packed)); + +struct detailed_non_pixel { + u8 pad1; + u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name + fb=color point data, fa=standard timing data, + f9=undefined, f8=mfg. reserved */ + u8 pad2; + union { + struct detailed_data_string str; + struct detailed_data_monitor_range range; + struct detailed_data_wpindex color; + struct std_timing timings[5]; + } data; +} __attribute__((packed)); + +#define EDID_DETAIL_STD_MODES 0xfa +#define EDID_DETAIL_CPDATA 0xfb +#define EDID_DETAIL_NAME 0xfc +#define EDID_DETAIL_RANGE 0xfd +#define EDID_DETAIL_STRING 0xfe +#define EDID_DETAIL_SERIAL 0xff + +struct detailed_timing { + u16 pixel_clock; /* need to multiply by 10 KHz */ + union { + struct detailed_pixel_timing pixel_data; + struct detailed_non_pixel other_data; + } data; +} __attribute__((packed)); + +struct edid { + u8 header[8]; + /* Vendor & product info */ + u16 mfg_id; + u16 prod_code; + u32 serial; + u8 mfg_week; + u8 mfg_year; + /* EDID version */ + u8 version; + u8 revision; + /* Display info: */ + /* input definition */ + u8 serration_vsync:1; + u8 sync_on_green:1; + u8 composite_sync:1; + u8 separate_syncs:1; + u8 blank_to_black:1; + u8 video_level:2; + u8 digital:1; /* bits below must be zero if set */ + u8 width_cm; + u8 height_cm; + u8 gamma; + /* feature support */ + u8 default_gtf:1; + u8 preferred_timing:1; + u8 standard_color:1; + u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ + u8 pm_active_off:1; + u8 pm_suspend:1; + u8 pm_standby:1; + /* Color characteristics */ + u8 red_green_lo; + u8 black_white_lo; + u8 red_x; + u8 red_y; + u8 green_x; + u8 green_y; + u8 blue_x; + u8 blue_y; + u8 white_x; + u8 white_y; + /* Est. timings and mfg rsvd timings*/ + struct est_timings established_timings; + /* Standard timings 1-8*/ + struct std_timing standard_timings[8]; + /* Detailing timings 1-4 */ + struct detailed_timing detailed_timings[4]; + /* Number of 128 byte ext. blocks */ + u8 extensions; + /* Checksum */ + u8 checksum; +} __attribute__((packed)); + +static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; + +static bool edid_valid(struct edid *edid) +{ + int i; + u8 csum = 0; + u8 *raw_edid = (u8 *)edid; + + if (memcmp(edid->header, edid_header, sizeof(edid_header))) + goto bad; + if (edid->version != 1) + goto bad; + if (edid->revision <= 0 || edid->revision > 3) + goto bad; + + for (i = 0; i < EDID_LENGTH; i++) + csum += raw_edid[i]; + if (csum) + goto bad; + + return 1; + +bad: + return 0; +} + +/** + * drm_mode_std - convert standard mode info (width, height, refresh) into mode + * @t: standard timing params + * + * Take the standard timing params (in this case width, aspect, and refresh) + * and convert them into a real mode using CVT. + * + * Punts for now. + */ +struct drm_display_mode *drm_mode_std(struct std_timing *t) +{ +// struct fb_videomode mode; + +// fb_find_mode_cvt(&mode, 0, 0); + /* JJJ: convert to drm_display_mode */ + struct drm_display_mode *mode; + int hsize = t->hsize * 8 + 248, vsize; + + mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!mode) + return NULL; + + if (t->aspect_ratio == 0) + vsize = (hsize * 10) / 16; + else if (t->aspect_ratio == 1) + vsize = (hsize * 3) / 4; + else if (t->aspect_ratio == 2) + vsize = (hsize * 4) / 5; + else + vsize = (hsize * 9) / 16; + + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", hsize, vsize); + + return mode; +} + +struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing, + bool preferred) +{ + struct drm_display_mode *mode; + struct detailed_pixel_timing *pt = &timing->data.pixel_data; + + if (pt->stereo) { + printk(KERN_ERR "stereo mode not supported\n"); + return NULL; + } + if (!pt->separate_sync) { + printk(KERN_ERR "integrated sync not supported\n"); + return NULL; + } + + mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + if (!mode) + return NULL; + + mode->type = DRM_MODE_TYPE_DRIVER; + mode->type |= preferred ? DRM_MODE_TYPE_PREFERRED : 0; + mode->clock = timing->pixel_clock / 100; + + mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; + mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | + pt->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + + ((pt->hsync_pulse_width_hi << 8) | + pt->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((pt->hblank_hi << 8) | pt->hblank_lo); + + mode->vdisplay = (pt->vactive_hi << 8) | pt->vactive_lo; + mode->vsync_start = mode->vdisplay + ((pt->vsync_offset_hi << 8) | + pt->vsync_offset_lo); + mode->vsync_end = mode->vsync_start + + ((pt->vsync_pulse_width_hi << 8) | + pt->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + ((pt->vblank_hi << 8) | pt->vblank_lo); + + snprintf(mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d", mode->hdisplay, + mode->vdisplay); + + if (pt->interlaced) + mode->flags |= V_INTERLACE; + + mode->flags |= pt->hsync_positive ? V_PHSYNC : V_NHSYNC; + mode->flags |= pt->vsync_positive ? V_PVSYNC : V_NVSYNC; + + return mode; +} + +static struct drm_display_mode established_modes[] = { + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, + 968, 1056, 0, 600, 601, 605, 628, 0, + V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, + 896, 1024, 0, 600, 601, 603, 625, 0, + V_PHSYNC | V_PVSYNC) }, /* 800x600@56Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, + 720, 840, 0, 480, 481, 484, 500, 0, + V_NHSYNC | V_NVSYNC) }, /* 640x480@75Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, + 704, 832, 0, 480, 489, 491, 520, 0, + V_NHSYNC | V_NVSYNC) }, /* 640x480@72Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, + 768, 864, 0, 480, 483, 486, 525, 0, + V_NHSYNC | V_NVSYNC) }, /* 640x480@67Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, + 752, 800, 0, 480, 490, 492, 525, 0, + V_NHSYNC | V_NVSYNC) }, /* 640x480@60Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, + 846, 900, 0, 400, 421, 423, 449, 0, + V_NHSYNC | V_NVSYNC) }, /* 720x400@88Hz */ + { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, + 846, 900, 0, 400, 412, 414, 449, 0, + V_NHSYNC | V_PVSYNC) }, /* 720x400@70Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + V_PHSYNC | V_PVSYNC) }, /* 1280x1024@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, + 1136, 1312, 0, 768, 769, 772, 800, 0, + V_PHSYNC | V_PVSYNC) }, /* 1024x768@75Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, + 1184, 1328, 0, 768, 771, 777, 806, 0, + V_NHSYNC | V_NVSYNC) }, /* 1024x768@70Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + V_NHSYNC | V_NVSYNC) }, /* 1024x768@60Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, + 1208, 1264, 0, 768, 768, 776, 817, 0, + V_PHSYNC | V_PVSYNC | V_INTERLACE) }, /* 1024x768@43Hz */ + { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, + 928, 1152, 0, 624, 625, 628, 667, 0, + V_NHSYNC | V_NVSYNC) }, /* 832x624@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, + 896, 1056, 0, 600, 601, 604, 625, 0, + V_PHSYNC | V_PVSYNC) }, /* 800x600@75Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, + 976, 1040, 0, 600, 637, 643, 666, 0, + V_PHSYNC | V_PVSYNC) }, /* 800x600@72Hz */ + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, + 1344, 1600, 0, 864, 865, 868, 900, 0, + V_PHSYNC | V_PVSYNC) }, /* 1152x864@75Hz */ +}; + +#define EDID_EST_TIMINGS 16 +#define EDID_STD_TIMINGS 8 +#define EDID_DETAILED_TIMINGS 4 + +/** + * 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 add_established_modes(struct drm_output *output, struct edid *edid) +{ + unsigned long est_bits = edid->established_timings.t1 | + (edid->established_timings.t2 << 8) | + ((edid->established_timings.mfg_rsvd & 0x80) << 9); + int i, modes = 0; + + for (i = 0; i <= EDID_EST_TIMINGS; i++) + if (est_bits & (1<standard_timings[i]; + + /* If std timings bytes are 1, 1 it's empty */ + if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) + continue; + + drm_mode_probed_add(output, + drm_mode_std(&edid->standard_timings[i])); + modes++; + } + + return modes; +} + +/** + * add_detailed_modes - get detailed mode info from EDID data + * @edid: EDID block to scan + * + * Some of the detailed timing sections may contain mode information. Grab + * it and add it to the list. + */ +static int add_detailed_info(struct drm_output *output, struct edid *edid) +{ + int i, j, modes = 0; + bool preferred = 0; + + for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { + struct detailed_timing *timing = &edid->detailed_timings[i]; + struct detailed_non_pixel *data = &timing->data.other_data; + + /* EDID up to and including 1.2 may put monitor info here */ + if (edid->version == 1 && edid->revision < 3) + continue; + + /* Detailed mode timing */ + if (timing->pixel_clock) { + if (i == 0 && edid->preferred_timing) + preferred = 1; + drm_mode_probed_add(output, + drm_mode_detailed(timing, preferred)); + modes++; + continue; + } + + /* Other timing or info */ + switch (data->type) { + case EDID_DETAIL_SERIAL: + break; + case EDID_DETAIL_STRING: + break; + case EDID_DETAIL_RANGE: + break; + case EDID_DETAIL_NAME: + break; + case EDID_DETAIL_CPDATA: + break; + case EDID_DETAIL_STD_MODES: + /* Five modes per detailed section */ + for (j = 0; j < 5; i++) { + struct std_timing *std; + + std = &data->data.timings[j]; + drm_mode_probed_add(output, drm_mode_std(std)); + modes++; + } + break; + default: + break; + } + } + + return modes; +} + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @output: output we're probing + * @adapter: i2c adapter to use for DDC + * + * Poke the given output's i2c channel to grab EDID data if possible. If we + * get any, add the specified modes to the output's mode list. + * + * Return number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter) +{ + struct edid *edid; + u8 *raw_edid; + int i, est_modes, std_modes, det_modes; + + edid = (struct edid *)fb_ddc_read(adapter); + + if (!edid) { + dev_warn(&output->dev->pdev->dev, "no EDID data\n"); + goto out_err; + } + + if (!edid_valid(edid)) { + dev_warn(&output->dev->pdev->dev, "EDID invalid, ignoring.\n"); + goto out_err; + } + + est_modes = add_established_modes(output, edid); + std_modes = add_standard_modes(output, edid); + det_modes = add_detailed_info(output, edid); + printk(KERN_ERR "est modes: %d, std_modes: %d, det_modes: %d\n", + est_modes, std_modes, det_modes); + + raw_edid = (u8 *)edid; + printk(KERN_ERR "EDID:\n" KERN_ERR); + for (i = 0; i < EDID_LENGTH; i++) { + if (i != 0 && ((i % 16) == 0)) + printk("\n" KERN_ERR); + printk("%02x", raw_edid[i] & 0xff); + } + printk("\n"); + + printk(KERN_ERR "EDID info:\n"); + printk(KERN_ERR " mfg_id: 0x%04x\n", edid->mfg_id); + printk(KERN_ERR " digital? %s\n", edid->digital ? "Yes" : "No"); + printk(KERN_ERR " extensions: %d\n", edid->extensions); + + return est_modes + std_modes + det_modes; + +out_err: + kfree(edid); + return 0; +} +EXPORT_SYMBOL(drm_add_edid_modes); -- cgit v1.2.3 From 5bffbd6e275efffbb649c20c528a11412ccf99cd Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 5 Apr 2007 13:34:50 +1000 Subject: initial userspace interface to get modes --- linux-core/drm_edid.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'linux-core/drm_edid.c') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 3c123751..bf1ea94c 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -212,7 +212,8 @@ bad: * * Punts for now. */ -struct drm_display_mode *drm_mode_std(struct std_timing *t) +struct drm_display_mode *drm_mode_std(struct drm_device *dev, + struct std_timing *t) { // struct fb_videomode mode; @@ -221,7 +222,7 @@ struct drm_display_mode *drm_mode_std(struct std_timing *t) struct drm_display_mode *mode; int hsize = t->hsize * 8 + 248, vsize; - mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + mode = drm_crtc_mode_create(dev); if (!mode) return NULL; @@ -239,7 +240,8 @@ struct drm_display_mode *drm_mode_std(struct std_timing *t) return mode; } -struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing, +struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, + struct detailed_timing *timing, bool preferred) { struct drm_display_mode *mode; @@ -254,7 +256,7 @@ struct drm_display_mode *drm_mode_detailed(struct detailed_timing *timing, return NULL; } - mode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); + mode = drm_crtc_mode_create(dev); if (!mode) return NULL; @@ -357,6 +359,7 @@ static struct drm_display_mode established_modes[] = { */ static int add_established_modes(struct drm_output *output, struct edid *edid) { + struct drm_device *dev = output->dev; unsigned long est_bits = edid->established_timings.t1 | (edid->established_timings.t2 << 8) | ((edid->established_timings.mfg_rsvd & 0x80) << 9); @@ -365,7 +368,7 @@ static int add_established_modes(struct drm_output *output, struct edid *edid) for (i = 0; i <= EDID_EST_TIMINGS; i++) if (est_bits & (1<dev; for (i = 0; i < EDID_STD_TIMINGS; i++) { struct std_timing *t = &edid->standard_timings[i]; @@ -391,7 +395,7 @@ static int add_standard_modes(struct drm_output *output, struct edid *edid) continue; drm_mode_probed_add(output, - drm_mode_std(&edid->standard_timings[i])); + drm_mode_std(dev, &edid->standard_timings[i])); modes++; } @@ -409,6 +413,7 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) { int i, j, modes = 0; bool preferred = 0; + struct drm_device *dev = output->dev; for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { struct detailed_timing *timing = &edid->detailed_timings[i]; @@ -423,7 +428,7 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) if (i == 0 && edid->preferred_timing) preferred = 1; drm_mode_probed_add(output, - drm_mode_detailed(timing, preferred)); + drm_mode_detailed(dev, timing, preferred)); modes++; continue; } @@ -446,7 +451,7 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) struct std_timing *std; std = &data->data.timings[j]; - drm_mode_probed_add(output, drm_mode_std(std)); + drm_mode_probed_add(output, drm_mode_std(dev, std)); modes++; } break; -- cgit v1.2.3 From 6f3534a13abb0c8afb157511d0871dbc35bc403d Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 5 Apr 2007 09:21:31 -0700 Subject: Add copyrights before I forget --- linux-core/drm_edid.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'linux-core/drm_edid.c') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 3c123751..7e254eee 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2007 Intel Corporation + * Jesse Barnes + */ + #include #include #include "drmP.h" @@ -73,7 +78,7 @@ struct detailed_data_monitor_range { u16 sec_gtf_toggle; /* A000=use above, 20=use below */ u8 hfreq_start_khz; /* need to multiply by 2 */ u8 c; /* need to divide by 2 */ - u16 m; + u16 m; /* FIXME: byte order */ u8 k; u8 j; /* need to divide by 2 */ } __attribute__((packed)); @@ -126,9 +131,9 @@ struct detailed_timing { struct edid { u8 header[8]; /* Vendor & product info */ - u16 mfg_id; - u16 prod_code; - u32 serial; + u16 mfg_id; /* FIXME: byte order */ + u16 prod_code; /* FIXME: byte order */ + u32 serial; /* FIXME: byte order */ u8 mfg_week; u8 mfg_year; /* EDID version */ -- cgit v1.2.3 From 13d4ea90c09fa834eb6eecaa082780aace78dac7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sat, 7 Apr 2007 19:24:09 -0700 Subject: various cleanups to EDID code: - pull in FB DDC code (we'll have to rewrite it anyway it appears) - add comments - note a few FIXMEs - make it less quiet, and more informative when it actually does print --- linux-core/drm_edid.c | 240 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 175 insertions(+), 65 deletions(-) (limited to 'linux-core/drm_edid.c') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index 33018da6..fcd97d67 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -1,18 +1,15 @@ /* * Copyright (c) 2007 Intel Corporation * Jesse Barnes + * + * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from + * FB layer. */ - #include -#include +#include #include "drmP.h" -#include "intel_drv.h" - -/* - * DDC/EDID probing rippped off from FB layer - */ -#include "edid.h" +#define EDID_LENGTH 128 #define DDC_ADDR 0x50 #ifdef BIG_ENDIAN @@ -75,7 +72,7 @@ struct detailed_data_monitor_range { u8 min_hfreq_khz; u8 max_hfreq_khz; u8 pixel_clock_mhz; /* need to multiply by 10 */ - u16 sec_gtf_toggle; /* A000=use above, 20=use below */ + u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ u8 hfreq_start_khz; /* need to multiply by 2 */ u8 c; /* need to divide by 2 */ u16 m; /* FIXME: byte order */ @@ -114,14 +111,14 @@ struct detailed_non_pixel { } __attribute__((packed)); #define EDID_DETAIL_STD_MODES 0xfa -#define EDID_DETAIL_CPDATA 0xfb -#define EDID_DETAIL_NAME 0xfc -#define EDID_DETAIL_RANGE 0xfd -#define EDID_DETAIL_STRING 0xfe -#define EDID_DETAIL_SERIAL 0xff +#define EDID_DETAIL_MONITOR_CPDATA 0xfb +#define EDID_DETAIL_MONITOR_NAME 0xfc +#define EDID_DETAIL_MONITOR_RANGE 0xfd +#define EDID_DETAIL_MONITOR_STRING 0xfe +#define EDID_DETAIL_MONITOR_SERIAL 0xff struct detailed_timing { - u16 pixel_clock; /* need to multiply by 10 KHz */ + u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ union { struct detailed_pixel_timing pixel_data; struct detailed_non_pixel other_data; @@ -184,6 +181,14 @@ struct edid { static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; +/** + * edid_valid - sanity check EDID data + * @edid: EDID data + * + * Sanity check the EDID block by looking at the header, the version number + * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's + * valid. + */ static bool edid_valid(struct edid *edid) { int i; @@ -215,7 +220,8 @@ bad: * Take the standard timing params (in this case width, aspect, and refresh) * and convert them into a real mode using CVT. * - * Punts for now. + * Punts for now, but should eventually use the FB layer's CVT based mode + * generation code. */ struct drm_display_mode *drm_mode_std(struct drm_device *dev, struct std_timing *t) @@ -245,19 +251,28 @@ struct drm_display_mode *drm_mode_std(struct drm_device *dev, return mode; } +/** + * drm_mode_detailed - create a new mode from an EDID detailed timing section + * @timing: EDID detailed timing info + * @preferred: is this a preferred mode? + * + * An EDID detailed timing block contains enough info for us to create and + * return a new struct drm_display_mode. The @preferred flag will be set + * if this is the display's preferred timing, and we'll use it to indicate + * to the other layers that this mode is desired. + */ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, - struct detailed_timing *timing, - bool preferred) + struct detailed_timing *timing) { struct drm_display_mode *mode; struct detailed_pixel_timing *pt = &timing->data.pixel_data; if (pt->stereo) { - printk(KERN_ERR "stereo mode not supported\n"); + printk(KERN_WARNING "stereo mode not supported\n"); return NULL; } if (!pt->separate_sync) { - printk(KERN_ERR "integrated sync not supported\n"); + printk(KERN_WARNING "integrated sync not supported\n"); return NULL; } @@ -266,7 +281,6 @@ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, return NULL; mode->type = DRM_MODE_TYPE_DRIVER; - mode->type |= preferred ? DRM_MODE_TYPE_PREFERRED : 0; mode->clock = timing->pixel_clock / 100; mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; @@ -297,7 +311,10 @@ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, return mode; } -static struct drm_display_mode established_modes[] = { +/* + * Detailed mode info for the EDID "established modes" data to use. + */ +static struct drm_display_mode edid_est_modes[] = { { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628, 0, V_PHSYNC | V_PVSYNC) }, /* 800x600@60Hz */ @@ -372,8 +389,9 @@ static int add_established_modes(struct drm_output *output, struct edid *edid) for (i = 0; i <= EDID_EST_TIMINGS; i++) if (est_bits & (1<dev; + int i, modes = 0; for (i = 0; i < EDID_STD_TIMINGS; i++) { struct std_timing *t = &edid->standard_timings[i]; + struct drm_display_mode *newmode; /* If std timings bytes are 1, 1 it's empty */ if (t->hsize == 1 && (t->aspect_ratio | t->vfreq) == 1) continue; - drm_mode_probed_add(output, - drm_mode_std(dev, &edid->standard_timings[i])); + newmode = drm_mode_std(dev, &edid->standard_timings[i]); + drm_mode_probed_add(output, newmode); modes++; } @@ -416,13 +435,13 @@ static int add_standard_modes(struct drm_output *output, struct edid *edid) */ static int add_detailed_info(struct drm_output *output, struct edid *edid) { - int i, j, modes = 0; - bool preferred = 0; struct drm_device *dev = output->dev; + int i, j, modes = 0; for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { struct detailed_timing *timing = &edid->detailed_timings[i]; struct detailed_non_pixel *data = &timing->data.other_data; + struct drm_display_mode *newmode; /* EDID up to and including 1.2 may put monitor info here */ if (edid->version == 1 && edid->revision < 3) @@ -430,33 +449,38 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) /* Detailed mode timing */ if (timing->pixel_clock) { + newmode = drm_mode_detailed(dev, timing); + /* First detailed mode is preferred */ if (i == 0 && edid->preferred_timing) - preferred = 1; - drm_mode_probed_add(output, - drm_mode_detailed(dev, timing, preferred)); + newmode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(output, newmode); + modes++; continue; } /* Other timing or info */ switch (data->type) { - case EDID_DETAIL_SERIAL: + case EDID_DETAIL_MONITOR_SERIAL: break; - case EDID_DETAIL_STRING: + case EDID_DETAIL_MONITOR_STRING: break; - case EDID_DETAIL_RANGE: + case EDID_DETAIL_MONITOR_RANGE: + /* Get monitor range data */ break; - case EDID_DETAIL_NAME: + case EDID_DETAIL_MONITOR_NAME: break; - case EDID_DETAIL_CPDATA: + case EDID_DETAIL_MONITOR_CPDATA: break; case EDID_DETAIL_STD_MODES: /* Five modes per detailed section */ for (j = 0; j < 5; i++) { struct std_timing *std; + struct drm_display_mode *newmode; std = &data->data.timings[j]; - drm_mode_probed_add(output, drm_mode_std(dev, std)); + newmode = drm_mode_std(dev, std); + drm_mode_probed_add(output, newmode); modes++; } break; @@ -468,6 +492,108 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) return modes; } +#define DDC_ADDR 0x50 + +static unsigned char *drm_do_probe_ddc_edid(struct i2c_adapter *adapter) +{ + unsigned char start = 0x0; + unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); + struct i2c_msg msgs[] = { + { + .addr = DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &start, + }, { + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = EDID_LENGTH, + .buf = buf, + } + }; + + if (!buf) { + dev_warn(&adapter->dev, "unable to allocate memory for EDID " + "block.\n"); + return NULL; + } + + if (i2c_transfer(adapter, msgs, 2) == 2) + return buf; + + dev_info(&adapter->dev, "unable to read EDID block.\n"); + kfree(buf); + return NULL; +} + +static unsigned char *drm_ddc_read(struct i2c_adapter *adapter) +{ + struct i2c_algo_bit_data *algo_data = adapter->algo_data; + unsigned char *edid = NULL; + int i, j; + + /* + * Startup the bus: + * Set clock line high (but give it time to come up) + * Then set clock & data low + */ + algo_data->setscl(algo_data->data, 1); + udelay(550); /* startup delay */ + algo_data->setscl(algo_data->data, 0); + algo_data->setsda(algo_data->data, 0); + + for (i = 0; i < 3; i++) { + /* For some old monitors we need the + * following process to initialize/stop DDC + */ + algo_data->setsda(algo_data->data, 0); + msleep(13); + + algo_data->setscl(algo_data->data, 1); + for (j = 0; j < 5; j++) { + msleep(10); + if (algo_data->getscl(algo_data->data)) + break; + } + if (j == 5) + continue; + + algo_data->setsda(algo_data->data, 0); + msleep(15); + algo_data->setscl(algo_data->data, 0); + msleep(15); + algo_data->setsda(algo_data->data, 1); + msleep(15); + + /* Do the real work */ + edid = drm_do_probe_ddc_edid(adapter); + algo_data->setsda(algo_data->data, 0); + algo_data->setscl(algo_data->data, 0); + msleep(15); + + algo_data->setscl(algo_data->data, 1); + for (j = 0; j < 10; j++) { + msleep(10); + if (algo_data->getscl(algo_data->data)) + break; + } + + algo_data->setsda(algo_data->data, 1); + msleep(15); + algo_data->setscl(algo_data->data, 0); + if (edid) + break; + } + /* Release the DDC lines when done or the Apple Cinema HD display + * will switch off + */ + algo_data->setsda(algo_data->data, 0); + algo_data->setscl(algo_data->data, 0); + algo_data->setscl(algo_data->data, 1); + + return edid; +} + /** * drm_add_edid_modes - add modes from EDID data, if available * @output: output we're probing @@ -481,42 +607,26 @@ static int add_detailed_info(struct drm_output *output, struct edid *edid) int drm_add_edid_modes(struct drm_output *output, struct i2c_adapter *adapter) { struct edid *edid; - u8 *raw_edid; - int i, est_modes, std_modes, det_modes; - - edid = (struct edid *)fb_ddc_read(adapter); + int num_modes = 0; + edid = (struct edid *)drm_ddc_read(adapter); if (!edid) { - dev_warn(&output->dev->pdev->dev, "no EDID data\n"); + dev_warn(&output->dev->pdev->dev, "%s: no EDID data\n", + output->name); goto out_err; } if (!edid_valid(edid)) { - dev_warn(&output->dev->pdev->dev, "EDID invalid, ignoring.\n"); + dev_warn(&output->dev->pdev->dev, "%s: EDID invalid.\n", + output->name); goto out_err; } - est_modes = add_established_modes(output, edid); - std_modes = add_standard_modes(output, edid); - det_modes = add_detailed_info(output, edid); - printk(KERN_ERR "est modes: %d, std_modes: %d, det_modes: %d\n", - est_modes, std_modes, det_modes); - - raw_edid = (u8 *)edid; - printk(KERN_ERR "EDID:\n" KERN_ERR); - for (i = 0; i < EDID_LENGTH; i++) { - if (i != 0 && ((i % 16) == 0)) - printk("\n" KERN_ERR); - printk("%02x", raw_edid[i] & 0xff); - } - printk("\n"); - - printk(KERN_ERR "EDID info:\n"); - printk(KERN_ERR " mfg_id: 0x%04x\n", edid->mfg_id); - printk(KERN_ERR " digital? %s\n", edid->digital ? "Yes" : "No"); - printk(KERN_ERR " extensions: %d\n", edid->extensions); + num_modes += add_established_modes(output, edid); + num_modes += add_standard_modes(output, edid); + num_modes += add_detailed_info(output, edid); - return est_modes + std_modes + det_modes; + return num_modes; out_err: kfree(edid); -- cgit v1.2.3 From 183cbd92dd016f8935f9b58ef9345fde1391173e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 10 Apr 2007 09:47:37 -0700 Subject: Finish bringing in LVDS code, re-add to Makefile. Needed other changes too: - move EDID structures to drm_edid.h - add EDID info structure to drm_output - add a few routines to intel_display for getting current mode info - add some prototypes to intel_drv.h and drm_crtc.h --- linux-core/drm_edid.c | 173 +------------------------------------------------- 1 file changed, 3 insertions(+), 170 deletions(-) (limited to 'linux-core/drm_edid.c') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index fcd97d67..b79bc2db 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -4,181 +4,14 @@ * * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from * FB layer. + * Copyright (C) 2006 Dennis Munsie */ #include #include #include "drmP.h" +#include "drm_edid.h" -#define EDID_LENGTH 128 -#define DDC_ADDR 0x50 - -#ifdef BIG_ENDIAN -#error "EDID structure is little endian, need big endian versions" -#endif - -struct est_timings { - u8 t1; - u8 t2; - u8 mfg_rsvd; -} __attribute__((packed)); - -struct std_timing { - u8 hsize; /* need to multiply by 8 then add 248 */ - u8 vfreq:6; /* need to add 60 */ - u8 aspect_ratio:2; /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ -} __attribute__((packed)); - -/* If detailed data is pixel timing */ -struct detailed_pixel_timing { - u8 hactive_lo; - u8 hblank_lo; - u8 hblank_hi:4; - u8 hactive_hi:4; - u8 vactive_lo; - u8 vblank_lo; - u8 vblank_hi:4; - u8 vactive_hi:4; - u8 hsync_offset_lo; - u8 hsync_pulse_width_lo; - u8 vsync_pulse_width_lo:4; - u8 vsync_offset_lo:4; - u8 hsync_pulse_width_hi:2; - u8 hsync_offset_hi:2; - u8 vsync_pulse_width_hi:2; - u8 vsync_offset_hi:2; - u8 width_mm_lo; - u8 height_mm_lo; - u8 height_mm_hi:4; - u8 width_mm_hi:4; - u8 hborder; - u8 vborder; - u8 unknown0:1; - u8 vsync_positive:1; - u8 hsync_positive:1; - u8 separate_sync:2; - u8 stereo:1; - u8 unknown6:1; - u8 interlaced:1; -} __attribute__((packed)); - -/* If it's not pixel timing, it'll be one of the below */ -struct detailed_data_string { - u8 str[13]; -} __attribute__((packed)); - -struct detailed_data_monitor_range { - u8 min_vfreq; - u8 max_vfreq; - u8 min_hfreq_khz; - u8 max_hfreq_khz; - u8 pixel_clock_mhz; /* need to multiply by 10 */ - u16 sec_gtf_toggle; /* A000=use above, 20=use below */ /* FIXME: byte order */ - u8 hfreq_start_khz; /* need to multiply by 2 */ - u8 c; /* need to divide by 2 */ - u16 m; /* FIXME: byte order */ - u8 k; - u8 j; /* need to divide by 2 */ -} __attribute__((packed)); - -struct detailed_data_wpindex { - u8 white_y_lo:2; - u8 white_x_lo:2; - u8 pad:4; - u8 white_x_hi; - u8 white_y_hi; - u8 gamma; /* need to divide by 100 then add 1 */ -} __attribute__((packed)); - -struct detailed_data_color_point { - u8 windex1; - u8 wpindex1[3]; - u8 windex2; - u8 wpindex2[3]; -} __attribute__((packed)); - -struct detailed_non_pixel { - u8 pad1; - u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name - fb=color point data, fa=standard timing data, - f9=undefined, f8=mfg. reserved */ - u8 pad2; - union { - struct detailed_data_string str; - struct detailed_data_monitor_range range; - struct detailed_data_wpindex color; - struct std_timing timings[5]; - } data; -} __attribute__((packed)); - -#define EDID_DETAIL_STD_MODES 0xfa -#define EDID_DETAIL_MONITOR_CPDATA 0xfb -#define EDID_DETAIL_MONITOR_NAME 0xfc -#define EDID_DETAIL_MONITOR_RANGE 0xfd -#define EDID_DETAIL_MONITOR_STRING 0xfe -#define EDID_DETAIL_MONITOR_SERIAL 0xff - -struct detailed_timing { - u16 pixel_clock; /* need to multiply by 10 KHz */ /* FIXME: byte order */ - union { - struct detailed_pixel_timing pixel_data; - struct detailed_non_pixel other_data; - } data; -} __attribute__((packed)); - -struct edid { - u8 header[8]; - /* Vendor & product info */ - u16 mfg_id; /* FIXME: byte order */ - u16 prod_code; /* FIXME: byte order */ - u32 serial; /* FIXME: byte order */ - u8 mfg_week; - u8 mfg_year; - /* EDID version */ - u8 version; - u8 revision; - /* Display info: */ - /* input definition */ - u8 serration_vsync:1; - u8 sync_on_green:1; - u8 composite_sync:1; - u8 separate_syncs:1; - u8 blank_to_black:1; - u8 video_level:2; - u8 digital:1; /* bits below must be zero if set */ - u8 width_cm; - u8 height_cm; - u8 gamma; - /* feature support */ - u8 default_gtf:1; - u8 preferred_timing:1; - u8 standard_color:1; - u8 display_type:2; /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ - u8 pm_active_off:1; - u8 pm_suspend:1; - u8 pm_standby:1; - /* Color characteristics */ - u8 red_green_lo; - u8 black_white_lo; - u8 red_x; - u8 red_y; - u8 green_x; - u8 green_y; - u8 blue_x; - u8 blue_y; - u8 white_x; - u8 white_y; - /* Est. timings and mfg rsvd timings*/ - struct est_timings established_timings; - /* Standard timings 1-8*/ - struct std_timing standard_timings[8]; - /* Detailing timings 1-4 */ - struct detailed_timing detailed_timings[4]; - /* Number of 128 byte ext. blocks */ - u8 extensions; - /* Checksum */ - u8 checksum; -} __attribute__((packed)); - +/* Valid EDID header has these bytes */ static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; /** -- cgit v1.2.3 From c731b68091aa7284ee3a89c8a7ea3fdabac45a54 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 11 Apr 2007 11:42:00 -0700 Subject: Fix EDID pixel clock calculation. --- linux-core/drm_edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux-core/drm_edid.c') diff --git a/linux-core/drm_edid.c b/linux-core/drm_edid.c index b79bc2db..9acdc8da 100644 --- a/linux-core/drm_edid.c +++ b/linux-core/drm_edid.c @@ -114,7 +114,7 @@ struct drm_display_mode *drm_mode_detailed(drm_device_t *dev, return NULL; mode->type = DRM_MODE_TYPE_DRIVER; - mode->clock = timing->pixel_clock / 100; + mode->clock = timing->pixel_clock * 10; mode->hdisplay = (pt->hactive_hi << 8) | pt->hactive_lo; mode->hsync_start = mode->hdisplay + ((pt->hsync_offset_hi << 8) | -- cgit v1.2.3