Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 916523 Details for
Bug 806091
HDMI infoframes break LG Flatron display (regression)
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
User-defined EDID quirks patch - Linux 3.15
linux-3.15-Add-user-defined-EDID-quirks-capability.patch (text/plain), 27.09 KB, created by
Ian Pilcher
on 2014-07-08 18:22:14 UTC
(
hide
)
Description:
User-defined EDID quirks patch - Linux 3.15
Filename:
MIME Type:
Creator:
Ian Pilcher
Created:
2014-07-08 18:22:14 UTC
Size:
27.09 KB
patch
obsolete
>diff -urN linux.orig/Documentation/EDID/edid_quirks.txt linux/Documentation/EDID/edid_quirks.txt >--- linux.orig/Documentation/EDID/edid_quirks.txt 1969-12-31 18:00:00.000000000 -0600 >+++ linux/Documentation/EDID/edid_quirks.txt 2014-07-08 12:55:03.853216110 -0500 >@@ -0,0 +1,126 @@ >+ EDID Quirks >+ ============= >+ Ian Pilcher <arequipeno@gmail.com> >+ August 11, 2012 >+ >+ >+ "EDID blocks out in the wild have a variety of bugs" >+ -- from drivers/gpu/drm/drm_edid.c >+ >+ >+Overview >+======== >+ >+EDID quirks provide a mechanism for working around display hardware with buggy >+EDID data. >+ >+An individual EDID quirk maps a display type (identified by its EDID >+manufacturer ID and product code[1]) to a set of "quirk flags." The kernel >+includes a variety of built-in quirks. (They are stored in the edid_quirk_list >+array in drivers/gpu/drm/drm_edid.c.) >+ >+An example of a built-in EDID quirk is: >+ >+ ACR:0xad46:0x00000001 >+ >+The first field is the manufacturer ID (Acer, Inc.), the second field is the >+manufacturer's product code, and the third field contains the quirk flags for >+that display type. >+ >+The quirk flags are defined in drivers/gpu/drm/drm_edid.c. Each flag has a >+symbolic name beginning with EDID_QUIRK_, along with a numerical value. Each >+flag should also have an associated comment which provides an idea of its >+effect. Note that the values in the source file are expressed as bit shifts[2]: >+ >+ * 1 << 0: 0x0001 >+ * 1 << 1: 0x0002 >+ * 1 << 2: 0x0004 >+ * etc. >+ >+ >+sysfs interface >+=============== >+ >+The current EDID quirk list can be read from /sys/class/drm/edid_quirks: >+ >+ # cat /sys/class/drm/edid_quirks >+ ACR:0xad46:0x00000001 >+ API:0x7602:0x00000001 >+ ACR:0x0977:0x00000020 >+ 0x9e6a:0x077e:0x00000080 >+ ... >+ >+("Nonconformant" manufacturer IDs are displayed as hexadecimal values.) >+ >+The number of total "slots" in the list can be read from >+/sys/class/drm/edid_quirks_size. This total includes both occupied slots (i.e. >+the current list) and any slots available for additional quirks. The number of >+available slots can be calculated by subtracting the number of quirks in the >+current list from the total number of slots. >+ >+If a slot is available, an additional quirk can be added to the list by writing >+it to /sys/class/drm/edid_quirks: >+ >+ # echo FOO:0xffff:0x100 > /sys/class/drm/edid_quirks >+ >+Manufacturer IDs can also be specified numerically. (This is the only way to >+specify a nonconformant ID.) This command is equivalent to the previous one: >+ >+ # echo 0x19ef:0xffff:0x100 > /sys/class/drm/edid_quirks >+ >+Numeric values can also be specified in decimal or octal formats; a number that >+begins with a 0 is assumed to be octal: >+ >+ # echo FOO:65535:0400 > /sys/class/drm/edid_quirks >+ >+An existing quirk can be replaced by writing a new set of flags: >+ >+ # echo FOO:0xffff:0x200 > /sys/class/drm/edid_quirks >+ >+A quirk can be deleted from the list by writing an empty flag set (0). This >+makes the slot occupied by that quirk available. >+ >+ # echo FOO:0xffff:0 > /sys/class/drm/edid_quirks >+ >+Writing an "at symbol" (@) clears the entire quirk list: >+ >+ # echo @ > /sys/class/drm/edid_quirks >+ >+Multiple changes to the list can be specified in a comma (or newline) separated >+list. For example, the following command clears all of the existing quirks in >+the list and adds 3 new quirks: >+ >+ # echo @,FOO:0xffff:0x100,BAR:0x1111:0x001,BAZ:0x2222:0x002 > \ >+ /sys/class/drm/edid_quirks >+ >+Note however, that any error (an incorrectly formatted quirk or an attempt to >+add a quirk when no slot is available) will abort processing of any further >+changes, potentially making it difficult to determine exactly which change >+caused the error and what changes were made. For this reason, making changes >+one at a time is recommended, particularly if the changes are being made by a >+script or program. >+ >+ >+Module parameter >+================ >+ >+The EDID quirk list can also be modified via the edid_quirks module parameter >+(drm.edid_quirks on the kernel command line). The effect of setting this >+parameter is identical to the effect of writing its value to >+/sys/class/drm/edid_quirks, with one important difference. When an error is >+encountered during module parameter parsing or processing, any remaining quirks >+in the parameter string will still be processed. (It is hoped that this approach >+maximizes the probability of producing a working display.) >+ >+ >+Follow-up >+========= >+ >+If you encounter a display that requires an additional EDID quirk in order to >+function properly, please report it to the direct rendering development mailing >+list <dri-devel@lists.freedesktop.org>. >+ >+ >+[1] See http://en.wikipedia.org/wiki/Extended_display_identification_data for a >+ description of the manufacturer ID and product code fields. >+[2] https://en.wikipedia.org/wiki/Bitwise_operation#Bit_shifts >diff -urN linux.orig/drivers/gpu/drm/drm_drv.c linux/drivers/gpu/drm/drm_drv.c >--- linux.orig/drivers/gpu/drm/drm_drv.c 2014-06-08 13:19:54.000000000 -0500 >+++ linux/drivers/gpu/drm/drm_drv.c 2014-07-08 12:55:03.853216110 -0500 >@@ -203,6 +203,8 @@ > goto err_p3; > } > >+ drm_edid_quirks_param_process(); >+ > DRM_INFO("Initialized %s %d.%d.%d %s\n", > CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); > return 0; >diff -urN linux.orig/drivers/gpu/drm/drm_edid.c linux/drivers/gpu/drm/drm_edid.c >--- linux.orig/drivers/gpu/drm/drm_edid.c 2014-07-08 12:50:52.777234701 -0500 >+++ linux/drivers/gpu/drm/drm_edid.c 2014-07-08 12:55:03.855216110 -0500 >@@ -32,6 +32,7 @@ > #include <linux/hdmi.h> > #include <linux/i2c.h> > #include <linux/module.h> >+#include <linux/ctype.h> > #include <drm/drmP.h> > #include <drm/drm_edid.h> > >@@ -86,57 +87,94 @@ > #define LEVEL_GTF2 2 > #define LEVEL_CVT 3 > >-static struct edid_quirk { >- char vendor[4]; >- int product_id; >- u32 quirks; >-} edid_quirk_list[] = { >- /* Acer AL1706 */ >- { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, >- /* Acer F51 */ >- { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, >- /* Unknown Acer */ >- { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, >- >- /* Belinea 10 15 55 */ >- { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, >- { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, >- >- /* Envision Peripherals, Inc. EN-7100e */ >- { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, >- /* Envision EN2028 */ >- { "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 }, >- >- /* Funai Electronics PM36B */ >- { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | >- EDID_QUIRK_DETAILED_IN_CM }, >- >- /* LG Philips LCD LP154W01-A5 */ >- { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, >- { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, >- >- /* Philips 107p5 CRT */ >- { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, >- >- /* Proview AY765C */ >- { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, >- >- /* Samsung SyncMaster 205BW. Note: irony */ >- { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, >- /* Samsung SyncMaster 22[5-6]BW */ >- { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, >- { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, >- >- /* ViewSonic VA2026w */ >- { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING }, >+union edid_quirk { >+ struct { >+ union edid_display_id display_id; >+ u32 quirks; >+ } __attribute__((packed)) s; >+ u64 u; >+}; > >- /* Medion MD 30217 PG */ >- { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, >+#define EDID_MFG_ID(c1, c2, c3) cpu_to_be16( \ >+ (c1 & 0x1f) << 10 | \ >+ (c2 & 0x1f) << 5 | \ >+ (c3 & 0x1f) \ >+ ) >+ >+#define EDID_QUIRK_LIST_SIZE 24 >+ >+union edid_quirk edid_quirk_list[EDID_QUIRK_LIST_SIZE] = { >+ >+ /* Acer AL1706 */ >+ { { { { EDID_MFG_ID('A', 'C', 'R'), cpu_to_le16(44358) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ /* Acer F51 */ >+ { { { { EDID_MFG_ID('A', 'P', 'I'), cpu_to_le16(0x7602) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ /* Unknown Acer */ >+ { { { { EDID_MFG_ID('A', 'C', 'R'), cpu_to_le16(2423) } }, >+ EDID_QUIRK_FIRST_DETAILED_PREFERRED } }, >+ >+ /* Belinea 10 15 55 */ >+ { { { { EDID_MFG_ID('M', 'A', 'X'), cpu_to_le16(1516) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ { { { { EDID_MFG_ID('M', 'A', 'X'), cpu_to_le16(0x77e) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ >+ /* Envision Peripherals, Inc. EN-7100e */ >+ { { { { EDID_MFG_ID('E', 'P', 'I'), cpu_to_le16(59264) } }, >+ EDID_QUIRK_135_CLOCK_TOO_HIGH } }, >+ /* Envision EN2028 */ >+ { { { { EDID_MFG_ID('E', 'P', 'I'), cpu_to_le16(8232) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ >+ /* Funai Electronics PM36B */ >+ { { { { EDID_MFG_ID('F', 'C', 'M'), cpu_to_le16(13600) } }, >+ EDID_QUIRK_PREFER_LARGE_75 | EDID_QUIRK_DETAILED_IN_CM } }, >+ >+ /* LG Philips LCD LP154W01-A5 */ >+ { { { { EDID_MFG_ID('L', 'P', 'L'), cpu_to_le16(0) } }, >+ EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE } }, >+ { { { { EDID_MFG_ID('L', 'P', 'L'), cpu_to_le16(0x2a00) } }, >+ EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE } }, >+ >+ /* Philips 107p5 CRT */ >+ { { { { EDID_MFG_ID('P', 'H', 'L'), cpu_to_le16(57364) } }, >+ EDID_QUIRK_FIRST_DETAILED_PREFERRED } }, >+ >+ /* Proview AY765C */ >+ { { { { EDID_MFG_ID('P', 'T', 'S'), cpu_to_le16(765) } }, >+ EDID_QUIRK_FIRST_DETAILED_PREFERRED } }, >+ >+ /* Samsung SyncMaster 205BW. Note: irony */ >+ { { { { EDID_MFG_ID('S', 'A', 'M'), cpu_to_le16(541) } }, >+ EDID_QUIRK_DETAILED_SYNC_PP } }, >+ /* Samsung SyncMaster 22[5-6]BW */ >+ { { { { EDID_MFG_ID('S', 'A', 'M'), cpu_to_le16(596) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ { { { { EDID_MFG_ID('S', 'A', 'M'), cpu_to_le16(638) } }, >+ EDID_QUIRK_PREFER_LARGE_60 } }, >+ >+ /* ViewSonic VA2026w */ >+ { { { { EDID_MFG_ID('V', 'S', 'C'), cpu_to_le16(5020) } }, >+ EDID_QUIRK_FORCE_REDUCED_BLANKING } }, >+ >+ /* Medion MD 30217 PG */ >+ { { { { EDID_MFG_ID('M', 'E', 'D'), cpu_to_le16(0x7b8) } }, >+ EDID_QUIRK_PREFER_LARGE_75 } }, > > /* Panel in Samsung NP700G7A-S01PL notebook reports 6bpc */ >- { "SEC", 0xd033, EDID_QUIRK_FORCE_8BPC }, >+ { { { { EDID_MFG_ID('S', 'E', 'C'), cpu_to_le16(0xd033) } }, >+ EDID_QUIRK_FORCE_8BPC } }, >+ >+ /* >+ * When adding built-in quirks, please adjust EDID_QUIRK_LIST_SIZE to >+ * provide some room for user-supplied quirks. >+ */ > }; > >+DEFINE_MUTEX(edid_quirk_list_mutex); >+ > /* > * Autogenerated from the DMT spec. > * This table is copied from xfree86/modes/xf86EdidModes.c. >@@ -980,6 +1018,377 @@ > .vrefresh = 24, }, > }; > >+/** >+ * drm_edid_mfg_format - format an "encoded" EDID manufacturer ID for printing >+ * @mfg_id: the encoded manufacturer ID >+ * @buf: destination buffer for the formatted manufacturer ID (minimum 7 bytes) >+ * @strip: if non-zero, the returned pointer will skip any leading spaces >+ * >+ * An EDID manufacturer ID is supposed to consist of 3 capital letters (A-Z). >+ * Each letter is stored as a 5-bit value between 1 and 26, taking up 15 bits of >+ * the 16-bit ID. The remaining bit should always be 0. If display manufacturers >+ * always did things correctly, however, EDID quirks wouldn't be required in >+ * the first place. This function does the following: >+ * >+ * - Broken IDs are printed in hexadecimal (0xffff). >+ * - "Correct" IDs are formatted as a 3-letter ID string, preceded by 3 spaces; >+ * the spaces ensure that both output formats are the same length. >+ * >+ * Thus, a formatted manufacturer ID is always 6 characters long (not including >+ * the terminating 0). >+ * >+ * If @strip is 0, or the manufacturer ID has been formatted as a hexadecimal >+ * number, @buf is returned. If @strip is non-zero, and the manufacturer ID has >+ * been formatted as a 3-letter string, a pointer to the first non-space >+ * character (@buf + 3) is returned. >+ */ >+static const char *drm_edid_mfg_format(__be16 mfg_id, char *buf, int strip) >+{ >+ u16 id = be16_to_cpu(mfg_id); >+ >+ if (id & 0x8000) >+ goto bad_id; >+ >+ buf[3] = ((id & 0x7c00) >> 10) + '@'; >+ if (!isupper(buf[3])) >+ goto bad_id; >+ >+ buf[4] = ((id & 0x03e0) >> 5) + '@'; >+ if (!isupper(buf[4])) >+ goto bad_id; >+ >+ buf[5] = (id & 0x001f) + '@'; >+ if (!isupper(buf[5])) >+ goto bad_id; >+ >+ memset(buf, ' ', 3); >+ buf[6] = 0; >+ >+ return strip ? (buf + 3) : buf; >+ >+bad_id: >+ sprintf(buf, "0x%04hx", id); >+ return buf; >+} >+ >+#define EDID_MFG_BUF_SIZE 7 >+ >+/** >+ * drm_edid_display_id_format - format an EDID "display ID" (manufacturer ID >+ * and product code) for printing >+ * @display_id: the display ID >+ * @buf: destination buffer for the formatted display ID (minimum 14 bytes) >+ * @strip: if non-zero, the returned pointer will skip any leading spaces >+ * >+ * A formatted display ID is always 13 characters long (not including the >+ * terminating 0). >+ * >+ * If @strip is 0, or the manufacturer ID has been formatted as a hexadecimal >+ * number, @buf is returned. If @strip is non-zero, and the manufacturer ID has >+ * been formatted as a 3-letter string, a pointer to the first non-space >+ * character (@buf + 3) is returned. >+ */ >+static const char *drm_edid_display_id_format(union edid_display_id display_id, >+ char *buf, int strip) >+{ >+ const char *s; >+ >+ s = drm_edid_mfg_format(display_id.s.mfg_id, buf, strip); >+ sprintf(buf + EDID_MFG_BUF_SIZE - 1, ":0x%04hx", >+ le16_to_cpu(display_id.s.prod_code)); >+ >+ return s; >+} >+ >+#define EDID_DISPLAY_ID_BUF_SIZE (EDID_MFG_BUF_SIZE + 7) >+ >+/** >+ * drm_edid_quirk_format - format an EDID quirk for printing >+ * @quirk: the quirk >+ * @buf: destination buffer for the formatted quirk (minimum 25 bytes) >+ * @strip: if non-zero, the returned pointer will skip any leading spaces >+ * >+ * A formatted EDID quirk is always 24 characters long (not including the >+ * terminating 0). >+ * >+ * If @strip is 0, or the manufacturer ID has been formatted as a hexadecimal >+ * number, @buf is returned. If @strip is non-zero, and the manufacturer ID has >+ * been formatted as a 3-letter string, a pointer to the first non-space >+ * character (@buf + 3) is returned. >+ */ >+static const char *drm_edid_quirk_format(const union edid_quirk *quirk, >+ char *buf, int strip) >+{ >+ const char *s; >+ >+ s = drm_edid_display_id_format(quirk->s.display_id, buf, strip); >+ sprintf(buf + EDID_DISPLAY_ID_BUF_SIZE - 1, ":0x%08x", quirk->s.quirks); >+ >+ return s; >+} >+ >+#define EDID_QUIRK_BUF_SIZE (EDID_DISPLAY_ID_BUF_SIZE + 11) >+ >+/** >+ * drm_edid_quirk_parse - parse an EDID quirk >+ * @s: string containing the quirk to be parsed >+ * @quirk: destination for parsed quirk >+ * >+ * Returns 0 on success, < 0 (currently -EINVAL) on error. >+ */ >+static int drm_edid_quirk_parse(const char *s, union edid_quirk *quirk) >+{ >+ char buf[EDID_QUIRK_BUF_SIZE]; >+ s32 mfg; >+ s32 product; >+ s64 quirks; >+ char *c; >+ >+ if (sscanf(s, "%i:%i:%lli", &mfg, &product, &quirks) == 3) { >+ if (mfg < 0 || mfg > 0xffff) >+ goto error; >+ quirk->s.display_id.s.mfg_id = cpu_to_be16((u16)mfg); >+ } else { >+ if (sscanf(s, "%3s:%i:%lli", buf, &product, &quirks) != 3 || >+ !isupper(buf[0]) || >+ !isupper(buf[1]) || >+ !isupper(buf[2])) >+ goto error; >+ quirk->s.display_id.s.mfg_id = >+ EDID_MFG_ID(buf[0], buf[1], buf[2]); >+ } >+ >+ if (product < 0 || product > 0xffff || >+ quirks < 0 || quirks > 0xffffffffLL) >+ goto error; >+ >+ quirk->s.display_id.s.prod_code = cpu_to_le16((u16)product); >+ quirk->s.quirks = (u32)quirks; >+ >+ DRM_DEBUG("Successfully parsed EDID quirk: %s\n", >+ drm_edid_quirk_format(quirk, buf, 1)); >+ >+ return 0; >+ >+error: >+ c = strpbrk(s, ",\n"); >+ if (c == NULL) { >+ printk(KERN_WARNING "Invalid EDID quirk: '%s'\n", s); >+ } else { >+ printk(KERN_WARNING "Invalid EDID quirk: '%.*s'\n", >+ (int)(c - s), s); >+ } >+ >+ return -EINVAL; >+} >+ >+/** >+ * drm_edid_quirk_find_by_id - find the EDID quirk matching a display ID >+ * @display_id: the display ID to match >+ * >+ * Caller MUST hold edid_quirk_list_mutex. >+ * >+ * Returns a pointer to the matching quirk list entry, NULL if no such entry >+ * exists. >+ */ >+static union edid_quirk *drm_edid_quirk_find_by_id(union edid_display_id id) >+{ >+ union edid_quirk *q = edid_quirk_list; >+ >+ do { >+ if (q->s.display_id.u == id.u && q->s.quirks != 0) >+ return q; >+ } while (++q < edid_quirk_list + ARRAY_SIZE(edid_quirk_list)); >+ >+ return NULL; >+} >+ >+/** >+ * drm_edid_quirk_find_slot - find an empty slot in the EDID quirk list >+ * >+ * Caller MUST hold edid_quirk_list_mutex. >+ * >+ * Returns a pointer to the first empty slot, NULL if no empty slots exist. >+ */ >+static union edid_quirk *drm_edid_quirk_find_empty(void) >+{ >+ union edid_quirk *q = edid_quirk_list; >+ >+ do { >+ if (q->s.quirks == 0) >+ return q; >+ } while (++q < edid_quirk_list + ARRAY_SIZE(edid_quirk_list)); >+ >+ return NULL; >+} >+ >+/** >+ * drm_edid_quirk_process - process a newly parsed EDID quirk >+ * @quirk: the quirk to be processed >+ * >+ * Depending on the newly parsed quirk and the contents of the quirks list, this >+ * function will add, remove, or replace a quirk. >+ * >+ * Returns 0 on success, < 0 on error (-ENOSPC if there is no free slot for a >+ * new quirk). Note that trying to remove a quirk that isn't present is not >+ * considered an error. >+ */ >+static int drm_edid_quirk_process(const union edid_quirk *quirk) >+{ >+ char buf[EDID_QUIRK_BUF_SIZE]; >+ union edid_quirk *q; >+ int res = 0; >+ >+ mutex_lock(&edid_quirk_list_mutex); >+ >+ if (quirk->s.quirks == 0) { >+ DRM_INFO("Removing EDID quirk for display %s\n", >+ drm_edid_display_id_format(quirk->s.display_id, >+ buf, 1)); >+ q = drm_edid_quirk_find_by_id(quirk->s.display_id); >+ if (q == NULL) { >+ printk(KERN_WARNING "No quirk found for display %s\n", >+ drm_edid_display_id_format(quirk->s.display_id, >+ buf, 1)); >+ } else { >+ q->u = 0; >+ } >+ } else { >+ DRM_INFO("Adding EDID quirk: %s\n", >+ drm_edid_quirk_format(quirk, buf, 1)); >+ q = drm_edid_quirk_find_by_id(quirk->s.display_id); >+ if (q == NULL) { >+ q = drm_edid_quirk_find_empty(); >+ if (q == NULL) { >+ printk(KERN_WARNING >+ "No free slot in EDID quirk list\n"); >+ res = -ENOSPC; >+ } else { >+ q->u = quirk->u; >+ } >+ } else { >+ DRM_INFO("Replacing existing quirk: %s\n", >+ drm_edid_quirk_format(q, buf, 1)); >+ q->s.quirks = quirk->s.quirks; >+ } >+ } >+ >+ mutex_unlock(&edid_quirk_list_mutex); >+ >+ return res; >+} >+ >+/** >+ * drm_edid_quirks_process - parse and process a comma separated list of EDID >+ * quirks >+ * @s: string containing the quirks to be processed >+ * @strict: if non-zero, any parsing or processing error aborts further >+ * processing >+ * >+ * Returns 0 on success, < 0 if any error is encountered. (If multiple errors >+ * occur when strict is set to 0, the last error encountered is returned.) >+ */ >+static int drm_edid_quirks_process(const char *s, int strict) >+{ >+ union edid_quirk quirk; >+ int res = 0; >+ >+ do { >+ >+ if (*s == '@') { >+ DRM_INFO("Clearing EDID quirk list\n"); >+ mutex_lock(&edid_quirk_list_mutex); >+ memset(edid_quirk_list, 0, sizeof edid_quirk_list); >+ mutex_unlock(&edid_quirk_list_mutex); >+ } else { >+ res = drm_edid_quirk_parse(s, &quirk); >+ if (res != 0) { >+ if (strict) >+ goto error; >+ continue; >+ } >+ >+ res = drm_edid_quirk_process(&quirk); >+ if (res != 0) { >+ if (strict) >+ goto error; >+ } >+ } >+ >+ s = strpbrk(s, ",\n"); >+ >+ } while (s != NULL && *(++s) != 0); >+ >+ return res; >+ >+error: >+ printk(KERN_WARNING "Aborting EDID quirk parsing\n"); >+ return res; >+} >+ >+/** >+ * drm_edid_quirks_param_process - process the edid_quirks module parameter >+ */ >+void drm_edid_quirks_param_process(void) >+{ >+ if (drm_edid_quirks != NULL) >+ drm_edid_quirks_process(drm_edid_quirks, 0); >+} >+ >+/** >+ * drm_edid_quirks_size_show - show the size of the EDID quirk list in sysfs >+ * @buf: destination buffer (PAGE_SIZE bytes) >+ */ >+ssize_t drm_edid_quirks_size_show(struct class *class, >+ struct class_attribute *attr, char *buf) >+{ >+ return sprintf(buf, "%zu\n", ARRAY_SIZE(edid_quirk_list)); >+} >+ >+/** >+ * drm_edid_quirks_show - show the contents of the EDID quirk list in sysfs >+ * @buf: destination buffer (PAGE_SIZE bytes) >+ */ >+ssize_t drm_edid_quirks_show(struct class *class, struct class_attribute *attr, >+ char *buf) >+{ >+ const union edid_quirk *q = edid_quirk_list; >+ ssize_t count = 0; >+ >+ BUILD_BUG_ON(ARRAY_SIZE(edid_quirk_list) > >+ PAGE_SIZE / EDID_QUIRK_BUF_SIZE); >+ >+ mutex_lock(&edid_quirk_list_mutex); >+ >+ do { >+ if (q->s.quirks != 0) { >+ drm_edid_quirk_format(q, buf + count, 0); >+ (buf + count)[EDID_QUIRK_BUF_SIZE - 1] = '\n'; >+ count += EDID_QUIRK_BUF_SIZE; >+ } >+ } while (++q < edid_quirk_list + ARRAY_SIZE(edid_quirk_list)); >+ >+ mutex_unlock(&edid_quirk_list_mutex); >+ >+ return count; >+} >+ >+/** >+ * drm_edid_quirks_store - parse and process EDID qurik list changes written >+ * to sysfs attribute >+ */ >+ssize_t drm_edid_quirks_store(struct class *class, struct class_attribute *attr, >+ const char *buf, size_t count) >+{ >+ int res; >+ >+ res = drm_edid_quirks_process(buf, 1); >+ if (res != 0) >+ return res; >+ >+ return count; >+} >+ > /*** DDC fetch and block validation ***/ > > static const u8 edid_header[] = { >@@ -1301,25 +1710,6 @@ > /*** EDID parsing ***/ > > /** >- * edid_vendor - match a string against EDID's obfuscated vendor field >- * @edid: EDID to match >- * @vendor: vendor string >- * >- * Returns true if @vendor is in @edid, false otherwise >- */ >-static bool edid_vendor(struct edid *edid, char *vendor) >-{ >- char edid_vendor[3]; >- >- edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; >- edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | >- ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; >- edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; >- >- return !strncmp(edid_vendor, vendor, 3); >-} >- >-/** > * edid_get_quirks - return quirk flags for a given EDID > * @edid: EDID to process > * >@@ -1327,18 +1717,18 @@ > */ > static u32 edid_get_quirks(struct edid *edid) > { >- struct edid_quirk *quirk; >- int i; >+ union edid_quirk *q; >+ u32 quirks = 0; > >- for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { >- quirk = &edid_quirk_list[i]; >+ mutex_lock(&edid_quirk_list_mutex); > >- if (edid_vendor(edid, quirk->vendor) && >- (EDID_PRODUCT_ID(edid) == quirk->product_id)) >- return quirk->quirks; >- } >+ q = drm_edid_quirk_find_by_id(edid->display_id); >+ if (q != NULL) >+ quirks = q->s.quirks; > >- return 0; >+ mutex_unlock(&edid_quirk_list_mutex); >+ >+ return quirks; > } > > #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) >@@ -2062,7 +2452,7 @@ > closure->modes += drm_dmt_modes_for_range(closure->connector, > closure->edid, > timing); >- >+ > if (!version_greater(closure->edid, 1, 1)) > return; /* GTF not defined yet */ > >@@ -2301,7 +2691,7 @@ > > static int > add_cvt_modes(struct drm_connector *connector, struct edid *edid) >-{ >+{ > struct detailed_mode_closure closure = { > connector, edid, 0, 0, 0 > }; >@@ -3061,10 +3451,7 @@ > > eld[0] = 2 << 3; /* ELD version: 2 */ > >- eld[16] = edid->mfg_id[0]; >- eld[17] = edid->mfg_id[1]; >- eld[18] = edid->prod_code[0]; >- eld[19] = edid->prod_code[1]; >+ *(u32 *)(&eld[16]) = edid->display_id.u; > > if (cea_revision(cea) >= 3) { > int i, start, end; >diff -urN linux.orig/drivers/gpu/drm/drm_stub.c linux/drivers/gpu/drm/drm_stub.c >--- linux.orig/drivers/gpu/drm/drm_stub.c 2014-06-08 13:19:54.000000000 -0500 >+++ linux/drivers/gpu/drm/drm_stub.c 2014-07-08 13:00:45.041190847 -0500 >@@ -55,6 +55,9 @@ > unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ > EXPORT_SYMBOL(drm_timestamp_precision); > >+char *drm_edid_quirks = NULL; >+EXPORT_SYMBOL(drm_edid_quirks); >+ > /* > * Default to use monotonic timestamps for wait-for-vblank and page-flip > * complete events. >@@ -69,6 +72,8 @@ > MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); > MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); > MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); >+MODULE_PARM_DESC(edid_quirks, "MFG:prod:flags[,MFG:prod:flags[...]]\n" >+ "(See Documentation/EDID/edid_quirks.txt)"); > > module_param_named(debug, drm_debug, int, 0600); > module_param_named(rnodes, drm_rnodes, int, 0600); >@@ -76,6 +81,7 @@ > module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); > module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); > module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); >+module_param_named(edid_quirks, drm_edid_quirks, charp, 0400); > > static DEFINE_SPINLOCK(drm_minor_lock); > struct idr drm_minors_idr; >diff -urN linux.orig/drivers/gpu/drm/drm_sysfs.c linux/drivers/gpu/drm/drm_sysfs.c >--- linux.orig/drivers/gpu/drm/drm_sysfs.c 2014-06-08 13:19:54.000000000 -0500 >+++ linux/drivers/gpu/drm/drm_sysfs.c 2014-07-08 12:55:03.856216110 -0500 >@@ -110,6 +110,11 @@ > __stringify(CORE_PATCHLEVEL) " " > CORE_DATE); > >+static CLASS_ATTR(edid_quirks_size, 0400, drm_edid_quirks_size_show, 0); >+ >+static CLASS_ATTR(edid_quirks, 0600, drm_edid_quirks_show, >+ drm_edid_quirks_store); >+ > /** > * drm_sysfs_create - create a struct drm_sysfs_class structure > * @owner: pointer to the module that is to "own" this struct drm_sysfs_class >@@ -138,10 +143,22 @@ > if (err) > goto err_out_class; > >+ err = class_create_file(class, &class_attr_edid_quirks_size); >+ if (err) >+ goto err_out_version; >+ >+ err = class_create_file(class, &class_attr_edid_quirks); >+ if (err) >+ goto err_out_quirks_size; >+ > class->devnode = drm_devnode; > > return class; > >+err_out_quirks_size: >+ class_remove_file(class, &class_attr_edid_quirks_size); >+err_out_version: >+ class_remove_file(class, &class_attr_version.attr); > err_out_class: > class_destroy(class); > err_out: >@@ -157,6 +174,8 @@ > { > if ((drm_class == NULL) || (IS_ERR(drm_class))) > return; >+ class_remove_file(drm_class, &class_attr_edid_quirks); >+ class_remove_file(drm_class, &class_attr_edid_quirks_size); > class_remove_file(drm_class, &class_attr_version.attr); > class_destroy(drm_class); > drm_class = NULL; >diff -urN linux.orig/include/drm/drm_edid.h linux/include/drm/drm_edid.h >--- linux.orig/include/drm/drm_edid.h 2014-06-08 13:19:54.000000000 -0500 >+++ linux/include/drm/drm_edid.h 2014-07-08 12:55:03.856216110 -0500 >@@ -202,11 +202,18 @@ > #define DRM_EDID_FEATURE_PM_SUSPEND (1 << 6) > #define DRM_EDID_FEATURE_PM_STANDBY (1 << 7) > >+union edid_display_id { >+ struct { >+ __be16 mfg_id; >+ __le16 prod_code; >+ } __attribute__((packed)) s; >+ u32 u; >+}; >+ > struct edid { > u8 header[8]; > /* Vendor & product info */ >- u8 mfg_id[2]; >- u8 prod_code[2]; >+ union edid_display_id display_id; > u32 serial; /* FIXME: byte order */ > u8 mfg_week; > u8 mfg_year; >@@ -242,8 +249,6 @@ > u8 checksum; > } __attribute__((packed)); > >-#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) >- > /* Short Audio Descriptor */ > struct cea_sad { > u8 format; >diff -urN linux.orig/include/drm/drmP.h linux/include/drm/drmP.h >--- linux.orig/include/drm/drmP.h 2014-06-08 13:19:54.000000000 -0500 >+++ linux/include/drm/drmP.h 2014-07-08 12:55:03.857216110 -0500 >@@ -1418,6 +1418,7 @@ > > extern unsigned int drm_vblank_offdelay; > extern unsigned int drm_timestamp_precision; >+extern char *drm_edid_quirks; > extern unsigned int drm_timestamp_monotonic; > > extern struct class *drm_class; >@@ -1548,6 +1549,15 @@ > struct vm_area_struct *vma); > int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); > >+ /* EDID support (drm_edid.c) */ >+void drm_edid_quirks_param_process(void); >+ssize_t drm_edid_quirks_size_show(struct class *class, >+ struct class_attribute *attr, char *buf); >+ssize_t drm_edid_quirks_show(struct class *class, struct class_attribute *attr, >+ char *buf); >+ssize_t drm_edid_quirks_store(struct class *class, struct class_attribute *attr, >+ const char *buf, size_t count); >+ > #include <drm/drm_global.h> > > static inline void
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 806091
:
572083
|
572084
|
572085
|
572086
|
572087
|
572089
|
572090
|
572789
|
572790
|
573177
|
575607
|
581917
|
603613
|
642917
|
642918
|
642919
|
678930
|
678931
|
678932
|
711667
|
711668
|
711669
|
738572
|
916523
|
916524
|
916525
|
937268
|
937269