Blame view

drivers/gpu/drm/drm_edid.c 45.2 KB
f453ba046   Dave Airlie   DRM: add mode set...
1
2
3
4
  /*
   * Copyright (c) 2006 Luc Verhaegen (quirks list)
   * Copyright (c) 2007-2008 Intel Corporation
   *   Jesse Barnes <jesse.barnes@intel.com>
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
5
   * Copyright 2010 Red Hat, Inc.
f453ba046   Dave Airlie   DRM: add mode set...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   *
   * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from
   * FB layer.
   *   Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sub license,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice (including the
   * next paragraph) shall be included in all copies or substantial portions
   * of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   */
  #include <linux/kernel.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
f453ba046   Dave Airlie   DRM: add mode set...
32
  #include <linux/i2c.h>
2d1a8a48a   Paul Gortmaker   gpu: Add export.h...
33
  #include <linux/export.h>
f453ba046   Dave Airlie   DRM: add mode set...
34
35
  #include "drmP.h"
  #include "drm_edid.h"
38fcbb674   Adam Jackson   drm/edid: Split m...
36
  #include "drm_edid_modes.h"
f453ba046   Dave Airlie   DRM: add mode set...
37

139315796   Adam Jackson   drm/edid: Rewrite...
38
39
40
  #define version_greater(edid, maj, min) \
  	(((edid)->version > (maj)) || \
  	 ((edid)->version == (maj) && (edid)->revision > (min)))
f453ba046   Dave Airlie   DRM: add mode set...
41

d1ff6409b   Adam Jackson   drm/edid: Add tes...
42
43
44
  #define EDID_EST_TIMINGS 16
  #define EDID_STD_TIMINGS 8
  #define EDID_DETAILED_TIMINGS 4
f453ba046   Dave Airlie   DRM: add mode set...
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  
  /*
   * EDID blocks out in the wild have a variety of bugs, try to collect
   * them here (note that userspace may work around broken monitors first,
   * but fixes should make their way here so that the kernel "just works"
   * on as many displays as possible).
   */
  
  /* First detailed mode wrong, use largest 60Hz mode */
  #define EDID_QUIRK_PREFER_LARGE_60		(1 << 0)
  /* Reported 135MHz pixel clock is too high, needs adjustment */
  #define EDID_QUIRK_135_CLOCK_TOO_HIGH		(1 << 1)
  /* Prefer the largest mode at 75 Hz */
  #define EDID_QUIRK_PREFER_LARGE_75		(1 << 2)
  /* Detail timing is in cm not mm */
  #define EDID_QUIRK_DETAILED_IN_CM		(1 << 3)
  /* Detailed timing descriptors have bogus size values, so just take the
   * maximum size and use that.
   */
  #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE	(1 << 4)
  /* Monitor forgot to set the first detailed is preferred bit. */
  #define EDID_QUIRK_FIRST_DETAILED_PREFERRED	(1 << 5)
  /* use +hsync +vsync for detailed mode */
  #define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)
3c537889e   Alex Deucher   drm/radeon/kms: a...
69

139315796   Adam Jackson   drm/edid: Rewrite...
70
71
72
73
74
75
76
  struct detailed_mode_closure {
  	struct drm_connector *connector;
  	struct edid *edid;
  	bool preferred;
  	u32 quirks;
  	int modes;
  };
f453ba046   Dave Airlie   DRM: add mode set...
77

5c61259e6   Zhao Yakui   drm/mode: get the...
78
79
  #define LEVEL_DMT	0
  #define LEVEL_GTF	1
7a3743500   Adam Jackson   drm/edid: Add sec...
80
81
  #define LEVEL_GTF2	2
  #define LEVEL_CVT	3
5c61259e6   Zhao Yakui   drm/mode: get the...
82

f453ba046   Dave Airlie   DRM: add mode set...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  static struct edid_quirk {
  	char *vendor;
  	int product_id;
  	u32 quirks;
  } edid_quirk_list[] = {
  	/* Acer AL1706 */
  	{ "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
  	/* Acer F51 */
  	{ "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 },
  	/* Unknown Acer */
  	{ "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
  
  	/* Belinea 10 15 55 */
  	{ "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 },
  	{ "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 },
  
  	/* Envision Peripherals, Inc. EN-7100e */
  	{ "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH },
ba1163de2   Adam Jackson   drm/edid/quirks: ...
101
102
  	/* Envision EN2028 */
  	{ "EPI", 8232, EDID_QUIRK_PREFER_LARGE_60 },
f453ba046   Dave Airlie   DRM: add mode set...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  
  	/* Funai Electronics PM36B */
  	{ "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 |
  	  EDID_QUIRK_DETAILED_IN_CM },
  
  	/* LG Philips LCD LP154W01-A5 */
  	{ "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
  	{ "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE },
  
  	/* Philips 107p5 CRT */
  	{ "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
  
  	/* Proview AY765C */
  	{ "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED },
  
  	/* Samsung SyncMaster 205BW.  Note: irony */
  	{ "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP },
  	/* Samsung SyncMaster 22[5-6]BW */
  	{ "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 },
  	{ "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 },
  };
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
124
  /*** DDC fetch and block validation ***/
f453ba046   Dave Airlie   DRM: add mode set...
125

083ae0560   Adam Jackson   drm/edid: const c...
126
127
128
  static const u8 edid_header[] = {
  	0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00
  };
f453ba046   Dave Airlie   DRM: add mode set...
129

051963d48   Thomas Reim   drm: Separate EDI...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
   /*
   * Sanity check the header of the base EDID block.  Return 8 if the header
   * is perfect, down to 0 if it's totally wrong.
   */
  int drm_edid_header_is_valid(const u8 *raw_edid)
  {
  	int i, score = 0;
  
  	for (i = 0; i < sizeof(edid_header); i++)
  		if (raw_edid[i] == edid_header[i])
  			score++;
  
  	return score;
  }
  EXPORT_SYMBOL(drm_edid_header_is_valid);
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
145
146
147
  /*
   * Sanity check the EDID block (base or extension).  Return 0 if the block
   * doesn't check out, or 1 if it's valid.
f453ba046   Dave Airlie   DRM: add mode set...
148
   */
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
149
150
  static bool
  drm_edid_block_valid(u8 *raw_edid)
f453ba046   Dave Airlie   DRM: add mode set...
151
  {
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
152
  	int i;
f453ba046   Dave Airlie   DRM: add mode set...
153
  	u8 csum = 0;
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
154
  	struct edid *edid = (struct edid *)raw_edid;
f453ba046   Dave Airlie   DRM: add mode set...
155

61e57a8d7   Adam Jackson   drm/edid: Fix sec...
156
  	if (raw_edid[0] == 0x00) {
051963d48   Thomas Reim   drm: Separate EDI...
157
  		int score = drm_edid_header_is_valid(raw_edid);
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
158
159
160
161
162
163
164
165
166
  		if (score == 8) ;
  		else if (score >= 6) {
  			DRM_DEBUG("Fixing EDID header, your hardware may be failing
  ");
  			memcpy(raw_edid, edid_header, sizeof(edid_header));
  		} else {
  			goto bad;
  		}
  	}
f453ba046   Dave Airlie   DRM: add mode set...
167
168
169
170
171
172
  
  	for (i = 0; i < EDID_LENGTH; i++)
  		csum += raw_edid[i];
  	if (csum) {
  		DRM_ERROR("EDID checksum is invalid, remainder is %d
  ", csum);
4a638b4e3   Adam Jackson   drm/edid: Allow n...
173
174
175
176
  
  		/* allow CEA to slide through, switches mangle this */
  		if (raw_edid[0] != 0x02)
  			goto bad;
f453ba046   Dave Airlie   DRM: add mode set...
177
  	}
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
178
179
180
181
182
183
184
185
  	/* per-block-type checks */
  	switch (raw_edid[0]) {
  	case 0: /* base */
  		if (edid->version != 1) {
  			DRM_ERROR("EDID has major version %d, instead of 1
  ", edid->version);
  			goto bad;
  		}
862b89c06   Adam Jackson   drm/edid: Fix up ...
186

61e57a8d7   Adam Jackson   drm/edid: Fix sec...
187
188
189
190
  		if (edid->revision > 4)
  			DRM_DEBUG("EDID minor > 4, assuming backward compatibility
  ");
  		break;
862b89c06   Adam Jackson   drm/edid: Fix up ...
191

61e57a8d7   Adam Jackson   drm/edid: Fix sec...
192
193
194
  	default:
  		break;
  	}
47ee4ccf7   Adam Jackson   drm/edid: Retry E...
195

f453ba046   Dave Airlie   DRM: add mode set...
196
197
198
199
  	return 1;
  
  bad:
  	if (raw_edid) {
f49dadb82   Dave Airlie   drm: make debug l...
200
201
  		printk(KERN_ERR "Raw EDID:
  ");
0aff47f29   Tormod Volden   drm: really make ...
202
203
  		print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1,
  			       raw_edid, EDID_LENGTH, false);
f453ba046   Dave Airlie   DRM: add mode set...
204
205
206
  	}
  	return 0;
  }
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  
  /**
   * drm_edid_is_valid - sanity check EDID data
   * @edid: EDID data
   *
   * Sanity-check an entire EDID record (including extensions)
   */
  bool drm_edid_is_valid(struct edid *edid)
  {
  	int i;
  	u8 *raw = (u8 *)edid;
  
  	if (!edid)
  		return false;
  
  	for (i = 0; i <= edid->extensions; i++)
  		if (!drm_edid_block_valid(raw + i * EDID_LENGTH))
  			return false;
  
  	return true;
  }
3c537889e   Alex Deucher   drm/radeon/kms: a...
228
  EXPORT_SYMBOL(drm_edid_is_valid);
f453ba046   Dave Airlie   DRM: add mode set...
229

61e57a8d7   Adam Jackson   drm/edid: Fix sec...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  #define DDC_ADDR 0x50
  #define DDC_SEGMENT_ADDR 0x30
  /**
   * Get EDID information via I2C.
   *
   * \param adapter : i2c device adaptor
   * \param buf     : EDID data buffer to be filled
   * \param len     : EDID data buffer length
   * \return 0 on success or -1 on failure.
   *
   * Try to fetch EDID information by calling i2c driver function.
   */
  static int
  drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
  		      int block, int len)
  {
  	unsigned char start = block * EDID_LENGTH;
4819d2e43   Chris Wilson   drm: Retry i2c tr...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  	int ret, retries = 5;
  
  	/* The core i2c driver will automatically retry the transfer if the
  	 * adapter reports EAGAIN. However, we find that bit-banging transfers
  	 * are susceptible to errors under a heavily loaded machine and
  	 * generate spurious NAKs and timeouts. Retrying the transfer
  	 * of the individual block a few times seems to overcome this.
  	 */
  	do {
  		struct i2c_msg msgs[] = {
  			{
  				.addr	= DDC_ADDR,
  				.flags	= 0,
  				.len	= 1,
  				.buf	= &start,
  			}, {
  				.addr	= DDC_ADDR,
  				.flags	= I2C_M_RD,
  				.len	= len,
  				.buf	= buf,
  			}
  		};
  		ret = i2c_transfer(adapter, msgs, 2);
  	} while (ret != 2 && --retries);
  
  	return ret == 2 ? 0 : -1;
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
273
  }
4a9a8b71e   Dave Airlie   drm/radeon: worka...
274
275
276
277
278
279
280
281
282
283
  static bool drm_edid_is_zero(u8 *in_edid, int length)
  {
  	int i;
  	u32 *raw_edid = (u32 *)in_edid;
  
  	for (i = 0; i < length / 4; i++)
  		if (*(raw_edid + i) != 0)
  			return false;
  	return true;
  }
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
284
285
286
  static u8 *
  drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
  {
0ea75e233   Sam Tygier   DRM: ignore inval...
287
  	int i, j = 0, valid_extensions = 0;
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
288
289
290
291
292
293
294
295
296
297
298
  	u8 *block, *new;
  
  	if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
  		return NULL;
  
  	/* base block fetch */
  	for (i = 0; i < 4; i++) {
  		if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
  			goto out;
  		if (drm_edid_block_valid(block))
  			break;
4a9a8b71e   Dave Airlie   drm/radeon: worka...
299
300
301
302
  		if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) {
  			connector->null_edid_counter++;
  			goto carp;
  		}
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  	}
  	if (i == 4)
  		goto carp;
  
  	/* if there's no extensions, we're done */
  	if (block[0x7e] == 0)
  		return block;
  
  	new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL);
  	if (!new)
  		goto out;
  	block = new;
  
  	for (j = 1; j <= block[0x7e]; j++) {
  		for (i = 0; i < 4; i++) {
0ea75e233   Sam Tygier   DRM: ignore inval...
318
319
320
  			if (drm_do_probe_ddc_edid(adapter,
  				  block + (valid_extensions + 1) * EDID_LENGTH,
  				  j, EDID_LENGTH))
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
321
  				goto out;
0ea75e233   Sam Tygier   DRM: ignore inval...
322
323
  			if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH)) {
  				valid_extensions++;
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
324
  				break;
0ea75e233   Sam Tygier   DRM: ignore inval...
325
  			}
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
326
327
  		}
  		if (i == 4)
0ea75e233   Sam Tygier   DRM: ignore inval...
328
329
330
331
332
333
334
335
336
337
338
339
340
  			dev_warn(connector->dev->dev,
  			 "%s: Ignoring invalid EDID block %d.
  ",
  			 drm_get_connector_name(connector), j);
  	}
  
  	if (valid_extensions != block[0x7e]) {
  		block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
  		block[0x7e] = valid_extensions;
  		new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL);
  		if (!new)
  			goto out;
  		block = new;
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
341
342
343
344
345
  	}
  
  	return block;
  
  carp:
dcdb16740   Jordan Crouse   drm: Add support ...
346
347
  	dev_warn(connector->dev->dev, "%s: EDID block %d invalid.
  ",
61e57a8d7   Adam Jackson   drm/edid: Fix sec...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  		 drm_get_connector_name(connector), j);
  
  out:
  	kfree(block);
  	return NULL;
  }
  
  /**
   * Probe DDC presence.
   *
   * \param adapter : i2c device adaptor
   * \return 1 on success
   */
  static bool
  drm_probe_ddc(struct i2c_adapter *adapter)
  {
  	unsigned char out;
  
  	return (drm_do_probe_ddc_edid(adapter, &out, 0, 1) == 0);
  }
  
  /**
   * drm_get_edid - get EDID data, if available
   * @connector: connector we're probing
   * @adapter: i2c adapter to use for DDC
   *
   * Poke the given i2c channel to grab EDID data if possible.  If found,
   * attach it to the connector.
   *
   * Return edid data or NULL if we couldn't find any.
   */
  struct edid *drm_get_edid(struct drm_connector *connector,
  			  struct i2c_adapter *adapter)
  {
  	struct edid *edid = NULL;
  
  	if (drm_probe_ddc(adapter))
  		edid = (struct edid *)drm_do_get_edid(connector, adapter);
  
  	connector->display_info.raw_edid = (char *)edid;
  
  	return edid;
  
  }
  EXPORT_SYMBOL(drm_get_edid);
  
  /*** EDID parsing ***/
f453ba046   Dave Airlie   DRM: add mode set...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
  /**
   * edid_vendor - match a string against EDID's obfuscated vendor field
   * @edid: EDID to match
   * @vendor: vendor string
   *
   * Returns true if @vendor is in @edid, false otherwise
   */
  static bool edid_vendor(struct edid *edid, char *vendor)
  {
  	char edid_vendor[3];
  
  	edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@';
  	edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) |
  			  ((edid->mfg_id[1] & 0xe0) >> 5)) + '@';
16456c872   Dave Airlie   drm: fix typo in ...
409
  	edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@';
f453ba046   Dave Airlie   DRM: add mode set...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
  
  	return !strncmp(edid_vendor, vendor, 3);
  }
  
  /**
   * edid_get_quirks - return quirk flags for a given EDID
   * @edid: EDID to process
   *
   * This tells subsequent routines what fixes they need to apply.
   */
  static u32 edid_get_quirks(struct edid *edid)
  {
  	struct edid_quirk *quirk;
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) {
  		quirk = &edid_quirk_list[i];
  
  		if (edid_vendor(edid, quirk->vendor) &&
  		    (EDID_PRODUCT_ID(edid) == quirk->product_id))
  			return quirk->quirks;
  	}
  
  	return 0;
  }
  
  #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
  #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh))
f453ba046   Dave Airlie   DRM: add mode set...
438
439
440
441
442
443
444
445
446
447
448
449
  /**
   * edid_fixup_preferred - set preferred modes based on quirk list
   * @connector: has mode list to fix up
   * @quirks: quirks list
   *
   * Walk the mode list for @connector, clearing the preferred status
   * on existing modes and setting it anew for the right mode ala @quirks.
   */
  static void edid_fixup_preferred(struct drm_connector *connector,
  				 u32 quirks)
  {
  	struct drm_display_mode *t, *cur_mode, *preferred_mode;
f890607b1   Dave Airlie   drm: fix useless ...
450
  	int target_refresh = 0;
f453ba046   Dave Airlie   DRM: add mode set...
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
  
  	if (list_empty(&connector->probed_modes))
  		return;
  
  	if (quirks & EDID_QUIRK_PREFER_LARGE_60)
  		target_refresh = 60;
  	if (quirks & EDID_QUIRK_PREFER_LARGE_75)
  		target_refresh = 75;
  
  	preferred_mode = list_first_entry(&connector->probed_modes,
  					  struct drm_display_mode, head);
  
  	list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) {
  		cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
  
  		if (cur_mode == preferred_mode)
  			continue;
  
  		/* Largest mode is preferred */
  		if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
  			preferred_mode = cur_mode;
  
  		/* At a given size, try to get closest to target refresh */
  		if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
  		    MODE_REFRESH_DIFF(cur_mode, target_refresh) <
  		    MODE_REFRESH_DIFF(preferred_mode, target_refresh)) {
  			preferred_mode = cur_mode;
  		}
  	}
  
  	preferred_mode->type |= DRM_MODE_TYPE_PREFERRED;
  }
1d42bbc8f   Dave Airlie   drm/fbdev: fix cl...
483
484
  struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
  					   int hsize, int vsize, int fresh)
559ee21d2   Zhao Yakui   drm/kms: try to f...
485
  {
b1f559ecd   Chris Wilson   drm: Mark constan...
486
  	struct drm_display_mode *mode = NULL;
07a5e6324   Adam Jackson   drm/edid: Add DMT...
487
  	int i;
559ee21d2   Zhao Yakui   drm/kms: try to f...
488

07a5e6324   Adam Jackson   drm/edid: Add DMT...
489
  	for (i = 0; i < drm_num_dmt_modes; i++) {
b1f559ecd   Chris Wilson   drm: Mark constan...
490
  		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
559ee21d2   Zhao Yakui   drm/kms: try to f...
491
492
493
494
495
496
497
498
499
500
  		if (hsize == ptr->hdisplay &&
  			vsize == ptr->vdisplay &&
  			fresh == drm_mode_vrefresh(ptr)) {
  			/* get the expected default mode */
  			mode = drm_mode_duplicate(dev, ptr);
  			break;
  		}
  	}
  	return mode;
  }
1d42bbc8f   Dave Airlie   drm/fbdev: fix cl...
501
  EXPORT_SYMBOL(drm_mode_find_dmt);
23425caee   Adam Jackson   drm/edid: Ignore ...
502

d1ff6409b   Adam Jackson   drm/edid: Add tes...
503
504
505
  typedef void detailed_cb(struct detailed_timing *timing, void *closure);
  
  static void
4d76a2213   Adam Jackson   drm/edid: Add det...
506
507
508
  cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
  {
  	int i, n = 0;
4966b2a93   Christian Schmidt   Fix wrong assumpt...
509
  	u8 d = ext[0x02];
4d76a2213   Adam Jackson   drm/edid: Add det...
510
  	u8 *det_base = ext + d;
4966b2a93   Christian Schmidt   Fix wrong assumpt...
511
  	n = (127 - d) / 18;
4d76a2213   Adam Jackson   drm/edid: Add det...
512
513
514
515
516
  	for (i = 0; i < n; i++)
  		cb((struct detailed_timing *)(det_base + 18 * i), closure);
  }
  
  static void
cbba98f8f   Adam Jackson   drm/edid: Add det...
517
518
519
520
521
522
523
524
525
526
527
528
529
  vtb_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
  {
  	unsigned int i, n = min((int)ext[0x02], 6);
  	u8 *det_base = ext + 5;
  
  	if (ext[0x01] != 1)
  		return; /* unknown version */
  
  	for (i = 0; i < n; i++)
  		cb((struct detailed_timing *)(det_base + 18 * i), closure);
  }
  
  static void
d1ff6409b   Adam Jackson   drm/edid: Add tes...
530
531
532
533
534
535
536
537
538
539
  drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure)
  {
  	int i;
  	struct edid *edid = (struct edid *)raw_edid;
  
  	if (edid == NULL)
  		return;
  
  	for (i = 0; i < EDID_DETAILED_TIMINGS; i++)
  		cb(&(edid->detailed_timings[i]), closure);
4d76a2213   Adam Jackson   drm/edid: Add det...
540
541
542
543
544
545
  	for (i = 1; i <= raw_edid[0x7e]; i++) {
  		u8 *ext = raw_edid + (i * EDID_LENGTH);
  		switch (*ext) {
  		case CEA_EXT:
  			cea_for_each_detailed_block(ext, cb, closure);
  			break;
cbba98f8f   Adam Jackson   drm/edid: Add det...
546
547
548
  		case VTB_EXT:
  			vtb_for_each_detailed_block(ext, cb, closure);
  			break;
4d76a2213   Adam Jackson   drm/edid: Add det...
549
550
551
552
  		default:
  			break;
  		}
  	}
d1ff6409b   Adam Jackson   drm/edid: Add tes...
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
  }
  
  static void
  is_rb(struct detailed_timing *t, void *data)
  {
  	u8 *r = (u8 *)t;
  	if (r[3] == EDID_DETAIL_MONITOR_RANGE)
  		if (r[15] & 0x10)
  			*(bool *)data = true;
  }
  
  /* EDID 1.4 defines this explicitly.  For EDID 1.3, we guess, badly. */
  static bool
  drm_monitor_supports_rb(struct edid *edid)
  {
  	if (edid->revision >= 4) {
  		bool ret;
  		drm_for_each_detailed_block((u8 *)edid, is_rb, &ret);
  		return ret;
  	}
  
  	return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0);
  }
7a3743500   Adam Jackson   drm/edid: Add sec...
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
  static void
  find_gtf2(struct detailed_timing *t, void *data)
  {
  	u8 *r = (u8 *)t;
  	if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02)
  		*(u8 **)data = r;
  }
  
  /* Secondary GTF curve kicks in above some break frequency */
  static int
  drm_gtf2_hbreak(struct edid *edid)
  {
  	u8 *r = NULL;
  	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
  	return r ? (r[12] * 2) : 0;
  }
  
  static int
  drm_gtf2_2c(struct edid *edid)
  {
  	u8 *r = NULL;
  	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
  	return r ? r[13] : 0;
  }
  
  static int
  drm_gtf2_m(struct edid *edid)
  {
  	u8 *r = NULL;
  	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
  	return r ? (r[15] << 8) + r[14] : 0;
  }
  
  static int
  drm_gtf2_k(struct edid *edid)
  {
  	u8 *r = NULL;
  	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
  	return r ? r[16] : 0;
  }
  
  static int
  drm_gtf2_2j(struct edid *edid)
  {
  	u8 *r = NULL;
  	drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r);
  	return r ? r[17] : 0;
  }
  
  /**
   * standard_timing_level - get std. timing level(CVT/GTF/DMT)
   * @edid: EDID block to scan
   */
  static int standard_timing_level(struct edid *edid)
  {
  	if (edid->revision >= 2) {
  		if (edid->revision >= 4 && (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
  			return LEVEL_CVT;
  		if (drm_gtf2_hbreak(edid))
  			return LEVEL_GTF2;
  		return LEVEL_GTF;
  	}
  	return LEVEL_DMT;
  }
23425caee   Adam Jackson   drm/edid: Ignore ...
640
641
642
643
644
645
646
647
648
649
650
  /*
   * 0 is reserved.  The spec says 0x01 fill for unused timings.  Some old
   * monitors fill with ascii space (0x20) instead.
   */
  static int
  bad_std_timing(u8 a, u8 b)
  {
  	return (a == 0x00 && b == 0x00) ||
  	       (a == 0x01 && b == 0x01) ||
  	       (a == 0x20 && b == 0x20);
  }
f453ba046   Dave Airlie   DRM: add mode set...
651
652
653
  /**
   * drm_mode_std - convert standard mode info (width, height, refresh) into mode
   * @t: standard timing params
5c61259e6   Zhao Yakui   drm/mode: get the...
654
   * @timing_level: standard timing level
f453ba046   Dave Airlie   DRM: add mode set...
655
656
   *
   * Take the standard timing params (in this case width, aspect, and refresh)
5c61259e6   Zhao Yakui   drm/mode: get the...
657
   * and convert them into a real mode using CVT/GTF/DMT.
f453ba046   Dave Airlie   DRM: add mode set...
658
   */
7ca6adb37   Adam Jackson   drm/edid: Strengt...
659
  static struct drm_display_mode *
7a3743500   Adam Jackson   drm/edid: Add sec...
660
661
  drm_mode_std(struct drm_connector *connector, struct edid *edid,
  	     struct std_timing *t, int revision)
f453ba046   Dave Airlie   DRM: add mode set...
662
  {
7ca6adb37   Adam Jackson   drm/edid: Strengt...
663
664
  	struct drm_device *dev = connector->dev;
  	struct drm_display_mode *m, *mode = NULL;
5c61259e6   Zhao Yakui   drm/mode: get the...
665
666
  	int hsize, vsize;
  	int vrefresh_rate;
0454beab0   Michel Dänzer   drm: EDID endiann...
667
668
  	unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
  		>> EDID_TIMING_ASPECT_SHIFT;
5c61259e6   Zhao Yakui   drm/mode: get the...
669
670
  	unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK)
  		>> EDID_TIMING_VFREQ_SHIFT;
7a3743500   Adam Jackson   drm/edid: Add sec...
671
  	int timing_level = standard_timing_level(edid);
5c61259e6   Zhao Yakui   drm/mode: get the...
672

23425caee   Adam Jackson   drm/edid: Ignore ...
673
674
  	if (bad_std_timing(t->hsize, t->vfreq_aspect))
  		return NULL;
5c61259e6   Zhao Yakui   drm/mode: get the...
675
676
677
678
679
  	/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
  	hsize = t->hsize * 8 + 248;
  	/* vrefresh_rate = vfreq + 60 */
  	vrefresh_rate = vfreq + 60;
  	/* the vdisplay is calculated based on the aspect ratio */
f066a17d9   Adam Jackson   drm/edid: Fix sta...
680
681
682
683
684
685
  	if (aspect_ratio == 0) {
  		if (revision < 3)
  			vsize = hsize;
  		else
  			vsize = (hsize * 10) / 16;
  	} else if (aspect_ratio == 1)
f453ba046   Dave Airlie   DRM: add mode set...
686
  		vsize = (hsize * 3) / 4;
0454beab0   Michel Dänzer   drm: EDID endiann...
687
  	else if (aspect_ratio == 2)
f453ba046   Dave Airlie   DRM: add mode set...
688
689
690
  		vsize = (hsize * 4) / 5;
  	else
  		vsize = (hsize * 9) / 16;
a0910c8e3   Adam Jackson   drm/edid: Fix the...
691
692
693
694
695
696
697
698
  
  	/* HDTV hack, part 1 */
  	if (vrefresh_rate == 60 &&
  	    ((hsize == 1360 && vsize == 765) ||
  	     (hsize == 1368 && vsize == 769))) {
  		hsize = 1366;
  		vsize = 768;
  	}
7ca6adb37   Adam Jackson   drm/edid: Strengt...
699
700
701
702
703
704
  	/*
  	 * If this connector already has a mode for this size and refresh
  	 * rate (because it came from detailed or CVT info), use that
  	 * instead.  This way we don't have to guess at interlace or
  	 * reduced blanking.
  	 */
522032da7   Adam Jackson   drm/edid: When ch...
705
  	list_for_each_entry(m, &connector->probed_modes, head)
7ca6adb37   Adam Jackson   drm/edid: Strengt...
706
707
708
  		if (m->hdisplay == hsize && m->vdisplay == vsize &&
  		    drm_mode_vrefresh(m) == vrefresh_rate)
  			return NULL;
a0910c8e3   Adam Jackson   drm/edid: Fix the...
709
710
711
  	/* HDTV hack, part 2 */
  	if (hsize == 1366 && vsize == 768 && vrefresh_rate == 60) {
  		mode = drm_cvt_mode(dev, 1366, 768, vrefresh_rate, 0, 0,
d50ba256b   Dave Airlie   drm/kms: start ad...
712
  				    false);
559ee21d2   Zhao Yakui   drm/kms: try to f...
713
  		mode->hdisplay = 1366;
a4967de6c   Adam Jackson   drm/edid: Fix the...
714
715
  		mode->hsync_start = mode->hsync_start - 1;
  		mode->hsync_end = mode->hsync_end - 1;
559ee21d2   Zhao Yakui   drm/kms: try to f...
716
717
  		return mode;
  	}
a0910c8e3   Adam Jackson   drm/edid: Fix the...
718

559ee21d2   Zhao Yakui   drm/kms: try to f...
719
  	/* check whether it can be found in default mode table */
1d42bbc8f   Dave Airlie   drm/fbdev: fix cl...
720
  	mode = drm_mode_find_dmt(dev, hsize, vsize, vrefresh_rate);
559ee21d2   Zhao Yakui   drm/kms: try to f...
721
722
  	if (mode)
  		return mode;
5c61259e6   Zhao Yakui   drm/mode: get the...
723
724
  	switch (timing_level) {
  	case LEVEL_DMT:
5c61259e6   Zhao Yakui   drm/mode: get the...
725
726
727
728
  		break;
  	case LEVEL_GTF:
  		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
  		break;
7a3743500   Adam Jackson   drm/edid: Add sec...
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
  	case LEVEL_GTF2:
  		/*
  		 * This is potentially wrong if there's ever a monitor with
  		 * more than one ranges section, each claiming a different
  		 * secondary GTF curve.  Please don't do that.
  		 */
  		mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
  		if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
  			kfree(mode);
  			mode = drm_gtf_mode_complex(dev, hsize, vsize,
  						    vrefresh_rate, 0, 0,
  						    drm_gtf2_m(edid),
  						    drm_gtf2_2c(edid),
  						    drm_gtf2_k(edid),
  						    drm_gtf2_2j(edid));
  		}
  		break;
5c61259e6   Zhao Yakui   drm/mode: get the...
746
  	case LEVEL_CVT:
d50ba256b   Dave Airlie   drm/kms: start ad...
747
748
  		mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0,
  				    false);
5c61259e6   Zhao Yakui   drm/mode: get the...
749
750
  		break;
  	}
f453ba046   Dave Airlie   DRM: add mode set...
751
752
  	return mode;
  }
b58db2c6d   Adam Jackson   drm/edid: Fix int...
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
  /*
   * EDID is delightfully ambiguous about how interlaced modes are to be
   * encoded.  Our internal representation is of frame height, but some
   * HDTV detailed timings are encoded as field height.
   *
   * The format list here is from CEA, in frame size.  Technically we
   * should be checking refresh rate too.  Whatever.
   */
  static void
  drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
  			    struct detailed_pixel_timing *pt)
  {
  	int i;
  	static const struct {
  		int w, h;
  	} cea_interlaced[] = {
  		{ 1920, 1080 },
  		{  720,  480 },
  		{ 1440,  480 },
  		{ 2880,  480 },
  		{  720,  576 },
  		{ 1440,  576 },
  		{ 2880,  576 },
  	};
b58db2c6d   Adam Jackson   drm/edid: Fix int...
777
778
779
  
  	if (!(pt->misc & DRM_EDID_PT_INTERLACED))
  		return;
3c5814116   Kulikov Vasiliy   drm: drm_edid: us...
780
  	for (i = 0; i < ARRAY_SIZE(cea_interlaced); i++) {
b58db2c6d   Adam Jackson   drm/edid: Fix int...
781
782
783
784
785
786
787
788
789
790
791
792
  		if ((mode->hdisplay == cea_interlaced[i].w) &&
  		    (mode->vdisplay == cea_interlaced[i].h / 2)) {
  			mode->vdisplay *= 2;
  			mode->vsync_start *= 2;
  			mode->vsync_end *= 2;
  			mode->vtotal *= 2;
  			mode->vtotal |= 1;
  		}
  	}
  
  	mode->flags |= DRM_MODE_FLAG_INTERLACE;
  }
f453ba046   Dave Airlie   DRM: add mode set...
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
  /**
   * drm_mode_detailed - create a new mode from an EDID detailed timing section
   * @dev: DRM device (needed to create new mode)
   * @edid: EDID block
   * @timing: EDID detailed timing info
   * @quirks: quirks to apply
   *
   * An EDID detailed timing block contains enough info for us to create and
   * return a new struct drm_display_mode.
   */
  static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
  						  struct edid *edid,
  						  struct detailed_timing *timing,
  						  u32 quirks)
  {
  	struct drm_display_mode *mode;
  	struct detailed_pixel_timing *pt = &timing->data.pixel_data;
0454beab0   Michel Dänzer   drm: EDID endiann...
810
811
812
813
  	unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo;
  	unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo;
  	unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo;
  	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;
e14cbee40   Michel Dänzer   drm: Fix shifts w...
814
815
816
817
  	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;
  	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo;
  	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4;
  	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);
f453ba046   Dave Airlie   DRM: add mode set...
818

fc4389663   Adam Jackson   drm: ignore EDID ...
819
  	/* ignore tiny modes */
0454beab0   Michel Dänzer   drm: EDID endiann...
820
  	if (hactive < 64 || vactive < 64)
fc4389663   Adam Jackson   drm: ignore EDID ...
821
  		return NULL;
0454beab0   Michel Dänzer   drm: EDID endiann...
822
  	if (pt->misc & DRM_EDID_PT_STEREO) {
f453ba046   Dave Airlie   DRM: add mode set...
823
824
825
826
  		printk(KERN_WARNING "stereo mode not supported
  ");
  		return NULL;
  	}
0454beab0   Michel Dänzer   drm: EDID endiann...
827
  	if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
79b7dcb2a   Jerome Glisse   drm: EDID accept ...
828
829
  		printk(KERN_WARNING "composite sync not supported
  ");
f453ba046   Dave Airlie   DRM: add mode set...
830
  	}
fcb456114   Zhao Yakui   drm: Add the basi...
831
832
833
834
835
836
837
  	/* it is incorrect if hsync/vsync width is zero */
  	if (!hsync_pulse_width || !vsync_pulse_width) {
  		DRM_DEBUG_KMS("Incorrect Detailed timing. "
  				"Wrong Hsync/Vsync pulse width
  ");
  		return NULL;
  	}
f453ba046   Dave Airlie   DRM: add mode set...
838
839
840
841
842
843
844
  	mode = drm_mode_create(dev);
  	if (!mode)
  		return NULL;
  
  	mode->type = DRM_MODE_TYPE_DRIVER;
  
  	if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH)
0454beab0   Michel Dänzer   drm: EDID endiann...
845
846
847
848
849
850
851
852
853
854
855
856
857
  		timing->pixel_clock = cpu_to_le16(1088);
  
  	mode->clock = le16_to_cpu(timing->pixel_clock) * 10;
  
  	mode->hdisplay = hactive;
  	mode->hsync_start = mode->hdisplay + hsync_offset;
  	mode->hsync_end = mode->hsync_start + hsync_pulse_width;
  	mode->htotal = mode->hdisplay + hblank;
  
  	mode->vdisplay = vactive;
  	mode->vsync_start = mode->vdisplay + vsync_offset;
  	mode->vsync_end = mode->vsync_start + vsync_pulse_width;
  	mode->vtotal = mode->vdisplay + vblank;
f453ba046   Dave Airlie   DRM: add mode set...
858

7064fef56   Jesse Barnes   drm: work around ...
859
860
861
862
863
  	/* Some EDIDs have bogus h/vtotal values */
  	if (mode->hsync_end > mode->htotal)
  		mode->htotal = mode->hsync_end + 1;
  	if (mode->vsync_end > mode->vtotal)
  		mode->vtotal = mode->vsync_end + 1;
b58db2c6d   Adam Jackson   drm/edid: Fix int...
864
  	drm_mode_do_interlace_quirk(mode, pt);
f453ba046   Dave Airlie   DRM: add mode set...
865

171fdd892   Adam Jackson   drm/modes: Fix in...
866
  	drm_mode_set_name(mode);
f453ba046   Dave Airlie   DRM: add mode set...
867
  	if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
0454beab0   Michel Dänzer   drm: EDID endiann...
868
  		pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
f453ba046   Dave Airlie   DRM: add mode set...
869
  	}
0454beab0   Michel Dänzer   drm: EDID endiann...
870
871
872
873
  	mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ?
  		DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
  	mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ?
  		DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
f453ba046   Dave Airlie   DRM: add mode set...
874

e14cbee40   Michel Dänzer   drm: Fix shifts w...
875
876
  	mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4;
  	mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8;
f453ba046   Dave Airlie   DRM: add mode set...
877
878
879
880
881
882
883
884
885
886
887
888
889
  
  	if (quirks & EDID_QUIRK_DETAILED_IN_CM) {
  		mode->width_mm *= 10;
  		mode->height_mm *= 10;
  	}
  
  	if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) {
  		mode->width_mm = edid->width_cm * 10;
  		mode->height_mm = edid->height_cm * 10;
  	}
  
  	return mode;
  }
07a5e6324   Adam Jackson   drm/edid: Add DMT...
890
  static bool
b1f559ecd   Chris Wilson   drm: Mark constan...
891
  mode_is_rb(const struct drm_display_mode *mode)
07a5e6324   Adam Jackson   drm/edid: Add DMT...
892
  {
b17e52ef7   Adam Jackson   drm/edid: Extend ...
893
894
895
896
897
  	return (mode->htotal - mode->hdisplay == 160) &&
  	       (mode->hsync_end - mode->hdisplay == 80) &&
  	       (mode->hsync_end - mode->hsync_start == 32) &&
  	       (mode->vsync_start - mode->vdisplay == 3);
  }
07a5e6324   Adam Jackson   drm/edid: Add DMT...
898

b17e52ef7   Adam Jackson   drm/edid: Extend ...
899
  static bool
b1f559ecd   Chris Wilson   drm: Mark constan...
900
901
  mode_in_hsync_range(const struct drm_display_mode *mode,
  		    struct edid *edid, u8 *t)
b17e52ef7   Adam Jackson   drm/edid: Extend ...
902
903
904
905
906
907
908
909
910
  {
  	int hsync, hmin, hmax;
  
  	hmin = t[7];
  	if (edid->revision >= 4)
  	    hmin += ((t[4] & 0x04) ? 255 : 0);
  	hmax = t[8];
  	if (edid->revision >= 4)
  	    hmax += ((t[4] & 0x08) ? 255 : 0);
07a5e6324   Adam Jackson   drm/edid: Add DMT...
911
  	hsync = drm_mode_hsync(mode);
07a5e6324   Adam Jackson   drm/edid: Add DMT...
912

b17e52ef7   Adam Jackson   drm/edid: Extend ...
913
914
915
916
  	return (hsync <= hmax && hsync >= hmin);
  }
  
  static bool
b1f559ecd   Chris Wilson   drm: Mark constan...
917
918
  mode_in_vsync_range(const struct drm_display_mode *mode,
  		    struct edid *edid, u8 *t)
b17e52ef7   Adam Jackson   drm/edid: Extend ...
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
  {
  	int vsync, vmin, vmax;
  
  	vmin = t[5];
  	if (edid->revision >= 4)
  	    vmin += ((t[4] & 0x01) ? 255 : 0);
  	vmax = t[6];
  	if (edid->revision >= 4)
  	    vmax += ((t[4] & 0x02) ? 255 : 0);
  	vsync = drm_mode_vrefresh(mode);
  
  	return (vsync <= vmax && vsync >= vmin);
  }
  
  static u32
  range_pixel_clock(struct edid *edid, u8 *t)
  {
  	/* unspecified */
  	if (t[9] == 0 || t[9] == 255)
  		return 0;
  
  	/* 1.4 with CVT support gives us real precision, yay */
  	if (edid->revision >= 4 && t[10] == 0x04)
  		return (t[9] * 10000) - ((t[12] >> 2) * 250);
  
  	/* 1.3 is pathetic, so fuzz up a bit */
  	return t[9] * 10000 + 5001;
  }
b17e52ef7   Adam Jackson   drm/edid: Extend ...
947
  static bool
b1f559ecd   Chris Wilson   drm: Mark constan...
948
  mode_in_range(const struct drm_display_mode *mode, struct edid *edid,
b17e52ef7   Adam Jackson   drm/edid: Extend ...
949
950
951
952
953
954
  	      struct detailed_timing *timing)
  {
  	u32 max_clock;
  	u8 *t = (u8 *)timing;
  
  	if (!mode_in_hsync_range(mode, edid, t))
07a5e6324   Adam Jackson   drm/edid: Add DMT...
955
  		return false;
b17e52ef7   Adam Jackson   drm/edid: Extend ...
956
  	if (!mode_in_vsync_range(mode, edid, t))
07a5e6324   Adam Jackson   drm/edid: Add DMT...
957
  		return false;
b17e52ef7   Adam Jackson   drm/edid: Extend ...
958
  	if ((max_clock = range_pixel_clock(edid, t)))
07a5e6324   Adam Jackson   drm/edid: Add DMT...
959
960
  		if (mode->clock > max_clock)
  			return false;
b17e52ef7   Adam Jackson   drm/edid: Extend ...
961
962
963
964
965
966
967
968
  
  	/* 1.4 max horizontal check */
  	if (edid->revision >= 4 && t[10] == 0x04)
  		if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3))))
  			return false;
  
  	if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid))
  		return false;
07a5e6324   Adam Jackson   drm/edid: Add DMT...
969
970
971
972
973
974
975
976
  
  	return true;
  }
  
  /*
   * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
   * need to account for them.
   */
b17e52ef7   Adam Jackson   drm/edid: Extend ...
977
978
979
  static int
  drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
  			struct detailed_timing *timing)
07a5e6324   Adam Jackson   drm/edid: Add DMT...
980
981
982
983
984
985
  {
  	int i, modes = 0;
  	struct drm_display_mode *newmode;
  	struct drm_device *dev = connector->dev;
  
  	for (i = 0; i < drm_num_dmt_modes; i++) {
b17e52ef7   Adam Jackson   drm/edid: Extend ...
986
  		if (mode_in_range(drm_dmt_modes + i, edid, timing)) {
07a5e6324   Adam Jackson   drm/edid: Add DMT...
987
988
989
990
991
992
993
994
995
996
  			newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
  			if (newmode) {
  				drm_mode_probed_add(connector, newmode);
  				modes++;
  			}
  		}
  	}
  
  	return modes;
  }
139315796   Adam Jackson   drm/edid: Rewrite...
997
998
  static void
  do_inferred_modes(struct detailed_timing *timing, void *c)
9340d8cfe   Adam Jackson   drm/edid: Decode ...
999
  {
139315796   Adam Jackson   drm/edid: Rewrite...
1000
1001
1002
  	struct detailed_mode_closure *closure = c;
  	struct detailed_non_pixel *data = &timing->data.other_data;
  	int gtf = (closure->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
9340d8cfe   Adam Jackson   drm/edid: Decode ...
1003

139315796   Adam Jackson   drm/edid: Rewrite...
1004
1005
1006
1007
1008
  	if (gtf && data->type == EDID_DETAIL_MONITOR_RANGE)
  		closure->modes += drm_gtf_modes_for_range(closure->connector,
  							  closure->edid,
  							  timing);
  }
69da30158   Adam Jackson   drm/edid: Skip em...
1009

139315796   Adam Jackson   drm/edid: Rewrite...
1010
1011
1012
1013
1014
1015
  static int
  add_inferred_modes(struct drm_connector *connector, struct edid *edid)
  {
  	struct detailed_mode_closure closure = {
  		connector, edid, 0, 0, 0
  	};
9340d8cfe   Adam Jackson   drm/edid: Decode ...
1016

139315796   Adam Jackson   drm/edid: Rewrite...
1017
1018
1019
  	if (version_greater(edid, 1, 0))
  		drm_for_each_detailed_block((u8 *)edid, do_inferred_modes,
  					    &closure);
9340d8cfe   Adam Jackson   drm/edid: Decode ...
1020

139315796   Adam Jackson   drm/edid: Rewrite...
1021
  	return closure.modes;
9340d8cfe   Adam Jackson   drm/edid: Decode ...
1022
  }
2255be14c   Adam Jackson   drm/edid: Add mod...
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
  static int
  drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
  {
  	int i, j, m, modes = 0;
  	struct drm_display_mode *mode;
  	u8 *est = ((u8 *)timing) + 5;
  
  	for (i = 0; i < 6; i++) {
  		for (j = 7; j > 0; j--) {
  			m = (i * 8) + (7 - j);
3c5814116   Kulikov Vasiliy   drm: drm_edid: us...
1033
  			if (m >= ARRAY_SIZE(est3_modes))
2255be14c   Adam Jackson   drm/edid: Add mod...
1034
1035
  				break;
  			if (est[i] & (1 << j)) {
1d42bbc8f   Dave Airlie   drm/fbdev: fix cl...
1036
1037
1038
1039
1040
  				mode = drm_mode_find_dmt(connector->dev,
  							 est3_modes[m].w,
  							 est3_modes[m].h,
  							 est3_modes[m].r
  							 /*, est3_modes[m].rb */);
2255be14c   Adam Jackson   drm/edid: Add mod...
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
  				if (mode) {
  					drm_mode_probed_add(connector, mode);
  					modes++;
  				}
  			}
  		}
  	}
  
  	return modes;
  }
139315796   Adam Jackson   drm/edid: Rewrite...
1051
1052
  static void
  do_established_modes(struct detailed_timing *timing, void *c)
9cf00977d   Adam Jackson   drm/edid: Unify d...
1053
  {
139315796   Adam Jackson   drm/edid: Rewrite...
1054
  	struct detailed_mode_closure *closure = c;
9cf00977d   Adam Jackson   drm/edid: Unify d...
1055
  	struct detailed_non_pixel *data = &timing->data.other_data;
9cf00977d   Adam Jackson   drm/edid: Unify d...
1056

139315796   Adam Jackson   drm/edid: Rewrite...
1057
1058
1059
  	if (data->type == EDID_DETAIL_EST_TIMINGS)
  		closure->modes += drm_est3_modes(closure->connector, timing);
  }
9cf00977d   Adam Jackson   drm/edid: Unify d...
1060

139315796   Adam Jackson   drm/edid: Rewrite...
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
  /**
   * add_established_modes - get est. modes from EDID and add them
   * @edid: EDID block to scan
   *
   * Each EDID block contains a bitmap of the supported "established modes" list
   * (defined above).  Tease them out and add them to the global modes list.
   */
  static int
  add_established_modes(struct drm_connector *connector, struct edid *edid)
  {
  	struct drm_device *dev = connector->dev;
  	unsigned long est_bits = edid->established_timings.t1 |
  		(edid->established_timings.t2 << 8) |
  		((edid->established_timings.mfg_rsvd & 0x80) << 9);
  	int i, modes = 0;
  	struct detailed_mode_closure closure = {
  		connector, edid, 0, 0, 0
  	};
9cf00977d   Adam Jackson   drm/edid: Unify d...
1079

139315796   Adam Jackson   drm/edid: Rewrite...
1080
1081
1082
1083
1084
1085
1086
1087
1088
  	for (i = 0; i <= EDID_EST_TIMINGS; i++) {
  		if (est_bits & (1<<i)) {
  			struct drm_display_mode *newmode;
  			newmode = drm_mode_duplicate(dev, &edid_est_modes[i]);
  			if (newmode) {
  				drm_mode_probed_add(connector, newmode);
  				modes++;
  			}
  		}
9cf00977d   Adam Jackson   drm/edid: Unify d...
1089
  	}
139315796   Adam Jackson   drm/edid: Rewrite...
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
  	if (version_greater(edid, 1, 0))
  		    drm_for_each_detailed_block((u8 *)edid,
  						do_established_modes, &closure);
  
  	return modes + closure.modes;
  }
  
  static void
  do_standard_modes(struct detailed_timing *timing, void *c)
  {
  	struct detailed_mode_closure *closure = c;
  	struct detailed_non_pixel *data = &timing->data.other_data;
  	struct drm_connector *connector = closure->connector;
  	struct edid *edid = closure->edid;
  
  	if (data->type == EDID_DETAIL_STD_MODES) {
  		int i;
9cf00977d   Adam Jackson   drm/edid: Unify d...
1107
1108
1109
1110
1111
  		for (i = 0; i < 6; i++) {
  			struct std_timing *std;
  			struct drm_display_mode *newmode;
  
  			std = &data->data.timings[i];
7a3743500   Adam Jackson   drm/edid: Add sec...
1112
1113
  			newmode = drm_mode_std(connector, edid, std,
  					       edid->revision);
9cf00977d   Adam Jackson   drm/edid: Unify d...
1114
1115
  			if (newmode) {
  				drm_mode_probed_add(connector, newmode);
139315796   Adam Jackson   drm/edid: Rewrite...
1116
  				closure->modes++;
9cf00977d   Adam Jackson   drm/edid: Unify d...
1117
1118
  			}
  		}
9cf00977d   Adam Jackson   drm/edid: Unify d...
1119
  	}
9cf00977d   Adam Jackson   drm/edid: Unify d...
1120
  }
f453ba046   Dave Airlie   DRM: add mode set...
1121
  /**
139315796   Adam Jackson   drm/edid: Rewrite...
1122
   * add_standard_modes - get std. modes from EDID and add them
f453ba046   Dave Airlie   DRM: add mode set...
1123
   * @edid: EDID block to scan
f453ba046   Dave Airlie   DRM: add mode set...
1124
   *
139315796   Adam Jackson   drm/edid: Rewrite...
1125
1126
   * Standard modes can be calculated using the appropriate standard (DMT,
   * GTF or CVT. Grab them from @edid and add them to the list.
f453ba046   Dave Airlie   DRM: add mode set...
1127
   */
139315796   Adam Jackson   drm/edid: Rewrite...
1128
1129
  static int
  add_standard_modes(struct drm_connector *connector, struct edid *edid)
f453ba046   Dave Airlie   DRM: add mode set...
1130
  {
9cf00977d   Adam Jackson   drm/edid: Unify d...
1131
  	int i, modes = 0;
139315796   Adam Jackson   drm/edid: Rewrite...
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
  	struct detailed_mode_closure closure = {
  		connector, edid, 0, 0, 0
  	};
  
  	for (i = 0; i < EDID_STD_TIMINGS; i++) {
  		struct drm_display_mode *newmode;
  
  		newmode = drm_mode_std(connector, edid,
  				       &edid->standard_timings[i],
  				       edid->revision);
  		if (newmode) {
  			drm_mode_probed_add(connector, newmode);
  			modes++;
  		}
  	}
  
  	if (version_greater(edid, 1, 0))
  		drm_for_each_detailed_block((u8 *)edid, do_standard_modes,
  					    &closure);
  
  	/* XXX should also look for standard codes in VTB blocks */
  
  	return modes + closure.modes;
  }
f453ba046   Dave Airlie   DRM: add mode set...
1156

139315796   Adam Jackson   drm/edid: Rewrite...
1157
1158
1159
1160
1161
1162
1163
1164
1165
  static int drm_cvt_modes(struct drm_connector *connector,
  			 struct detailed_timing *timing)
  {
  	int i, j, modes = 0;
  	struct drm_display_mode *newmode;
  	struct drm_device *dev = connector->dev;
  	struct cvt_timing *cvt;
  	const int rates[] = { 60, 85, 75, 60, 50 };
  	const u8 empty[3] = { 0, 0, 0 };
a327f6b80   Adam Jackson   drm/edid: Fix pre...
1166

139315796   Adam Jackson   drm/edid: Rewrite...
1167
1168
1169
  	for (i = 0; i < 4; i++) {
  		int uninitialized_var(width), height;
  		cvt = &(timing->data.other_data.data.cvt[i]);
f453ba046   Dave Airlie   DRM: add mode set...
1170

139315796   Adam Jackson   drm/edid: Rewrite...
1171
  		if (!memcmp(cvt->code, empty, 3))
9cf00977d   Adam Jackson   drm/edid: Unify d...
1172
  			continue;
f453ba046   Dave Airlie   DRM: add mode set...
1173

139315796   Adam Jackson   drm/edid: Rewrite...
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
  		height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
  		switch (cvt->code[1] & 0x0c) {
  		case 0x00:
  			width = height * 4 / 3;
  			break;
  		case 0x04:
  			width = height * 16 / 9;
  			break;
  		case 0x08:
  			width = height * 16 / 10;
  			break;
  		case 0x0c:
  			width = height * 15 / 9;
  			break;
  		}
  
  		for (j = 1; j < 5; j++) {
  			if (cvt->code[2] & (1 << j)) {
  				newmode = drm_cvt_mode(dev, width, height,
  						       rates[j], j == 0,
  						       false, false);
  				if (newmode) {
  					drm_mode_probed_add(connector, newmode);
  					modes++;
  				}
  			}
  		}
f453ba046   Dave Airlie   DRM: add mode set...
1201
1202
1203
1204
  	}
  
  	return modes;
  }
9cf00977d   Adam Jackson   drm/edid: Unify d...
1205

139315796   Adam Jackson   drm/edid: Rewrite...
1206
1207
  static void
  do_cvt_mode(struct detailed_timing *timing, void *c)
882f02195   Zhao Yakui   drm/kms: Parse th...
1208
  {
139315796   Adam Jackson   drm/edid: Rewrite...
1209
1210
  	struct detailed_mode_closure *closure = c;
  	struct detailed_non_pixel *data = &timing->data.other_data;
882f02195   Zhao Yakui   drm/kms: Parse th...
1211

139315796   Adam Jackson   drm/edid: Rewrite...
1212
1213
1214
  	if (data->type == EDID_DETAIL_CVT_3BYTE)
  		closure->modes += drm_cvt_modes(closure->connector, timing);
  }
882f02195   Zhao Yakui   drm/kms: Parse th...
1215

139315796   Adam Jackson   drm/edid: Rewrite...
1216
1217
1218
1219
1220
1221
  static int
  add_cvt_modes(struct drm_connector *connector, struct edid *edid)
  {	
  	struct detailed_mode_closure closure = {
  		connector, edid, 0, 0, 0
  	};
882f02195   Zhao Yakui   drm/kms: Parse th...
1222

139315796   Adam Jackson   drm/edid: Rewrite...
1223
1224
  	if (version_greater(edid, 1, 2))
  		drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure);
882f02195   Zhao Yakui   drm/kms: Parse th...
1225

139315796   Adam Jackson   drm/edid: Rewrite...
1226
  	/* XXX should also look for CVT codes in VTB blocks */
882f02195   Zhao Yakui   drm/kms: Parse th...
1227

139315796   Adam Jackson   drm/edid: Rewrite...
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
  	return closure.modes;
  }
  
  static void
  do_detailed_mode(struct detailed_timing *timing, void *c)
  {
  	struct detailed_mode_closure *closure = c;
  	struct drm_display_mode *newmode;
  
  	if (timing->pixel_clock) {
  		newmode = drm_mode_detailed(closure->connector->dev,
  					    closure->edid, timing,
  					    closure->quirks);
  		if (!newmode)
  			return;
  
  		if (closure->preferred)
  			newmode->type |= DRM_MODE_TYPE_PREFERRED;
  
  		drm_mode_probed_add(closure->connector, newmode);
  		closure->modes++;
  		closure->preferred = 0;
882f02195   Zhao Yakui   drm/kms: Parse th...
1250
  	}
139315796   Adam Jackson   drm/edid: Rewrite...
1251
  }
882f02195   Zhao Yakui   drm/kms: Parse th...
1252

139315796   Adam Jackson   drm/edid: Rewrite...
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
  /*
   * add_detailed_modes - Add modes from detailed timings
   * @connector: attached connector
   * @edid: EDID block to scan
   * @quirks: quirks to apply
   */
  static int
  add_detailed_modes(struct drm_connector *connector, struct edid *edid,
  		   u32 quirks)
  {
  	struct detailed_mode_closure closure = {
  		connector,
  		edid,
  		1,
  		quirks,
  		0
  	};
  
  	if (closure.preferred && !version_greater(edid, 1, 3))
  		closure.preferred =
  		    (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
  
  	drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure);
  
  	return closure.modes;
882f02195   Zhao Yakui   drm/kms: Parse th...
1278
  }
f453ba046   Dave Airlie   DRM: add mode set...
1279

f23c20c83   Ma Ling   drm: detect hdmi ...
1280
  #define HDMI_IDENTIFIER 0x000C03
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1281
  #define AUDIO_BLOCK	0x01
54ac76f85   Christian Schmidt   drm/edid: support...
1282
  #define VIDEO_BLOCK     0x02
f23c20c83   Ma Ling   drm: detect hdmi ...
1283
  #define VENDOR_BLOCK    0x03
76adaa34d   Wu Fengguang   drm: support rout...
1284
  #define SPEAKER_BLOCK	0x04
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1285
  #define EDID_BASIC_AUDIO	(1 << 6)
f23c20c83   Ma Ling   drm: detect hdmi ...
1286
  /**
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1287
   * Search EDID for CEA extension block.
f23c20c83   Ma Ling   drm: detect hdmi ...
1288
   */
eccaca28e   Ben Skeggs   drm: export drm_f...
1289
  u8 *drm_find_cea_extension(struct edid *edid)
f23c20c83   Ma Ling   drm: detect hdmi ...
1290
  {
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1291
1292
  	u8 *edid_ext = NULL;
  	int i;
f23c20c83   Ma Ling   drm: detect hdmi ...
1293
1294
1295
  
  	/* No EDID or EDID extensions */
  	if (edid == NULL || edid->extensions == 0)
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1296
  		return NULL;
f23c20c83   Ma Ling   drm: detect hdmi ...
1297

f23c20c83   Ma Ling   drm: detect hdmi ...
1298
  	/* Find CEA extension */
7466f4cc5   Adam Jackson   drm/edid: Remove ...
1299
  	for (i = 0; i < edid->extensions; i++) {
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1300
1301
  		edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
  		if (edid_ext[0] == CEA_EXT)
f23c20c83   Ma Ling   drm: detect hdmi ...
1302
1303
  			break;
  	}
7466f4cc5   Adam Jackson   drm/edid: Remove ...
1304
  	if (i == edid->extensions)
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1305
1306
1307
1308
  		return NULL;
  
  	return edid_ext;
  }
eccaca28e   Ben Skeggs   drm: export drm_f...
1309
  EXPORT_SYMBOL(drm_find_cea_extension);
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1310

54ac76f85   Christian Schmidt   drm/edid: support...
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
  static int
  do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
  {
  	struct drm_device *dev = connector->dev;
  	u8 * mode, cea_mode;
  	int modes = 0;
  
  	for (mode = db; mode < db + len; mode++) {
  		cea_mode = (*mode & 127) - 1; /* CEA modes are numbered 1..127 */
  		if (cea_mode < drm_num_cea_modes) {
  			struct drm_display_mode *newmode;
  			newmode = drm_mode_duplicate(dev,
  						     &edid_cea_modes[cea_mode]);
  			if (newmode) {
  				drm_mode_probed_add(connector, newmode);
  				modes++;
  			}
  		}
  	}
  
  	return modes;
  }
  
  static int
  add_cea_modes(struct drm_connector *connector, struct edid *edid)
  {
  	u8 * cea = drm_find_cea_extension(edid);
  	u8 * db, dbl;
  	int modes = 0;
  
  	if (cea && cea[1] >= 3) {
  		for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
  			dbl = db[0] & 0x1f;
  			if (((db[0] & 0xe0) >> 5) == VIDEO_BLOCK)
  				modes += do_cea_modes (connector, db+1, dbl);
  		}
  	}
  
  	return modes;
  }
76adaa34d   Wu Fengguang   drm: support rout...
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
  static void
  parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
  {
  	connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
  
  	connector->dvi_dual = db[6] & 1;
  	connector->max_tmds_clock = db[7] * 5;
  
  	connector->latency_present[0] = db[8] >> 7;
  	connector->latency_present[1] = (db[8] >> 6) & 1;
  	connector->video_latency[0] = db[9];
  	connector->audio_latency[0] = db[10];
  	connector->video_latency[1] = db[11];
  	connector->audio_latency[1] = db[12];
  
  	DRM_LOG_KMS("HDMI: DVI dual %d, "
  		    "max TMDS clock %d, "
  		    "latency present %d %d, "
  		    "video latency %d %d, "
  		    "audio latency %d %d
  ",
  		    connector->dvi_dual,
  		    connector->max_tmds_clock,
  	      (int) connector->latency_present[0],
  	      (int) connector->latency_present[1],
  		    connector->video_latency[0],
  		    connector->video_latency[1],
  		    connector->audio_latency[0],
  		    connector->audio_latency[1]);
  }
  
  static void
  monitor_name(struct detailed_timing *t, void *data)
  {
  	if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
  		*(u8 **)data = t->data.other_data.data.str.str;
  }
  
  /**
   * drm_edid_to_eld - build ELD from EDID
   * @connector: connector corresponding to the HDMI/DP sink
   * @edid: EDID to parse
   *
   * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
   * Some ELD fields are left to the graphics driver caller:
   * - Conn_Type
   * - HDCP
   * - Port_ID
   */
  void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
  {
  	uint8_t *eld = connector->eld;
  	u8 *cea;
  	u8 *name;
  	u8 *db;
  	int sad_count = 0;
  	int mnl;
  	int dbl;
  
  	memset(eld, 0, sizeof(connector->eld));
  
  	cea = drm_find_cea_extension(edid);
  	if (!cea) {
  		DRM_DEBUG_KMS("ELD: no CEA Extension found
  ");
  		return;
  	}
  
  	name = NULL;
  	drm_for_each_detailed_block((u8 *)edid, monitor_name, &name);
  	for (mnl = 0; name && mnl < 13; mnl++) {
  		if (name[mnl] == 0x0a)
  			break;
  		eld[20 + mnl] = name[mnl];
  	}
  	eld[4] = (cea[1] << 5) | mnl;
  	DRM_DEBUG_KMS("ELD monitor %s
  ", eld + 20);
  
  	eld[0] = 2 << 3;		/* ELD version: 2 */
  
  	eld[16] = edid->mfg_id[0];
  	eld[17] = edid->mfg_id[1];
  	eld[18] = edid->prod_code[0];
  	eld[19] = edid->prod_code[1];
a0ab734d6   Christian Schmidt   drm_edid_to_eld: ...
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
  	if (cea[1] >= 3)
  		for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
  			dbl = db[0] & 0x1f;
  			
  			switch ((db[0] & 0xe0) >> 5) {
  			case AUDIO_BLOCK:
  				/* Audio Data Block, contains SADs */
  				sad_count = dbl / 3;
  				memcpy(eld + 20 + mnl, &db[1], dbl);
  				break;
  			case SPEAKER_BLOCK:
                                  /* Speaker Allocation Data Block */
  				eld[7] = db[1];
  				break;
  			case VENDOR_BLOCK:
  				/* HDMI Vendor-Specific Data Block */
  				if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
  					parse_hdmi_vsdb(connector, db);
  				break;
  			default:
  				break;
  			}
76adaa34d   Wu Fengguang   drm: support rout...
1458
  		}
76adaa34d   Wu Fengguang   drm: support rout...
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
  	eld[5] |= sad_count << 4;
  	eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
  
  	DRM_DEBUG_KMS("ELD size %d, SAD count %d
  ", (int)eld[2], sad_count);
  }
  EXPORT_SYMBOL(drm_edid_to_eld);
  
  /**
   * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
   * @connector: connector associated with the HDMI/DP sink
   * @mode: the display mode
   */
  int drm_av_sync_delay(struct drm_connector *connector,
  		      struct drm_display_mode *mode)
  {
  	int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
  	int a, v;
  
  	if (!connector->latency_present[0])
  		return 0;
  	if (!connector->latency_present[1])
  		i = 0;
  
  	a = connector->audio_latency[i];
  	v = connector->video_latency[i];
  
  	/*
  	 * HDMI/DP sink doesn't support audio or video?
  	 */
  	if (a == 255 || v == 255)
  		return 0;
  
  	/*
  	 * Convert raw EDID values to millisecond.
  	 * Treat unknown latency as 0ms.
  	 */
  	if (a)
  		a = min(2 * (a - 1), 500);
  	if (v)
  		v = min(2 * (v - 1), 500);
  
  	return max(v - a, 0);
  }
  EXPORT_SYMBOL(drm_av_sync_delay);
  
  /**
   * drm_select_eld - select one ELD from multiple HDMI/DP sinks
   * @encoder: the encoder just changed display mode
   * @mode: the adjusted display mode
   *
   * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
   * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
   */
  struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
  				     struct drm_display_mode *mode)
  {
  	struct drm_connector *connector;
  	struct drm_device *dev = encoder->dev;
  
  	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
  		if (connector->encoder == encoder && connector->eld[0])
  			return connector;
  
  	return NULL;
  }
  EXPORT_SYMBOL(drm_select_eld);
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
  /**
   * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
   * @edid: monitor EDID information
   *
   * Parse the CEA extension according to CEA-861-B.
   * Return true if HDMI, false if not or unknown.
   */
  bool drm_detect_hdmi_monitor(struct edid *edid)
  {
  	u8 *edid_ext;
  	int i, hdmi_id;
  	int start_offset, end_offset;
  	bool is_hdmi = false;
  
  	edid_ext = drm_find_cea_extension(edid);
  	if (!edid_ext)
f23c20c83   Ma Ling   drm: detect hdmi ...
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
  		goto end;
  
  	/* Data block offset in CEA extension block */
  	start_offset = 4;
  	end_offset = edid_ext[2];
  
  	/*
  	 * Because HDMI identifier is in Vendor Specific Block,
  	 * search it from all data blocks of CEA extension.
  	 */
  	for (i = start_offset; i < end_offset;
  		/* Increased by data block len */
  		i += ((edid_ext[i] & 0x1f) + 1)) {
  		/* Find vendor specific block */
  		if ((edid_ext[i] >> 5) == VENDOR_BLOCK) {
  			hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) |
  				  edid_ext[i + 3] << 16;
  			/* Find HDMI identifier */
  			if (hdmi_id == HDMI_IDENTIFIER)
  				is_hdmi = true;
  			break;
  		}
  	}
  
  end:
  	return is_hdmi;
  }
  EXPORT_SYMBOL(drm_detect_hdmi_monitor);
f453ba046   Dave Airlie   DRM: add mode set...
1570
  /**
8fe9790d1   Zhenyu Wang   drm/edid: add hel...
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
   * drm_detect_monitor_audio - check monitor audio capability
   *
   * Monitor should have CEA extension block.
   * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic
   * audio' only. If there is any audio extension block and supported
   * audio format, assume at least 'basic audio' support, even if 'basic
   * audio' is not defined in EDID.
   *
   */
  bool drm_detect_monitor_audio(struct edid *edid)
  {
  	u8 *edid_ext;
  	int i, j;
  	bool has_audio = false;
  	int start_offset, end_offset;
  
  	edid_ext = drm_find_cea_extension(edid);
  	if (!edid_ext)
  		goto end;
  
  	has_audio = ((edid_ext[3] & EDID_BASIC_AUDIO) != 0);
  
  	if (has_audio) {
  		DRM_DEBUG_KMS("Monitor has basic audio support
  ");
  		goto end;
  	}
  
  	/* Data block offset in CEA extension block */
  	start_offset = 4;
  	end_offset = edid_ext[2];
  
  	for (i = start_offset; i < end_offset;
  			i += ((edid_ext[i] & 0x1f) + 1)) {
  		if ((edid_ext[i] >> 5) == AUDIO_BLOCK) {
  			has_audio = true;
  			for (j = 1; j < (edid_ext[i] & 0x1f); j += 3)
  				DRM_DEBUG_KMS("CEA audio format %d
  ",
  					      (edid_ext[i + j] >> 3) & 0xf);
  			goto end;
  		}
  	}
  end:
  	return has_audio;
  }
  EXPORT_SYMBOL(drm_detect_monitor_audio);
  
  /**
3b11228b5   Jesse Barnes   drm: add bit dept...
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
   * drm_add_display_info - pull display info out if present
   * @edid: EDID data
   * @info: display info (attached to connector)
   *
   * Grab any available display info and stuff it into the drm_display_info
   * structure that's part of the connector.  Useful for tracking bpp and
   * color spaces.
   */
  static void drm_add_display_info(struct edid *edid,
  				 struct drm_display_info *info)
  {
ebec9a7bf   Jesse Barnes   drm: track CEA ve...
1631
  	u8 *edid_ext;
3b11228b5   Jesse Barnes   drm: add bit dept...
1632
1633
1634
1635
1636
  	info->width_mm = edid->width_cm * 10;
  	info->height_mm = edid->height_cm * 10;
  
  	/* driver figures it out in this case */
  	info->bpc = 0;
da05a5a71   Jesse Barnes   drm: parse color ...
1637
  	info->color_formats = 0;
3b11228b5   Jesse Barnes   drm: add bit dept...
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
  
  	/* Only defined for 1.4 with digital displays */
  	if (edid->revision < 4)
  		return;
  
  	if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
  		return;
  
  	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
  	case DRM_EDID_DIGITAL_DEPTH_6:
  		info->bpc = 6;
  		break;
  	case DRM_EDID_DIGITAL_DEPTH_8:
  		info->bpc = 8;
  		break;
  	case DRM_EDID_DIGITAL_DEPTH_10:
  		info->bpc = 10;
  		break;
  	case DRM_EDID_DIGITAL_DEPTH_12:
  		info->bpc = 12;
  		break;
  	case DRM_EDID_DIGITAL_DEPTH_14:
  		info->bpc = 14;
  		break;
  	case DRM_EDID_DIGITAL_DEPTH_16:
  		info->bpc = 16;
  		break;
  	case DRM_EDID_DIGITAL_DEPTH_UNDEF:
  	default:
  		info->bpc = 0;
  		break;
  	}
da05a5a71   Jesse Barnes   drm: parse color ...
1670
1671
1672
1673
1674
1675
  
  	info->color_formats = DRM_COLOR_FORMAT_RGB444;
  	if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB444)
  		info->color_formats = DRM_COLOR_FORMAT_YCRCB444;
  	if (info->color_formats & DRM_EDID_FEATURE_RGB_YCRCB422)
  		info->color_formats = DRM_COLOR_FORMAT_YCRCB422;
ebec9a7bf   Jesse Barnes   drm: track CEA ve...
1676
1677
1678
1679
1680
1681
1682
  
  	/* Get data from CEA blocks if present */
  	edid_ext = drm_find_cea_extension(edid);
  	if (!edid_ext)
  		return;
  
  	info->cea_rev = edid_ext[1];
3b11228b5   Jesse Barnes   drm: add bit dept...
1683
1684
1685
  }
  
  /**
f453ba046   Dave Airlie   DRM: add mode set...
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
   * drm_add_edid_modes - add modes from EDID data, if available
   * @connector: connector we're probing
   * @edid: edid data
   *
   * Add the specified modes to the connector's mode list.
   *
   * Return number of modes added or 0 if we couldn't find any.
   */
  int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
  {
  	int num_modes = 0;
  	u32 quirks;
  
  	if (edid == NULL) {
  		return 0;
  	}
3c537889e   Alex Deucher   drm/radeon/kms: a...
1702
  	if (!drm_edid_is_valid(edid)) {
dcdb16740   Jordan Crouse   drm: Add support ...
1703
1704
  		dev_warn(connector->dev->dev, "%s: EDID invalid.
  ",
f453ba046   Dave Airlie   DRM: add mode set...
1705
1706
1707
1708
1709
  			 drm_get_connector_name(connector));
  		return 0;
  	}
  
  	quirks = edid_get_quirks(edid);
c867df704   Adam Jackson   drm/edid: Reshuff...
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
  	/*
  	 * EDID spec says modes should be preferred in this order:
  	 * - preferred detailed mode
  	 * - other detailed modes from base block
  	 * - detailed modes from extension blocks
  	 * - CVT 3-byte code modes
  	 * - standard timing codes
  	 * - established timing codes
  	 * - modes inferred from GTF or CVT range information
  	 *
139315796   Adam Jackson   drm/edid: Rewrite...
1720
  	 * We get this pretty much right.
c867df704   Adam Jackson   drm/edid: Reshuff...
1721
1722
1723
  	 *
  	 * XXX order for additional mode types in extension blocks?
  	 */
139315796   Adam Jackson   drm/edid: Rewrite...
1724
1725
  	num_modes += add_detailed_modes(connector, edid, quirks);
  	num_modes += add_cvt_modes(connector, edid);
c867df704   Adam Jackson   drm/edid: Reshuff...
1726
1727
  	num_modes += add_standard_modes(connector, edid);
  	num_modes += add_established_modes(connector, edid);
139315796   Adam Jackson   drm/edid: Rewrite...
1728
  	num_modes += add_inferred_modes(connector, edid);
54ac76f85   Christian Schmidt   drm/edid: support...
1729
  	num_modes += add_cea_modes(connector, edid);
f453ba046   Dave Airlie   DRM: add mode set...
1730
1731
1732
  
  	if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
  		edid_fixup_preferred(connector, quirks);
3b11228b5   Jesse Barnes   drm: add bit dept...
1733
  	drm_add_display_info(edid, &connector->display_info);
f453ba046   Dave Airlie   DRM: add mode set...
1734
1735
1736
1737
  
  	return num_modes;
  }
  EXPORT_SYMBOL(drm_add_edid_modes);
f0fda0a47   Zhao Yakui   drm/kms: add a fu...
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
  
  /**
   * drm_add_modes_noedid - add modes for the connectors without EDID
   * @connector: connector we're probing
   * @hdisplay: the horizontal display limit
   * @vdisplay: the vertical display limit
   *
   * Add the specified modes to the connector's mode list. Only when the
   * hdisplay/vdisplay is not beyond the given limit, it will be added.
   *
   * Return number of modes added or 0 if we couldn't find any.
   */
  int drm_add_modes_noedid(struct drm_connector *connector,
  			int hdisplay, int vdisplay)
  {
  	int i, count, num_modes = 0;
b1f559ecd   Chris Wilson   drm: Mark constan...
1754
  	struct drm_display_mode *mode;
f0fda0a47   Zhao Yakui   drm/kms: add a fu...
1755
1756
1757
1758
1759
1760
1761
1762
1763
  	struct drm_device *dev = connector->dev;
  
  	count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
  	if (hdisplay < 0)
  		hdisplay = 0;
  	if (vdisplay < 0)
  		vdisplay = 0;
  
  	for (i = 0; i < count; i++) {
b1f559ecd   Chris Wilson   drm: Mark constan...
1764
  		const struct drm_display_mode *ptr = &drm_dmt_modes[i];
f0fda0a47   Zhao Yakui   drm/kms: add a fu...
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
  		if (hdisplay && vdisplay) {
  			/*
  			 * Only when two are valid, they will be used to check
  			 * whether the mode should be added to the mode list of
  			 * the connector.
  			 */
  			if (ptr->hdisplay > hdisplay ||
  					ptr->vdisplay > vdisplay)
  				continue;
  		}
f985dedb5   Adam Jackson   drm/modes: Limit ...
1775
1776
  		if (drm_mode_vrefresh(ptr) > 61)
  			continue;
f0fda0a47   Zhao Yakui   drm/kms: add a fu...
1777
1778
1779
1780
1781
1782
1783
1784
1785
  		mode = drm_mode_duplicate(dev, ptr);
  		if (mode) {
  			drm_mode_probed_add(connector, mode);
  			num_modes++;
  		}
  	}
  	return num_modes;
  }
  EXPORT_SYMBOL(drm_add_modes_noedid);