Merge tag 'v3.10.23' into develop-3.10
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / hdmi / rk_hdmi_edid.c
1 #include "rk_hdmi.h"\r
2 #include "../../edid.h"\r
3 \r
4 #define hdmi_edid_error(fmt, ...) \\r
5         printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)\r
6 \r
7 #if 0\r
8 #define hdmi_edid_debug(fmt, ...) \\r
9         printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)\r
10 #else\r
11 #define hdmi_edid_debug(fmt, ...)       \r
12 #endif\r
13 \r
14 typedef enum HDMI_EDID_ERRORCODE\r
15 {\r
16         E_HDMI_EDID_SUCCESS = 0,\r
17         E_HDMI_EDID_PARAM,\r
18         E_HDMI_EDID_HEAD,\r
19         E_HDMI_EDID_CHECKSUM,\r
20         E_HDMI_EDID_VERSION,\r
21         E_HDMI_EDID_UNKOWNDATA,\r
22         E_HDMI_EDID_NOMEMORY\r
23 }HDMI_EDID_ErrorCode;\r
24 \r
25 static const unsigned int double_aspect_vic[] = {3, 7, 9, 11, 13, 15, 18, 22, 24, 26, 28, 30, 36, 38, 43, 45, 49, 51, 53, 55, 57, 59};\r
26 static int hdmi_edid_checksum(unsigned char *buf)\r
27 {\r
28         int i;\r
29         int checksum = 0;\r
30         \r
31         for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)\r
32                 checksum += buf[i];     \r
33         \r
34         checksum &= 0xff;\r
35         \r
36         if(checksum == 0)\r
37                 return E_HDMI_EDID_SUCCESS;\r
38         else\r
39                 return E_HDMI_EDID_CHECKSUM;\r
40 }\r
41 \r
42 /*\r
43         @Des    Parse Detail Timing Descriptor.\r
44         @Param  buf     :       pointer to DTD data.\r
45         @Param  pvic:   VIC of DTD descripted.\r
46  */\r
47 static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode)\r
48 {\r
49         mode->xres = H_ACTIVE;\r
50         mode->yres = V_ACTIVE;\r
51         mode->pixclock = PIXEL_CLOCK;\r
52 //      mode->pixclock /= 1000;\r
53 //      mode->pixclock = KHZ2PICOS(mode->pixclock);\r
54         mode->right_margin = H_SYNC_OFFSET;\r
55         mode->left_margin = (H_ACTIVE + H_BLANKING) -\r
56                 (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);\r
57         mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -\r
58                 V_SYNC_WIDTH;\r
59         mode->lower_margin = V_SYNC_OFFSET;\r
60         mode->hsync_len = H_SYNC_WIDTH;\r
61         mode->vsync_len = V_SYNC_WIDTH;\r
62         if (HSYNC_POSITIVE)\r
63                 mode->sync |= FB_SYNC_HOR_HIGH_ACT;\r
64         if (VSYNC_POSITIVE)\r
65                 mode->sync |= FB_SYNC_VERT_HIGH_ACT;\r
66         mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *\r
67                                      (V_ACTIVE + V_BLANKING));\r
68         if (INTERLACED) {\r
69                 mode->yres *= 2;\r
70                 mode->upper_margin *= 2;\r
71                 mode->lower_margin *= 2;\r
72                 mode->vsync_len *= 2;\r
73                 mode->vmode |= FB_VMODE_INTERLACED;\r
74         }\r
75         mode->flag = FB_MODE_IS_DETAILED;\r
76 \r
77         hdmi_edid_debug("<<<<<<<<Detailed Time>>>>>>>>>\n");\r
78         hdmi_edid_debug("%d KHz Refresh %d Hz",  PIXEL_CLOCK/1000, mode->refresh);\r
79         hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,\r
80                H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);\r
81         hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,\r
82                V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);\r
83         hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",\r
84                (VSYNC_POSITIVE) ? "+" : "-");\r
85         return E_HDMI_EDID_SUCCESS;\r
86 }\r
87 \r
88 static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid)\r
89 {\r
90         int rc, i;\r
91         \r
92         if(buf == NULL || extend_num == NULL)\r
93                 return E_HDMI_EDID_PARAM;\r
94                 \r
95         #ifdef DEBUG    \r
96         for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)\r
97         {\r
98                 hdmi_edid_debug("%02x ", buf[i]&0xff);\r
99                 if((i+1) % 16 == 0)\r
100                         hdmi_edid_debug("\n");\r
101         }\r
102         #endif\r
103         \r
104         // Check first 8 byte to ensure it is an edid base block.\r
105         if( buf[0] != 0x00 ||\r
106             buf[1] != 0xFF ||\r
107             buf[2] != 0xFF ||\r
108             buf[3] != 0xFF ||\r
109             buf[4] != 0xFF ||\r
110             buf[5] != 0xFF ||\r
111             buf[6] != 0xFF ||\r
112             buf[7] != 0x00)\r
113     {\r
114         hdmi_edid_error("[EDID] check header error\n");\r
115         return E_HDMI_EDID_HEAD;\r
116     }\r
117     \r
118     *extend_num = buf[0x7e];\r
119     #ifdef DEBUG\r
120     hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]);\r
121     #endif\r
122     \r
123     // Checksum\r
124     rc = hdmi_edid_checksum(buf);\r
125     if( rc != E_HDMI_EDID_SUCCESS)\r
126     {\r
127         hdmi_edid_error("[EDID] base block checksum error\n");\r
128         return E_HDMI_EDID_CHECKSUM;\r
129     }\r
130 \r
131         pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);\r
132         if(pedid->specs == NULL)\r
133                 return E_HDMI_EDID_NOMEMORY;\r
134                 \r
135         fb_edid_to_monspecs(buf, pedid->specs);\r
136         \r
137     return E_HDMI_EDID_SUCCESS;\r
138 }\r
139 \r
140 // Parse CEA Short Video Descriptor\r
141 static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid)\r
142 {\r
143         const struct fb_videomode *mode;\r
144         int count, i, j, vic;\r
145 \r
146         count = buf[0] & 0x1F;\r
147         for(i = 0; i < count; i++)\r
148         {\r
149                 hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n", buf[1 + i], buf[1 + i] & 0x7f, buf[1 + i] >> 7);\r
150                 vic = buf[1 + i] & 0x7f;\r
151                 for(j = 0; j < ARRAY_SIZE(double_aspect_vic); j++)\r
152                 {\r
153                         if(vic == double_aspect_vic[j])\r
154                         {       \r
155                                 vic--;\r
156                                 break;\r
157                         }\r
158                 }\r
159                 if(vic)\r
160                 {\r
161                         mode = hdmi_vic_to_videomode(vic);\r
162                         if(mode)\r
163                         {       \r
164                                 hdmi_add_videomode(mode, &pedid->modelist);\r
165                         }\r
166                 }\r
167         }\r
168         return 0;\r
169 }\r
170 \r
171 // Parse CEA Short Audio Descriptor\r
172 static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid)\r
173 {\r
174         int i, count;\r
175         \r
176         count = buf[0] & 0x1F;\r
177         pedid->audio = kmalloc((count/3)*sizeof(struct hdmi_audio), GFP_KERNEL);\r
178         if(pedid->audio == NULL)\r
179                 return E_HDMI_EDID_NOMEMORY;\r
180         pedid->audio_num = count/3;\r
181         for(i = 0; i < pedid->audio_num; i++)\r
182         {\r
183                 pedid->audio[i].type = (buf[1 + i*3] >> 3) & 0x0F;\r
184                 pedid->audio[i].channel = (buf[1 + i*3] & 0x07) + 1;\r
185                 pedid->audio[i].rate = buf[1 + i*3 + 1];\r
186                 if(pedid->audio[i].type == HDMI_AUDIO_LPCM)//LPCM \r
187                 {\r
188                         pedid->audio[i].word_length = buf[1 + i*3 + 2];\r
189                 }\r
190 //              printk("[EDID-CEA] type %d channel %d rate %d word length %d\n", \r
191 //                      pedid->audio[i].type, pedid->audio[i].channel, pedid->audio[i].rate, pedid->audio[i].word_length);\r
192         }\r
193         return E_HDMI_EDID_SUCCESS;\r
194 }\r
195 // Parse CEA 861 Serial Extension.\r
196 static int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_edid *pedid)\r
197 {\r
198         unsigned int ddc_offset, native_dtd_num, cur_offset = 4;\r
199         unsigned int underscan_support, baseaudio_support;\r
200         unsigned int tag, IEEEOUI = 0;\r
201 //      unsigned int supports_ai,  dc_48bit, dc_36bit, dc_30bit, dc_y444;\r
202 //      unsigned char vic;\r
203         \r
204         if(buf == NULL)\r
205                 return E_HDMI_EDID_PARAM;\r
206                 \r
207         // Check ces extension version\r
208         if(buf[1] != 3)\r
209         {\r
210                 hdmi_edid_error("[EDID-CEA] error version.\n");\r
211                 return E_HDMI_EDID_VERSION;\r
212         }\r
213         \r
214         ddc_offset = buf[2];\r
215         underscan_support = (buf[3] >> 7) & 0x01;\r
216         baseaudio_support = (buf[3] >> 6) & 0x01;\r
217         pedid->ycbcr444 = (buf[3] >> 5) & 0x01;\r
218         pedid->ycbcr422 = (buf[3] >> 4) & 0x01;\r
219         native_dtd_num = buf[3] & 0x0F;\r
220         pedid->base_audio_support = baseaudio_support;\r
221         \r
222         // Parse data block\r
223         while(cur_offset < ddc_offset)\r
224         {\r
225                 tag = buf[cur_offset] >> 5;\r
226                 switch(tag)\r
227                 {\r
228                         case 0x02:      // Video Data Block\r
229                                 hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n");\r
230                                 hdmi_edid_get_cea_svd(buf + cur_offset, pedid);\r
231                                 break;\r
232                         case 0x01:      // Audio Data Block\r
233                                 hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n");\r
234                                 hdmi_edid_parse_cea_sad(buf + cur_offset, pedid);\r
235                                 break;\r
236                         case 0x04:      // Speaker Allocation Data Block\r
237                                 hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocatio Data Block.\n");\r
238                                 break;\r
239                         case 0x03:      // Vendor Specific Data Block\r
240                                 hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n");\r
241 \r
242                                 IEEEOUI = buf[cur_offset + 2 + 1];\r
243                                 IEEEOUI <<= 8;\r
244                                 IEEEOUI += buf[cur_offset + 1 + 1];\r
245                                 IEEEOUI <<= 8;\r
246                                 IEEEOUI += buf[cur_offset + 1];\r
247                                 hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI);\r
248                                 if(IEEEOUI == 0x0c03)\r
249                                         pedid->sink_hdmi = 1;\r
250 //                              if(count > 5)\r
251 //                              {\r
252 //                                      pedid->deepcolor = (buf[cur_offset + 5] >> 3) & 0x0F;\r
253 //                                      supports_ai = buf[cur_offset + 5] >> 7;\r
254 //                                      dc_48bit = (buf[cur_offset + 5] >> 6) & 0x1;\r
255 //                                      dc_36bit = (buf[cur_offset + 5] >> 5) & 0x1;\r
256 //                                      dc_30bit = (buf[cur_offset + 5] >> 4) & 0x1;\r
257 //                                      dc_y444 = (buf[cur_offset + 5] >> 3) & 0x1;\r
258 //                                      hdmi_edid_debug("[EDID-CEA] supports_ai %d dc_48bit %d dc_36bit %d dc_30bit %d dc_y444 %d \n", supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444);\r
259 //                              }\r
260 //                              if(count > 6)\r
261 //                                      pedid->maxtmdsclock = buf[cur_offset + 6] * 5000000;\r
262 //                              if(count > 7)\r
263 //                              {\r
264 //                                      pedid->latency_fields_present = (buf[cur_offset + 7] & 0x80) ? 1:0;\r
265 //                                      pedid->i_latency_fields_present = (buf[cur_offset + 7] & 0x40) ? 1:0;\r
266 //                              }\r
267 //                              if(count > 9 && pedid->latency_fields_present)\r
268 //                              {\r
269 //                                      pedid->video_latency = buf[cur_offset + 8];\r
270 //                                      pedid->audio_latency = buf[cur_offset + 9];\r
271 //                              }\r
272 //                              if(count > 11 && pedid->i_latency_fields_present)\r
273 //                              {\r
274 //                                      pedid->interlaced_video_latency = buf[cur_offset + 10];\r
275 //                                      pedid->interlaced_audio_latency = buf[cur_offset + 11];\r
276 //                              }\r
277                                 break;          \r
278                         case 0x05:      // VESA DTC Data Block\r
279                                 hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n");\r
280                                 break;\r
281                         case 0x07:      // Use Extended Tag\r
282                                 hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n");\r
283                                 break;\r
284                         default:\r
285                                 hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n");\r
286                                 break;\r
287                 }\r
288                 cur_offset += (buf[cur_offset] & 0x1F) + 1;\r
289         }\r
290 #if 1   \r
291 {\r
292         // Parse DTD\r
293         struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL);\r
294         if(vmode == NULL)\r
295                 return E_HDMI_EDID_SUCCESS; \r
296         while(ddc_offset < HDMI_EDID_BLOCK_SIZE - 2)    //buf[126] = 0 and buf[127] = checksum\r
297         {\r
298                 if(!buf[ddc_offset] && !buf[ddc_offset + 1])\r
299                         break;\r
300                 memset(vmode, 0, sizeof(struct fb_videomode));\r
301                 hdmi_edid_parse_dtd(buf + ddc_offset, vmode);\r
302                 hdmi_add_videomode(vmode, &pedid->modelist);\r
303                 ddc_offset += 18;\r
304         }\r
305         kfree(vmode);\r
306 }\r
307 #endif\r
308         return E_HDMI_EDID_SUCCESS;\r
309 }\r
310 \r
311 static int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_edid *pedid)\r
312 {\r
313         int rc;\r
314         \r
315         if(buf == NULL || pedid == NULL)\r
316                 return E_HDMI_EDID_PARAM;\r
317                 \r
318         // Checksum\r
319     rc = hdmi_edid_checksum(buf);\r
320     if( rc != E_HDMI_EDID_SUCCESS)\r
321     {\r
322         hdmi_edid_error("[EDID] extensions block checksum error\n");\r
323         return E_HDMI_EDID_CHECKSUM;\r
324     }\r
325     \r
326     switch(buf[0])\r
327     {\r
328         case 0xF0:\r
329                 hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n");\r
330                 break;\r
331         case 0x02:\r
332                 hdmi_edid_debug("[EDID-EXTEND] It is a  CEA 861 Series Extension.\n");\r
333                 hdmi_edid_parse_extensions_cea(buf, pedid);\r
334                 break;\r
335         case 0x10:\r
336                 hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n");\r
337                 break;\r
338         case 0x40:\r
339                 hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n");\r
340                 break;\r
341         case 0x50:\r
342                 hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n");\r
343                 break;\r
344         case 0x60:\r
345                 hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n");\r
346                 break;\r
347         default:\r
348                 hdmi_edid_error("[EDID-EXTEND] Unkowned extension.\n");\r
349                 return E_HDMI_EDID_UNKOWNDATA;\r
350     }\r
351     \r
352     return E_HDMI_EDID_SUCCESS;\r
353 }\r
354 \r
355 \r
356 int hdmi_sys_parse_edid(struct hdmi* hdmi)\r
357 {\r
358         struct hdmi_edid *pedid;\r
359         unsigned char *buff = NULL;\r
360         int rc = HDMI_ERROR_SUCESS, extendblock = 0, i;\r
361         \r
362         if(hdmi == NULL)\r
363                 return HDMI_ERROR_FALSE;\r
364 \r
365         pedid = &(hdmi->edid);\r
366         memset(pedid, 0, sizeof(struct hdmi_edid));\r
367         INIT_LIST_HEAD(&pedid->modelist);\r
368         \r
369         buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL);\r
370         if(buff == NULL)\r
371         {               \r
372                 hdmi_dbg(hdmi->dev, "[%s] can not allocate memory for edid buff.\n", __FUNCTION__);\r
373                 return -1;\r
374         }\r
375         // Read base block edid.\r
376         memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);\r
377         rc = hdmi->read_edid(0, buff);\r
378         if(rc)\r
379         {\r
380                 dev_err(hdmi->dev, "[HDMI] read edid base block error\n");\r
381                 goto out;\r
382         }\r
383         rc = hdmi_edid_parse_base(buff, &extendblock, pedid);\r
384         if(rc)\r
385         {\r
386                 dev_err(hdmi->dev, "[HDMI] parse edid base block error\n");\r
387                 goto out;\r
388         }\r
389         for(i = 1; i < extendblock + 1; i++)\r
390         {\r
391                 memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);\r
392                 rc = hdmi->read_edid(i, buff);\r
393                 if(rc)\r
394                 {\r
395                         printk("[HDMI] read edid block %d error\n", i); \r
396                         goto out;\r
397                 }\r
398                 rc = hdmi_edid_parse_extensions(buff, pedid);\r
399                 if(rc)\r
400                 {\r
401                         dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n", i);\r
402                         continue;\r
403                 }\r
404         }\r
405 out:\r
406         if(buff)\r
407                 kfree(buff);\r
408         rc = hdmi_ouputmode_select(hdmi, rc);\r
409         return rc;\r
410 }\r