xref: /freebsd/sys/dev/drm2/drm_edid.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1*592ffb21SWarner Losh /*
2*592ffb21SWarner Losh  * Copyright (c) 2006 Luc Verhaegen (quirks list)
3*592ffb21SWarner Losh  * Copyright (c) 2007-2008 Intel Corporation
4*592ffb21SWarner Losh  *   Jesse Barnes <jesse.barnes@intel.com>
5*592ffb21SWarner Losh  * Copyright 2010 Red Hat, Inc.
6*592ffb21SWarner Losh  *
7*592ffb21SWarner Losh  * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
8*592ffb21SWarner Losh  * FB layer.
9*592ffb21SWarner Losh  *   Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
10*592ffb21SWarner Losh  *
11*592ffb21SWarner Losh  * Permission is hereby granted, free of charge, to any person obtaining a
12*592ffb21SWarner Losh  * copy of this software and associated documentation files (the "Software"),
13*592ffb21SWarner Losh  * to deal in the Software without restriction, including without limitation
14*592ffb21SWarner Losh  * the rights to use, copy, modify, merge, publish, distribute, sub license,
15*592ffb21SWarner Losh  * and/or sell copies of the Software, and to permit persons to whom the
16*592ffb21SWarner Losh  * Software is furnished to do so, subject to the following conditions:
17*592ffb21SWarner Losh  *
18*592ffb21SWarner Losh  * The above copyright notice and this permission notice (including the
19*592ffb21SWarner Losh  * next paragraph) shall be included in all copies or substantial portions
20*592ffb21SWarner Losh  * of the Software.
21*592ffb21SWarner Losh  *
22*592ffb21SWarner Losh  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23*592ffb21SWarner Losh  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24*592ffb21SWarner Losh  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
25*592ffb21SWarner Losh  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26*592ffb21SWarner Losh  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27*592ffb21SWarner Losh  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28*592ffb21SWarner Losh  * DEALINGS IN THE SOFTWARE.
29*592ffb21SWarner Losh  */
30*592ffb21SWarner Losh 
31*592ffb21SWarner Losh #include <sys/cdefs.h>
32*592ffb21SWarner Losh #include <dev/drm2/drmP.h>
33*592ffb21SWarner Losh #include <dev/drm2/drm_edid.h>
34*592ffb21SWarner Losh #include "drm_edid_modes.h"
35*592ffb21SWarner Losh #include <dev/iicbus/iic.h>
36*592ffb21SWarner Losh #include <dev/iicbus/iiconf.h>
37*592ffb21SWarner Losh #include "iicbus_if.h"
38*592ffb21SWarner Losh 
39*592ffb21SWarner Losh #define version_greater(edid, maj, min) \
40*592ffb21SWarner Losh 	(((edid)->version > (maj)) || \
41*592ffb21SWarner Losh 	 ((edid)->version == (maj) && (edid)->revision > (min)))
42*592ffb21SWarner Losh 
43*592ffb21SWarner Losh #define EDID_EST_TIMINGS 16
44*592ffb21SWarner Losh #define EDID_STD_TIMINGS 8
45*592ffb21SWarner Losh #define EDID_DETAILED_TIMINGS 4
46*592ffb21SWarner Losh 
47*592ffb21SWarner Losh /*
48*592ffb21SWarner Losh  * EDID blocks out in the wild have a variety of bugs, try to collect
49*592ffb21SWarner Losh  * them here (note that userspace may work around broken monitors first,
50*592ffb21SWarner Losh  * but fixes should make their way here so that the kernel "just works"
51*592ffb21SWarner Losh  * on as many displays as possible).
52*592ffb21SWarner Losh  */
53*592ffb21SWarner Losh 
54*592ffb21SWarner Losh /* First detailed mode wrong, use largest 60Hz mode */
55*592ffb21SWarner Losh #define EDID_QUIRK_PREFER_LARGE_60		(1 << 0)
56*592ffb21SWarner Losh /* Reported 135MHz pixel clock is too high, needs adjustment */
57*592ffb21SWarner Losh #define EDID_QUIRK_135_CLOCK_TOO_HIGH		(1 << 1)
58*592ffb21SWarner Losh /* Prefer the largest mode at 75 Hz */
59*592ffb21SWarner Losh #define EDID_QUIRK_PREFER_LARGE_75		(1 << 2)
60*592ffb21SWarner Losh /* Detail timing is in cm not mm */
61*592ffb21SWarner Losh #define EDID_QUIRK_DETAILED_IN_CM		(1 << 3)
62*592ffb21SWarner Losh /* Detailed timing descriptors have bogus size values, so just take the
63*592ffb21SWarner Losh  * maximum size and use that.
64*592ffb21SWarner Losh  */
65*592ffb21SWarner Losh #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE	(1 << 4)
66*592ffb21SWarner Losh /* Monitor forgot to set the first detailed is preferred bit. */
67*592ffb21SWarner Losh #define EDID_QUIRK_FIRST_DETAILED_PREFERRED	(1 << 5)
68*592ffb21SWarner Losh /* use +hsync +vsync for detailed mode */
69*592ffb21SWarner Losh #define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)
70*592ffb21SWarner Losh /* Force reduced-blanking timings for detailed modes */
71*592ffb21SWarner Losh #define EDID_QUIRK_FORCE_REDUCED_BLANKING	(1 << 7)
72*592ffb21SWarner Losh 
73*592ffb21SWarner Losh struct detailed_mode_closure {
74*592ffb21SWarner Losh 	struct drm_connector *connector;
75*592ffb21SWarner Losh 	struct edid *edid;
76*592ffb21SWarner Losh 	bool preferred;
77*592ffb21SWarner Losh 	u32 quirks;
78*592ffb21SWarner Losh 	int modes;
79*592ffb21SWarner Losh };
80*592ffb21SWarner Losh 
81*592ffb21SWarner Losh #define LEVEL_DMT	0
82*592ffb21SWarner Losh #define LEVEL_GTF	1
83*592ffb21SWarner Losh #define LEVEL_GTF2	2
84*592ffb21SWarner Losh #define LEVEL_CVT	3
85*592ffb21SWarner Losh 
86*592ffb21SWarner Losh static struct edid_quirk {
87*592ffb21SWarner Losh 	char vendor[4];
88*592ffb21SWarner Losh 	int product_id;
89*592ffb21SWarner Losh 	u32 quirks;
90*592ffb21SWarner Losh } edid_quirk_list[] = {
91*592ffb21SWarner Losh 	/* Acer AL1706 */
92*592ffb21SWarner Losh 	{ "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
93*592ffb21SWarner Losh 	/* Acer F51 */
94*592ffb21SWarner Losh 	{ "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 },
95*592ffb21SWarner Losh 	/* Unknown Acer */
96*592ffb21SWarner Losh 	{ "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
97*592ffb21SWarner Losh 
98*592ffb21SWarner Losh 	/* Belinea 10 15 55 */
99*592ffb21SWarner Losh 	{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
100*592ffb21SWarner Losh 	{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
101*592ffb21SWarner Losh 
102*592ffb21SWarner Losh 	/* Envision Peripherals, Inc. EN-7100e */
103*592ffb21SWarner Losh 	{ "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
104*592ffb21SWarner Losh 	/* Envision EN2028 */
105*592ffb21SWarner Losh 	{ "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 },
106*592ffb21SWarner Losh 
107*592ffb21SWarner Losh 	/* Funai Electronics PM36B */
108*592ffb21SWarner Losh 	{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
109*592ffb21SWarner Losh 	  EDID_QUIRK_DETAILED_IN_CM },
110*592ffb21SWarner Losh 
111*592ffb21SWarner Losh 	/* LG Philips LCD LP154W01-A5 */
112*592ffb21SWarner Losh 	{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
113*592ffb21SWarner Losh 	{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
114*592ffb21SWarner Losh 
115*592ffb21SWarner Losh 	/* Philips 107p5 CRT */
116*592ffb21SWarner Losh 	{ "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
117*592ffb21SWarner Losh 
118*592ffb21SWarner Losh 	/* Proview AY765C */
119*592ffb21SWarner Losh 	{ "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
120*592ffb21SWarner Losh 
121*592ffb21SWarner Losh 	/* Samsung SyncMaster 205BW.  Note: irony */
122*592ffb21SWarner Losh 	{ "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP },
123*592ffb21SWarner Losh 	/* Samsung SyncMaster 22[5-6]BW */
124*592ffb21SWarner Losh 	{ "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
125*592ffb21SWarner Losh 	{ "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
126*592ffb21SWarner Losh 
127*592ffb21SWarner Losh 	/* ViewSonic VA2026w */
128*592ffb21SWarner Losh 	{ "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING },
129*592ffb21SWarner Losh };
130*592ffb21SWarner Losh 
131*592ffb21SWarner Losh /*** DDC fetch and block validation ***/
132*592ffb21SWarner Losh 
133*592ffb21SWarner Losh static const u8 edid_header[] = {
134*592ffb21SWarner Losh 	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
135*592ffb21SWarner Losh };
136*592ffb21SWarner Losh 
137*592ffb21SWarner Losh  /*
138*592ffb21SWarner Losh  * Sanity check the header of the base EDID block.  Return 8 if the header
139*592ffb21SWarner Losh  * is perfect, down to 0 if it's totally wrong.
140*592ffb21SWarner Losh  */
drm_edid_header_is_valid(const u8 * raw_edid)141*592ffb21SWarner Losh int drm_edid_header_is_valid(const u8 *raw_edid)
142*592ffb21SWarner Losh {
143*592ffb21SWarner Losh 	int i, score = 0;
144*592ffb21SWarner Losh 
145*592ffb21SWarner Losh 	for (i = 0; i < sizeof(edid_header); i++)
146*592ffb21SWarner Losh 		if (raw_edid[i] == edid_header[i])
147*592ffb21SWarner Losh 			score++;
148*592ffb21SWarner Losh 
149*592ffb21SWarner Losh 	return score;
150*592ffb21SWarner Losh }
151*592ffb21SWarner Losh EXPORT_SYMBOL(drm_edid_header_is_valid);
152*592ffb21SWarner Losh 
153*592ffb21SWarner Losh static int edid_fixup __read_mostly = 6;
154*592ffb21SWarner Losh module_param_named(edid_fixup, edid_fixup, int, 0400);
155*592ffb21SWarner Losh MODULE_PARM_DESC(edid_fixup,
156*592ffb21SWarner Losh 		 "Minimum number of valid EDID header bytes (0-8, default 6)");
157*592ffb21SWarner Losh 
158*592ffb21SWarner Losh /*
159*592ffb21SWarner Losh  * Sanity check the EDID block (base or extension).  Return 0 if the block
160*592ffb21SWarner Losh  * doesn't check out, or 1 if it's valid.
161*592ffb21SWarner Losh  */
drm_edid_block_valid(u8 * raw_edid,int block,bool print_bad_edid)162*592ffb21SWarner Losh bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
163*592ffb21SWarner Losh {
164*592ffb21SWarner Losh 	int i;
165*592ffb21SWarner Losh 	u8 csum = 0;
166*592ffb21SWarner Losh 	struct edid *edid = (struct edid *)raw_edid;
167*592ffb21SWarner Losh 
168*592ffb21SWarner Losh 	if (edid_fixup > 8 || edid_fixup < 0)
169*592ffb21SWarner Losh 		edid_fixup = 6;
170*592ffb21SWarner Losh 
171*592ffb21SWarner Losh 	if (block == 0) {
172*592ffb21SWarner Losh 		int score = drm_edid_header_is_valid(raw_edid);
173*592ffb21SWarner Losh 		if (score == 8) ;
174*592ffb21SWarner Losh 		else if (score >= edid_fixup) {
175*592ffb21SWarner Losh 			DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
176*592ffb21SWarner Losh 			memcpy(raw_edid, edid_header, sizeof(edid_header));
177*592ffb21SWarner Losh 		} else {
178*592ffb21SWarner Losh 			goto bad;
179*592ffb21SWarner Losh 		}
180*592ffb21SWarner Losh 	}
181*592ffb21SWarner Losh 
182*592ffb21SWarner Losh 	for (i = 0; i < EDID_LENGTH; i++)
183*592ffb21SWarner Losh 		csum += raw_edid[i];
184*592ffb21SWarner Losh 	if (csum) {
185*592ffb21SWarner Losh 		if (print_bad_edid) {
186*592ffb21SWarner Losh 			DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
187*592ffb21SWarner Losh 		}
188*592ffb21SWarner Losh 
189*592ffb21SWarner Losh 		/* allow CEA to slide through, switches mangle this */
190*592ffb21SWarner Losh 		if (raw_edid[0] != 0x02)
191*592ffb21SWarner Losh 			goto bad;
192*592ffb21SWarner Losh 	}
193*592ffb21SWarner Losh 
194*592ffb21SWarner Losh 	/* per-block-type checks */
195*592ffb21SWarner Losh 	switch (raw_edid[0]) {
196*592ffb21SWarner Losh 	case 0: /* base */
197*592ffb21SWarner Losh 		if (edid->version != 1) {
198*592ffb21SWarner Losh 			DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
199*592ffb21SWarner Losh 			goto bad;
200*592ffb21SWarner Losh 		}
201*592ffb21SWarner Losh 
202*592ffb21SWarner Losh 		if (edid->revision > 4)
203*592ffb21SWarner Losh 			DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
204*592ffb21SWarner Losh 		break;
205*592ffb21SWarner Losh 
206*592ffb21SWarner Losh 	default:
207*592ffb21SWarner Losh 		break;
208*592ffb21SWarner Losh 	}
209*592ffb21SWarner Losh 
210*592ffb21SWarner Losh 	return 1;
211*592ffb21SWarner Losh 
212*592ffb21SWarner Losh bad:
213*592ffb21SWarner Losh 	if (raw_edid && print_bad_edid) {
214*592ffb21SWarner Losh 		DRM_DEBUG_KMS("Raw EDID:\n");
215*592ffb21SWarner Losh 		for (i = 0; i < EDID_LENGTH; ) {
216*592ffb21SWarner Losh 			printf("%02x", raw_edid[i]);
217*592ffb21SWarner Losh 			i++;
218*592ffb21SWarner Losh 			if (i % 16 == 0 || i == EDID_LENGTH)
219*592ffb21SWarner Losh 				printf("\n");
220*592ffb21SWarner Losh 			else if (i % 8 == 0)
221*592ffb21SWarner Losh 				printf("  ");
222*592ffb21SWarner Losh 			else
223*592ffb21SWarner Losh 				printf(" ");
224*592ffb21SWarner Losh 		}
225*592ffb21SWarner Losh 	}
226*592ffb21SWarner Losh 	return 0;
227*592ffb21SWarner Losh }
228*592ffb21SWarner Losh EXPORT_SYMBOL(drm_edid_block_valid);
229*592ffb21SWarner Losh 
230*592ffb21SWarner Losh /**
231*592ffb21SWarner Losh  * drm_edid_is_valid - sanity check EDID data
232*592ffb21SWarner Losh  * @edid: EDID data
233*592ffb21SWarner Losh  *
234*592ffb21SWarner Losh  * Sanity-check an entire EDID record (including extensions)
235*592ffb21SWarner Losh  */
drm_edid_is_valid(struct edid * edid)236*592ffb21SWarner Losh bool drm_edid_is_valid(struct edid *edid)
237*592ffb21SWarner Losh {
238*592ffb21SWarner Losh 	int i;
239*592ffb21SWarner Losh 	u8 *raw = (u8 *)edid;
240*592ffb21SWarner Losh 
241*592ffb21SWarner Losh 	if (!edid)
242*592ffb21SWarner Losh 		return false;
243*592ffb21SWarner Losh 
244*592ffb21SWarner Losh 	for (i = 0; i <= edid->extensions; i++)
245*592ffb21SWarner Losh 		if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true))
246*592ffb21SWarner Losh 			return false;
247*592ffb21SWarner Losh 
248*592ffb21SWarner Losh 	return true;
249*592ffb21SWarner Losh }
250*592ffb21SWarner Losh EXPORT_SYMBOL(drm_edid_is_valid);
251*592ffb21SWarner Losh 
252*592ffb21SWarner Losh #define DDC_SEGMENT_ADDR 0x30
253*592ffb21SWarner Losh /**
254*592ffb21SWarner Losh  * Get EDID information via I2C.
255*592ffb21SWarner Losh  *
256*592ffb21SWarner Losh  * \param adapter : i2c device adaptor
257*592ffb21SWarner Losh  * \param buf     : EDID data buffer to be filled
258*592ffb21SWarner Losh  * \param len     : EDID data buffer length
259*592ffb21SWarner Losh  * \return 0 on success or -1 on failure.
260*592ffb21SWarner Losh  *
261*592ffb21SWarner Losh  * Try to fetch EDID information by calling i2c driver function.
262*592ffb21SWarner Losh  */
263*592ffb21SWarner Losh static int
drm_do_probe_ddc_edid(device_t adapter,unsigned char * buf,int block,int len)264*592ffb21SWarner Losh drm_do_probe_ddc_edid(device_t adapter, unsigned char *buf,
265*592ffb21SWarner Losh 		      int block, int len)
266*592ffb21SWarner Losh {
267*592ffb21SWarner Losh 	unsigned char start = block * EDID_LENGTH;
268*592ffb21SWarner Losh 	unsigned char segment = block >> 1;
269*592ffb21SWarner Losh 	unsigned char xfers = segment ? 3 : 2;
270*592ffb21SWarner Losh 	int ret, retries = 5;
271*592ffb21SWarner Losh 
272*592ffb21SWarner Losh 	/* The core i2c driver will automatically retry the transfer if the
273*592ffb21SWarner Losh 	 * adapter reports EAGAIN. However, we find that bit-banging transfers
274*592ffb21SWarner Losh 	 * are susceptible to errors under a heavily loaded machine and
275*592ffb21SWarner Losh 	 * generate spurious NAKs and timeouts. Retrying the transfer
276*592ffb21SWarner Losh 	 * of the individual block a few times seems to overcome this.
277*592ffb21SWarner Losh 	 */
278*592ffb21SWarner Losh 	do {
279*592ffb21SWarner Losh 		struct iic_msg msgs[] = {
280*592ffb21SWarner Losh 			{
281*592ffb21SWarner Losh 				.slave	= DDC_SEGMENT_ADDR << 1,
282*592ffb21SWarner Losh 				.flags	= 0,
283*592ffb21SWarner Losh 				.len	= 1,
284*592ffb21SWarner Losh 				.buf	= &segment,
285*592ffb21SWarner Losh 			}, {
286*592ffb21SWarner Losh 				.slave	= DDC_ADDR << 1,
287*592ffb21SWarner Losh 				.flags	= 0,
288*592ffb21SWarner Losh 				.len	= 1,
289*592ffb21SWarner Losh 				.buf	= &start,
290*592ffb21SWarner Losh 			}, {
291*592ffb21SWarner Losh 				.slave	= DDC_ADDR << 1,
292*592ffb21SWarner Losh 				.flags	= IIC_M_RD,
293*592ffb21SWarner Losh 				.len	= len,
294*592ffb21SWarner Losh 				.buf	= buf,
295*592ffb21SWarner Losh 			}
296*592ffb21SWarner Losh 		};
297*592ffb21SWarner Losh 
298*592ffb21SWarner Losh 	/*
299*592ffb21SWarner Losh 	 * Avoid sending the segment addr to not upset non-compliant ddc
300*592ffb21SWarner Losh 	 * monitors.
301*592ffb21SWarner Losh 	 */
302*592ffb21SWarner Losh 		ret = iicbus_transfer(adapter, &msgs[3 - xfers], xfers);
303*592ffb21SWarner Losh 
304*592ffb21SWarner Losh 		if (ret != 0)
305*592ffb21SWarner Losh 			DRM_DEBUG_KMS("iicbus_transfer countdown %d error %d\n",
306*592ffb21SWarner Losh 			    retries, ret);
307*592ffb21SWarner Losh 	} while (ret != 0 && --retries);
308*592ffb21SWarner Losh 
309*592ffb21SWarner Losh 	return ret == 0 ? 0 : -1;
310*592ffb21SWarner Losh }
311*592ffb21SWarner Losh 
drm_edid_is_zero(u8 * in_edid,int length)312*592ffb21SWarner Losh static bool drm_edid_is_zero(u8 *in_edid, int length)
313*592ffb21SWarner Losh {
314*592ffb21SWarner Losh 	int i;
315*592ffb21SWarner Losh 	u32 *raw_edid = (u32 *)in_edid;
316*592ffb21SWarner Losh 
317*592ffb21SWarner Losh 	for (i = 0; i < length / 4; i++)
318*592ffb21SWarner Losh 		if (*(raw_edid + i) != 0)
319*592ffb21SWarner Losh 			return false;
320*592ffb21SWarner Losh 
321*592ffb21SWarner Losh 	return true;
322*592ffb21SWarner Losh }
323*592ffb21SWarner Losh 
324*592ffb21SWarner Losh static u8 *
drm_do_get_edid(struct drm_connector * connector,device_t adapter)325*592ffb21SWarner Losh drm_do_get_edid(struct drm_connector *connector, device_t adapter)
326*592ffb21SWarner Losh {
327*592ffb21SWarner Losh 	int i, j = 0, valid_extensions = 0;
328*592ffb21SWarner Losh 	u8 *block, *new;
329*592ffb21SWarner Losh 	bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_DEBUGBITS_KMS);
330*592ffb21SWarner Losh 
331*592ffb21SWarner Losh 	if ((block = malloc(EDID_LENGTH, DRM_MEM_KMS, M_NOWAIT)) == NULL)
332*592ffb21SWarner Losh 		return NULL;
333*592ffb21SWarner Losh 
334*592ffb21SWarner Losh 	/* base block fetch */
335*592ffb21SWarner Losh 	for (i = 0; i < 4; i++) {
336*592ffb21SWarner Losh 		if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
337*592ffb21SWarner Losh 			goto out;
338*592ffb21SWarner Losh 		if (drm_edid_block_valid(block, 0, print_bad_edid))
339*592ffb21SWarner Losh 			break;
340*592ffb21SWarner Losh 		if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) {
341*592ffb21SWarner Losh 			connector->null_edid_counter++;
342*592ffb21SWarner Losh 			goto carp;
343*592ffb21SWarner Losh 		}
344*592ffb21SWarner Losh 	}
345*592ffb21SWarner Losh 	if (i == 4)
346*592ffb21SWarner Losh 		goto carp;
347*592ffb21SWarner Losh 
348*592ffb21SWarner Losh 	/* if there's no extensions, we're done */
349*592ffb21SWarner Losh 	if (block[0x7e] == 0)
350*592ffb21SWarner Losh 		return block;
351*592ffb21SWarner Losh 
352*592ffb21SWarner Losh 	new = reallocf(block, (block[0x7e] + 1) * EDID_LENGTH, DRM_MEM_KMS,
353*592ffb21SWarner Losh 	    M_NOWAIT);
354*592ffb21SWarner Losh 	if (!new) {
355*592ffb21SWarner Losh 		block = NULL;
356*592ffb21SWarner Losh 		goto out;
357*592ffb21SWarner Losh 	}
358*592ffb21SWarner Losh 	block = new;
359*592ffb21SWarner Losh 
360*592ffb21SWarner Losh 	for (j = 1; j <= block[0x7e]; j++) {
361*592ffb21SWarner Losh 		for (i = 0; i < 4; i++) {
362*592ffb21SWarner Losh 			if (drm_do_probe_ddc_edid(adapter,
363*592ffb21SWarner Losh 				  block + (valid_extensions + 1) * EDID_LENGTH,
364*592ffb21SWarner Losh 				  j, EDID_LENGTH))
365*592ffb21SWarner Losh 				goto out;
366*592ffb21SWarner Losh 			if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) {
367*592ffb21SWarner Losh 				valid_extensions++;
368*592ffb21SWarner Losh 				break;
369*592ffb21SWarner Losh 			}
370*592ffb21SWarner Losh 		}
371*592ffb21SWarner Losh 
372*592ffb21SWarner Losh 		if (i == 4 && print_bad_edid) {
373*592ffb21SWarner Losh 			dev_warn(connector->dev->dev,
374*592ffb21SWarner Losh 			 "%s: Ignoring invalid EDID block %d.\n",
375*592ffb21SWarner Losh 			 drm_get_connector_name(connector), j);
376*592ffb21SWarner Losh 
377*592ffb21SWarner Losh 			connector->bad_edid_counter++;
378*592ffb21SWarner Losh 		}
379*592ffb21SWarner Losh 	}
380*592ffb21SWarner Losh 
381*592ffb21SWarner Losh 	if (valid_extensions != block[0x7e]) {
382*592ffb21SWarner Losh 		block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
383*592ffb21SWarner Losh 		block[0x7e] = valid_extensions;
384*592ffb21SWarner Losh 		new = reallocf(block, (valid_extensions + 1) * EDID_LENGTH,
385*592ffb21SWarner Losh 		    DRM_MEM_KMS, M_NOWAIT);
386*592ffb21SWarner Losh 		if (!new)
387*592ffb21SWarner Losh 			goto out;
388*592ffb21SWarner Losh 		block = new;
389*592ffb21SWarner Losh 	}
390*592ffb21SWarner Losh 
391*592ffb21SWarner Losh 	return block;
392*592ffb21SWarner Losh 
393*592ffb21SWarner Losh carp:
394*592ffb21SWarner Losh 	if (print_bad_edid) {
395*592ffb21SWarner Losh 		dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
396*592ffb21SWarner Losh 			 drm_get_connector_name(connector), j);
397*592ffb21SWarner Losh 	}
398*592ffb21SWarner Losh 	connector->bad_edid_counter++;
399*592ffb21SWarner Losh 
400*592ffb21SWarner Losh out:
401*592ffb21SWarner Losh 	free(block, DRM_MEM_KMS);
402*592ffb21SWarner Losh 	return NULL;
403*592ffb21SWarner Losh }
404*592ffb21SWarner Losh 
405*592ffb21SWarner Losh /**
406*592ffb21SWarner Losh  * Probe DDC presence.
407*592ffb21SWarner Losh  *
408*592ffb21SWarner Losh  * \param adapter : i2c device adaptor
409*592ffb21SWarner Losh  * \return 1 on success
410*592ffb21SWarner Losh  */
411*592ffb21SWarner Losh bool
drm_probe_ddc(device_t adapter)412*592ffb21SWarner Losh drm_probe_ddc(device_t adapter)
413*592ffb21SWarner Losh {
414*592ffb21SWarner Losh 	unsigned char out;
415*592ffb21SWarner Losh 
416*592ffb21SWarner Losh 	return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0);
417*592ffb21SWarner Losh }
418*592ffb21SWarner Losh EXPORT_SYMBOL(drm_probe_ddc);
419*592ffb21SWarner Losh 
420*592ffb21SWarner Losh /**
421*592ffb21SWarner Losh  * drm_get_edid - get EDID data, if available
422*592ffb21SWarner Losh  * @connector: connector we're probing
423*592ffb21SWarner Losh  * @adapter: i2c adapter to use for DDC
424*592ffb21SWarner Losh  *
425*592ffb21SWarner Losh  * Poke the given i2c channel to grab EDID data if possible.  If found,
426*592ffb21SWarner Losh  * attach it to the connector.
427*592ffb21SWarner Losh  *
428*592ffb21SWarner Losh  * Return edid data or NULL if we couldn't find any.
429*592ffb21SWarner Losh  */
drm_get_edid(struct drm_connector * connector,device_t adapter)430*592ffb21SWarner Losh struct edid *drm_get_edid(struct drm_connector *connector,
431*592ffb21SWarner Losh 			  device_t adapter)
432*592ffb21SWarner Losh {
433*592ffb21SWarner Losh 	struct edid *edid = NULL;
434*592ffb21SWarner Losh 
435*592ffb21SWarner Losh 	if (drm_probe_ddc(adapter))
436*592ffb21SWarner Losh 		edid = (struct edid *)drm_do_get_edid(connector, adapter);
437*592ffb21SWarner Losh 
438*592ffb21SWarner Losh 	return edid;
439*592ffb21SWarner Losh }
440*592ffb21SWarner Losh EXPORT_SYMBOL(drm_get_edid);
441*592ffb21SWarner Losh 
442*592ffb21SWarner Losh /*** EDID parsing ***/
443*592ffb21SWarner Losh 
444*592ffb21SWarner Losh /**
445*592ffb21SWarner Losh  * edid_vendor - match a string against EDID's obfuscated vendor field
446*592ffb21SWarner Losh  * @edid: EDID to match
447*592ffb21SWarner Losh  * @vendor: vendor string
448*592ffb21SWarner Losh  *
449*592ffb21SWarner Losh  * Returns true if @vendor is in @edid, false otherwise
450*592ffb21SWarner Losh  */
edid_vendor(struct edid * edid,char * vendor)451*592ffb21SWarner Losh static bool edid_vendor(struct edid *edid, char *vendor)
452*592ffb21SWarner Losh {
453*592ffb21SWarner Losh 	char edid_vendor[3];
454*592ffb21SWarner Losh 
455*592ffb21SWarner Losh 	edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@';
456*592ffb21SWarner Losh 	edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) |
457*592ffb21SWarner Losh 			  ((edid->mfg_id[1] & 0xe0) >> 5)) + '@';
458*592ffb21SWarner Losh 	edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@';
459*592ffb21SWarner Losh 
460*592ffb21SWarner Losh 	return !strncmp(edid_vendor, vendor, 3);
461*592ffb21SWarner Losh }
462*592ffb21SWarner Losh 
463*592ffb21SWarner Losh /**
464*592ffb21SWarner Losh  * edid_get_quirks - return quirk flags for a given EDID
465*592ffb21SWarner Losh  * @edid: EDID to process
466*592ffb21SWarner Losh  *
467*592ffb21SWarner Losh  * This tells subsequent routines what fixes they need to apply.
468*592ffb21SWarner Losh  */
edid_get_quirks(struct edid * edid)469*592ffb21SWarner Losh static u32 edid_get_quirks(struct edid *edid)
470*592ffb21SWarner Losh {
471*592ffb21SWarner Losh 	struct edid_quirk *quirk;
472*592ffb21SWarner Losh 	int i;
473*592ffb21SWarner Losh 
474*592ffb21SWarner Losh 	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
475*592ffb21SWarner Losh 		quirk = &edid_quirk_list[i];
476*592ffb21SWarner Losh 
477*592ffb21SWarner Losh 		if (edid_vendor(edid, quirk->vendor) &&
478*592ffb21SWarner Losh 		    (EDID_PRODUCT_ID(edid) == quirk->product_id))
479*592ffb21SWarner Losh 			return quirk->quirks;
480*592ffb21SWarner Losh 	}
481*592ffb21SWarner Losh 
482*592ffb21SWarner Losh 	return 0;
483*592ffb21SWarner Losh }
484*592ffb21SWarner Losh 
485*592ffb21SWarner Losh #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
486*592ffb21SWarner Losh #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
487*592ffb21SWarner Losh 
488*592ffb21SWarner Losh /**
489*592ffb21SWarner Losh  * edid_fixup_preferred - set preferred modes based on quirk list
490*592ffb21SWarner Losh  * @connector: has mode list to fix up
491*592ffb21SWarner Losh  * @quirks: quirks list
492*592ffb21SWarner Losh  *
493*592ffb21SWarner Losh  * Walk the mode list for @connector, clearing the preferred status
494*592ffb21SWarner Losh  * on existing modes and setting it anew for the right mode ala @quirks.
495*592ffb21SWarner Losh  */
edid_fixup_preferred(struct drm_connector * connector,u32 quirks)496*592ffb21SWarner Losh static void edid_fixup_preferred(struct drm_connector *connector,
497*592ffb21SWarner Losh 				 u32 quirks)
498*592ffb21SWarner Losh {
499*592ffb21SWarner Losh 	struct drm_display_mode *t, *cur_mode, *preferred_mode;
500*592ffb21SWarner Losh 	int target_refresh = 0;
501*592ffb21SWarner Losh 
502*592ffb21SWarner Losh 	if (list_empty(&connector->probed_modes))
503*592ffb21SWarner Losh 		return;
504*592ffb21SWarner Losh 
505*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_PREFER_LARGE_60)
506*592ffb21SWarner Losh 		target_refresh = 60;
507*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_PREFER_LARGE_75)
508*592ffb21SWarner Losh 		target_refresh = 75;
509*592ffb21SWarner Losh 
510*592ffb21SWarner Losh 	preferred_mode = list_first_entry(&connector->probed_modes,
511*592ffb21SWarner Losh 					  struct drm_display_mode, head);
512*592ffb21SWarner Losh 
513*592ffb21SWarner Losh 	list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
514*592ffb21SWarner Losh 		cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
515*592ffb21SWarner Losh 
516*592ffb21SWarner Losh 		if (cur_mode == preferred_mode)
517*592ffb21SWarner Losh 			continue;
518*592ffb21SWarner Losh 
519*592ffb21SWarner Losh 		/* Largest mode is preferred */
520*592ffb21SWarner Losh 		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
521*592ffb21SWarner Losh 			preferred_mode = cur_mode;
522*592ffb21SWarner Losh 
523*592ffb21SWarner Losh 		/* At a given size, try to get closest to target refresh */
524*592ffb21SWarner Losh 		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
525*592ffb21SWarner Losh 		    MODE_REFRESH_DIFF(cur_mode, target_refresh) <
526*592ffb21SWarner Losh 		    MODE_REFRESH_DIFF(preferred_mode, target_refresh)) {
527*592ffb21SWarner Losh 			preferred_mode = cur_mode;
528*592ffb21SWarner Losh 		}
529*592ffb21SWarner Losh 	}
530*592ffb21SWarner Losh 
531*592ffb21SWarner Losh 	preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
532*592ffb21SWarner Losh }
533*592ffb21SWarner Losh 
534*592ffb21SWarner Losh static bool
mode_is_rb(const struct drm_display_mode * mode)535*592ffb21SWarner Losh mode_is_rb(const struct drm_display_mode *mode)
536*592ffb21SWarner Losh {
537*592ffb21SWarner Losh 	return (mode->htotal - mode->hdisplay == 160) &&
538*592ffb21SWarner Losh 	       (mode->hsync_end - mode->hdisplay == 80) &&
539*592ffb21SWarner Losh 	       (mode->hsync_end - mode->hsync_start == 32) &&
540*592ffb21SWarner Losh 	       (mode->vsync_start - mode->vdisplay == 3);
541*592ffb21SWarner Losh }
542*592ffb21SWarner Losh 
543*592ffb21SWarner Losh /*
544*592ffb21SWarner Losh  * drm_mode_find_dmt - Create a copy of a mode if present in DMT
545*592ffb21SWarner Losh  * @dev: Device to duplicate against
546*592ffb21SWarner Losh  * @hsize: Mode width
547*592ffb21SWarner Losh  * @vsize: Mode height
548*592ffb21SWarner Losh  * @fresh: Mode refresh rate
549*592ffb21SWarner Losh  * @rb: Mode reduced-blanking-ness
550*592ffb21SWarner Losh  *
551*592ffb21SWarner Losh  * Walk the DMT mode list looking for a match for the given parameters.
552*592ffb21SWarner Losh  * Return a newly allocated copy of the mode, or NULL if not found.
553*592ffb21SWarner Losh  */
drm_mode_find_dmt(struct drm_device * dev,int hsize,int vsize,int fresh,bool rb)554*592ffb21SWarner Losh struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
555*592ffb21SWarner Losh 					   int hsize, int vsize, int fresh,
556*592ffb21SWarner Losh 					   bool rb)
557*592ffb21SWarner Losh {
558*592ffb21SWarner Losh 	int i;
559*592ffb21SWarner Losh 
560*592ffb21SWarner Losh 	for (i = 0; i < drm_num_dmt_modes; i++) {
561*592ffb21SWarner Losh 		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
562*592ffb21SWarner Losh 		if (hsize != ptr->hdisplay)
563*592ffb21SWarner Losh 			continue;
564*592ffb21SWarner Losh 		if (vsize != ptr->vdisplay)
565*592ffb21SWarner Losh 			continue;
566*592ffb21SWarner Losh 		if (fresh != drm_mode_vrefresh(ptr))
567*592ffb21SWarner Losh 			continue;
568*592ffb21SWarner Losh 		if (rb != mode_is_rb(ptr))
569*592ffb21SWarner Losh 			continue;
570*592ffb21SWarner Losh 
571*592ffb21SWarner Losh 		return drm_mode_duplicate(dev, ptr);
572*592ffb21SWarner Losh 	}
573*592ffb21SWarner Losh 
574*592ffb21SWarner Losh 	return NULL;
575*592ffb21SWarner Losh }
576*592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_find_dmt);
577*592ffb21SWarner Losh 
578*592ffb21SWarner Losh typedef void detailed_cb(struct detailed_timing *timing, void *closure);
579*592ffb21SWarner Losh 
580*592ffb21SWarner Losh static void
cea_for_each_detailed_block(u8 * ext,detailed_cb * cb,void * closure)581*592ffb21SWarner Losh cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
582*592ffb21SWarner Losh {
583*592ffb21SWarner Losh 	int i, n = 0;
584*592ffb21SWarner Losh 	u8 d = ext[0x02];
585*592ffb21SWarner Losh 	u8 *det_base = ext + d;
586*592ffb21SWarner Losh 
587*592ffb21SWarner Losh 	n = (127 - d) / 18;
588*592ffb21SWarner Losh 	for (i = 0; i < n; i++)
589*592ffb21SWarner Losh 		cb((struct detailed_timing *)(det_base + 18 * i), closure);
590*592ffb21SWarner Losh }
591*592ffb21SWarner Losh 
592*592ffb21SWarner Losh static void
vtb_for_each_detailed_block(u8 * ext,detailed_cb * cb,void * closure)593*592ffb21SWarner Losh vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
594*592ffb21SWarner Losh {
595*592ffb21SWarner Losh 	unsigned int i, n = min((int)ext[0x02], 6);
596*592ffb21SWarner Losh 	u8 *det_base = ext + 5;
597*592ffb21SWarner Losh 
598*592ffb21SWarner Losh 	if (ext[0x01] != 1)
599*592ffb21SWarner Losh 		return; /* unknown version */
600*592ffb21SWarner Losh 
601*592ffb21SWarner Losh 	for (i = 0; i < n; i++)
602*592ffb21SWarner Losh 		cb((struct detailed_timing *)(det_base + 18 * i), closure);
603*592ffb21SWarner Losh }
604*592ffb21SWarner Losh 
605*592ffb21SWarner Losh static void
drm_for_each_detailed_block(u8 * raw_edid,detailed_cb * cb,void * closure)606*592ffb21SWarner Losh drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
607*592ffb21SWarner Losh {
608*592ffb21SWarner Losh 	int i;
609*592ffb21SWarner Losh 	struct edid *edid = (struct edid *)raw_edid;
610*592ffb21SWarner Losh 
611*592ffb21SWarner Losh 	if (edid == NULL)
612*592ffb21SWarner Losh 		return;
613*592ffb21SWarner Losh 
614*592ffb21SWarner Losh 	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
615*592ffb21SWarner Losh 		cb(&(edid->detailed_timings[i]), closure);
616*592ffb21SWarner Losh 
617*592ffb21SWarner Losh 	for (i = 1; i <= raw_edid[0x7e]; i++) {
618*592ffb21SWarner Losh 		u8 *ext = raw_edid + (i * EDID_LENGTH);
619*592ffb21SWarner Losh 		switch (*ext) {
620*592ffb21SWarner Losh 		case CEA_EXT:
621*592ffb21SWarner Losh 			cea_for_each_detailed_block(ext, cb, closure);
622*592ffb21SWarner Losh 			break;
623*592ffb21SWarner Losh 		case VTB_EXT:
624*592ffb21SWarner Losh 			vtb_for_each_detailed_block(ext, cb, closure);
625*592ffb21SWarner Losh 			break;
626*592ffb21SWarner Losh 		default:
627*592ffb21SWarner Losh 			break;
628*592ffb21SWarner Losh 		}
629*592ffb21SWarner Losh 	}
630*592ffb21SWarner Losh }
631*592ffb21SWarner Losh 
632*592ffb21SWarner Losh static void
is_rb(struct detailed_timing * t,void * data)633*592ffb21SWarner Losh is_rb(struct detailed_timing *t, void *data)
634*592ffb21SWarner Losh {
635*592ffb21SWarner Losh 	u8 *r = (u8 *)t;
636*592ffb21SWarner Losh 	if (r[3] == EDID_DETAIL_MONITOR_RANGE)
637*592ffb21SWarner Losh 		if (r[15] & 0x10)
638*592ffb21SWarner Losh 			*(bool *)data = true;
639*592ffb21SWarner Losh }
640*592ffb21SWarner Losh 
641*592ffb21SWarner Losh /* EDID 1.4 defines this explicitly.  For EDID 1.3, we guess, badly. */
642*592ffb21SWarner Losh static bool
drm_monitor_supports_rb(struct edid * edid)643*592ffb21SWarner Losh drm_monitor_supports_rb(struct edid *edid)
644*592ffb21SWarner Losh {
645*592ffb21SWarner Losh 	if (edid->revision >= 4) {
646*592ffb21SWarner Losh 		bool ret = false;
647*592ffb21SWarner Losh 		drm_for_each_detailed_block((u8 *)edid, is_rb, &ret);
648*592ffb21SWarner Losh 		return ret;
649*592ffb21SWarner Losh 	}
650*592ffb21SWarner Losh 
651*592ffb21SWarner Losh 	return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
652*592ffb21SWarner Losh }
653*592ffb21SWarner Losh 
654*592ffb21SWarner Losh static void
find_gtf2(struct detailed_timing * t,void * data)655*592ffb21SWarner Losh find_gtf2(struct detailed_timing *t, void *data)
656*592ffb21SWarner Losh {
657*592ffb21SWarner Losh 	u8 *r = (u8 *)t;
658*592ffb21SWarner Losh 	if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02)
659*592ffb21SWarner Losh 		*(u8 **)data = r;
660*592ffb21SWarner Losh }
661*592ffb21SWarner Losh 
662*592ffb21SWarner Losh /* Secondary GTF curve kicks in above some break frequency */
663*592ffb21SWarner Losh static int
drm_gtf2_hbreak(struct edid * edid)664*592ffb21SWarner Losh drm_gtf2_hbreak(struct edid *edid)
665*592ffb21SWarner Losh {
666*592ffb21SWarner Losh 	u8 *r = NULL;
667*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
668*592ffb21SWarner Losh 	return r ? (r[12] * 2) : 0;
669*592ffb21SWarner Losh }
670*592ffb21SWarner Losh 
671*592ffb21SWarner Losh static int
drm_gtf2_2c(struct edid * edid)672*592ffb21SWarner Losh drm_gtf2_2c(struct edid *edid)
673*592ffb21SWarner Losh {
674*592ffb21SWarner Losh 	u8 *r = NULL;
675*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
676*592ffb21SWarner Losh 	return r ? r[13] : 0;
677*592ffb21SWarner Losh }
678*592ffb21SWarner Losh 
679*592ffb21SWarner Losh static int
drm_gtf2_m(struct edid * edid)680*592ffb21SWarner Losh drm_gtf2_m(struct edid *edid)
681*592ffb21SWarner Losh {
682*592ffb21SWarner Losh 	u8 *r = NULL;
683*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
684*592ffb21SWarner Losh 	return r ? (r[15] << 8) + r[14] : 0;
685*592ffb21SWarner Losh }
686*592ffb21SWarner Losh 
687*592ffb21SWarner Losh static int
drm_gtf2_k(struct edid * edid)688*592ffb21SWarner Losh drm_gtf2_k(struct edid *edid)
689*592ffb21SWarner Losh {
690*592ffb21SWarner Losh 	u8 *r = NULL;
691*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
692*592ffb21SWarner Losh 	return r ? r[16] : 0;
693*592ffb21SWarner Losh }
694*592ffb21SWarner Losh 
695*592ffb21SWarner Losh static int
drm_gtf2_2j(struct edid * edid)696*592ffb21SWarner Losh drm_gtf2_2j(struct edid *edid)
697*592ffb21SWarner Losh {
698*592ffb21SWarner Losh 	u8 *r = NULL;
699*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
700*592ffb21SWarner Losh 	return r ? r[17] : 0;
701*592ffb21SWarner Losh }
702*592ffb21SWarner Losh 
703*592ffb21SWarner Losh /**
704*592ffb21SWarner Losh  * standard_timing_level - get std. timing level(CVT/GTF/DMT)
705*592ffb21SWarner Losh  * @edid: EDID block to scan
706*592ffb21SWarner Losh  */
standard_timing_level(struct edid * edid)707*592ffb21SWarner Losh static int standard_timing_level(struct edid *edid)
708*592ffb21SWarner Losh {
709*592ffb21SWarner Losh 	if (edid->revision >= 2) {
710*592ffb21SWarner Losh 		if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
711*592ffb21SWarner Losh 			return LEVEL_CVT;
712*592ffb21SWarner Losh 		if (drm_gtf2_hbreak(edid))
713*592ffb21SWarner Losh 			return LEVEL_GTF2;
714*592ffb21SWarner Losh 		return LEVEL_GTF;
715*592ffb21SWarner Losh 	}
716*592ffb21SWarner Losh 	return LEVEL_DMT;
717*592ffb21SWarner Losh }
718*592ffb21SWarner Losh 
719*592ffb21SWarner Losh /*
720*592ffb21SWarner Losh  * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
721*592ffb21SWarner Losh  * monitors fill with ascii space (0x20) instead.
722*592ffb21SWarner Losh  */
723*592ffb21SWarner Losh static int
bad_std_timing(u8 a,u8 b)724*592ffb21SWarner Losh bad_std_timing(u8 a, u8 b)
725*592ffb21SWarner Losh {
726*592ffb21SWarner Losh 	return (a == 0x00 && b == 0x00) ||
727*592ffb21SWarner Losh 	       (a == 0x01 && b == 0x01) ||
728*592ffb21SWarner Losh 	       (a == 0x20 && b == 0x20);
729*592ffb21SWarner Losh }
730*592ffb21SWarner Losh 
731*592ffb21SWarner Losh /**
732*592ffb21SWarner Losh  * drm_mode_std - convert standard mode info (width, height, refresh) into mode
733*592ffb21SWarner Losh  * @t: standard timing params
734*592ffb21SWarner Losh  * @timing_level: standard timing level
735*592ffb21SWarner Losh  *
736*592ffb21SWarner Losh  * Take the standard timing params (in this case width, aspect, and refresh)
737*592ffb21SWarner Losh  * and convert them into a real mode using CVT/GTF/DMT.
738*592ffb21SWarner Losh  */
739*592ffb21SWarner Losh static struct drm_display_mode *
drm_mode_std(struct drm_connector * connector,struct edid * edid,struct std_timing * t,int revision)740*592ffb21SWarner Losh drm_mode_std(struct drm_connector *connector, struct edid *edid,
741*592ffb21SWarner Losh 	     struct std_timing *t, int revision)
742*592ffb21SWarner Losh {
743*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
744*592ffb21SWarner Losh 	struct drm_display_mode *m, *mode = NULL;
745*592ffb21SWarner Losh 	int hsize, vsize;
746*592ffb21SWarner Losh 	int vrefresh_rate;
747*592ffb21SWarner Losh 	unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
748*592ffb21SWarner Losh 		>> EDID_TIMING_ASPECT_SHIFT;
749*592ffb21SWarner Losh 	unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
750*592ffb21SWarner Losh 		>> EDID_TIMING_VFREQ_SHIFT;
751*592ffb21SWarner Losh 	int timing_level = standard_timing_level(edid);
752*592ffb21SWarner Losh 
753*592ffb21SWarner Losh 	if (bad_std_timing(t->hsize, t->vfreq_aspect))
754*592ffb21SWarner Losh 		return NULL;
755*592ffb21SWarner Losh 
756*592ffb21SWarner Losh 	/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
757*592ffb21SWarner Losh 	hsize = t->hsize * 8 + 248;
758*592ffb21SWarner Losh 	/* vrefresh_rate = vfreq + 60 */
759*592ffb21SWarner Losh 	vrefresh_rate = vfreq + 60;
760*592ffb21SWarner Losh 	/* the vdisplay is calculated based on the aspect ratio */
761*592ffb21SWarner Losh 	if (aspect_ratio == 0) {
762*592ffb21SWarner Losh 		if (revision < 3)
763*592ffb21SWarner Losh 			vsize = hsize;
764*592ffb21SWarner Losh 		else
765*592ffb21SWarner Losh 			vsize = (hsize * 10) / 16;
766*592ffb21SWarner Losh 	} else if (aspect_ratio == 1)
767*592ffb21SWarner Losh 		vsize = (hsize * 3) / 4;
768*592ffb21SWarner Losh 	else if (aspect_ratio == 2)
769*592ffb21SWarner Losh 		vsize = (hsize * 4) / 5;
770*592ffb21SWarner Losh 	else
771*592ffb21SWarner Losh 		vsize = (hsize * 9) / 16;
772*592ffb21SWarner Losh 
773*592ffb21SWarner Losh 	/* HDTV hack, part 1 */
774*592ffb21SWarner Losh 	if (vrefresh_rate == 60 &&
775*592ffb21SWarner Losh 	    ((hsize == 1360 && vsize == 765) ||
776*592ffb21SWarner Losh 	     (hsize == 1368 && vsize == 769))) {
777*592ffb21SWarner Losh 		hsize = 1366;
778*592ffb21SWarner Losh 		vsize = 768;
779*592ffb21SWarner Losh 	}
780*592ffb21SWarner Losh 
781*592ffb21SWarner Losh 	/*
782*592ffb21SWarner Losh 	 * If this connector already has a mode for this size and refresh
783*592ffb21SWarner Losh 	 * rate (because it came from detailed or CVT info), use that
784*592ffb21SWarner Losh 	 * instead.  This way we don't have to guess at interlace or
785*592ffb21SWarner Losh 	 * reduced blanking.
786*592ffb21SWarner Losh 	 */
787*592ffb21SWarner Losh 	list_for_each_entry(m, &connector->probed_modes, head)
788*592ffb21SWarner Losh 		if (m->hdisplay == hsize && m->vdisplay == vsize &&
789*592ffb21SWarner Losh 		    drm_mode_vrefresh(m) == vrefresh_rate)
790*592ffb21SWarner Losh 			return NULL;
791*592ffb21SWarner Losh 
792*592ffb21SWarner Losh 	/* HDTV hack, part 2 */
793*592ffb21SWarner Losh 	if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) {
794*592ffb21SWarner Losh 		mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0,
795*592ffb21SWarner Losh 				    false);
796*592ffb21SWarner Losh 		mode->hdisplay = 1366;
797*592ffb21SWarner Losh 		mode->hsync_start = mode->hsync_start - 1;
798*592ffb21SWarner Losh 		mode->hsync_end = mode->hsync_end - 1;
799*592ffb21SWarner Losh 		return mode;
800*592ffb21SWarner Losh 	}
801*592ffb21SWarner Losh 
802*592ffb21SWarner Losh 	/* check whether it can be found in default mode table */
803*592ffb21SWarner Losh 	if (drm_monitor_supports_rb(edid)) {
804*592ffb21SWarner Losh 		mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate,
805*592ffb21SWarner Losh 					 true);
806*592ffb21SWarner Losh 		if (mode)
807*592ffb21SWarner Losh 			return mode;
808*592ffb21SWarner Losh 	}
809*592ffb21SWarner Losh 	mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate, false);
810*592ffb21SWarner Losh 	if (mode)
811*592ffb21SWarner Losh 		return mode;
812*592ffb21SWarner Losh 
813*592ffb21SWarner Losh 	/* okay, generate it */
814*592ffb21SWarner Losh 	switch (timing_level) {
815*592ffb21SWarner Losh 	case LEVEL_DMT:
816*592ffb21SWarner Losh 		break;
817*592ffb21SWarner Losh 	case LEVEL_GTF:
818*592ffb21SWarner Losh 		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
819*592ffb21SWarner Losh 		break;
820*592ffb21SWarner Losh 	case LEVEL_GTF2:
821*592ffb21SWarner Losh 		/*
822*592ffb21SWarner Losh 		 * This is potentially wrong if there's ever a monitor with
823*592ffb21SWarner Losh 		 * more than one ranges section, each claiming a different
824*592ffb21SWarner Losh 		 * secondary GTF curve.  Please don't do that.
825*592ffb21SWarner Losh 		 */
826*592ffb21SWarner Losh 		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
827*592ffb21SWarner Losh 		if (!mode)
828*592ffb21SWarner Losh 			return NULL;
829*592ffb21SWarner Losh 		if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
830*592ffb21SWarner Losh 			drm_mode_destroy(dev, mode);
831*592ffb21SWarner Losh 			mode = drm_gtf_mode_complex(dev, hsize, vsize,
832*592ffb21SWarner Losh 						    vrefresh_rate, 0, 0,
833*592ffb21SWarner Losh 						    drm_gtf2_m(edid),
834*592ffb21SWarner Losh 						    drm_gtf2_2c(edid),
835*592ffb21SWarner Losh 						    drm_gtf2_k(edid),
836*592ffb21SWarner Losh 						    drm_gtf2_2j(edid));
837*592ffb21SWarner Losh 		}
838*592ffb21SWarner Losh 		break;
839*592ffb21SWarner Losh 	case LEVEL_CVT:
840*592ffb21SWarner Losh 		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
841*592ffb21SWarner Losh 				    false);
842*592ffb21SWarner Losh 		break;
843*592ffb21SWarner Losh 	}
844*592ffb21SWarner Losh 	return mode;
845*592ffb21SWarner Losh }
846*592ffb21SWarner Losh 
847*592ffb21SWarner Losh /*
848*592ffb21SWarner Losh  * EDID is delightfully ambiguous about how interlaced modes are to be
849*592ffb21SWarner Losh  * encoded.  Our internal representation is of frame height, but some
850*592ffb21SWarner Losh  * HDTV detailed timings are encoded as field height.
851*592ffb21SWarner Losh  *
852*592ffb21SWarner Losh  * The format list here is from CEA, in frame size.  Technically we
853*592ffb21SWarner Losh  * should be checking refresh rate too.  Whatever.
854*592ffb21SWarner Losh  */
855*592ffb21SWarner Losh static void
drm_mode_do_interlace_quirk(struct drm_display_mode * mode,struct detailed_pixel_timing * pt)856*592ffb21SWarner Losh drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
857*592ffb21SWarner Losh 			    struct detailed_pixel_timing *pt)
858*592ffb21SWarner Losh {
859*592ffb21SWarner Losh 	int i;
860*592ffb21SWarner Losh 	static const struct {
861*592ffb21SWarner Losh 		int w, h;
862*592ffb21SWarner Losh 	} cea_interlaced[] = {
863*592ffb21SWarner Losh 		{ 1920, 1080 },
864*592ffb21SWarner Losh 		{  720,  480 },
865*592ffb21SWarner Losh 		{ 1440,  480 },
866*592ffb21SWarner Losh 		{ 2880,  480 },
867*592ffb21SWarner Losh 		{  720,  576 },
868*592ffb21SWarner Losh 		{ 1440,  576 },
869*592ffb21SWarner Losh 		{ 2880,  576 },
870*592ffb21SWarner Losh 	};
871*592ffb21SWarner Losh 
872*592ffb21SWarner Losh 	if (!(pt->misc & DRM_EDID_PT_INTERLACED))
873*592ffb21SWarner Losh 		return;
874*592ffb21SWarner Losh 
875*592ffb21SWarner Losh 	for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
876*592ffb21SWarner Losh 		if ((mode->hdisplay == cea_interlaced[i].w) &&
877*592ffb21SWarner Losh 		    (mode->vdisplay == cea_interlaced[i].h / 2)) {
878*592ffb21SWarner Losh 			mode->vdisplay *= 2;
879*592ffb21SWarner Losh 			mode->vsync_start *= 2;
880*592ffb21SWarner Losh 			mode->vsync_end *= 2;
881*592ffb21SWarner Losh 			mode->vtotal *= 2;
882*592ffb21SWarner Losh 			mode->vtotal |= 1;
883*592ffb21SWarner Losh 		}
884*592ffb21SWarner Losh 	}
885*592ffb21SWarner Losh 
886*592ffb21SWarner Losh 	mode->flags |= DRM_MODE_FLAG_INTERLACE;
887*592ffb21SWarner Losh }
888*592ffb21SWarner Losh 
889*592ffb21SWarner Losh /**
890*592ffb21SWarner Losh  * drm_mode_detailed - create a new mode from an EDID detailed timing section
891*592ffb21SWarner Losh  * @dev: DRM device (needed to create new mode)
892*592ffb21SWarner Losh  * @edid: EDID block
893*592ffb21SWarner Losh  * @timing: EDID detailed timing info
894*592ffb21SWarner Losh  * @quirks: quirks to apply
895*592ffb21SWarner Losh  *
896*592ffb21SWarner Losh  * An EDID detailed timing block contains enough info for us to create and
897*592ffb21SWarner Losh  * return a new struct drm_display_mode.
898*592ffb21SWarner Losh  */
drm_mode_detailed(struct drm_device * dev,struct edid * edid,struct detailed_timing * timing,u32 quirks)899*592ffb21SWarner Losh static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
900*592ffb21SWarner Losh 						  struct edid *edid,
901*592ffb21SWarner Losh 						  struct detailed_timing *timing,
902*592ffb21SWarner Losh 						  u32 quirks)
903*592ffb21SWarner Losh {
904*592ffb21SWarner Losh 	struct drm_display_mode *mode;
905*592ffb21SWarner Losh 	struct detailed_pixel_timing *pt = &timing->data.pixel_data;
906*592ffb21SWarner Losh 	unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo;
907*592ffb21SWarner Losh 	unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo;
908*592ffb21SWarner Losh 	unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo;
909*592ffb21SWarner Losh 	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;
910*592ffb21SWarner Losh 	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;
911*592ffb21SWarner Losh 	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo;
912*592ffb21SWarner Losh 	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4;
913*592ffb21SWarner Losh 	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);
914*592ffb21SWarner Losh 
915*592ffb21SWarner Losh 	/* ignore tiny modes */
916*592ffb21SWarner Losh 	if (hactive < 64 || vactive < 64)
917*592ffb21SWarner Losh 		return NULL;
918*592ffb21SWarner Losh 
919*592ffb21SWarner Losh 	if (pt->misc & DRM_EDID_PT_STEREO) {
920*592ffb21SWarner Losh 		printf("stereo mode not supported\n");
921*592ffb21SWarner Losh 		return NULL;
922*592ffb21SWarner Losh 	}
923*592ffb21SWarner Losh 	if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
924*592ffb21SWarner Losh 		printf("composite sync not supported\n");
925*592ffb21SWarner Losh 	}
926*592ffb21SWarner Losh 
927*592ffb21SWarner Losh 	/* it is incorrect if hsync/vsync width is zero */
928*592ffb21SWarner Losh 	if (!hsync_pulse_width || !vsync_pulse_width) {
929*592ffb21SWarner Losh 		DRM_DEBUG_KMS("Incorrect Detailed timing. "
930*592ffb21SWarner Losh 				"Wrong Hsync/Vsync pulse width\n");
931*592ffb21SWarner Losh 		return NULL;
932*592ffb21SWarner Losh 	}
933*592ffb21SWarner Losh 
934*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_FORCE_REDUCED_BLANKING) {
935*592ffb21SWarner Losh 		mode = drm_cvt_mode(dev, hactive, vactive, 60, true, false, false);
936*592ffb21SWarner Losh 		if (!mode)
937*592ffb21SWarner Losh 			return NULL;
938*592ffb21SWarner Losh 
939*592ffb21SWarner Losh 		goto set_size;
940*592ffb21SWarner Losh 	}
941*592ffb21SWarner Losh 
942*592ffb21SWarner Losh 	mode = drm_mode_create(dev);
943*592ffb21SWarner Losh 	if (!mode)
944*592ffb21SWarner Losh 		return NULL;
945*592ffb21SWarner Losh 
946*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
947*592ffb21SWarner Losh 		timing->pixel_clock = cpu_to_le16(1088);
948*592ffb21SWarner Losh 
949*592ffb21SWarner Losh 	mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
950*592ffb21SWarner Losh 
951*592ffb21SWarner Losh 	mode->hdisplay = hactive;
952*592ffb21SWarner Losh 	mode->hsync_start = mode->hdisplay + hsync_offset;
953*592ffb21SWarner Losh 	mode->hsync_end = mode->hsync_start + hsync_pulse_width;
954*592ffb21SWarner Losh 	mode->htotal = mode->hdisplay + hblank;
955*592ffb21SWarner Losh 
956*592ffb21SWarner Losh 	mode->vdisplay = vactive;
957*592ffb21SWarner Losh 	mode->vsync_start = mode->vdisplay + vsync_offset;
958*592ffb21SWarner Losh 	mode->vsync_end = mode->vsync_start + vsync_pulse_width;
959*592ffb21SWarner Losh 	mode->vtotal = mode->vdisplay + vblank;
960*592ffb21SWarner Losh 
961*592ffb21SWarner Losh 	/* Some EDIDs have bogus h/vtotal values */
962*592ffb21SWarner Losh 	if (mode->hsync_end > mode->htotal)
963*592ffb21SWarner Losh 		mode->htotal = mode->hsync_end + 1;
964*592ffb21SWarner Losh 	if (mode->vsync_end > mode->vtotal)
965*592ffb21SWarner Losh 		mode->vtotal = mode->vsync_end + 1;
966*592ffb21SWarner Losh 
967*592ffb21SWarner Losh 	drm_mode_do_interlace_quirk(mode, pt);
968*592ffb21SWarner Losh 
969*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
970*592ffb21SWarner Losh 		pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
971*592ffb21SWarner Losh 	}
972*592ffb21SWarner Losh 
973*592ffb21SWarner Losh 	mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
974*592ffb21SWarner Losh 		DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
975*592ffb21SWarner Losh 	mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
976*592ffb21SWarner Losh 		DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
977*592ffb21SWarner Losh 
978*592ffb21SWarner Losh set_size:
979*592ffb21SWarner Losh 	mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
980*592ffb21SWarner Losh 	mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
981*592ffb21SWarner Losh 
982*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
983*592ffb21SWarner Losh 		mode->width_mm *= 10;
984*592ffb21SWarner Losh 		mode->height_mm *= 10;
985*592ffb21SWarner Losh 	}
986*592ffb21SWarner Losh 
987*592ffb21SWarner Losh 	if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
988*592ffb21SWarner Losh 		mode->width_mm = edid->width_cm * 10;
989*592ffb21SWarner Losh 		mode->height_mm = edid->height_cm * 10;
990*592ffb21SWarner Losh 	}
991*592ffb21SWarner Losh 
992*592ffb21SWarner Losh 	mode->type = DRM_MODE_TYPE_DRIVER;
993*592ffb21SWarner Losh 	mode->vrefresh = drm_mode_vrefresh(mode);
994*592ffb21SWarner Losh 	drm_mode_set_name(mode);
995*592ffb21SWarner Losh 
996*592ffb21SWarner Losh 	return mode;
997*592ffb21SWarner Losh }
998*592ffb21SWarner Losh 
999*592ffb21SWarner Losh static bool
mode_in_hsync_range(const struct drm_display_mode * mode,struct edid * edid,u8 * t)1000*592ffb21SWarner Losh mode_in_hsync_range(const struct drm_display_mode *mode,
1001*592ffb21SWarner Losh 		    struct edid *edid, u8 *t)
1002*592ffb21SWarner Losh {
1003*592ffb21SWarner Losh 	int hsync, hmin, hmax;
1004*592ffb21SWarner Losh 
1005*592ffb21SWarner Losh 	hmin = t[7];
1006*592ffb21SWarner Losh 	if (edid->revision >= 4)
1007*592ffb21SWarner Losh 	    hmin += ((t[4] & 0x04) ? 255 : 0);
1008*592ffb21SWarner Losh 	hmax = t[8];
1009*592ffb21SWarner Losh 	if (edid->revision >= 4)
1010*592ffb21SWarner Losh 	    hmax += ((t[4] & 0x08) ? 255 : 0);
1011*592ffb21SWarner Losh 	hsync = drm_mode_hsync(mode);
1012*592ffb21SWarner Losh 
1013*592ffb21SWarner Losh 	return (hsync <= hmax && hsync >= hmin);
1014*592ffb21SWarner Losh }
1015*592ffb21SWarner Losh 
1016*592ffb21SWarner Losh static bool
mode_in_vsync_range(const struct drm_display_mode * mode,struct edid * edid,u8 * t)1017*592ffb21SWarner Losh mode_in_vsync_range(const struct drm_display_mode *mode,
1018*592ffb21SWarner Losh 		    struct edid *edid, u8 *t)
1019*592ffb21SWarner Losh {
1020*592ffb21SWarner Losh 	int vsync, vmin, vmax;
1021*592ffb21SWarner Losh 
1022*592ffb21SWarner Losh 	vmin = t[5];
1023*592ffb21SWarner Losh 	if (edid->revision >= 4)
1024*592ffb21SWarner Losh 	    vmin += ((t[4] & 0x01) ? 255 : 0);
1025*592ffb21SWarner Losh 	vmax = t[6];
1026*592ffb21SWarner Losh 	if (edid->revision >= 4)
1027*592ffb21SWarner Losh 	    vmax += ((t[4] & 0x02) ? 255 : 0);
1028*592ffb21SWarner Losh 	vsync = drm_mode_vrefresh(mode);
1029*592ffb21SWarner Losh 
1030*592ffb21SWarner Losh 	return (vsync <= vmax && vsync >= vmin);
1031*592ffb21SWarner Losh }
1032*592ffb21SWarner Losh 
1033*592ffb21SWarner Losh static u32
range_pixel_clock(struct edid * edid,u8 * t)1034*592ffb21SWarner Losh range_pixel_clock(struct edid *edid, u8 *t)
1035*592ffb21SWarner Losh {
1036*592ffb21SWarner Losh 	/* unspecified */
1037*592ffb21SWarner Losh 	if (t[9] == 0 || t[9] == 255)
1038*592ffb21SWarner Losh 		return 0;
1039*592ffb21SWarner Losh 
1040*592ffb21SWarner Losh 	/* 1.4 with CVT support gives us real precision, yay */
1041*592ffb21SWarner Losh 	if (edid->revision >= 4 && t[10] == 0x04)
1042*592ffb21SWarner Losh 		return (t[9] * 10000) - ((t[12] >> 2) * 250);
1043*592ffb21SWarner Losh 
1044*592ffb21SWarner Losh 	/* 1.3 is pathetic, so fuzz up a bit */
1045*592ffb21SWarner Losh 	return t[9] * 10000 + 5001;
1046*592ffb21SWarner Losh }
1047*592ffb21SWarner Losh 
1048*592ffb21SWarner Losh static bool
mode_in_range(const struct drm_display_mode * mode,struct edid * edid,struct detailed_timing * timing)1049*592ffb21SWarner Losh mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
1050*592ffb21SWarner Losh 	      struct detailed_timing *timing)
1051*592ffb21SWarner Losh {
1052*592ffb21SWarner Losh 	u32 max_clock;
1053*592ffb21SWarner Losh 	u8 *t = (u8 *)timing;
1054*592ffb21SWarner Losh 
1055*592ffb21SWarner Losh 	if (!mode_in_hsync_range(mode, edid, t))
1056*592ffb21SWarner Losh 		return false;
1057*592ffb21SWarner Losh 
1058*592ffb21SWarner Losh 	if (!mode_in_vsync_range(mode, edid, t))
1059*592ffb21SWarner Losh 		return false;
1060*592ffb21SWarner Losh 
1061*592ffb21SWarner Losh 	if ((max_clock = range_pixel_clock(edid, t)))
1062*592ffb21SWarner Losh 		if (mode->clock > max_clock)
1063*592ffb21SWarner Losh 			return false;
1064*592ffb21SWarner Losh 
1065*592ffb21SWarner Losh 	/* 1.4 max horizontal check */
1066*592ffb21SWarner Losh 	if (edid->revision >= 4 && t[10] == 0x04)
1067*592ffb21SWarner Losh 		if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
1068*592ffb21SWarner Losh 			return false;
1069*592ffb21SWarner Losh 
1070*592ffb21SWarner Losh 	if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid))
1071*592ffb21SWarner Losh 		return false;
1072*592ffb21SWarner Losh 
1073*592ffb21SWarner Losh 	return true;
1074*592ffb21SWarner Losh }
1075*592ffb21SWarner Losh 
valid_inferred_mode(const struct drm_connector * connector,const struct drm_display_mode * mode)1076*592ffb21SWarner Losh static bool valid_inferred_mode(const struct drm_connector *connector,
1077*592ffb21SWarner Losh 				const struct drm_display_mode *mode)
1078*592ffb21SWarner Losh {
1079*592ffb21SWarner Losh 	struct drm_display_mode *m;
1080*592ffb21SWarner Losh 	bool ok = false;
1081*592ffb21SWarner Losh 
1082*592ffb21SWarner Losh 	list_for_each_entry(m, &connector->probed_modes, head) {
1083*592ffb21SWarner Losh 		if (mode->hdisplay == m->hdisplay &&
1084*592ffb21SWarner Losh 		    mode->vdisplay == m->vdisplay &&
1085*592ffb21SWarner Losh 		    drm_mode_vrefresh(mode) == drm_mode_vrefresh(m))
1086*592ffb21SWarner Losh 			return false; /* duplicated */
1087*592ffb21SWarner Losh 		if (mode->hdisplay <= m->hdisplay &&
1088*592ffb21SWarner Losh 		    mode->vdisplay <= m->vdisplay)
1089*592ffb21SWarner Losh 			ok = true;
1090*592ffb21SWarner Losh 	}
1091*592ffb21SWarner Losh 	return ok;
1092*592ffb21SWarner Losh }
1093*592ffb21SWarner Losh 
1094*592ffb21SWarner Losh static int
drm_dmt_modes_for_range(struct drm_connector * connector,struct edid * edid,struct detailed_timing * timing)1095*592ffb21SWarner Losh drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid,
1096*592ffb21SWarner Losh 			struct detailed_timing *timing)
1097*592ffb21SWarner Losh {
1098*592ffb21SWarner Losh 	int i, modes = 0;
1099*592ffb21SWarner Losh 	struct drm_display_mode *newmode;
1100*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
1101*592ffb21SWarner Losh 
1102*592ffb21SWarner Losh 	for (i = 0; i < drm_num_dmt_modes; i++) {
1103*592ffb21SWarner Losh 		if (mode_in_range(drm_dmt_modes + i, edid, timing) &&
1104*592ffb21SWarner Losh 		    valid_inferred_mode(connector, drm_dmt_modes + i)) {
1105*592ffb21SWarner Losh 			newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
1106*592ffb21SWarner Losh 			if (newmode) {
1107*592ffb21SWarner Losh 				drm_mode_probed_add(connector, newmode);
1108*592ffb21SWarner Losh 				modes++;
1109*592ffb21SWarner Losh 			}
1110*592ffb21SWarner Losh 		}
1111*592ffb21SWarner Losh 	}
1112*592ffb21SWarner Losh 
1113*592ffb21SWarner Losh 	return modes;
1114*592ffb21SWarner Losh }
1115*592ffb21SWarner Losh 
1116*592ffb21SWarner Losh /* fix up 1366x768 mode from 1368x768;
1117*592ffb21SWarner Losh  * GFT/CVT can't express 1366 width which isn't dividable by 8
1118*592ffb21SWarner Losh  */
fixup_mode_1366x768(struct drm_display_mode * mode)1119*592ffb21SWarner Losh static void fixup_mode_1366x768(struct drm_display_mode *mode)
1120*592ffb21SWarner Losh {
1121*592ffb21SWarner Losh 	if (mode->hdisplay == 1368 && mode->vdisplay == 768) {
1122*592ffb21SWarner Losh 		mode->hdisplay = 1366;
1123*592ffb21SWarner Losh 		mode->hsync_start--;
1124*592ffb21SWarner Losh 		mode->hsync_end--;
1125*592ffb21SWarner Losh 		drm_mode_set_name(mode);
1126*592ffb21SWarner Losh 	}
1127*592ffb21SWarner Losh }
1128*592ffb21SWarner Losh 
1129*592ffb21SWarner Losh static int
drm_gtf_modes_for_range(struct drm_connector * connector,struct edid * edid,struct detailed_timing * timing)1130*592ffb21SWarner Losh drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
1131*592ffb21SWarner Losh 			struct detailed_timing *timing)
1132*592ffb21SWarner Losh {
1133*592ffb21SWarner Losh 	int i, modes = 0;
1134*592ffb21SWarner Losh 	struct drm_display_mode *newmode;
1135*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
1136*592ffb21SWarner Losh 
1137*592ffb21SWarner Losh 	for (i = 0; i < num_extra_modes; i++) {
1138*592ffb21SWarner Losh 		const struct minimode *m = &extra_modes[i];
1139*592ffb21SWarner Losh 		newmode = drm_gtf_mode(dev, m->w, m->h, m->r, 0, 0);
1140*592ffb21SWarner Losh 		if (!newmode)
1141*592ffb21SWarner Losh 			return modes;
1142*592ffb21SWarner Losh 
1143*592ffb21SWarner Losh 		fixup_mode_1366x768(newmode);
1144*592ffb21SWarner Losh 		if (!mode_in_range(newmode, edid, timing) ||
1145*592ffb21SWarner Losh 		    !valid_inferred_mode(connector, newmode)) {
1146*592ffb21SWarner Losh 			drm_mode_destroy(dev, newmode);
1147*592ffb21SWarner Losh 			continue;
1148*592ffb21SWarner Losh 		}
1149*592ffb21SWarner Losh 
1150*592ffb21SWarner Losh 		drm_mode_probed_add(connector, newmode);
1151*592ffb21SWarner Losh 		modes++;
1152*592ffb21SWarner Losh 	}
1153*592ffb21SWarner Losh 
1154*592ffb21SWarner Losh 	return modes;
1155*592ffb21SWarner Losh }
1156*592ffb21SWarner Losh 
1157*592ffb21SWarner Losh static int
drm_cvt_modes_for_range(struct drm_connector * connector,struct edid * edid,struct detailed_timing * timing)1158*592ffb21SWarner Losh drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
1159*592ffb21SWarner Losh 			struct detailed_timing *timing)
1160*592ffb21SWarner Losh {
1161*592ffb21SWarner Losh 	int i, modes = 0;
1162*592ffb21SWarner Losh 	struct drm_display_mode *newmode;
1163*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
1164*592ffb21SWarner Losh 	bool rb = drm_monitor_supports_rb(edid);
1165*592ffb21SWarner Losh 
1166*592ffb21SWarner Losh 	for (i = 0; i < num_extra_modes; i++) {
1167*592ffb21SWarner Losh 		const struct minimode *m = &extra_modes[i];
1168*592ffb21SWarner Losh 		newmode = drm_cvt_mode(dev, m->w, m->h, m->r, rb, 0, 0);
1169*592ffb21SWarner Losh 		if (!newmode)
1170*592ffb21SWarner Losh 			return modes;
1171*592ffb21SWarner Losh 
1172*592ffb21SWarner Losh 		fixup_mode_1366x768(newmode);
1173*592ffb21SWarner Losh 		if (!mode_in_range(newmode, edid, timing) ||
1174*592ffb21SWarner Losh 		    !valid_inferred_mode(connector, newmode)) {
1175*592ffb21SWarner Losh 			drm_mode_destroy(dev, newmode);
1176*592ffb21SWarner Losh 			continue;
1177*592ffb21SWarner Losh 		}
1178*592ffb21SWarner Losh 
1179*592ffb21SWarner Losh 		drm_mode_probed_add(connector, newmode);
1180*592ffb21SWarner Losh 		modes++;
1181*592ffb21SWarner Losh 	}
1182*592ffb21SWarner Losh 
1183*592ffb21SWarner Losh 	return modes;
1184*592ffb21SWarner Losh }
1185*592ffb21SWarner Losh 
1186*592ffb21SWarner Losh static void
do_inferred_modes(struct detailed_timing * timing,void * c)1187*592ffb21SWarner Losh do_inferred_modes(struct detailed_timing *timing, void *c)
1188*592ffb21SWarner Losh {
1189*592ffb21SWarner Losh 	struct detailed_mode_closure *closure = c;
1190*592ffb21SWarner Losh 	struct detailed_non_pixel *data = &timing->data.other_data;
1191*592ffb21SWarner Losh 	struct detailed_data_monitor_range *range = &data->data.range;
1192*592ffb21SWarner Losh 
1193*592ffb21SWarner Losh 	if (data->type != EDID_DETAIL_MONITOR_RANGE)
1194*592ffb21SWarner Losh 		return;
1195*592ffb21SWarner Losh 
1196*592ffb21SWarner Losh 	closure->modes += drm_dmt_modes_for_range(closure->connector,
1197*592ffb21SWarner Losh 						  closure->edid,
1198*592ffb21SWarner Losh 						  timing);
1199*592ffb21SWarner Losh 
1200*592ffb21SWarner Losh 	if (!version_greater(closure->edid, 1, 1))
1201*592ffb21SWarner Losh 		return; /* GTF not defined yet */
1202*592ffb21SWarner Losh 
1203*592ffb21SWarner Losh 	switch (range->flags) {
1204*592ffb21SWarner Losh 	case 0x02: /* secondary gtf, XXX could do more */
1205*592ffb21SWarner Losh 	case 0x00: /* default gtf */
1206*592ffb21SWarner Losh 		closure->modes += drm_gtf_modes_for_range(closure->connector,
1207*592ffb21SWarner Losh 							  closure->edid,
1208*592ffb21SWarner Losh 							  timing);
1209*592ffb21SWarner Losh 		break;
1210*592ffb21SWarner Losh 	case 0x04: /* cvt, only in 1.4+ */
1211*592ffb21SWarner Losh 		if (!version_greater(closure->edid, 1, 3))
1212*592ffb21SWarner Losh 			break;
1213*592ffb21SWarner Losh 
1214*592ffb21SWarner Losh 		closure->modes += drm_cvt_modes_for_range(closure->connector,
1215*592ffb21SWarner Losh 							  closure->edid,
1216*592ffb21SWarner Losh 							  timing);
1217*592ffb21SWarner Losh 		break;
1218*592ffb21SWarner Losh 	case 0x01: /* just the ranges, no formula */
1219*592ffb21SWarner Losh 	default:
1220*592ffb21SWarner Losh 		break;
1221*592ffb21SWarner Losh 	}
1222*592ffb21SWarner Losh }
1223*592ffb21SWarner Losh 
1224*592ffb21SWarner Losh static int
add_inferred_modes(struct drm_connector * connector,struct edid * edid)1225*592ffb21SWarner Losh add_inferred_modes(struct drm_connector *connector, struct edid *edid)
1226*592ffb21SWarner Losh {
1227*592ffb21SWarner Losh 	struct detailed_mode_closure closure = {
1228*592ffb21SWarner Losh 		connector, edid, 0, 0, 0
1229*592ffb21SWarner Losh 	};
1230*592ffb21SWarner Losh 
1231*592ffb21SWarner Losh 	if (version_greater(edid, 1, 0))
1232*592ffb21SWarner Losh 		drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
1233*592ffb21SWarner Losh 					    &closure);
1234*592ffb21SWarner Losh 
1235*592ffb21SWarner Losh 	return closure.modes;
1236*592ffb21SWarner Losh }
1237*592ffb21SWarner Losh 
1238*592ffb21SWarner Losh static int
drm_est3_modes(struct drm_connector * connector,struct detailed_timing * timing)1239*592ffb21SWarner Losh drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
1240*592ffb21SWarner Losh {
1241*592ffb21SWarner Losh 	int i, j, m, modes = 0;
1242*592ffb21SWarner Losh 	struct drm_display_mode *mode;
1243*592ffb21SWarner Losh 	u8 *est = ((u8 *)timing) + 5;
1244*592ffb21SWarner Losh 
1245*592ffb21SWarner Losh 	for (i = 0; i < 6; i++) {
1246*592ffb21SWarner Losh 		for (j = 7; j > 0; j--) {
1247*592ffb21SWarner Losh 			m = (i * 8) + (7 - j);
1248*592ffb21SWarner Losh 			if (m >= ARRAY_SIZE(est3_modes))
1249*592ffb21SWarner Losh 				break;
1250*592ffb21SWarner Losh 			if (est[i] & (1 << j)) {
1251*592ffb21SWarner Losh 				mode = drm_mode_find_dmt(connector->dev,
1252*592ffb21SWarner Losh 							 est3_modes[m].w,
1253*592ffb21SWarner Losh 							 est3_modes[m].h,
1254*592ffb21SWarner Losh 							 est3_modes[m].r,
1255*592ffb21SWarner Losh 							 est3_modes[m].rb);
1256*592ffb21SWarner Losh 				if (mode) {
1257*592ffb21SWarner Losh 					drm_mode_probed_add(connector, mode);
1258*592ffb21SWarner Losh 					modes++;
1259*592ffb21SWarner Losh 				}
1260*592ffb21SWarner Losh 			}
1261*592ffb21SWarner Losh 		}
1262*592ffb21SWarner Losh 	}
1263*592ffb21SWarner Losh 
1264*592ffb21SWarner Losh 	return modes;
1265*592ffb21SWarner Losh }
1266*592ffb21SWarner Losh 
1267*592ffb21SWarner Losh static void
do_established_modes(struct detailed_timing * timing,void * c)1268*592ffb21SWarner Losh do_established_modes(struct detailed_timing *timing, void *c)
1269*592ffb21SWarner Losh {
1270*592ffb21SWarner Losh 	struct detailed_mode_closure *closure = c;
1271*592ffb21SWarner Losh 	struct detailed_non_pixel *data = &timing->data.other_data;
1272*592ffb21SWarner Losh 
1273*592ffb21SWarner Losh 	if (data->type == EDID_DETAIL_EST_TIMINGS)
1274*592ffb21SWarner Losh 		closure->modes += drm_est3_modes(closure->connector, timing);
1275*592ffb21SWarner Losh }
1276*592ffb21SWarner Losh 
1277*592ffb21SWarner Losh /**
1278*592ffb21SWarner Losh  * add_established_modes - get est. modes from EDID and add them
1279*592ffb21SWarner Losh  * @edid: EDID block to scan
1280*592ffb21SWarner Losh  *
1281*592ffb21SWarner Losh  * Each EDID block contains a bitmap of the supported "established modes" list
1282*592ffb21SWarner Losh  * (defined above).  Tease them out and add them to the global modes list.
1283*592ffb21SWarner Losh  */
1284*592ffb21SWarner Losh static int
add_established_modes(struct drm_connector * connector,struct edid * edid)1285*592ffb21SWarner Losh add_established_modes(struct drm_connector *connector, struct edid *edid)
1286*592ffb21SWarner Losh {
1287*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
1288*592ffb21SWarner Losh 	unsigned long est_bits = edid->established_timings.t1 |
1289*592ffb21SWarner Losh 		(edid->established_timings.t2 << 8) |
1290*592ffb21SWarner Losh 		((edid->established_timings.mfg_rsvd & 0x80) << 9);
1291*592ffb21SWarner Losh 	int i, modes = 0;
1292*592ffb21SWarner Losh 	struct detailed_mode_closure closure = {
1293*592ffb21SWarner Losh 		connector, edid, 0, 0, 0
1294*592ffb21SWarner Losh 	};
1295*592ffb21SWarner Losh 
1296*592ffb21SWarner Losh 	for (i = 0; i <= EDID_EST_TIMINGS; i++) {
1297*592ffb21SWarner Losh 		if (est_bits & (1<<i)) {
1298*592ffb21SWarner Losh 			struct drm_display_mode *newmode;
1299*592ffb21SWarner Losh 			newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
1300*592ffb21SWarner Losh 			if (newmode) {
1301*592ffb21SWarner Losh 				drm_mode_probed_add(connector, newmode);
1302*592ffb21SWarner Losh 				modes++;
1303*592ffb21SWarner Losh 			}
1304*592ffb21SWarner Losh 		}
1305*592ffb21SWarner Losh 	}
1306*592ffb21SWarner Losh 
1307*592ffb21SWarner Losh 	if (version_greater(edid, 1, 0))
1308*592ffb21SWarner Losh 		    drm_for_each_detailed_block((u8 *)edid,
1309*592ffb21SWarner Losh 						do_established_modes, &closure);
1310*592ffb21SWarner Losh 
1311*592ffb21SWarner Losh 	return modes + closure.modes;
1312*592ffb21SWarner Losh }
1313*592ffb21SWarner Losh 
1314*592ffb21SWarner Losh static void
do_standard_modes(struct detailed_timing * timing,void * c)1315*592ffb21SWarner Losh do_standard_modes(struct detailed_timing *timing, void *c)
1316*592ffb21SWarner Losh {
1317*592ffb21SWarner Losh 	struct detailed_mode_closure *closure = c;
1318*592ffb21SWarner Losh 	struct detailed_non_pixel *data = &timing->data.other_data;
1319*592ffb21SWarner Losh 	struct drm_connector *connector = closure->connector;
1320*592ffb21SWarner Losh 	struct edid *edid = closure->edid;
1321*592ffb21SWarner Losh 
1322*592ffb21SWarner Losh 	if (data->type == EDID_DETAIL_STD_MODES) {
1323*592ffb21SWarner Losh 		int i;
1324*592ffb21SWarner Losh 		for (i = 0; i < 6; i++) {
1325*592ffb21SWarner Losh 			struct std_timing *std;
1326*592ffb21SWarner Losh 			struct drm_display_mode *newmode;
1327*592ffb21SWarner Losh 
1328*592ffb21SWarner Losh 			std = &data->data.timings[i];
1329*592ffb21SWarner Losh 			newmode = drm_mode_std(connector, edid, std,
1330*592ffb21SWarner Losh 					       edid->revision);
1331*592ffb21SWarner Losh 			if (newmode) {
1332*592ffb21SWarner Losh 				drm_mode_probed_add(connector, newmode);
1333*592ffb21SWarner Losh 				closure->modes++;
1334*592ffb21SWarner Losh 			}
1335*592ffb21SWarner Losh 		}
1336*592ffb21SWarner Losh 	}
1337*592ffb21SWarner Losh }
1338*592ffb21SWarner Losh 
1339*592ffb21SWarner Losh /**
1340*592ffb21SWarner Losh  * add_standard_modes - get std. modes from EDID and add them
1341*592ffb21SWarner Losh  * @edid: EDID block to scan
1342*592ffb21SWarner Losh  *
1343*592ffb21SWarner Losh  * Standard modes can be calculated using the appropriate standard (DMT,
1344*592ffb21SWarner Losh  * GTF or CVT. Grab them from @edid and add them to the list.
1345*592ffb21SWarner Losh  */
1346*592ffb21SWarner Losh static int
add_standard_modes(struct drm_connector * connector,struct edid * edid)1347*592ffb21SWarner Losh add_standard_modes(struct drm_connector *connector, struct edid *edid)
1348*592ffb21SWarner Losh {
1349*592ffb21SWarner Losh 	int i, modes = 0;
1350*592ffb21SWarner Losh 	struct detailed_mode_closure closure = {
1351*592ffb21SWarner Losh 		connector, edid, 0, 0, 0
1352*592ffb21SWarner Losh 	};
1353*592ffb21SWarner Losh 
1354*592ffb21SWarner Losh 	for (i = 0; i < EDID_STD_TIMINGS; i++) {
1355*592ffb21SWarner Losh 		struct drm_display_mode *newmode;
1356*592ffb21SWarner Losh 
1357*592ffb21SWarner Losh 		newmode = drm_mode_std(connector, edid,
1358*592ffb21SWarner Losh 				       &edid->standard_timings[i],
1359*592ffb21SWarner Losh 				       edid->revision);
1360*592ffb21SWarner Losh 		if (newmode) {
1361*592ffb21SWarner Losh 			drm_mode_probed_add(connector, newmode);
1362*592ffb21SWarner Losh 			modes++;
1363*592ffb21SWarner Losh 		}
1364*592ffb21SWarner Losh 	}
1365*592ffb21SWarner Losh 
1366*592ffb21SWarner Losh 	if (version_greater(edid, 1, 0))
1367*592ffb21SWarner Losh 		drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
1368*592ffb21SWarner Losh 					    &closure);
1369*592ffb21SWarner Losh 
1370*592ffb21SWarner Losh 	/* XXX should also look for standard codes in VTB blocks */
1371*592ffb21SWarner Losh 
1372*592ffb21SWarner Losh 	return modes + closure.modes;
1373*592ffb21SWarner Losh }
1374*592ffb21SWarner Losh 
drm_cvt_modes(struct drm_connector * connector,struct detailed_timing * timing)1375*592ffb21SWarner Losh static int drm_cvt_modes(struct drm_connector *connector,
1376*592ffb21SWarner Losh 			 struct detailed_timing *timing)
1377*592ffb21SWarner Losh {
1378*592ffb21SWarner Losh 	int i, j, modes = 0;
1379*592ffb21SWarner Losh 	struct drm_display_mode *newmode;
1380*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
1381*592ffb21SWarner Losh 	struct cvt_timing *cvt;
1382*592ffb21SWarner Losh 	const int rates[] = { 60, 85, 75, 60, 50 };
1383*592ffb21SWarner Losh 	const u8 empty[3] = { 0, 0, 0 };
1384*592ffb21SWarner Losh 
1385*592ffb21SWarner Losh 	for (i = 0; i < 4; i++) {
1386*592ffb21SWarner Losh 		int width = 0, height;
1387*592ffb21SWarner Losh 		cvt = &(timing->data.other_data.data.cvt[i]);
1388*592ffb21SWarner Losh 
1389*592ffb21SWarner Losh 		if (!memcmp(cvt->code, empty, 3))
1390*592ffb21SWarner Losh 			continue;
1391*592ffb21SWarner Losh 
1392*592ffb21SWarner Losh 		height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
1393*592ffb21SWarner Losh 		switch (cvt->code[1] & 0x0c) {
1394*592ffb21SWarner Losh 		case 0x00:
1395*592ffb21SWarner Losh 			width = height * 4 / 3;
1396*592ffb21SWarner Losh 			break;
1397*592ffb21SWarner Losh 		case 0x04:
1398*592ffb21SWarner Losh 			width = height * 16 / 9;
1399*592ffb21SWarner Losh 			break;
1400*592ffb21SWarner Losh 		case 0x08:
1401*592ffb21SWarner Losh 			width = height * 16 / 10;
1402*592ffb21SWarner Losh 			break;
1403*592ffb21SWarner Losh 		case 0x0c:
1404*592ffb21SWarner Losh 			width = height * 15 / 9;
1405*592ffb21SWarner Losh 			break;
1406*592ffb21SWarner Losh 		}
1407*592ffb21SWarner Losh 
1408*592ffb21SWarner Losh 		for (j = 1; j < 5; j++) {
1409*592ffb21SWarner Losh 			if (cvt->code[2] & (1 << j)) {
1410*592ffb21SWarner Losh 				newmode = drm_cvt_mode(dev, width, height,
1411*592ffb21SWarner Losh 						       rates[j], j == 0,
1412*592ffb21SWarner Losh 						       false, false);
1413*592ffb21SWarner Losh 				if (newmode) {
1414*592ffb21SWarner Losh 					drm_mode_probed_add(connector, newmode);
1415*592ffb21SWarner Losh 					modes++;
1416*592ffb21SWarner Losh 				}
1417*592ffb21SWarner Losh 			}
1418*592ffb21SWarner Losh 		}
1419*592ffb21SWarner Losh 	}
1420*592ffb21SWarner Losh 
1421*592ffb21SWarner Losh 	return modes;
1422*592ffb21SWarner Losh }
1423*592ffb21SWarner Losh 
1424*592ffb21SWarner Losh static void
do_cvt_mode(struct detailed_timing * timing,void * c)1425*592ffb21SWarner Losh do_cvt_mode(struct detailed_timing *timing, void *c)
1426*592ffb21SWarner Losh {
1427*592ffb21SWarner Losh 	struct detailed_mode_closure *closure = c;
1428*592ffb21SWarner Losh 	struct detailed_non_pixel *data = &timing->data.other_data;
1429*592ffb21SWarner Losh 
1430*592ffb21SWarner Losh 	if (data->type == EDID_DETAIL_CVT_3BYTE)
1431*592ffb21SWarner Losh 		closure->modes += drm_cvt_modes(closure->connector, timing);
1432*592ffb21SWarner Losh }
1433*592ffb21SWarner Losh 
1434*592ffb21SWarner Losh static int
add_cvt_modes(struct drm_connector * connector,struct edid * edid)1435*592ffb21SWarner Losh add_cvt_modes(struct drm_connector *connector, struct edid *edid)
1436*592ffb21SWarner Losh {
1437*592ffb21SWarner Losh 	struct detailed_mode_closure closure = {
1438*592ffb21SWarner Losh 		connector, edid, 0, 0, 0
1439*592ffb21SWarner Losh 	};
1440*592ffb21SWarner Losh 
1441*592ffb21SWarner Losh 	if (version_greater(edid, 1, 2))
1442*592ffb21SWarner Losh 		drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
1443*592ffb21SWarner Losh 
1444*592ffb21SWarner Losh 	/* XXX should also look for CVT codes in VTB blocks */
1445*592ffb21SWarner Losh 
1446*592ffb21SWarner Losh 	return closure.modes;
1447*592ffb21SWarner Losh }
1448*592ffb21SWarner Losh 
1449*592ffb21SWarner Losh static void
do_detailed_mode(struct detailed_timing * timing,void * c)1450*592ffb21SWarner Losh do_detailed_mode(struct detailed_timing *timing, void *c)
1451*592ffb21SWarner Losh {
1452*592ffb21SWarner Losh 	struct detailed_mode_closure *closure = c;
1453*592ffb21SWarner Losh 	struct drm_display_mode *newmode;
1454*592ffb21SWarner Losh 
1455*592ffb21SWarner Losh 	if (timing->pixel_clock) {
1456*592ffb21SWarner Losh 		newmode = drm_mode_detailed(closure->connector->dev,
1457*592ffb21SWarner Losh 					    closure->edid, timing,
1458*592ffb21SWarner Losh 					    closure->quirks);
1459*592ffb21SWarner Losh 		if (!newmode)
1460*592ffb21SWarner Losh 			return;
1461*592ffb21SWarner Losh 
1462*592ffb21SWarner Losh 		if (closure->preferred)
1463*592ffb21SWarner Losh 			newmode->type |= DRM_MODE_TYPE_PREFERRED;
1464*592ffb21SWarner Losh 
1465*592ffb21SWarner Losh 		drm_mode_probed_add(closure->connector, newmode);
1466*592ffb21SWarner Losh 		closure->modes++;
1467*592ffb21SWarner Losh 		closure->preferred = 0;
1468*592ffb21SWarner Losh 	}
1469*592ffb21SWarner Losh }
1470*592ffb21SWarner Losh 
1471*592ffb21SWarner Losh /*
1472*592ffb21SWarner Losh  * add_detailed_modes - Add modes from detailed timings
1473*592ffb21SWarner Losh  * @connector: attached connector
1474*592ffb21SWarner Losh  * @edid: EDID block to scan
1475*592ffb21SWarner Losh  * @quirks: quirks to apply
1476*592ffb21SWarner Losh  */
1477*592ffb21SWarner Losh static int
add_detailed_modes(struct drm_connector * connector,struct edid * edid,u32 quirks)1478*592ffb21SWarner Losh add_detailed_modes(struct drm_connector *connector, struct edid *edid,
1479*592ffb21SWarner Losh 		   u32 quirks)
1480*592ffb21SWarner Losh {
1481*592ffb21SWarner Losh 	struct detailed_mode_closure closure = {
1482*592ffb21SWarner Losh 		connector,
1483*592ffb21SWarner Losh 		edid,
1484*592ffb21SWarner Losh 		1,
1485*592ffb21SWarner Losh 		quirks,
1486*592ffb21SWarner Losh 		0
1487*592ffb21SWarner Losh 	};
1488*592ffb21SWarner Losh 
1489*592ffb21SWarner Losh 	if (closure.preferred && !version_greater(edid, 1, 3))
1490*592ffb21SWarner Losh 		closure.preferred =
1491*592ffb21SWarner Losh 		    (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
1492*592ffb21SWarner Losh 
1493*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
1494*592ffb21SWarner Losh 
1495*592ffb21SWarner Losh 	return closure.modes;
1496*592ffb21SWarner Losh }
1497*592ffb21SWarner Losh 
1498*592ffb21SWarner Losh #define HDMI_IDENTIFIER 0x000C03
1499*592ffb21SWarner Losh #define AUDIO_BLOCK	0x01
1500*592ffb21SWarner Losh #define VIDEO_BLOCK     0x02
1501*592ffb21SWarner Losh #define VENDOR_BLOCK    0x03
1502*592ffb21SWarner Losh #define SPEAKER_BLOCK	0x04
1503*592ffb21SWarner Losh #define EDID_BASIC_AUDIO	(1 << 6)
1504*592ffb21SWarner Losh #define EDID_CEA_YCRCB444	(1 << 5)
1505*592ffb21SWarner Losh #define EDID_CEA_YCRCB422	(1 << 4)
1506*592ffb21SWarner Losh 
1507*592ffb21SWarner Losh /**
1508*592ffb21SWarner Losh  * Search EDID for CEA extension block.
1509*592ffb21SWarner Losh  */
drm_find_cea_extension(struct edid * edid)1510*592ffb21SWarner Losh u8 *drm_find_cea_extension(struct edid *edid)
1511*592ffb21SWarner Losh {
1512*592ffb21SWarner Losh 	u8 *edid_ext = NULL;
1513*592ffb21SWarner Losh 	int i;
1514*592ffb21SWarner Losh 
1515*592ffb21SWarner Losh 	/* No EDID or EDID extensions */
1516*592ffb21SWarner Losh 	if (edid == NULL || edid->extensions == 0)
1517*592ffb21SWarner Losh 		return NULL;
1518*592ffb21SWarner Losh 
1519*592ffb21SWarner Losh 	/* Find CEA extension */
1520*592ffb21SWarner Losh 	for (i = 0; i < edid->extensions; i++) {
1521*592ffb21SWarner Losh 		edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
1522*592ffb21SWarner Losh 		if (edid_ext[0] == CEA_EXT)
1523*592ffb21SWarner Losh 			break;
1524*592ffb21SWarner Losh 	}
1525*592ffb21SWarner Losh 
1526*592ffb21SWarner Losh 	if (i == edid->extensions)
1527*592ffb21SWarner Losh 		return NULL;
1528*592ffb21SWarner Losh 
1529*592ffb21SWarner Losh 	return edid_ext;
1530*592ffb21SWarner Losh }
1531*592ffb21SWarner Losh EXPORT_SYMBOL(drm_find_cea_extension);
1532*592ffb21SWarner Losh 
1533*592ffb21SWarner Losh /*
1534*592ffb21SWarner Losh  * Looks for a CEA mode matching given drm_display_mode.
1535*592ffb21SWarner Losh  * Returns its CEA Video ID code, or 0 if not found.
1536*592ffb21SWarner Losh  */
drm_match_cea_mode(struct drm_display_mode * to_match)1537*592ffb21SWarner Losh u8 drm_match_cea_mode(struct drm_display_mode *to_match)
1538*592ffb21SWarner Losh {
1539*592ffb21SWarner Losh 	const struct drm_display_mode *cea_mode;
1540*592ffb21SWarner Losh 	u8 mode;
1541*592ffb21SWarner Losh 
1542*592ffb21SWarner Losh 	for (mode = 0; mode < drm_num_cea_modes; mode++) {
1543*592ffb21SWarner Losh 		cea_mode = (const struct drm_display_mode *)&edid_cea_modes[mode];
1544*592ffb21SWarner Losh 
1545*592ffb21SWarner Losh 		if (drm_mode_equal(to_match, cea_mode))
1546*592ffb21SWarner Losh 			return mode + 1;
1547*592ffb21SWarner Losh 	}
1548*592ffb21SWarner Losh 	return 0;
1549*592ffb21SWarner Losh }
1550*592ffb21SWarner Losh EXPORT_SYMBOL(drm_match_cea_mode);
1551*592ffb21SWarner Losh 
1552*592ffb21SWarner Losh 
1553*592ffb21SWarner Losh static int
do_cea_modes(struct drm_connector * connector,u8 * db,u8 len)1554*592ffb21SWarner Losh do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
1555*592ffb21SWarner Losh {
1556*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
1557*592ffb21SWarner Losh 	u8 * mode, cea_mode;
1558*592ffb21SWarner Losh 	int modes = 0;
1559*592ffb21SWarner Losh 
1560*592ffb21SWarner Losh 	for (mode = db; mode < db + len; mode++) {
1561*592ffb21SWarner Losh 		cea_mode = (*mode & 127) - 1; /* CEA modes are numbered 1..127 */
1562*592ffb21SWarner Losh 		if (cea_mode < drm_num_cea_modes) {
1563*592ffb21SWarner Losh 			struct drm_display_mode *newmode;
1564*592ffb21SWarner Losh 			newmode = drm_mode_duplicate(dev,
1565*592ffb21SWarner Losh 						     &edid_cea_modes[cea_mode]);
1566*592ffb21SWarner Losh 			if (newmode) {
1567*592ffb21SWarner Losh 				drm_mode_probed_add(connector, newmode);
1568*592ffb21SWarner Losh 				modes++;
1569*592ffb21SWarner Losh 			}
1570*592ffb21SWarner Losh 		}
1571*592ffb21SWarner Losh 	}
1572*592ffb21SWarner Losh 
1573*592ffb21SWarner Losh 	return modes;
1574*592ffb21SWarner Losh }
1575*592ffb21SWarner Losh 
1576*592ffb21SWarner Losh static int
cea_db_payload_len(const u8 * db)1577*592ffb21SWarner Losh cea_db_payload_len(const u8 *db)
1578*592ffb21SWarner Losh {
1579*592ffb21SWarner Losh 	return db[0] & 0x1f;
1580*592ffb21SWarner Losh }
1581*592ffb21SWarner Losh 
1582*592ffb21SWarner Losh static int
cea_db_tag(const u8 * db)1583*592ffb21SWarner Losh cea_db_tag(const u8 *db)
1584*592ffb21SWarner Losh {
1585*592ffb21SWarner Losh 	return db[0] >> 5;
1586*592ffb21SWarner Losh }
1587*592ffb21SWarner Losh 
1588*592ffb21SWarner Losh static int
cea_revision(const u8 * cea)1589*592ffb21SWarner Losh cea_revision(const u8 *cea)
1590*592ffb21SWarner Losh {
1591*592ffb21SWarner Losh 	return cea[1];
1592*592ffb21SWarner Losh }
1593*592ffb21SWarner Losh 
1594*592ffb21SWarner Losh static int
cea_db_offsets(const u8 * cea,int * start,int * end)1595*592ffb21SWarner Losh cea_db_offsets(const u8 *cea, int *start, int *end)
1596*592ffb21SWarner Losh {
1597*592ffb21SWarner Losh 	/* Data block offset in CEA extension block */
1598*592ffb21SWarner Losh 	*start = 4;
1599*592ffb21SWarner Losh 	*end = cea[2];
1600*592ffb21SWarner Losh 	if (*end == 0)
1601*592ffb21SWarner Losh 		*end = 127;
1602*592ffb21SWarner Losh 	if (*end < 4 || *end > 127)
1603*592ffb21SWarner Losh 		return -ERANGE;
1604*592ffb21SWarner Losh 	return 0;
1605*592ffb21SWarner Losh }
1606*592ffb21SWarner Losh 
1607*592ffb21SWarner Losh #define for_each_cea_db(cea, i, start, end) \
1608*592ffb21SWarner Losh 	for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
1609*592ffb21SWarner Losh 
1610*592ffb21SWarner Losh static int
add_cea_modes(struct drm_connector * connector,struct edid * edid)1611*592ffb21SWarner Losh add_cea_modes(struct drm_connector *connector, struct edid *edid)
1612*592ffb21SWarner Losh {
1613*592ffb21SWarner Losh 	u8 * cea = drm_find_cea_extension(edid);
1614*592ffb21SWarner Losh 	u8 * db, dbl;
1615*592ffb21SWarner Losh 	int modes = 0;
1616*592ffb21SWarner Losh 
1617*592ffb21SWarner Losh 	if (cea && cea_revision(cea) >= 3) {
1618*592ffb21SWarner Losh 		int i, start, end;
1619*592ffb21SWarner Losh 
1620*592ffb21SWarner Losh 		if (cea_db_offsets(cea, &start, &end))
1621*592ffb21SWarner Losh 			return 0;
1622*592ffb21SWarner Losh 
1623*592ffb21SWarner Losh 		for_each_cea_db(cea, i, start, end) {
1624*592ffb21SWarner Losh 			db = &cea[i];
1625*592ffb21SWarner Losh 			dbl = cea_db_payload_len(db);
1626*592ffb21SWarner Losh 
1627*592ffb21SWarner Losh 			if (cea_db_tag(db) == VIDEO_BLOCK)
1628*592ffb21SWarner Losh 				modes += do_cea_modes (connector, db+1, dbl);
1629*592ffb21SWarner Losh 		}
1630*592ffb21SWarner Losh 	}
1631*592ffb21SWarner Losh 
1632*592ffb21SWarner Losh 	return modes;
1633*592ffb21SWarner Losh }
1634*592ffb21SWarner Losh 
1635*592ffb21SWarner Losh static void
parse_hdmi_vsdb(struct drm_connector * connector,const u8 * db)1636*592ffb21SWarner Losh parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
1637*592ffb21SWarner Losh {
1638*592ffb21SWarner Losh 	u8 len = cea_db_payload_len(db);
1639*592ffb21SWarner Losh 
1640*592ffb21SWarner Losh 	if (len >= 6) {
1641*592ffb21SWarner Losh 		connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
1642*592ffb21SWarner Losh 		connector->dvi_dual = db[6] & 1;
1643*592ffb21SWarner Losh 	}
1644*592ffb21SWarner Losh 	if (len >= 7)
1645*592ffb21SWarner Losh 		connector->max_tmds_clock = db[7] * 5;
1646*592ffb21SWarner Losh 	if (len >= 8) {
1647*592ffb21SWarner Losh 		connector->latency_present[0] = db[8] >> 7;
1648*592ffb21SWarner Losh 		connector->latency_present[1] = (db[8] >> 6) & 1;
1649*592ffb21SWarner Losh 	}
1650*592ffb21SWarner Losh 	if (len >= 9)
1651*592ffb21SWarner Losh 		connector->video_latency[0] = db[9];
1652*592ffb21SWarner Losh 	if (len >= 10)
1653*592ffb21SWarner Losh 		connector->audio_latency[0] = db[10];
1654*592ffb21SWarner Losh 	if (len >= 11)
1655*592ffb21SWarner Losh 		connector->video_latency[1] = db[11];
1656*592ffb21SWarner Losh 	if (len >= 12)
1657*592ffb21SWarner Losh 		connector->audio_latency[1] = db[12];
1658*592ffb21SWarner Losh 
1659*592ffb21SWarner Losh 	DRM_DEBUG_KMS("HDMI: DVI dual %d, "
1660*592ffb21SWarner Losh 		    "max TMDS clock %d, "
1661*592ffb21SWarner Losh 		    "latency present %d %d, "
1662*592ffb21SWarner Losh 		    "video latency %d %d, "
1663*592ffb21SWarner Losh 		    "audio latency %d %d\n",
1664*592ffb21SWarner Losh 		    connector->dvi_dual,
1665*592ffb21SWarner Losh 		    connector->max_tmds_clock,
1666*592ffb21SWarner Losh 	      (int) connector->latency_present[0],
1667*592ffb21SWarner Losh 	      (int) connector->latency_present[1],
1668*592ffb21SWarner Losh 		    connector->video_latency[0],
1669*592ffb21SWarner Losh 		    connector->video_latency[1],
1670*592ffb21SWarner Losh 		    connector->audio_latency[0],
1671*592ffb21SWarner Losh 		    connector->audio_latency[1]);
1672*592ffb21SWarner Losh }
1673*592ffb21SWarner Losh 
1674*592ffb21SWarner Losh static void
monitor_name(struct detailed_timing * t,void * data)1675*592ffb21SWarner Losh monitor_name(struct detailed_timing *t, void *data)
1676*592ffb21SWarner Losh {
1677*592ffb21SWarner Losh 	if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
1678*592ffb21SWarner Losh 		*(u8 **)data = t->data.other_data.data.str.str;
1679*592ffb21SWarner Losh }
1680*592ffb21SWarner Losh 
cea_db_is_hdmi_vsdb(const u8 * db)1681*592ffb21SWarner Losh static bool cea_db_is_hdmi_vsdb(const u8 *db)
1682*592ffb21SWarner Losh {
1683*592ffb21SWarner Losh 	int hdmi_id;
1684*592ffb21SWarner Losh 
1685*592ffb21SWarner Losh 	if (cea_db_tag(db) != VENDOR_BLOCK)
1686*592ffb21SWarner Losh 		return false;
1687*592ffb21SWarner Losh 
1688*592ffb21SWarner Losh 	if (cea_db_payload_len(db) < 5)
1689*592ffb21SWarner Losh 		return false;
1690*592ffb21SWarner Losh 
1691*592ffb21SWarner Losh 	hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
1692*592ffb21SWarner Losh 
1693*592ffb21SWarner Losh 	return hdmi_id == HDMI_IDENTIFIER;
1694*592ffb21SWarner Losh }
1695*592ffb21SWarner Losh 
1696*592ffb21SWarner Losh /**
1697*592ffb21SWarner Losh  * drm_edid_to_eld - build ELD from EDID
1698*592ffb21SWarner Losh  * @connector: connector corresponding to the HDMI/DP sink
1699*592ffb21SWarner Losh  * @edid: EDID to parse
1700*592ffb21SWarner Losh  *
1701*592ffb21SWarner Losh  * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
1702*592ffb21SWarner Losh  * Some ELD fields are left to the graphics driver caller:
1703*592ffb21SWarner Losh  * - Conn_Type
1704*592ffb21SWarner Losh  * - HDCP
1705*592ffb21SWarner Losh  * - Port_ID
1706*592ffb21SWarner Losh  */
drm_edid_to_eld(struct drm_connector * connector,struct edid * edid)1707*592ffb21SWarner Losh void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
1708*592ffb21SWarner Losh {
1709*592ffb21SWarner Losh 	uint8_t *eld = connector->eld;
1710*592ffb21SWarner Losh 	u8 *cea;
1711*592ffb21SWarner Losh 	u8 *name;
1712*592ffb21SWarner Losh 	u8 *db;
1713*592ffb21SWarner Losh 	int sad_count = 0;
1714*592ffb21SWarner Losh 	int mnl;
1715*592ffb21SWarner Losh 	int dbl;
1716*592ffb21SWarner Losh 
1717*592ffb21SWarner Losh 	memset(eld, 0, sizeof(connector->eld));
1718*592ffb21SWarner Losh 
1719*592ffb21SWarner Losh 	cea = drm_find_cea_extension(edid);
1720*592ffb21SWarner Losh 	if (!cea) {
1721*592ffb21SWarner Losh 		DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
1722*592ffb21SWarner Losh 		return;
1723*592ffb21SWarner Losh 	}
1724*592ffb21SWarner Losh 
1725*592ffb21SWarner Losh 	name = NULL;
1726*592ffb21SWarner Losh 	drm_for_each_detailed_block((u8 *)edid, monitor_name, &name);
1727*592ffb21SWarner Losh 	for (mnl = 0; name && mnl < 13; mnl++) {
1728*592ffb21SWarner Losh 		if (name[mnl] == 0x0a)
1729*592ffb21SWarner Losh 			break;
1730*592ffb21SWarner Losh 		eld[20 + mnl] = name[mnl];
1731*592ffb21SWarner Losh 	}
1732*592ffb21SWarner Losh 	eld[4] = (cea[1] << 5) | mnl;
1733*592ffb21SWarner Losh 	DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20);
1734*592ffb21SWarner Losh 
1735*592ffb21SWarner Losh 	eld[0] = 2 << 3;		/* ELD version: 2 */
1736*592ffb21SWarner Losh 
1737*592ffb21SWarner Losh 	eld[16] = edid->mfg_id[0];
1738*592ffb21SWarner Losh 	eld[17] = edid->mfg_id[1];
1739*592ffb21SWarner Losh 	eld[18] = edid->prod_code[0];
1740*592ffb21SWarner Losh 	eld[19] = edid->prod_code[1];
1741*592ffb21SWarner Losh 
1742*592ffb21SWarner Losh 	if (cea_revision(cea) >= 3) {
1743*592ffb21SWarner Losh 		int i, start, end;
1744*592ffb21SWarner Losh 
1745*592ffb21SWarner Losh 		if (cea_db_offsets(cea, &start, &end)) {
1746*592ffb21SWarner Losh 			start = 0;
1747*592ffb21SWarner Losh 			end = 0;
1748*592ffb21SWarner Losh 		}
1749*592ffb21SWarner Losh 
1750*592ffb21SWarner Losh 		for_each_cea_db(cea, i, start, end) {
1751*592ffb21SWarner Losh 			db = &cea[i];
1752*592ffb21SWarner Losh 			dbl = cea_db_payload_len(db);
1753*592ffb21SWarner Losh 
1754*592ffb21SWarner Losh 			switch (cea_db_tag(db)) {
1755*592ffb21SWarner Losh 			case AUDIO_BLOCK:
1756*592ffb21SWarner Losh 				/* Audio Data Block, contains SADs */
1757*592ffb21SWarner Losh 				sad_count = dbl / 3;
1758*592ffb21SWarner Losh 				if (dbl >= 1)
1759*592ffb21SWarner Losh 					memcpy(eld + 20 + mnl, &db[1], dbl);
1760*592ffb21SWarner Losh 				break;
1761*592ffb21SWarner Losh 			case SPEAKER_BLOCK:
1762*592ffb21SWarner Losh 				/* Speaker Allocation Data Block */
1763*592ffb21SWarner Losh 				if (dbl >= 1)
1764*592ffb21SWarner Losh 					eld[7] = db[1];
1765*592ffb21SWarner Losh 				break;
1766*592ffb21SWarner Losh 			case VENDOR_BLOCK:
1767*592ffb21SWarner Losh 				/* HDMI Vendor-Specific Data Block */
1768*592ffb21SWarner Losh 				if (cea_db_is_hdmi_vsdb(db))
1769*592ffb21SWarner Losh 					parse_hdmi_vsdb(connector, db);
1770*592ffb21SWarner Losh 				break;
1771*592ffb21SWarner Losh 			default:
1772*592ffb21SWarner Losh 				break;
1773*592ffb21SWarner Losh 			}
1774*592ffb21SWarner Losh 		}
1775*592ffb21SWarner Losh 	}
1776*592ffb21SWarner Losh 	eld[5] |= sad_count << 4;
1777*592ffb21SWarner Losh 	eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
1778*592ffb21SWarner Losh 
1779*592ffb21SWarner Losh 	DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count);
1780*592ffb21SWarner Losh }
1781*592ffb21SWarner Losh EXPORT_SYMBOL(drm_edid_to_eld);
1782*592ffb21SWarner Losh 
1783*592ffb21SWarner Losh /**
1784*592ffb21SWarner Losh  * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
1785*592ffb21SWarner Losh  * @connector: connector associated with the HDMI/DP sink
1786*592ffb21SWarner Losh  * @mode: the display mode
1787*592ffb21SWarner Losh  */
drm_av_sync_delay(struct drm_connector * connector,struct drm_display_mode * mode)1788*592ffb21SWarner Losh int drm_av_sync_delay(struct drm_connector *connector,
1789*592ffb21SWarner Losh 		      struct drm_display_mode *mode)
1790*592ffb21SWarner Losh {
1791*592ffb21SWarner Losh 	int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
1792*592ffb21SWarner Losh 	int a, v;
1793*592ffb21SWarner Losh 
1794*592ffb21SWarner Losh 	if (!connector->latency_present[0])
1795*592ffb21SWarner Losh 		return 0;
1796*592ffb21SWarner Losh 	if (!connector->latency_present[1])
1797*592ffb21SWarner Losh 		i = 0;
1798*592ffb21SWarner Losh 
1799*592ffb21SWarner Losh 	a = connector->audio_latency[i];
1800*592ffb21SWarner Losh 	v = connector->video_latency[i];
1801*592ffb21SWarner Losh 
1802*592ffb21SWarner Losh 	/*
1803*592ffb21SWarner Losh 	 * HDMI/DP sink doesn't support audio or video?
1804*592ffb21SWarner Losh 	 */
1805*592ffb21SWarner Losh 	if (a == 255 || v == 255)
1806*592ffb21SWarner Losh 		return 0;
1807*592ffb21SWarner Losh 
1808*592ffb21SWarner Losh 	/*
1809*592ffb21SWarner Losh 	 * Convert raw EDID values to millisecond.
1810*592ffb21SWarner Losh 	 * Treat unknown latency as 0ms.
1811*592ffb21SWarner Losh 	 */
1812*592ffb21SWarner Losh 	if (a)
1813*592ffb21SWarner Losh 		a = min(2 * (a - 1), 500);
1814*592ffb21SWarner Losh 	if (v)
1815*592ffb21SWarner Losh 		v = min(2 * (v - 1), 500);
1816*592ffb21SWarner Losh 
1817*592ffb21SWarner Losh 	return max(v - a, 0);
1818*592ffb21SWarner Losh }
1819*592ffb21SWarner Losh EXPORT_SYMBOL(drm_av_sync_delay);
1820*592ffb21SWarner Losh 
1821*592ffb21SWarner Losh /**
1822*592ffb21SWarner Losh  * drm_select_eld - select one ELD from multiple HDMI/DP sinks
1823*592ffb21SWarner Losh  * @encoder: the encoder just changed display mode
1824*592ffb21SWarner Losh  * @mode: the adjusted display mode
1825*592ffb21SWarner Losh  *
1826*592ffb21SWarner Losh  * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
1827*592ffb21SWarner Losh  * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
1828*592ffb21SWarner Losh  */
drm_select_eld(struct drm_encoder * encoder,struct drm_display_mode * mode)1829*592ffb21SWarner Losh struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
1830*592ffb21SWarner Losh 				     struct drm_display_mode *mode)
1831*592ffb21SWarner Losh {
1832*592ffb21SWarner Losh 	struct drm_connector *connector;
1833*592ffb21SWarner Losh 	struct drm_device *dev = encoder->dev;
1834*592ffb21SWarner Losh 
1835*592ffb21SWarner Losh 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
1836*592ffb21SWarner Losh 		if (connector->encoder == encoder && connector->eld[0])
1837*592ffb21SWarner Losh 			return connector;
1838*592ffb21SWarner Losh 
1839*592ffb21SWarner Losh 	return NULL;
1840*592ffb21SWarner Losh }
1841*592ffb21SWarner Losh EXPORT_SYMBOL(drm_select_eld);
1842*592ffb21SWarner Losh 
1843*592ffb21SWarner Losh /**
1844*592ffb21SWarner Losh  * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
1845*592ffb21SWarner Losh  * @edid: monitor EDID information
1846*592ffb21SWarner Losh  *
1847*592ffb21SWarner Losh  * Parse the CEA extension according to CEA-861-B.
1848*592ffb21SWarner Losh  * Return true if HDMI, false if not or unknown.
1849*592ffb21SWarner Losh  */
drm_detect_hdmi_monitor(struct edid * edid)1850*592ffb21SWarner Losh bool drm_detect_hdmi_monitor(struct edid *edid)
1851*592ffb21SWarner Losh {
1852*592ffb21SWarner Losh 	u8 *edid_ext;
1853*592ffb21SWarner Losh 	int i;
1854*592ffb21SWarner Losh 	int start_offset, end_offset;
1855*592ffb21SWarner Losh 
1856*592ffb21SWarner Losh 	edid_ext = drm_find_cea_extension(edid);
1857*592ffb21SWarner Losh 	if (!edid_ext)
1858*592ffb21SWarner Losh 		return false;
1859*592ffb21SWarner Losh 
1860*592ffb21SWarner Losh 	if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
1861*592ffb21SWarner Losh 		return false;
1862*592ffb21SWarner Losh 
1863*592ffb21SWarner Losh 	/*
1864*592ffb21SWarner Losh 	 * Because HDMI identifier is in Vendor Specific Block,
1865*592ffb21SWarner Losh 	 * search it from all data blocks of CEA extension.
1866*592ffb21SWarner Losh 	 */
1867*592ffb21SWarner Losh 	for_each_cea_db(edid_ext, i, start_offset, end_offset) {
1868*592ffb21SWarner Losh 		if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
1869*592ffb21SWarner Losh 			return true;
1870*592ffb21SWarner Losh 	}
1871*592ffb21SWarner Losh 
1872*592ffb21SWarner Losh 	return false;
1873*592ffb21SWarner Losh }
1874*592ffb21SWarner Losh EXPORT_SYMBOL(drm_detect_hdmi_monitor);
1875*592ffb21SWarner Losh 
1876*592ffb21SWarner Losh /**
1877*592ffb21SWarner Losh  * drm_detect_monitor_audio - check monitor audio capability
1878*592ffb21SWarner Losh  *
1879*592ffb21SWarner Losh  * Monitor should have CEA extension block.
1880*592ffb21SWarner Losh  * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
1881*592ffb21SWarner Losh  * audio' only. If there is any audio extension block and supported
1882*592ffb21SWarner Losh  * audio format, assume at least 'basic audio' support, even if 'basic
1883*592ffb21SWarner Losh  * audio' is not defined in EDID.
1884*592ffb21SWarner Losh  *
1885*592ffb21SWarner Losh  */
drm_detect_monitor_audio(struct edid * edid)1886*592ffb21SWarner Losh bool drm_detect_monitor_audio(struct edid *edid)
1887*592ffb21SWarner Losh {
1888*592ffb21SWarner Losh 	u8 *edid_ext;
1889*592ffb21SWarner Losh 	int i, j;
1890*592ffb21SWarner Losh 	bool has_audio = false;
1891*592ffb21SWarner Losh 	int start_offset, end_offset;
1892*592ffb21SWarner Losh 
1893*592ffb21SWarner Losh 	edid_ext = drm_find_cea_extension(edid);
1894*592ffb21SWarner Losh 	if (!edid_ext)
1895*592ffb21SWarner Losh 		goto end;
1896*592ffb21SWarner Losh 
1897*592ffb21SWarner Losh 	has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
1898*592ffb21SWarner Losh 
1899*592ffb21SWarner Losh 	if (has_audio) {
1900*592ffb21SWarner Losh 		DRM_DEBUG_KMS("Monitor has basic audio support\n");
1901*592ffb21SWarner Losh 		goto end;
1902*592ffb21SWarner Losh 	}
1903*592ffb21SWarner Losh 
1904*592ffb21SWarner Losh 	if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
1905*592ffb21SWarner Losh 		goto end;
1906*592ffb21SWarner Losh 
1907*592ffb21SWarner Losh 	for_each_cea_db(edid_ext, i, start_offset, end_offset) {
1908*592ffb21SWarner Losh 		if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) {
1909*592ffb21SWarner Losh 			has_audio = true;
1910*592ffb21SWarner Losh 			for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3)
1911*592ffb21SWarner Losh 				DRM_DEBUG_KMS("CEA audio format %d\n",
1912*592ffb21SWarner Losh 					      (edid_ext[i + j] >> 3) & 0xf);
1913*592ffb21SWarner Losh 			goto end;
1914*592ffb21SWarner Losh 		}
1915*592ffb21SWarner Losh 	}
1916*592ffb21SWarner Losh end:
1917*592ffb21SWarner Losh 	return has_audio;
1918*592ffb21SWarner Losh }
1919*592ffb21SWarner Losh EXPORT_SYMBOL(drm_detect_monitor_audio);
1920*592ffb21SWarner Losh 
1921*592ffb21SWarner Losh /**
1922*592ffb21SWarner Losh  * drm_add_display_info - pull display info out if present
1923*592ffb21SWarner Losh  * @edid: EDID data
1924*592ffb21SWarner Losh  * @info: display info (attached to connector)
1925*592ffb21SWarner Losh  *
1926*592ffb21SWarner Losh  * Grab any available display info and stuff it into the drm_display_info
1927*592ffb21SWarner Losh  * structure that's part of the connector.  Useful for tracking bpp and
1928*592ffb21SWarner Losh  * color spaces.
1929*592ffb21SWarner Losh  */
drm_add_display_info(struct edid * edid,struct drm_display_info * info)1930*592ffb21SWarner Losh static void drm_add_display_info(struct edid *edid,
1931*592ffb21SWarner Losh 				 struct drm_display_info *info)
1932*592ffb21SWarner Losh {
1933*592ffb21SWarner Losh 	u8 *edid_ext;
1934*592ffb21SWarner Losh 
1935*592ffb21SWarner Losh 	info->width_mm = edid->width_cm * 10;
1936*592ffb21SWarner Losh 	info->height_mm = edid->height_cm * 10;
1937*592ffb21SWarner Losh 
1938*592ffb21SWarner Losh 	/* driver figures it out in this case */
1939*592ffb21SWarner Losh 	info->bpc = 0;
1940*592ffb21SWarner Losh 	info->color_formats = 0;
1941*592ffb21SWarner Losh 
1942*592ffb21SWarner Losh 	if (edid->revision < 3)
1943*592ffb21SWarner Losh 		return;
1944*592ffb21SWarner Losh 
1945*592ffb21SWarner Losh 	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
1946*592ffb21SWarner Losh 		return;
1947*592ffb21SWarner Losh 
1948*592ffb21SWarner Losh 	/* Get data from CEA blocks if present */
1949*592ffb21SWarner Losh 	edid_ext = drm_find_cea_extension(edid);
1950*592ffb21SWarner Losh 	if (edid_ext) {
1951*592ffb21SWarner Losh 		info->cea_rev = edid_ext[1];
1952*592ffb21SWarner Losh 
1953*592ffb21SWarner Losh 		/* The existence of a CEA block should imply RGB support */
1954*592ffb21SWarner Losh 		info->color_formats = DRM_COLOR_FORMAT_RGB444;
1955*592ffb21SWarner Losh 		if (edid_ext[3] & EDID_CEA_YCRCB444)
1956*592ffb21SWarner Losh 			info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
1957*592ffb21SWarner Losh 		if (edid_ext[3] & EDID_CEA_YCRCB422)
1958*592ffb21SWarner Losh 			info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
1959*592ffb21SWarner Losh 	}
1960*592ffb21SWarner Losh 
1961*592ffb21SWarner Losh 	/* Only defined for 1.4 with digital displays */
1962*592ffb21SWarner Losh 	if (edid->revision < 4)
1963*592ffb21SWarner Losh 		return;
1964*592ffb21SWarner Losh 
1965*592ffb21SWarner Losh 	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
1966*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_6:
1967*592ffb21SWarner Losh 		info->bpc = 6;
1968*592ffb21SWarner Losh 		break;
1969*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_8:
1970*592ffb21SWarner Losh 		info->bpc = 8;
1971*592ffb21SWarner Losh 		break;
1972*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_10:
1973*592ffb21SWarner Losh 		info->bpc = 10;
1974*592ffb21SWarner Losh 		break;
1975*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_12:
1976*592ffb21SWarner Losh 		info->bpc = 12;
1977*592ffb21SWarner Losh 		break;
1978*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_14:
1979*592ffb21SWarner Losh 		info->bpc = 14;
1980*592ffb21SWarner Losh 		break;
1981*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_16:
1982*592ffb21SWarner Losh 		info->bpc = 16;
1983*592ffb21SWarner Losh 		break;
1984*592ffb21SWarner Losh 	case DRM_EDID_DIGITAL_DEPTH_UNDEF:
1985*592ffb21SWarner Losh 	default:
1986*592ffb21SWarner Losh 		info->bpc = 0;
1987*592ffb21SWarner Losh 		break;
1988*592ffb21SWarner Losh 	}
1989*592ffb21SWarner Losh 
1990*592ffb21SWarner Losh 	info->color_formats |= DRM_COLOR_FORMAT_RGB444;
1991*592ffb21SWarner Losh 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB444)
1992*592ffb21SWarner Losh 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
1993*592ffb21SWarner Losh 	if (edid->features & DRM_EDID_FEATURE_RGB_YCRCB422)
1994*592ffb21SWarner Losh 		info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
1995*592ffb21SWarner Losh }
1996*592ffb21SWarner Losh 
1997*592ffb21SWarner Losh /**
1998*592ffb21SWarner Losh  * drm_add_edid_modes - add modes from EDID data, if available
1999*592ffb21SWarner Losh  * @connector: connector we're probing
2000*592ffb21SWarner Losh  * @edid: edid data
2001*592ffb21SWarner Losh  *
2002*592ffb21SWarner Losh  * Add the specified modes to the connector's mode list.
2003*592ffb21SWarner Losh  *
2004*592ffb21SWarner Losh  * Return number of modes added or 0 if we couldn't find any.
2005*592ffb21SWarner Losh  */
drm_add_edid_modes(struct drm_connector * connector,struct edid * edid)2006*592ffb21SWarner Losh int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
2007*592ffb21SWarner Losh {
2008*592ffb21SWarner Losh 	int num_modes = 0;
2009*592ffb21SWarner Losh 	u32 quirks;
2010*592ffb21SWarner Losh 
2011*592ffb21SWarner Losh 	if (edid == NULL) {
2012*592ffb21SWarner Losh 		return 0;
2013*592ffb21SWarner Losh 	}
2014*592ffb21SWarner Losh 	if (!drm_edid_is_valid(edid)) {
2015*592ffb21SWarner Losh 		dev_warn(connector->dev->dev, "%s: EDID invalid.\n",
2016*592ffb21SWarner Losh 			 drm_get_connector_name(connector));
2017*592ffb21SWarner Losh 		return 0;
2018*592ffb21SWarner Losh 	}
2019*592ffb21SWarner Losh 
2020*592ffb21SWarner Losh 	quirks = edid_get_quirks(edid);
2021*592ffb21SWarner Losh 
2022*592ffb21SWarner Losh 	/*
2023*592ffb21SWarner Losh 	 * EDID spec says modes should be preferred in this order:
2024*592ffb21SWarner Losh 	 * - preferred detailed mode
2025*592ffb21SWarner Losh 	 * - other detailed modes from base block
2026*592ffb21SWarner Losh 	 * - detailed modes from extension blocks
2027*592ffb21SWarner Losh 	 * - CVT 3-byte code modes
2028*592ffb21SWarner Losh 	 * - standard timing codes
2029*592ffb21SWarner Losh 	 * - established timing codes
2030*592ffb21SWarner Losh 	 * - modes inferred from GTF or CVT range information
2031*592ffb21SWarner Losh 	 *
2032*592ffb21SWarner Losh 	 * We get this pretty much right.
2033*592ffb21SWarner Losh 	 *
2034*592ffb21SWarner Losh 	 * XXX order for additional mode types in extension blocks?
2035*592ffb21SWarner Losh 	 */
2036*592ffb21SWarner Losh 	num_modes += add_detailed_modes(connector, edid, quirks);
2037*592ffb21SWarner Losh 	num_modes += add_cvt_modes(connector, edid);
2038*592ffb21SWarner Losh 	num_modes += add_standard_modes(connector, edid);
2039*592ffb21SWarner Losh 	num_modes += add_established_modes(connector, edid);
2040*592ffb21SWarner Losh 	if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF)
2041*592ffb21SWarner Losh 		num_modes += add_inferred_modes(connector, edid);
2042*592ffb21SWarner Losh 	num_modes += add_cea_modes(connector, edid);
2043*592ffb21SWarner Losh 
2044*592ffb21SWarner Losh 	if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
2045*592ffb21SWarner Losh 		edid_fixup_preferred(connector, quirks);
2046*592ffb21SWarner Losh 
2047*592ffb21SWarner Losh 	drm_add_display_info(edid, &connector->display_info);
2048*592ffb21SWarner Losh 
2049*592ffb21SWarner Losh 	return num_modes;
2050*592ffb21SWarner Losh }
2051*592ffb21SWarner Losh EXPORT_SYMBOL(drm_add_edid_modes);
2052*592ffb21SWarner Losh 
2053*592ffb21SWarner Losh /**
2054*592ffb21SWarner Losh  * drm_add_modes_noedid - add modes for the connectors without EDID
2055*592ffb21SWarner Losh  * @connector: connector we're probing
2056*592ffb21SWarner Losh  * @hdisplay: the horizontal display limit
2057*592ffb21SWarner Losh  * @vdisplay: the vertical display limit
2058*592ffb21SWarner Losh  *
2059*592ffb21SWarner Losh  * Add the specified modes to the connector's mode list. Only when the
2060*592ffb21SWarner Losh  * hdisplay/vdisplay is not beyond the given limit, it will be added.
2061*592ffb21SWarner Losh  *
2062*592ffb21SWarner Losh  * Return number of modes added or 0 if we couldn't find any.
2063*592ffb21SWarner Losh  */
drm_add_modes_noedid(struct drm_connector * connector,int hdisplay,int vdisplay)2064*592ffb21SWarner Losh int drm_add_modes_noedid(struct drm_connector *connector,
2065*592ffb21SWarner Losh 			int hdisplay, int vdisplay)
2066*592ffb21SWarner Losh {
2067*592ffb21SWarner Losh 	int i, count, num_modes = 0;
2068*592ffb21SWarner Losh 	struct drm_display_mode *mode;
2069*592ffb21SWarner Losh 	struct drm_device *dev = connector->dev;
2070*592ffb21SWarner Losh 
2071*592ffb21SWarner Losh 	count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
2072*592ffb21SWarner Losh 	if (hdisplay < 0)
2073*592ffb21SWarner Losh 		hdisplay = 0;
2074*592ffb21SWarner Losh 	if (vdisplay < 0)
2075*592ffb21SWarner Losh 		vdisplay = 0;
2076*592ffb21SWarner Losh 
2077*592ffb21SWarner Losh 	for (i = 0; i < count; i++) {
2078*592ffb21SWarner Losh 		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
2079*592ffb21SWarner Losh 		if (hdisplay && vdisplay) {
2080*592ffb21SWarner Losh 			/*
2081*592ffb21SWarner Losh 			 * Only when two are valid, they will be used to check
2082*592ffb21SWarner Losh 			 * whether the mode should be added to the mode list of
2083*592ffb21SWarner Losh 			 * the connector.
2084*592ffb21SWarner Losh 			 */
2085*592ffb21SWarner Losh 			if (ptr->hdisplay > hdisplay ||
2086*592ffb21SWarner Losh 					ptr->vdisplay > vdisplay)
2087*592ffb21SWarner Losh 				continue;
2088*592ffb21SWarner Losh 		}
2089*592ffb21SWarner Losh 		if (drm_mode_vrefresh(ptr) > 61)
2090*592ffb21SWarner Losh 			continue;
2091*592ffb21SWarner Losh 		mode = drm_mode_duplicate(dev, ptr);
2092*592ffb21SWarner Losh 		if (mode) {
2093*592ffb21SWarner Losh 			drm_mode_probed_add(connector, mode);
2094*592ffb21SWarner Losh 			num_modes++;
2095*592ffb21SWarner Losh 		}
2096*592ffb21SWarner Losh 	}
2097*592ffb21SWarner Losh 	return num_modes;
2098*592ffb21SWarner Losh }
2099*592ffb21SWarner Losh EXPORT_SYMBOL(drm_add_modes_noedid);
2100*592ffb21SWarner Losh 
2101*592ffb21SWarner Losh /**
2102*592ffb21SWarner Losh  * drm_mode_cea_vic - return the CEA-861 VIC of a given mode
2103*592ffb21SWarner Losh  * @mode: mode
2104*592ffb21SWarner Losh  *
2105*592ffb21SWarner Losh  * RETURNS:
2106*592ffb21SWarner Losh  * The VIC number, 0 in case it's not a CEA-861 mode.
2107*592ffb21SWarner Losh  */
drm_mode_cea_vic(const struct drm_display_mode * mode)2108*592ffb21SWarner Losh uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode)
2109*592ffb21SWarner Losh {
2110*592ffb21SWarner Losh 	uint8_t i;
2111*592ffb21SWarner Losh 
2112*592ffb21SWarner Losh 	for (i = 0; i < drm_num_cea_modes; i++)
2113*592ffb21SWarner Losh 		if (drm_mode_equal(mode, &edid_cea_modes[i]))
2114*592ffb21SWarner Losh 			return i + 1;
2115*592ffb21SWarner Losh 
2116*592ffb21SWarner Losh 	return 0;
2117*592ffb21SWarner Losh }
2118*592ffb21SWarner Losh EXPORT_SYMBOL(drm_mode_cea_vic);
2119