• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1commit 061f4ee6ee04553ec034298b12b8e7a301e5799f
2Author: zhaoxc0502 <zhaoxc0502@thundersoft.com>
3Date:   Thu Jun 16 17:29:10 2022 +0800
4
5    linux_drivers_video
6
7    Change-Id: I90b98cd89f79c8a533f3d2554fbcf12b4c482fe8
8
9diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
10index 427a993c7..df9bd4b45 100644
11--- a/drivers/video/Kconfig
12+++ b/drivers/video/Kconfig
13@@ -15,7 +15,7 @@ source "drivers/char/agp/Kconfig"
14 source "drivers/gpu/vga/Kconfig"
15
16 source "drivers/gpu/host1x/Kconfig"
17-source "drivers/gpu/ipu-v3/Kconfig"
18+source "drivers/gpu/imx/Kconfig"
19
20 source "drivers/gpu/drm/Kconfig"
21
22diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
23index 1cf924f3a..7f2fe2913 100644
24--- a/drivers/video/backlight/pwm_bl.c
25+++ b/drivers/video/backlight/pwm_bl.c
26@@ -37,6 +37,7 @@ struct pwm_bl_data {
27 					int brightness);
28 	int			(*check_fb)(struct device *, struct fb_info *);
29 	void			(*exit)(struct device *);
30+	char			fb_id[16];
31 };
32
33 static void pwm_backlight_power_on(struct pwm_bl_data *pb)
34@@ -226,6 +227,17 @@ int pwm_backlight_brightness_default(struct device *dev,
35 	return 0;
36 }
37
38+static int pwm_backlight_check_fb_name(struct device *dev, struct fb_info *info)
39+{
40+	struct backlight_device *bl = dev_get_drvdata(dev);
41+	struct pwm_bl_data *pb = bl_get_data(bl);
42+
43+	if (strcmp(info->fix.id, pb->fb_id) == 0)
44+		return true;
45+
46+	return false;
47+}
48+
49 static int pwm_backlight_parse_dt(struct device *dev,
50 				  struct platform_pwm_backlight_data *data)
51 {
52@@ -238,12 +250,18 @@ static int pwm_backlight_parse_dt(struct device *dev,
53 	int length;
54 	u32 value;
55 	int ret;
56+	const char *names;
57
58 	if (!node)
59 		return -ENODEV;
60
61 	memset(data, 0, sizeof(*data));
62
63+	if (!of_property_read_string(node, "fb-names", &names)) {
64+		strncpy(data->fb_id, names, sizeof(data->fb_id));
65+		data->check_fb = &pwm_backlight_check_fb_name;
66+	}
67+
68 	/*
69 	 * These values are optional and set as 0 by default, the out values
70 	 * are modified only if a valid u32 value can be decoded.
71@@ -363,7 +381,6 @@ static int pwm_backlight_parse_dt(struct device *dev,
72
73 		data->max_brightness--;
74 	}
75-
76 	return 0;
77 }
78
79@@ -500,6 +517,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
80 	pb->enabled = false;
81 	pb->post_pwm_on_delay = data->post_pwm_on_delay;
82 	pb->pwm_off_delay = data->pwm_off_delay;
83+	strcpy(pb->fb_id, data->fb_id);
84
85 	pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
86 						  GPIOD_ASIS);
87diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
88index 477b9624b..13e4b74d8 100644
89--- a/drivers/video/fbdev/Makefile
90+++ b/drivers/video/fbdev/Makefile
91@@ -31,6 +31,7 @@ obj-$(CONFIG_FB_VIA)		  += via/
92 obj-$(CONFIG_FB_KYRO)             += kyro/
93 obj-$(CONFIG_FB_SAVAGE)		  += savage/
94 obj-$(CONFIG_FB_GEODE)		  += geode/
95+obj-$(CONFIG_FB_MXC)              += mxc/
96 obj-$(CONFIG_FB_NEOMAGIC)         += neofb.o
97 obj-$(CONFIG_FB_3DFX)             += tdfxfb.o
98 obj-$(CONFIG_FB_CONTROL)          += controlfb.o
99diff --git a/drivers/video/fbdev/mxc/Kconfig b/drivers/video/fbdev/mxc/Kconfig
100new file mode 100644
101index 000000000..acba4261c
102--- /dev/null
103+++ b/drivers/video/fbdev/mxc/Kconfig
104@@ -0,0 +1,20 @@
105+config FB_MXC
106+	tristate "MXC Framebuffer support"
107+	depends on FB
108+	select FB_CFB_FILLRECT
109+	select FB_CFB_COPYAREA
110+	select FB_CFB_IMAGEBLIT
111+	select FB_MODE_HELPERS
112+	default y
113+	help
114+	  This is a framebuffer device for the MXC LCD Controller.
115+	  See <http://www.linux-fbdev.org/> for information on framebuffer
116+	  devices.
117+
118+	  If you plan to use the LCD display with your MXC system, say
119+	  Y here.
120+config FB_MXC_EDID
121+	depends on FB_MXC && I2C
122+	tristate "MXC EDID support"
123+	default y
124+
125diff --git a/drivers/video/fbdev/mxc/Makefile b/drivers/video/fbdev/mxc/Makefile
126new file mode 100644
127index 000000000..d9a1cbf68
128--- /dev/null
129+++ b/drivers/video/fbdev/mxc/Makefile
130@@ -0,0 +1 @@
131+obj-$(CONFIG_FB_MXC_EDID)                       += mxc_edid.o
132diff --git a/drivers/video/fbdev/mxc/mxc_edid.c b/drivers/video/fbdev/mxc/mxc_edid.c
133new file mode 100644
134index 000000000..903172e6a
135--- /dev/null
136+++ b/drivers/video/fbdev/mxc/mxc_edid.c
137@@ -0,0 +1,770 @@
138+/*
139+ * Copyright 2009-2015 Freescale Semiconductor, Inc. All Rights Reserved.
140+ */
141+
142+/*
143+ * The code contained herein is licensed under the GNU General Public
144+ * License. You may obtain a copy of the GNU General Public License
145+ * Version 2 or later at the following locations:
146+ *
147+ * http://www.opensource.org/licenses/gpl-license.html
148+ * http://www.gnu.org/copyleft/gpl.html
149+ */
150+
151+/*!
152+ * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
153+ */
154+
155+/*!
156+ * @file mxc_edid.c
157+ *
158+ * @brief MXC EDID driver
159+ *
160+ * @ingroup Framebuffer
161+ */
162+
163+/*!
164+ * Include files
165+ */
166+#include <linux/i2c.h>
167+#include <linux/fb.h>
168+#include <video/mxc_edid.h>
169+#include "../edid.h"
170+
171+#undef DEBUG  /* define this for verbose EDID parsing output */
172+#ifdef DEBUG
173+#define DPRINTK(fmt, args...) printk(fmt, ## args)
174+#else
175+#define DPRINTK(fmt, args...)
176+#endif
177+
178+const struct fb_videomode mxc_cea_mode[64] = {
179+	/* #1: 640x480p@59.94/60Hz 4:3 */
180+	[1] = {
181+		NULL, 60, 640, 480, 39683, 48, 16, 33, 10, 96, 2, 0,
182+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
183+	},
184+	/* #2: 720x480p@59.94/60Hz 4:3 */
185+	[2] = {
186+		NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
187+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
188+	},
189+	/* #3: 720x480p@59.94/60Hz 16:9 */
190+	[3] = {
191+		NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
192+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
193+	},
194+	/* #4: 1280x720p@59.94/60Hz 16:9 */
195+	[4] = {
196+		NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5,
197+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
198+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
199+	},
200+	/* #5: 1920x1080i@59.94/60Hz 16:9 */
201+	[5] = {
202+		NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
203+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
204+		FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
205+	},
206+	/* #6: 720(1440)x480iH@59.94/60Hz 4:3 */
207+	[6] = {
208+		NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
209+		FB_VMODE_INTERLACED | FB_VMODE_ASPECT_4_3, 0,
210+	},
211+	/* #7: 720(1440)x480iH@59.94/60Hz 16:9 */
212+	[7] = {
213+		NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
214+		FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
215+	},
216+	/* #8: 720(1440)x240pH@59.94/60Hz 4:3 */
217+	[8] = {
218+		NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
219+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
220+	},
221+	/* #9: 720(1440)x240pH@59.94/60Hz 16:9 */
222+	[9] = {
223+		NULL, 60, 1440, 240, 37108, 114, 38, 15, 4, 124, 3, 0,
224+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
225+	},
226+	/* #14: 1440x480p@59.94/60Hz 4:3 */
227+	[14] = {
228+		NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
229+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
230+	},
231+	/* #15: 1440x480p@59.94/60Hz 16:9 */
232+	[15] = {
233+		NULL, 60, 1440, 480, 18500, 120, 32, 30, 9, 124, 6, 0,
234+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
235+	},
236+	/* #16: 1920x1080p@60Hz 16:9 */
237+	[16] = {
238+		NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5,
239+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
240+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
241+	},
242+	/* #17: 720x576pH@50Hz 4:3 */
243+	[17] = {
244+		NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
245+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
246+	},
247+	/* #18: 720x576pH@50Hz 16:9 */
248+	[18] = {
249+		NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
250+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
251+	},
252+	/* #19: 1280x720p@50Hz */
253+	[19] = {
254+		NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
255+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
256+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
257+	},
258+	/* #20: 1920x1080i@50Hz */
259+	[20] = {
260+		NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
261+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
262+		FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0,
263+	},
264+	/* #23: 720(1440)x288pH@50Hz 4:3 */
265+	[23] = {
266+		NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
267+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
268+	},
269+	/* #24: 720(1440)x288pH@50Hz 16:9 */
270+	[24] = {
271+		NULL, 50, 1440, 288, 37037, 138, 24, 19, 2, 126, 3, 0,
272+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
273+	},
274+	/* #29: 720(1440)x576pH@50Hz 4:3 */
275+	[29] = {
276+		NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
277+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0,
278+	},
279+	/* #30: 720(1440)x576pH@50Hz 16:9 */
280+	[30] = {
281+		NULL, 50, 1440, 576, 18518, 136, 24, 39, 5, 128, 5, 0,
282+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
283+	},
284+	/* #31: 1920x1080p@50Hz */
285+	[31] = {
286+		NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
287+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
288+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
289+	},
290+	/* #32: 1920x1080p@23.98/24Hz */
291+	[32] = {
292+		NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
293+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
294+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
295+	},
296+	/* #33: 1920x1080p@25Hz */
297+	[33] = {
298+		NULL, 25, 1920, 1080, 13468, 148, 528, 36, 4, 44, 5,
299+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
300+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
301+	},
302+	/* #34: 1920x1080p@30Hz */
303+	[34] = {
304+		NULL, 30, 1920, 1080, 13468, 148, 88, 36, 4, 44, 5,
305+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
306+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0,
307+	},
308+	/* #41: 1280x720p@100Hz 16:9 */
309+	[41] = {
310+		NULL, 100, 1280, 720, 6734, 220, 440, 20, 5, 40, 5,
311+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
312+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
313+	},
314+	/* #47: 1280x720p@119.88/120Hz 16:9 */
315+	[47] = {
316+		NULL, 120, 1280, 720, 6734, 220, 110, 20, 5, 40, 5,
317+		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
318+		FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0
319+	},
320+};
321+
322+/*
323+ * We have a special version of fb_mode_is_equal that ignores
324+ * pixclock, since for many CEA modes, 2 frequencies are supported
325+ * e.g. 640x480 @ 60Hz or 59.94Hz
326+ */
327+int mxc_edid_fb_mode_is_equal(bool use_aspect,
328+			const struct fb_videomode *mode1,
329+			const struct fb_videomode *mode2)
330+{
331+	u32 mask;
332+
333+	if (use_aspect)
334+		mask = ~0;
335+	else
336+		mask = ~FB_VMODE_ASPECT_MASK;
337+
338+	return (mode1->xres         == mode2->xres &&
339+		mode1->yres         == mode2->yres &&
340+		mode1->hsync_len    == mode2->hsync_len &&
341+		mode1->vsync_len    == mode2->vsync_len &&
342+		mode1->left_margin  == mode2->left_margin &&
343+		mode1->right_margin == mode2->right_margin &&
344+		mode1->upper_margin == mode2->upper_margin &&
345+		mode1->lower_margin == mode2->lower_margin &&
346+		mode1->sync         == mode2->sync &&
347+		/* refresh check, 59.94Hz and 60Hz have the same parameter
348+		 * in struct of mxc_cea_mode */
349+		abs(mode1->refresh - mode2->refresh) <= 1 &&
350+		(mode1->vmode & mask) == (mode2->vmode & mask));
351+}
352+
353+static void get_detailed_timing(unsigned char *block,
354+				struct fb_videomode *mode)
355+{
356+	mode->xres = H_ACTIVE;
357+	mode->yres = V_ACTIVE;
358+	mode->pixclock = PIXEL_CLOCK;
359+	mode->pixclock /= 1000;
360+	mode->pixclock = KHZ2PICOS(mode->pixclock);
361+	mode->right_margin = H_SYNC_OFFSET;
362+	mode->left_margin = (H_ACTIVE + H_BLANKING) -
363+		(H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
364+	mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
365+		V_SYNC_WIDTH;
366+	mode->lower_margin = V_SYNC_OFFSET;
367+	mode->hsync_len = H_SYNC_WIDTH;
368+	mode->vsync_len = V_SYNC_WIDTH;
369+	if (HSYNC_POSITIVE)
370+		mode->sync |= FB_SYNC_HOR_HIGH_ACT;
371+	if (VSYNC_POSITIVE)
372+		mode->sync |= FB_SYNC_VERT_HIGH_ACT;
373+	mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
374+				     (V_ACTIVE + V_BLANKING));
375+	if (INTERLACED) {
376+		mode->yres *= 2;
377+		mode->upper_margin *= 2;
378+		mode->lower_margin *= 2;
379+		mode->vsync_len *= 2;
380+		mode->vmode |= FB_VMODE_INTERLACED;
381+	}
382+	mode->flag = FB_MODE_IS_DETAILED;
383+
384+	if ((H_SIZE / 16) == (V_SIZE / 9))
385+		mode->vmode |= FB_VMODE_ASPECT_16_9;
386+	else if ((H_SIZE / 4) == (V_SIZE / 3))
387+		mode->vmode |= FB_VMODE_ASPECT_4_3;
388+	else if ((mode->xres / 16) == (mode->yres / 9))
389+		mode->vmode |= FB_VMODE_ASPECT_16_9;
390+	else if ((mode->xres / 4) == (mode->yres / 3))
391+		mode->vmode |= FB_VMODE_ASPECT_4_3;
392+
393+	if (mode->vmode & FB_VMODE_ASPECT_16_9)
394+		DPRINTK("Aspect ratio: 16:9\n");
395+	if (mode->vmode & FB_VMODE_ASPECT_4_3)
396+		DPRINTK("Aspect ratio: 4:3\n");
397+	DPRINTK("      %d MHz ",  PIXEL_CLOCK/1000000);
398+	DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
399+	       H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
400+	DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
401+	       V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
402+	DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
403+	       (VSYNC_POSITIVE) ? "+" : "-");
404+}
405+
406+int mxc_edid_parse_ext_blk(unsigned char *edid,
407+		struct mxc_edid_cfg *cfg,
408+		struct fb_monspecs *specs)
409+{
410+	char detail_timing_desc_offset;
411+	struct fb_videomode *mode, *m;
412+	unsigned char index = 0x0;
413+	unsigned char *block;
414+	int i, num = 0, revision;
415+
416+	if (edid[index++] != 0x2) /* only support cea ext block now */
417+		return 0;
418+	revision = edid[index++];
419+	DPRINTK("cea extent revision %d\n", revision);
420+	mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
421+	if (mode == NULL)
422+		return -1;
423+
424+	detail_timing_desc_offset = edid[index++];
425+
426+	if (revision >= 2) {
427+		cfg->cea_underscan = (edid[index] >> 7) & 0x1;
428+		cfg->cea_basicaudio = (edid[index] >> 6) & 0x1;
429+		cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1;
430+		cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1;
431+
432+		DPRINTK("CEA underscan %d\n", cfg->cea_underscan);
433+		DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio);
434+		DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444);
435+		DPRINTK("CEA ycbcr422 %d\n", cfg->cea_ycbcr422);
436+	}
437+
438+	if (revision >= 3) {
439+		/* short desc */
440+		DPRINTK("CEA Short desc timmings\n");
441+		index++;
442+		while (index < detail_timing_desc_offset) {
443+			unsigned char tagcode, blklen;
444+
445+			tagcode = (edid[index] >> 5) & 0x7;
446+			blklen = (edid[index]) & 0x1f;
447+
448+			DPRINTK("Tagcode %x Len %d\n", tagcode, blklen);
449+
450+			switch (tagcode) {
451+			case 0x2: /*Video data block*/
452+				{
453+					int cea_idx;
454+					i = 0;
455+					while (i < blklen) {
456+						index++;
457+						cea_idx = edid[index] & 0x7f;
458+						if (cea_idx < ARRAY_SIZE(mxc_cea_mode) &&
459+								(mxc_cea_mode[cea_idx].xres)) {
460+							DPRINTK("Support CEA Format #%d\n", cea_idx);
461+							mode[num] = mxc_cea_mode[cea_idx];
462+							mode[num].flag |= FB_MODE_IS_STANDARD;
463+							num++;
464+						}
465+						i++;
466+					}
467+					break;
468+				}
469+			case 0x3: /*Vendor specific data*/
470+				{
471+					unsigned char IEEE_reg_iden[3];
472+					unsigned char deep_color;
473+					unsigned char latency_present;
474+					unsigned char I_latency_present;
475+					unsigned char hdmi_video_present;
476+					unsigned char hdmi_3d_present;
477+					unsigned char hdmi_3d_multi_present;
478+					unsigned char hdmi_vic_len;
479+					unsigned char hdmi_3d_len;
480+					unsigned char index_inc = 0;
481+					unsigned char vsd_end;
482+
483+					vsd_end = index + blklen;
484+
485+					IEEE_reg_iden[0] = edid[index+1];
486+					IEEE_reg_iden[1] = edid[index+2];
487+					IEEE_reg_iden[2] = edid[index+3];
488+					cfg->physical_address[0] = (edid[index+4] & 0xf0) >> 4;
489+					cfg->physical_address[1] = (edid[index+4] & 0x0f);
490+					cfg->physical_address[2] = (edid[index+5] & 0xf0) >> 4;
491+					cfg->physical_address[3] = (edid[index+5] & 0x0f);
492+
493+					if ((IEEE_reg_iden[0] == 0x03) &&
494+							(IEEE_reg_iden[1] == 0x0c) &&
495+							(IEEE_reg_iden[2] == 0x00))
496+						cfg->hdmi_cap = 1;
497+
498+					if (blklen > 5) {
499+						deep_color = edid[index+6];
500+						if (deep_color & 0x80)
501+							cfg->vsd_support_ai = true;
502+						if (deep_color & 0x40)
503+							cfg->vsd_dc_48bit = true;
504+						if (deep_color & 0x20)
505+							cfg->vsd_dc_36bit = true;
506+						if (deep_color & 0x10)
507+							cfg->vsd_dc_30bit = true;
508+						if (deep_color & 0x08)
509+							cfg->vsd_dc_y444 = true;
510+						if (deep_color & 0x01)
511+							cfg->vsd_dvi_dual = true;
512+					}
513+
514+					DPRINTK("VSD hdmi capability %d\n", cfg->hdmi_cap);
515+					DPRINTK("VSD support ai %d\n", cfg->vsd_support_ai);
516+					DPRINTK("VSD support deep color 48bit %d\n", cfg->vsd_dc_48bit);
517+					DPRINTK("VSD support deep color 36bit %d\n", cfg->vsd_dc_36bit);
518+					DPRINTK("VSD support deep color 30bit %d\n", cfg->vsd_dc_30bit);
519+					DPRINTK("VSD support deep color y444 %d\n", cfg->vsd_dc_y444);
520+					DPRINTK("VSD support dvi dual %d\n", cfg->vsd_dvi_dual);
521+
522+					if (blklen > 6)
523+						cfg->vsd_max_tmdsclk_rate = edid[index+7] * 5;
524+					DPRINTK("VSD MAX TMDS CLOCK RATE %d\n", cfg->vsd_max_tmdsclk_rate);
525+
526+					if (blklen > 7) {
527+						latency_present = edid[index+8] >> 7;
528+						I_latency_present =  (edid[index+8] & 0x40) >> 6;
529+						hdmi_video_present = (edid[index+8] & 0x20) >> 5;
530+						cfg->vsd_cnc3 = (edid[index+8] & 0x8) >> 3;
531+						cfg->vsd_cnc2 = (edid[index+8] & 0x4) >> 2;
532+						cfg->vsd_cnc1 = (edid[index+8] & 0x2) >> 1;
533+						cfg->vsd_cnc0 = edid[index+8] & 0x1;
534+
535+						DPRINTK("VSD cnc0 %d\n", cfg->vsd_cnc0);
536+						DPRINTK("VSD cnc1 %d\n", cfg->vsd_cnc1);
537+						DPRINTK("VSD cnc2 %d\n", cfg->vsd_cnc2);
538+						DPRINTK("VSD cnc3 %d\n", cfg->vsd_cnc3);
539+						DPRINTK("latency_present %d\n", latency_present);
540+						DPRINTK("I_latency_present %d\n", I_latency_present);
541+						DPRINTK("hdmi_video_present %d\n", hdmi_video_present);
542+
543+					} else {
544+						index += blklen;
545+						break;
546+					}
547+
548+					index += 9;
549+
550+					/*latency present */
551+					if (latency_present) {
552+						cfg->vsd_video_latency = edid[index++];
553+						cfg->vsd_audio_latency = edid[index++];
554+
555+						if (I_latency_present) {
556+							cfg->vsd_I_video_latency = edid[index++];
557+							cfg->vsd_I_audio_latency = edid[index++];
558+						} else {
559+							cfg->vsd_I_video_latency = cfg->vsd_video_latency;
560+							cfg->vsd_I_audio_latency = cfg->vsd_audio_latency;
561+						}
562+
563+						DPRINTK("VSD latency video_latency  %d\n", cfg->vsd_video_latency);
564+						DPRINTK("VSD latency audio_latency  %d\n", cfg->vsd_audio_latency);
565+						DPRINTK("VSD latency I_video_latency  %d\n", cfg->vsd_I_video_latency);
566+						DPRINTK("VSD latency I_audio_latency  %d\n", cfg->vsd_I_audio_latency);
567+					}
568+
569+					if (hdmi_video_present) {
570+						hdmi_3d_present = edid[index] >> 7;
571+						hdmi_3d_multi_present = (edid[index] & 0x60) >> 5;
572+						index++;
573+						hdmi_vic_len = (edid[index] & 0xe0) >> 5;
574+						hdmi_3d_len = edid[index] & 0x1f;
575+						index++;
576+
577+						DPRINTK("hdmi_3d_present %d\n", hdmi_3d_present);
578+						DPRINTK("hdmi_3d_multi_present %d\n", hdmi_3d_multi_present);
579+						DPRINTK("hdmi_vic_len %d\n", hdmi_vic_len);
580+						DPRINTK("hdmi_3d_len %d\n", hdmi_3d_len);
581+
582+						if (hdmi_vic_len > 0) {
583+							for (i = 0; i < hdmi_vic_len; i++) {
584+								cfg->hdmi_vic[i] = edid[index++];
585+								DPRINTK("HDMI_vic=%d\n", cfg->hdmi_vic[i]);
586+							}
587+						}
588+
589+						if (hdmi_3d_len > 0) {
590+							if (hdmi_3d_present) {
591+								if (hdmi_3d_multi_present == 0x1) {
592+									cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
593+									index_inc = 2;
594+								} else if (hdmi_3d_multi_present == 0x2) {
595+									cfg->hdmi_3d_struct_all = (edid[index] << 8) | edid[index+1];
596+									cfg->hdmi_3d_mask_all = (edid[index+2] << 8) | edid[index+3];
597+									index_inc = 4;
598+								} else
599+									index_inc = 0;
600+							}
601+
602+							DPRINTK("HDMI 3d struct all =0x%x\n", cfg->hdmi_3d_struct_all);
603+							DPRINTK("HDMI 3d mask all =0x%x\n", cfg->hdmi_3d_mask_all);
604+
605+							/* Read 2D vic 3D_struct */
606+							if ((hdmi_3d_len - index_inc) > 0) {
607+								DPRINTK("Support 3D video format\n");
608+								i = 0;
609+								while ((hdmi_3d_len - index_inc) > 0) {
610+
611+									cfg->hdmi_3d_format[i].vic_order_2d = edid[index+index_inc] >> 4;
612+									cfg->hdmi_3d_format[i].struct_3d = edid[index+index_inc] & 0x0f;
613+									index_inc++;
614+
615+									if (cfg->hdmi_3d_format[i].struct_3d ==  8) {
616+										cfg->hdmi_3d_format[i].detail_3d = edid[index+index_inc] >> 4;
617+										index_inc++;
618+									} else if (cfg->hdmi_3d_format[i].struct_3d > 8) {
619+										cfg->hdmi_3d_format[i].detail_3d = 0;
620+										index_inc++;
621+									}
622+
623+									DPRINTK("vic_order_2d=%d, 3d_struct=%d, 3d_detail=0x%x\n",
624+											cfg->hdmi_3d_format[i].vic_order_2d,
625+											cfg->hdmi_3d_format[i].struct_3d,
626+											cfg->hdmi_3d_format[i].detail_3d);
627+									i++;
628+								}
629+							}
630+						}
631+					}
632+
633+					index = vsd_end;
634+
635+					break;
636+				}
637+			case 0x1: /*Audio data block*/
638+				{
639+					u8 audio_format, max_ch, byte1, byte2, byte3;
640+
641+					i = 0;
642+					cfg->max_channels = 0;
643+					cfg->sample_rates = 0;
644+					cfg->sample_sizes = 0;
645+
646+					while (i < blklen) {
647+						byte1 = edid[index + 1];
648+						byte2 = edid[index + 2];
649+						byte3 = edid[index + 3];
650+						index += 3;
651+						i += 3;
652+
653+						audio_format = byte1 >> 3;
654+						max_ch = (byte1 & 0x07) + 1;
655+
656+						DPRINTK("Audio Format Descriptor : %2d\n", audio_format);
657+						DPRINTK("Max Number of Channels  : %2d\n", max_ch);
658+						DPRINTK("Sample Rates            : %02x\n", byte2);
659+
660+						/* ALSA can't specify specific compressed
661+						 * formats, so only care about PCM for now. */
662+						if (audio_format == AUDIO_CODING_TYPE_LPCM) {
663+							if (max_ch > cfg->max_channels)
664+								cfg->max_channels = max_ch;
665+
666+							cfg->sample_rates |= byte2;
667+							cfg->sample_sizes |= byte3 & 0x7;
668+							DPRINTK("Sample Sizes            : %02x\n",
669+								byte3 & 0x7);
670+						}
671+					}
672+					break;
673+				}
674+			case 0x4: /*Speaker allocation block*/
675+				{
676+					i = 0;
677+					while (i < blklen) {
678+						cfg->speaker_alloc = edid[index + 1];
679+						index += 3;
680+						i += 3;
681+						DPRINTK("Speaker Alloc           : %02x\n", cfg->speaker_alloc);
682+					}
683+					break;
684+				}
685+			case 0x7: /*User extended block*/
686+			default:
687+				/* skip */
688+				DPRINTK("Not handle block, tagcode = 0x%x\n", tagcode);
689+				index += blklen;
690+				break;
691+			}
692+
693+			index++;
694+		}
695+	}
696+
697+	/* long desc */
698+	DPRINTK("CEA long desc timmings\n");
699+	index = detail_timing_desc_offset;
700+	block = edid + index;
701+	while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) {
702+		if (!(block[0] == 0x00 && block[1] == 0x00)) {
703+			get_detailed_timing(block, &mode[num]);
704+			num++;
705+		}
706+		block += DETAILED_TIMING_DESCRIPTION_SIZE;
707+		index += DETAILED_TIMING_DESCRIPTION_SIZE;
708+	}
709+
710+	if (!num) {
711+		kfree(mode);
712+		return 0;
713+	}
714+
715+	m = kmalloc((num + specs->modedb_len) *
716+			sizeof(struct fb_videomode), GFP_KERNEL);
717+	if (!m) {
718+		kfree(mode);
719+		return 0;
720+	}
721+
722+	if (specs->modedb_len) {
723+		memmove(m, specs->modedb,
724+			specs->modedb_len * sizeof(struct fb_videomode));
725+		kfree(specs->modedb);
726+	}
727+	memmove(m+specs->modedb_len, mode,
728+		num * sizeof(struct fb_videomode));
729+	kfree(mode);
730+
731+	specs->modedb_len += num;
732+	specs->modedb = m;
733+
734+	return 0;
735+}
736+EXPORT_SYMBOL(mxc_edid_parse_ext_blk);
737+
738+static int mxc_edid_readblk(struct i2c_adapter *adp,
739+		unsigned short addr, unsigned char *edid)
740+{
741+	int ret = 0, extblknum = 0;
742+	unsigned char regaddr = 0x0;
743+	struct i2c_msg msg[2] = {
744+		{
745+		.addr	= addr,
746+		.flags	= 0,
747+		.len	= 1,
748+		.buf	= &regaddr,
749+		}, {
750+		.addr	= addr,
751+		.flags	= I2C_M_RD,
752+		.len	= EDID_LENGTH,
753+		.buf	= edid,
754+		},
755+	};
756+
757+	ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
758+	if (ret != ARRAY_SIZE(msg)) {
759+		DPRINTK("unable to read EDID block\n");
760+		return -EIO;
761+	}
762+
763+	if (edid[1] == 0x00)
764+		return -ENOENT;
765+
766+	extblknum = edid[0x7E];
767+
768+	if (extblknum) {
769+		regaddr = 128;
770+		msg[1].buf = edid + EDID_LENGTH;
771+
772+		ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
773+		if (ret != ARRAY_SIZE(msg)) {
774+			DPRINTK("unable to read EDID ext block\n");
775+			return -EIO;
776+		}
777+	}
778+
779+	return extblknum;
780+}
781+
782+static int mxc_edid_readsegblk(struct i2c_adapter *adp, unsigned short addr,
783+			unsigned char *edid, int seg_num)
784+{
785+	int ret = 0;
786+	unsigned char segment = 0x1, regaddr = 0;
787+	struct i2c_msg msg[3] = {
788+		{
789+		.addr	= 0x30,
790+		.flags	= 0,
791+		.len	= 1,
792+		.buf	= &segment,
793+		}, {
794+		.addr	= addr,
795+		.flags	= 0,
796+		.len	= 1,
797+		.buf	= &regaddr,
798+		}, {
799+		.addr	= addr,
800+		.flags	= I2C_M_RD,
801+		.len	= EDID_LENGTH,
802+		.buf	= edid,
803+		},
804+	};
805+
806+	ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
807+	if (ret != ARRAY_SIZE(msg)) {
808+		DPRINTK("unable to read EDID block\n");
809+		return -EIO;
810+	}
811+
812+	if (seg_num == 2) {
813+		regaddr = 128;
814+		msg[2].buf = edid + EDID_LENGTH;
815+
816+		ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
817+		if (ret != ARRAY_SIZE(msg)) {
818+			DPRINTK("unable to read EDID block\n");
819+			return -EIO;
820+		}
821+	}
822+
823+	return ret;
824+}
825+
826+int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
827+{
828+	int i;
829+	struct fb_videomode m;
830+
831+	for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
832+		fb_var_to_videomode(&m, var);
833+		if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i]))
834+			break;
835+	}
836+
837+	if (i == ARRAY_SIZE(mxc_cea_mode))
838+		return 0;
839+
840+	return i;
841+}
842+EXPORT_SYMBOL(mxc_edid_var_to_vic);
843+
844+int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
845+{
846+	int i;
847+	bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK);
848+
849+	for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
850+		if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i]))
851+			break;
852+	}
853+
854+	if (i == ARRAY_SIZE(mxc_cea_mode))
855+		return 0;
856+
857+	return i;
858+}
859+EXPORT_SYMBOL(mxc_edid_mode_to_vic);
860+
861+/* make sure edid has 512 bytes*/
862+int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
863+	unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
864+{
865+	int ret = 0, extblknum;
866+	if (!adp || !edid || !cfg || !fbi)
867+		return -EINVAL;
868+
869+	memset(edid, 0, EDID_LENGTH*4);
870+	memset(cfg, 0, sizeof(struct mxc_edid_cfg));
871+
872+	extblknum = mxc_edid_readblk(adp, addr, edid);
873+	if (extblknum < 0)
874+		return extblknum;
875+
876+	/* edid first block parsing */
877+	memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
878+	fb_edid_to_monspecs(edid, &fbi->monspecs);
879+
880+	if (extblknum) {
881+		int i;
882+
883+		/* FIXME: mxc_edid_readsegblk() won't read more than 2 blocks
884+		 * and the for-loop will read past the end of the buffer! :-( */
885+		if (extblknum > 3) {
886+			WARN_ON(true);
887+			return -EINVAL;
888+		}
889+
890+		/* need read segment block? */
891+		if (extblknum > 1) {
892+			ret = mxc_edid_readsegblk(adp, addr,
893+				edid + EDID_LENGTH*2, extblknum - 1);
894+			if (ret < 0)
895+				return ret;
896+		}
897+
898+		for (i = 1; i <= extblknum; i++)
899+			/* edid ext block parsing */
900+			mxc_edid_parse_ext_blk(edid + i*EDID_LENGTH,
901+					cfg, &fbi->monspecs);
902+	}
903+
904+	return 0;
905+}
906+EXPORT_SYMBOL(mxc_edid_read);
907+
908