]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/video/mxc/mxc_edid.c
9e7c87ef43ccaa02de881bafbf4aba85b077d761
[karo-tx-linux.git] / drivers / video / mxc / mxc_edid.c
1 /*
2  * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
3  */
4
5 /*
6  * The code contained herein is licensed under the GNU General Public
7  * License. You may obtain a copy of the GNU General Public License
8  * Version 2 or later at the following locations:
9  *
10  * http://www.opensource.org/licenses/gpl-license.html
11  * http://www.gnu.org/copyleft/gpl.html
12  */
13
14 /*!
15  * @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
16  */
17
18 /*!
19  * @file mxc_edid.c
20  *
21  * @brief MXC EDID driver
22  *
23  * @ingroup Framebuffer
24  */
25
26 /*!
27  * Include files
28  */
29 #include <linux/i2c.h>
30 #include <linux/fb.h>
31 #include <mach/mxc_edid.h>
32 #include "../edid.h"
33
34 #undef DEBUG  /* define this for verbose EDID parsing output */
35
36 #ifdef DEBUG
37 #define DPRINTK(fmt, args...) printk(fmt, ## args)
38 #else
39 #define DPRINTK(fmt, args...)
40 #endif
41
42 const struct fb_videomode mxc_cea_mode[64] = {
43         /* #1: 640x480p@59.94/60Hz 4:3 */
44         [1] = {
45                 NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
46                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_4_3,
47         },
48         /* #2: 720x480p@59.94/60Hz 4:3 */
49         [2] = {
50                 NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
51                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_4_3,
52         },
53         /* #3: 720x480p@59.94/60Hz 16:9 */
54         [3] = {
55                 NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
56                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
57         },
58         /* #4: 1280x720p@59.94/60Hz 16:9 */
59         [4] = {
60                 NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5,
61                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
62                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
63         },
64         /* #5: 1920x1080i@59.94/60Hz 16:9 */
65         [5] = {
66                 NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
67                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
68                 FB_VMODE_INTERLACED, FB_MODE_ASPECT_16_9,
69         },
70         /* #6: 720(1440)x480iH@59.94/60Hz 4:3 */
71         [6] = {
72                 NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
73                 FB_VMODE_INTERLACED, FB_MODE_ASPECT_4_3,
74         },
75         /* #7: 720(1440)x480iH@59.94/60Hz 16:9 */
76         [7] = {
77                 NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
78                 FB_VMODE_INTERLACED, FB_MODE_ASPECT_16_9,
79         },
80         /* #8: 720(1440)x240pH@59.94/60Hz 4:3 */
81         [8] = {
82                 NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
83                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
84         },
85         /* #9: 720(1440)x240pH@59.94/60Hz 16:9 */
86         [9] = {
87                 NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
88                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
89         },
90         /* #16: 1920x1080p@60Hz 16:9 */
91         [16] = {
92                 NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5,
93                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
94                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
95         },
96         /* #17: 720x576pH@50Hz 4:3 */
97         [17] = {
98                 NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
99                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_4_3,
100         },
101         /* #18: 720x576pH@50Hz 16:9 */
102         [18] = {
103                 NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
104                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
105         },
106         /* #19: 1280x720p@50Hz */
107         [19] = {
108                 NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
109                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
110                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
111         },
112         /* #20: 1920x1080i@50Hz */
113         [20] = {
114                 NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
115                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
116                 FB_VMODE_INTERLACED, FB_MODE_ASPECT_16_9,
117         },
118         /* #31: 1920x1080p@50Hz */
119         [31] = {
120                 NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5,
121                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
122                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
123         },
124         /* #32: 1920x1080p@23.98/24Hz */
125         [32] = {
126                 NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
127                 FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
128                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_16_9,
129         },
130         /* #35: (2880)x480p4x@59.94/60Hz */
131         [35] = {
132                 NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
133                 FB_VMODE_NONINTERLACED, FB_MODE_ASPECT_4_3,
134         },
135 };
136
137 /*
138  * We have a special version of fb_mode_is_equal that ignores
139  * pixclock, since for many CEA modes, 2 frequencies are supported
140  * e.g. 640x480 @ 60Hz or 59.94Hz
141  */
142 int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1,
143                      const struct fb_videomode *mode2)
144 {
145         return (mode1->xres         == mode2->xres &&
146                 mode1->yres         == mode2->yres &&
147                 mode1->hsync_len    == mode2->hsync_len &&
148                 mode1->vsync_len    == mode2->vsync_len &&
149                 mode1->left_margin  == mode2->left_margin &&
150                 mode1->right_margin == mode2->right_margin &&
151                 mode1->upper_margin == mode2->upper_margin &&
152                 mode1->lower_margin == mode2->lower_margin &&
153                 mode1->sync         == mode2->sync &&
154                 mode1->vmode        == mode2->vmode);
155 }
156
157 static void get_detailed_timing(unsigned char *block,
158                                 struct fb_videomode *mode)
159 {
160         mode->xres = H_ACTIVE;
161         mode->yres = V_ACTIVE;
162         mode->pixclock = PIXEL_CLOCK;
163         mode->pixclock /= 1000;
164         mode->pixclock = KHZ2PICOS(mode->pixclock);
165         mode->right_margin = H_SYNC_OFFSET;
166         mode->left_margin = (H_ACTIVE + H_BLANKING) -
167                 (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
168         mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
169                 V_SYNC_WIDTH;
170         mode->lower_margin = V_SYNC_OFFSET;
171         mode->hsync_len = H_SYNC_WIDTH;
172         mode->vsync_len = V_SYNC_WIDTH;
173         if (HSYNC_POSITIVE)
174                 mode->sync |= FB_SYNC_HOR_HIGH_ACT;
175         if (VSYNC_POSITIVE)
176                 mode->sync |= FB_SYNC_VERT_HIGH_ACT;
177         mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
178                                      (V_ACTIVE + V_BLANKING));
179         if (INTERLACED) {
180                 mode->yres *= 2;
181                 mode->upper_margin *= 2;
182                 mode->lower_margin *= 2;
183                 mode->vsync_len *= 2;
184                 mode->vmode |= FB_VMODE_INTERLACED;
185         }
186         mode->flag = FB_MODE_IS_DETAILED;
187
188         DPRINTK("      %d MHz ",  PIXEL_CLOCK/1000000);
189         DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
190                H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
191         DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
192                V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
193         DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
194                (VSYNC_POSITIVE) ? "+" : "-");
195 }
196
197 int mxc_edid_parse_ext_blk(unsigned char *edid,
198                 struct mxc_edid_cfg *cfg,
199                 struct fb_monspecs *specs)
200 {
201         char detail_timing_desc_offset;
202         struct fb_videomode *mode, *m;
203         unsigned char index = 0x0;
204         unsigned char *block;
205         int i, num = 0, revision;
206
207         if (edid[index++] != 0x2) /* only support cea ext block now */
208                 return -1;
209         revision = edid[index++];
210         DPRINTK("cea extent revision %d\n", revision);
211         mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
212         if (mode == NULL)
213                 return -1;
214
215         detail_timing_desc_offset = edid[index++];
216
217         if (revision >= 2) {
218                 cfg->cea_underscan = (edid[index] >> 7) & 0x1;
219                 cfg->cea_basicaudio = (edid[index] >> 6) & 0x1;
220                 cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1;
221                 cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1;
222
223                 DPRINTK("CEA underscan %d\n", cfg->cea_underscan);
224                 DPRINTK("CEA basicaudio %d\n", cfg->cea_basicaudio);
225                 DPRINTK("CEA ycbcr444 %d\n", cfg->cea_ycbcr444);
226                 DPRINTK("CEA ycbcr422 %d\n", cfg->cea_ycbcr422);
227         }
228
229         if (revision >= 3) {
230                 /* short desc */
231                 DPRINTK("CEA Short desc timmings\n");
232                 index++;
233                 while (index < detail_timing_desc_offset) {
234                         unsigned char tagcode, blklen;
235
236                         tagcode = (edid[index] >> 5) & 0x7;
237                         blklen = (edid[index]) & 0x1f;
238
239                         DPRINTK("Tagcode %x Len %d\n", tagcode, blklen);
240
241                         switch (tagcode) {
242                         case 0x2: /*Video data block*/
243                                 {
244                                         int cea_idx;
245                                         i = 0;
246                                         while (i < blklen) {
247                                                 index++;
248                                                 cea_idx = edid[index] & 0x7f;
249                                                 if (cea_idx < ARRAY_SIZE(mxc_cea_mode) &&
250                                                                 (mxc_cea_mode[cea_idx].xres)) {
251                                                         DPRINTK("Support CEA Format #%d\n", cea_idx);
252                                                         mode[num] = mxc_cea_mode[cea_idx];
253                                                         mode[num].flag |= FB_MODE_IS_STANDARD;
254                                                         num++;
255                                                 }
256                                                 i++;
257                                         }
258                                         break;
259                                 }
260                         case 0x3: /*Vendor specific data*/
261                                 {
262                                         unsigned char IEEE_reg_iden[3];
263                                         unsigned char deep_color;
264                                         IEEE_reg_iden[0] = edid[index+1];
265                                         IEEE_reg_iden[1] = edid[index+2];
266                                         IEEE_reg_iden[2] = edid[index+3];
267                                         deep_color = edid[index+6];
268
269                                         if ((IEEE_reg_iden[0] == 0x03) &&
270                                                         (IEEE_reg_iden[1] == 0x0c) &&
271                                                         (IEEE_reg_iden[2] == 0x00))
272                                                 cfg->hdmi_cap = 1;
273
274                                         if (deep_color & 0x40)
275                                                 cfg->vsd_dc_48bit = true;
276                                         if (deep_color & 0x20)
277                                                 cfg->vsd_dc_36bit = true;
278                                         if (deep_color & 0x10)
279                                                 cfg->vsd_dc_30bit = true;
280                                         if (deep_color & 0x08)
281                                                 cfg->vsd_dc_y444 = true;
282                                         if (deep_color & 0x01)
283                                                 cfg->vsd_dvi_dual = true;
284
285                                         DPRINTK("VSD hdmi capability %d\n", cfg->hdmi_cap);
286                                         DPRINTK("VSD support deep color 48bit %d\n", cfg->vsd_dc_48bit);
287                                         DPRINTK("VSD support deep color 36bit %d\n", cfg->vsd_dc_36bit);
288                                         DPRINTK("VSD support deep color 30bit %d\n", cfg->vsd_dc_30bit);
289                                         DPRINTK("VSD support deep color y444 %d\n", cfg->vsd_dc_y444);
290                                         DPRINTK("VSD support dvi dual %d\n", cfg->vsd_dvi_dual);
291
292                                         index += blklen;
293                                         break;
294                                 }
295                         case 0x1: /*Audio data block*/
296                         case 0x4: /*Speaker allocation block*/
297                         case 0x7: /*User extended block*/
298                         default:
299                                 /* skip */
300                                 index += blklen;
301                                 break;
302                         }
303
304                         index++;
305                 }
306         }
307
308         /* long desc */
309         DPRINTK("CEA long desc timmings\n");
310         index = detail_timing_desc_offset;
311         block = edid + index;
312         while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) {
313                 if (!(block[0] == 0x00 && block[1] == 0x00)) {
314                         get_detailed_timing(block, &mode[num]);
315                         num++;
316                 }
317                 block += DETAILED_TIMING_DESCRIPTION_SIZE;
318                 index += DETAILED_TIMING_DESCRIPTION_SIZE;
319         }
320
321         if (!num) {
322                 kfree(mode);
323                 return 0;
324         }
325
326         m = kmalloc((num + specs->modedb_len) *
327                         sizeof(struct fb_videomode), GFP_KERNEL);
328         if (!m)
329                 return 0;
330
331         if (specs->modedb_len) {
332                 memmove(m, specs->modedb,
333                         specs->modedb_len * sizeof(struct fb_videomode));
334                 kfree(specs->modedb);
335         }
336         memmove(m+specs->modedb_len, mode,
337                 num * sizeof(struct fb_videomode));
338         kfree(mode);
339
340         specs->modedb_len += num;
341         specs->modedb = m;
342
343         return 0;
344 }
345
346 static int mxc_edid_readblk(struct i2c_adapter *adp,
347                 unsigned short addr, unsigned char *edid)
348 {
349         int ret = 0, extblknum = 0;
350         unsigned char regaddr = 0x0;
351         struct i2c_msg msg[2] = {
352                 {
353                 .addr   = addr,
354                 .flags  = 0,
355                 .len    = 1,
356                 .buf    = &regaddr,
357                 }, {
358                 .addr   = addr,
359                 .flags  = I2C_M_RD,
360                 .len    = EDID_LENGTH,
361                 .buf    = edid,
362                 },
363         };
364
365         ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
366         if (ret != ARRAY_SIZE(msg)) {
367                 DPRINTK("unable to read EDID block\n");
368                 return -EIO;
369         }
370
371         if (edid[1] == 0x00)
372                 return -ENOENT;
373
374         extblknum = edid[0x7E];
375
376         if (extblknum) {
377                 regaddr = 128;
378                 msg[1].buf = edid + EDID_LENGTH;
379
380                 ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
381                 if (ret != ARRAY_SIZE(msg)) {
382                         DPRINTK("unable to read EDID ext block\n");
383                         return -EIO;
384                 }
385         }
386
387         return extblknum;
388 }
389
390 static int mxc_edid_readsegblk(struct i2c_adapter *adp, unsigned short addr,
391                         unsigned char *edid, int seg_num)
392 {
393         int ret = 0;
394         unsigned char segment = 0x1, regaddr = 0;
395         struct i2c_msg msg[3] = {
396                 {
397                 .addr   = 0x30,
398                 .flags  = 0,
399                 .len    = 1,
400                 .buf    = &segment,
401                 }, {
402                 .addr   = addr,
403                 .flags  = 0,
404                 .len    = 1,
405                 .buf    = &regaddr,
406                 }, {
407                 .addr   = addr,
408                 .flags  = I2C_M_RD,
409                 .len    = EDID_LENGTH,
410                 .buf    = edid,
411                 },
412         };
413
414         ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
415         if (ret != ARRAY_SIZE(msg)) {
416                 DPRINTK("unable to read EDID block\n");
417                 return -EIO;
418         }
419
420         if (seg_num == 2) {
421                 regaddr = 128;
422                 msg[2].buf = edid + EDID_LENGTH;
423
424                 ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg));
425                 if (ret != ARRAY_SIZE(msg)) {
426                         DPRINTK("unable to read EDID block\n");
427                         return -EIO;
428                 }
429         }
430
431         return ret;
432 }
433
434 int mxc_edid_var_to_vic(struct fb_var_screeninfo *var)
435 {
436         int i;
437         struct fb_videomode m;
438
439         for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
440                 fb_var_to_videomode(&m, var);
441                 if (mxc_edid_fb_mode_is_equal(&m, &mxc_cea_mode[i]))
442                         break;
443         }
444
445         if (i == ARRAY_SIZE(mxc_cea_mode))
446                 return 0;
447
448         return i;
449 }
450
451 EXPORT_SYMBOL(mxc_edid_var_to_vic);
452
453 int mxc_edid_mode_to_vic(const struct fb_videomode *mode)
454 {
455         int i;
456         u32 aspect_flags = FB_MODE_ASPECT_16_9 | FB_MODE_ASPECT_4_3;
457
458         for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) {
459                 if (mxc_edid_fb_mode_is_equal(mode, &mxc_cea_mode[i])) {
460                         if (mode->flag & FB_MODE_IS_STANDARD) {
461                                 if ((mode->flag & aspect_flags) ==
462                                         mxc_cea_mode[i].flag)
463                                         break;
464                         } else
465                                 break;
466                 }
467         }
468
469         if (i == ARRAY_SIZE(mxc_cea_mode))
470                 return 0;
471
472         return i;
473 }
474 EXPORT_SYMBOL(mxc_edid_mode_to_vic);
475
476 /* make sure edid has 512 bytes*/
477 int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr,
478         unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi)
479 {
480         int ret = 0, extblknum;
481         if (!adp || !edid || !cfg || !fbi)
482                 return -EINVAL;
483
484         memset(edid, 0, EDID_LENGTH*4);
485         memset(cfg, 0, sizeof(struct mxc_edid_cfg));
486
487         extblknum = mxc_edid_readblk(adp, addr, edid);
488         if (extblknum < 0)
489                 return extblknum;
490
491         /* edid first block parsing */
492         memset(&fbi->monspecs, 0, sizeof(fbi->monspecs));
493         fb_edid_to_monspecs(edid, &fbi->monspecs);
494
495         if (extblknum) {
496                 int i;
497
498                 /* need read segment block? */
499                 if (extblknum > 1) {
500                         ret = mxc_edid_readsegblk(adp, addr,
501                                 edid + EDID_LENGTH*2, extblknum - 1);
502                         if (ret < 0)
503                                 return ret;
504                 }
505
506                 for (i = 1; i <= extblknum; i++)
507                         /* edid ext block parsing */
508                         mxc_edid_parse_ext_blk(edid + i*EDID_LENGTH,
509                                         cfg, &fbi->monspecs);
510         }
511
512         return 0;
513 }
514 EXPORT_SYMBOL(mxc_edid_read);
515