-#include "rk_hdmi.h"\r
-#include "../../edid.h"\r
-\r
-#define hdmi_edid_error(fmt, ...) \\r
- printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)\r
-\r
-#if 0\r
-#define hdmi_edid_debug(fmt, ...) \\r
- printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)\r
-#else\r
-#define hdmi_edid_debug(fmt, ...) \r
-#endif\r
-\r
-typedef enum HDMI_EDID_ERRORCODE\r
-{\r
- E_HDMI_EDID_SUCCESS = 0,\r
- E_HDMI_EDID_PARAM,\r
- E_HDMI_EDID_HEAD,\r
- E_HDMI_EDID_CHECKSUM,\r
- E_HDMI_EDID_VERSION,\r
- E_HDMI_EDID_UNKOWNDATA,\r
- E_HDMI_EDID_NOMEMORY\r
-}HDMI_EDID_ErrorCode;\r
-\r
-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
-static int hdmi_edid_checksum(unsigned char *buf)\r
-{\r
- int i;\r
- int checksum = 0;\r
- \r
- for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)\r
- checksum += buf[i]; \r
- \r
- checksum &= 0xff;\r
- \r
- if(checksum == 0)\r
- return E_HDMI_EDID_SUCCESS;\r
- else\r
- return E_HDMI_EDID_CHECKSUM;\r
-}\r
-\r
-/*\r
- @Des Parse Detail Timing Descriptor.\r
- @Param buf : pointer to DTD data.\r
- @Param pvic: VIC of DTD descripted.\r
- */\r
-static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode)\r
-{\r
- mode->xres = H_ACTIVE;\r
- mode->yres = V_ACTIVE;\r
- mode->pixclock = PIXEL_CLOCK;\r
-// mode->pixclock /= 1000;\r
-// mode->pixclock = KHZ2PICOS(mode->pixclock);\r
- mode->right_margin = H_SYNC_OFFSET;\r
- mode->left_margin = (H_ACTIVE + H_BLANKING) -\r
- (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);\r
- mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -\r
- V_SYNC_WIDTH;\r
- mode->lower_margin = V_SYNC_OFFSET;\r
- mode->hsync_len = H_SYNC_WIDTH;\r
- mode->vsync_len = V_SYNC_WIDTH;\r
- if (HSYNC_POSITIVE)\r
- mode->sync |= FB_SYNC_HOR_HIGH_ACT;\r
- if (VSYNC_POSITIVE)\r
- mode->sync |= FB_SYNC_VERT_HIGH_ACT;\r
- mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *\r
- (V_ACTIVE + V_BLANKING));\r
- if (INTERLACED) {\r
- mode->yres *= 2;\r
- mode->upper_margin *= 2;\r
- mode->lower_margin *= 2;\r
- mode->vsync_len *= 2;\r
- mode->vmode |= FB_VMODE_INTERLACED;\r
- }\r
- mode->flag = FB_MODE_IS_DETAILED;\r
-\r
- hdmi_edid_debug("<<<<<<<<Detailed Time>>>>>>>>>\n");\r
- hdmi_edid_debug("%d KHz Refresh %d Hz", PIXEL_CLOCK/1000, mode->refresh);\r
- hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,\r
- H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);\r
- hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,\r
- V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);\r
- hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",\r
- (VSYNC_POSITIVE) ? "+" : "-");\r
- return E_HDMI_EDID_SUCCESS;\r
-}\r
-\r
-static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid)\r
-{\r
- int rc, i;\r
- \r
- if(buf == NULL || extend_num == NULL)\r
- return E_HDMI_EDID_PARAM;\r
- \r
- #ifdef DEBUG \r
- for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)\r
- {\r
- hdmi_edid_debug("%02x ", buf[i]&0xff);\r
- if((i+1) % 16 == 0)\r
- hdmi_edid_debug("\n");\r
- }\r
- #endif\r
- \r
- // Check first 8 byte to ensure it is an edid base block.\r
- if( buf[0] != 0x00 ||\r
- buf[1] != 0xFF ||\r
- buf[2] != 0xFF ||\r
- buf[3] != 0xFF ||\r
- buf[4] != 0xFF ||\r
- buf[5] != 0xFF ||\r
- buf[6] != 0xFF ||\r
- buf[7] != 0x00)\r
- {\r
- hdmi_edid_error("[EDID] check header error\n");\r
- return E_HDMI_EDID_HEAD;\r
- }\r
- \r
- *extend_num = buf[0x7e];\r
- #ifdef DEBUG\r
- hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]);\r
- #endif\r
- \r
- // Checksum\r
- rc = hdmi_edid_checksum(buf);\r
- if( rc != E_HDMI_EDID_SUCCESS)\r
- {\r
- hdmi_edid_error("[EDID] base block checksum error\n");\r
- return E_HDMI_EDID_CHECKSUM;\r
- }\r
-\r
- pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);\r
- if(pedid->specs == NULL)\r
- return E_HDMI_EDID_NOMEMORY;\r
- \r
- fb_edid_to_monspecs(buf, pedid->specs);\r
- \r
- return E_HDMI_EDID_SUCCESS;\r
-}\r
-\r
-// Parse CEA Short Video Descriptor\r
-static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid)\r
-{\r
- const struct fb_videomode *mode;\r
- int count, i, j, vic;\r
-\r
- count = buf[0] & 0x1F;\r
- for(i = 0; i < count; i++)\r
- {\r
- hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n", buf[1 + i], buf[1 + i] & 0x7f, buf[1 + i] >> 7);\r
- vic = buf[1 + i] & 0x7f;\r
- for(j = 0; j < ARRAY_SIZE(double_aspect_vic); j++)\r
- {\r
- if(vic == double_aspect_vic[j])\r
- { \r
- vic--;\r
- break;\r
- }\r
- }\r
- if(vic)\r
- {\r
- mode = hdmi_vic_to_videomode(vic);\r
- if(mode)\r
- { \r
- hdmi_add_videomode(mode, &pedid->modelist);\r
- }\r
- }\r
- }\r
- return 0;\r
-}\r
-\r
-// Parse CEA Short Audio Descriptor\r
-static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid)\r
-{\r
- int i, count;\r
- \r
- count = buf[0] & 0x1F;\r
- pedid->audio = kmalloc((count/3)*sizeof(struct hdmi_audio), GFP_KERNEL);\r
- if(pedid->audio == NULL)\r
- return E_HDMI_EDID_NOMEMORY;\r
- pedid->audio_num = count/3;\r
- for(i = 0; i < pedid->audio_num; i++)\r
- {\r
- pedid->audio[i].type = (buf[1 + i*3] >> 3) & 0x0F;\r
- pedid->audio[i].channel = (buf[1 + i*3] & 0x07) + 1;\r
- pedid->audio[i].rate = buf[1 + i*3 + 1];\r
- if(pedid->audio[i].type == HDMI_AUDIO_LPCM)//LPCM \r
- {\r
- pedid->audio[i].word_length = buf[1 + i*3 + 2];\r
- }\r
-// printk("[EDID-CEA] type %d channel %d rate %d word length %d\n", \r
-// pedid->audio[i].type, pedid->audio[i].channel, pedid->audio[i].rate, pedid->audio[i].word_length);\r
- }\r
- return E_HDMI_EDID_SUCCESS;\r
-}\r
-// Parse CEA 861 Serial Extension.\r
-static int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_edid *pedid)\r
-{\r
- unsigned int ddc_offset, native_dtd_num, cur_offset = 4;\r
- unsigned int underscan_support, baseaudio_support;\r
- unsigned int tag, IEEEOUI = 0;\r
-// unsigned int supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444;\r
-// unsigned char vic;\r
- \r
- if(buf == NULL)\r
- return E_HDMI_EDID_PARAM;\r
- \r
- // Check ces extension version\r
- if(buf[1] != 3)\r
- {\r
- hdmi_edid_error("[EDID-CEA] error version.\n");\r
- return E_HDMI_EDID_VERSION;\r
- }\r
- \r
- ddc_offset = buf[2];\r
- underscan_support = (buf[3] >> 7) & 0x01;\r
- baseaudio_support = (buf[3] >> 6) & 0x01;\r
- pedid->ycbcr444 = (buf[3] >> 5) & 0x01;\r
- pedid->ycbcr422 = (buf[3] >> 4) & 0x01;\r
- native_dtd_num = buf[3] & 0x0F;\r
- pedid->base_audio_support = baseaudio_support;\r
- \r
- // Parse data block\r
- while(cur_offset < ddc_offset)\r
- {\r
- tag = buf[cur_offset] >> 5;\r
- switch(tag)\r
- {\r
- case 0x02: // Video Data Block\r
- hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n");\r
- hdmi_edid_get_cea_svd(buf + cur_offset, pedid);\r
- break;\r
- case 0x01: // Audio Data Block\r
- hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n");\r
- hdmi_edid_parse_cea_sad(buf + cur_offset, pedid);\r
- break;\r
- case 0x04: // Speaker Allocation Data Block\r
- hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocatio Data Block.\n");\r
- break;\r
- case 0x03: // Vendor Specific Data Block\r
- hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n");\r
-\r
- IEEEOUI = buf[cur_offset + 2 + 1];\r
- IEEEOUI <<= 8;\r
- IEEEOUI += buf[cur_offset + 1 + 1];\r
- IEEEOUI <<= 8;\r
- IEEEOUI += buf[cur_offset + 1];\r
- hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI);\r
- if(IEEEOUI == 0x0c03)\r
- pedid->sink_hdmi = 1;\r
-// if(count > 5)\r
-// {\r
-// pedid->deepcolor = (buf[cur_offset + 5] >> 3) & 0x0F;\r
-// supports_ai = buf[cur_offset + 5] >> 7;\r
-// dc_48bit = (buf[cur_offset + 5] >> 6) & 0x1;\r
-// dc_36bit = (buf[cur_offset + 5] >> 5) & 0x1;\r
-// dc_30bit = (buf[cur_offset + 5] >> 4) & 0x1;\r
-// dc_y444 = (buf[cur_offset + 5] >> 3) & 0x1;\r
-// 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
-// }\r
-// if(count > 6)\r
-// pedid->maxtmdsclock = buf[cur_offset + 6] * 5000000;\r
-// if(count > 7)\r
-// {\r
-// pedid->latency_fields_present = (buf[cur_offset + 7] & 0x80) ? 1:0;\r
-// pedid->i_latency_fields_present = (buf[cur_offset + 7] & 0x40) ? 1:0;\r
-// }\r
-// if(count > 9 && pedid->latency_fields_present)\r
-// {\r
-// pedid->video_latency = buf[cur_offset + 8];\r
-// pedid->audio_latency = buf[cur_offset + 9];\r
-// }\r
-// if(count > 11 && pedid->i_latency_fields_present)\r
-// {\r
-// pedid->interlaced_video_latency = buf[cur_offset + 10];\r
-// pedid->interlaced_audio_latency = buf[cur_offset + 11];\r
-// }\r
- break; \r
- case 0x05: // VESA DTC Data Block\r
- hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n");\r
- break;\r
- case 0x07: // Use Extended Tag\r
- hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n");\r
- break;\r
- default:\r
- hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n");\r
- break;\r
- }\r
- cur_offset += (buf[cur_offset] & 0x1F) + 1;\r
- }\r
-#if 1 \r
-{\r
- // Parse DTD\r
- struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL);\r
- if(vmode == NULL)\r
- return E_HDMI_EDID_SUCCESS; \r
- while(ddc_offset < HDMI_EDID_BLOCK_SIZE - 2) //buf[126] = 0 and buf[127] = checksum\r
- {\r
- if(!buf[ddc_offset] && !buf[ddc_offset + 1])\r
- break;\r
- memset(vmode, 0, sizeof(struct fb_videomode));\r
- hdmi_edid_parse_dtd(buf + ddc_offset, vmode);\r
- hdmi_add_videomode(vmode, &pedid->modelist);\r
- ddc_offset += 18;\r
- }\r
- kfree(vmode);\r
-}\r
-#endif\r
- return E_HDMI_EDID_SUCCESS;\r
-}\r
-\r
-static int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_edid *pedid)\r
-{\r
- int rc;\r
- \r
- if(buf == NULL || pedid == NULL)\r
- return E_HDMI_EDID_PARAM;\r
- \r
- // Checksum\r
- rc = hdmi_edid_checksum(buf);\r
- if( rc != E_HDMI_EDID_SUCCESS)\r
- {\r
- hdmi_edid_error("[EDID] extensions block checksum error\n");\r
- return E_HDMI_EDID_CHECKSUM;\r
- }\r
- \r
- switch(buf[0])\r
- {\r
- case 0xF0:\r
- hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n");\r
- break;\r
- case 0x02:\r
- hdmi_edid_debug("[EDID-EXTEND] It is a CEA 861 Series Extension.\n");\r
- hdmi_edid_parse_extensions_cea(buf, pedid);\r
- break;\r
- case 0x10:\r
- hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n");\r
- break;\r
- case 0x40:\r
- hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n");\r
- break;\r
- case 0x50:\r
- hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n");\r
- break;\r
- case 0x60:\r
- hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n");\r
- break;\r
- default:\r
- hdmi_edid_error("[EDID-EXTEND] Unkowned extension.\n");\r
- return E_HDMI_EDID_UNKOWNDATA;\r
- }\r
- \r
- return E_HDMI_EDID_SUCCESS;\r
-}\r
-\r
-\r
-int hdmi_sys_parse_edid(struct hdmi* hdmi)\r
-{\r
- struct hdmi_edid *pedid;\r
- unsigned char *buff = NULL;\r
- int rc = HDMI_ERROR_SUCESS, extendblock = 0, i;\r
- \r
- if(hdmi == NULL)\r
- return HDMI_ERROR_FALSE;\r
-\r
- pedid = &(hdmi->edid);\r
- memset(pedid, 0, sizeof(struct hdmi_edid));\r
- INIT_LIST_HEAD(&pedid->modelist);\r
- \r
- buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL);\r
- if(buff == NULL)\r
- { \r
- hdmi_dbg(hdmi->dev, "[%s] can not allocate memory for edid buff.\n", __FUNCTION__);\r
- return -1;\r
- }\r
- // Read base block edid.\r
- memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);\r
- rc = hdmi->read_edid(0, buff);\r
- if(rc)\r
- {\r
- dev_err(hdmi->dev, "[HDMI] read edid base block error\n");\r
- goto out;\r
- }\r
- rc = hdmi_edid_parse_base(buff, &extendblock, pedid);\r
- if(rc)\r
- {\r
- dev_err(hdmi->dev, "[HDMI] parse edid base block error\n");\r
- goto out;\r
- }\r
- for(i = 1; i < extendblock + 1; i++)\r
- {\r
- memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);\r
- rc = hdmi->read_edid(i, buff);\r
- if(rc)\r
- {\r
- printk("[HDMI] read edid block %d error\n", i); \r
- goto out;\r
- }\r
- rc = hdmi_edid_parse_extensions(buff, pedid);\r
- if(rc)\r
- {\r
- dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n", i);\r
- continue;\r
- }\r
- }\r
-out:\r
- if(buff)\r
- kfree(buff);\r
- rc = hdmi_ouputmode_select(hdmi, rc);\r
- return rc;\r
-}\r
+#include "rk_hdmi.h"
+#include "../../edid.h"
+
+#define hdmi_edid_error(fmt, ...) \
+ printk(pr_fmt(fmt), ##__VA_ARGS__)
+
+#if 0
+#define hdmi_edid_debug(fmt, ...) \
+ printk(pr_fmt(fmt), ##__VA_ARGS__)
+#else
+#define hdmi_edid_debug(fmt, ...)
+#endif
+
+enum HDMI_EDID_ERRORCODE {
+ E_HDMI_EDID_SUCCESS = 0,
+ E_HDMI_EDID_PARAM,
+ E_HDMI_EDID_HEAD,
+ E_HDMI_EDID_CHECKSUM,
+ E_HDMI_EDID_VERSION,
+ E_HDMI_EDID_UNKOWNDATA,
+ E_HDMI_EDID_NOMEMORY
+};
+
+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
+};
+
+static int hdmi_edid_checksum(unsigned char *buf)
+{
+ int i;
+ int checksum = 0;
+
+ for (i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)
+ checksum += buf[i];
+
+ checksum &= 0xff;
+
+ if (checksum == 0)
+ return E_HDMI_EDID_SUCCESS;
+ else
+ return E_HDMI_EDID_CHECKSUM;
+}
+
+/*
+ * @Des Parse Detail Timing Descriptor.
+ * @Param buf : pointer to DTD data.
+ * @Param pvic: VIC of DTD descripted.
+ */
+static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode)
+{
+ mode->xres = H_ACTIVE;
+ mode->yres = V_ACTIVE;
+ mode->pixclock = PIXEL_CLOCK;
+/*
+ mode->pixclock /= 1000;
+ mode->pixclock = KHZ2PICOS(mode->pixclock);
+*/
+ mode->right_margin = H_SYNC_OFFSET;
+ mode->left_margin = (H_ACTIVE + H_BLANKING) -
+ (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+ mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
+ V_SYNC_WIDTH;
+ mode->lower_margin = V_SYNC_OFFSET;
+ mode->hsync_len = H_SYNC_WIDTH;
+ mode->vsync_len = V_SYNC_WIDTH;
+ if (HSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (VSYNC_POSITIVE)
+ mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
+ (V_ACTIVE + V_BLANKING));
+ if (INTERLACED) {
+ mode->yres *= 2;
+ mode->upper_margin *= 2;
+ mode->lower_margin *= 2;
+ mode->vsync_len *= 2;
+ mode->vmode |= FB_VMODE_INTERLACED;
+ }
+ mode->flag = FB_MODE_IS_DETAILED;
+
+ hdmi_edid_debug("<<<<<<<<Detailed Time>>>>>>>>>\n");
+ hdmi_edid_debug("%d KHz Refresh %d Hz", PIXEL_CLOCK/1000,
+ mode->refresh);
+ hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
+ H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+ hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
+ V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+ hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
+ (VSYNC_POSITIVE) ? "+" : "-");
+ return E_HDMI_EDID_SUCCESS;
+}
+
+static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num,
+ struct hdmi_edid *pedid)
+{
+ int rc;
+#ifdef DEBUG
+ int i = 0;
+#endif
+
+ if (buf == NULL || extend_num == NULL)
+ return E_HDMI_EDID_PARAM;
+
+#ifdef DEBUG
+ for (i = 0; i < HDMI_EDID_BLOCK_SIZE; i++) {
+ hdmi_edid_debug("%02x ", buf[i]&0xff);
+ if ((i+1) % 16 == 0)
+ hdmi_edid_debug("\n");
+ }
+#endif
+
+ /* Check first 8 byte to ensure it is an edid base block. */
+ if (buf[0] != 0x00 ||
+ buf[1] != 0xFF ||
+ buf[2] != 0xFF ||
+ buf[3] != 0xFF ||
+ buf[4] != 0xFF ||
+ buf[5] != 0xFF ||
+ buf[6] != 0xFF ||
+ buf[7] != 0x00) {
+ hdmi_edid_error("[EDID] check header error\n");
+ return E_HDMI_EDID_HEAD;
+ }
+
+ *extend_num = buf[0x7e];
+#ifdef DEBUG
+ hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]);
+#endif
+
+ /* Checksum */
+ rc = hdmi_edid_checksum(buf);
+ if (rc != E_HDMI_EDID_SUCCESS) {
+ hdmi_edid_error("[EDID] base block checksum error\n");
+ return E_HDMI_EDID_CHECKSUM;
+ }
+
+ pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);
+ if (pedid->specs == NULL)
+ return E_HDMI_EDID_NOMEMORY;
+
+ fb_edid_to_monspecs(buf, pedid->specs);
+
+ return E_HDMI_EDID_SUCCESS;
+}
+
+/* Parse CEA Short Video Descriptor */
+static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid)
+{
+ const struct fb_videomode *mode;
+ int count, i, j, vic;
+
+ count = buf[0] & 0x1F;
+ for (i = 0; i < count; i++) {
+ hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n",
+ buf[1 + i],
+ buf[1 + i] & 0x7f,
+ buf[1 + i] >> 7);
+ #ifndef HDMI_VERSION_2
+ vic = buf[1 + i] & 0x7f;
+ #else
+ vic = buf[1 + i] & 0xff;
+ #endif
+ for (j = 0; j < ARRAY_SIZE(double_aspect_vic); j++) {
+ if (vic == double_aspect_vic[j]) {
+ vic--;
+ break;
+ }
+ }
+ if (vic) {
+ mode = hdmi_vic_to_videomode(vic);
+ if (mode)
+ hdmi_add_videomode(mode, &pedid->modelist);
+ }
+ }
+ return 0;
+}
+
+/* Parse CEA Short Audio Descriptor */
+static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid)
+{
+ int i, count;
+
+ count = buf[0] & 0x1F;
+ pedid->audio = kmalloc((count / 3) * sizeof(struct hdmi_audio),
+ GFP_KERNEL);
+ if (pedid->audio == NULL)
+ return E_HDMI_EDID_NOMEMORY;
+
+ pedid->audio_num = count / 3;
+ for (i = 0; i < pedid->audio_num; i++) {
+ pedid->audio[i].type = (buf[1 + i * 3] >> 3) & 0x0F;
+ pedid->audio[i].channel = (buf[1 + i * 3] & 0x07) + 1;
+ pedid->audio[i].rate = buf[1 + i * 3 + 1];
+ if (pedid->audio[i].type == HDMI_AUDIO_LPCM) /* LPCM */
+ pedid->audio[i].word_length = buf[1 + i * 3 + 2];
+/*
+ printk("[EDID-CEA] type %d channel %d rate %d word length %d\n",
+ pedid->audio[i].type, pedid->audio[i].channel,
+ pedid->audio[i].rate, pedid->audio[i].word_length);
+*/
+ }
+ return E_HDMI_EDID_SUCCESS;
+}
+
+/* Parse CEA Vendor Specific Data Block */
+static int hdmi_edid_parse_cea_sdb(unsigned char *buf, struct hdmi_edid *pedid)
+{
+ unsigned int count = 0, cur_offset = 0, i = 0;
+ unsigned int IEEEOUI = 0;
+ unsigned int supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444;
+ unsigned int len_3d, len_4k;
+ unsigned char vic = 0;
+ const struct fb_videomode *mode;
+
+ count = buf[0] & 0x1F;
+ IEEEOUI = buf[3];
+ IEEEOUI <<= 8;
+ IEEEOUI += buf[2];
+ IEEEOUI <<= 8;
+ IEEEOUI += buf[1];
+ hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI);
+ if (IEEEOUI == 0x0c03)
+ pedid->sink_hdmi = 1;
+
+ pedid->cecaddress = buf[cur_offset + 5];
+ pedid->cecaddress |= buf[cur_offset + 4] << 8;
+ hdmi_edid_debug("[EDID-CEA] CEC Physical addres is 0x%08x.\n", pedid->cecaddress);
+
+ if (count > 5) {
+ pedid->deepcolor = (buf[6] >> 3) & 0x0F;
+ supports_ai = buf[6] >> 7;
+ dc_48bit = (buf[6] >> 6) & 0x1;
+ dc_36bit = (buf[6] >> 5) & 0x1;
+ dc_30bit = (buf[6] >> 4) & 0x1;
+ dc_y444 = (buf[6] >> 3) & 0x1;
+ hdmi_edid_debug("[EDID-CEA] supports_ai %d\n"
+ "dc_48bit %d dc_36bit %d dc_30bit %d dc_y444 %d\n",
+ supports_ai,
+ dc_48bit, dc_36bit, dc_30bit, dc_y444);
+ }
+ if (count > 6)
+ pedid->maxtmdsclock = buf[7] * 5000000;
+
+ if (count > 7) {
+ pedid->latency_fields_present = (buf[8] & 0x80) ? 1 : 0;
+ pedid->i_latency_fields_present = (buf[8] & 0x40) ? 1 : 0;
+ pedid->video_present = (buf[8] & 0x20) ? 1 : 0;
+ }
+
+ cur_offset = 9;
+ if (count >= cur_offset) {
+ if (pedid->latency_fields_present == 1) {
+ pedid->video_latency = buf[cur_offset++];
+ pedid->audio_latency = buf[cur_offset++];
+ }
+ if (count >= cur_offset && pedid->i_latency_fields_present) {
+ pedid->interlaced_video_latency = buf[cur_offset++];
+ pedid->interlaced_audio_latency = buf[cur_offset++];
+ }
+ }
+
+ if (pedid->video_present == 0)
+ return E_HDMI_EDID_SUCCESS;
+
+ if (count >= cur_offset) {
+ pedid->support_3d = (buf[cur_offset++] & 0x80) ? 1 : 0;
+
+ len_4k = (buf[cur_offset] >> 5) & 0x07;
+ len_3d = buf[cur_offset] & 0x1F;
+ cur_offset++;
+ }
+ if (count >= cur_offset && len_4k > 0) {
+ for (i = 0; i < len_4k; i++) {
+ #ifndef HDMI_VERSION_2
+ vic = buf[cur_offset + i] & 0x7f;
+ if (vic > 0 && vic < 5)
+ vic = (vic == 4) ? 98 : (96 - vic);
+ hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n",
+ buf[cur_offset + i],
+ vic,
+ buf[cur_offset + i] >> 7);
+ #else
+ vic = buf[cur_offset + i] & 0xff;
+ hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n",
+ buf[cur_offset + i], vic);
+ #endif
+ if (vic) {
+ mode = hdmi_vic_to_videomode(vic);
+ if (mode)
+ hdmi_add_videomode(mode,
+ &pedid->modelist);
+ }
+ }
+ cur_offset += i;
+ }
+
+/* TODO Daisen wait to add
+ if (count >= cur_offset && pedid->support_3d && len_3d > 0) {
+
+ }
+*/
+ return E_HDMI_EDID_SUCCESS;
+}
+
+/* Parse CEA 861 Serial Extension. */
+static int hdmi_edid_parse_extensions_cea(unsigned char *buf,
+ struct hdmi_edid *pedid)
+{
+ unsigned int ddc_offset, native_dtd_num, cur_offset = 4;
+ unsigned int underscan_support, baseaudio_support;
+ unsigned int tag;
+
+ if (buf == NULL)
+ return E_HDMI_EDID_PARAM;
+
+ /* Check ces extension version */
+ if (buf[1] != 3) {
+ hdmi_edid_error("[EDID-CEA] error version.\n");
+ return E_HDMI_EDID_VERSION;
+ }
+
+ ddc_offset = buf[2];
+ underscan_support = (buf[3] >> 7) & 0x01;
+ baseaudio_support = (buf[3] >> 6) & 0x01;
+ pedid->ycbcr444 = (buf[3] >> 5) & 0x01;
+ pedid->ycbcr422 = (buf[3] >> 4) & 0x01;
+ native_dtd_num = buf[3] & 0x0F;
+ pedid->base_audio_support = baseaudio_support;
+
+ /* Parse data block */
+ while (cur_offset < ddc_offset) {
+ tag = buf[cur_offset] >> 5;
+ switch (tag) {
+ case 0x02: /* Video Data Block */
+ hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n");
+ hdmi_edid_get_cea_svd(buf + cur_offset, pedid);
+ break;
+ case 0x01: /* Audio Data Block */
+ hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n");
+ hdmi_edid_parse_cea_sad(buf + cur_offset, pedid);
+ break;
+ case 0x04: /* Speaker Allocation Data Block */
+ hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocation Data Block.\n");
+ break;
+ case 0x03: /* Vendor Specific Data Block */
+ hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n");
+ hdmi_edid_parse_cea_sdb(buf + cur_offset, pedid);
+ break;
+ case 0x05: /* VESA DTC Data Block */
+ hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n");
+ break;
+ case 0x07: /* Use Extended Tag */
+ hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n");
+ break;
+ default:
+ hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n");
+ break;
+ }
+ cur_offset += (buf[cur_offset] & 0x1F) + 1;
+ }
+#if 1
+{
+ /* Parse DTD */
+ struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode),
+ GFP_KERNEL);
+ if (vmode == NULL)
+ return E_HDMI_EDID_SUCCESS;
+ /* buf[126] = 0 and buf[127] = checksum */
+ while (ddc_offset < HDMI_EDID_BLOCK_SIZE - 2) {
+ if (!buf[ddc_offset] && !buf[ddc_offset + 1])
+ break;
+ memset(vmode, 0, sizeof(struct fb_videomode));
+ hdmi_edid_parse_dtd(buf + ddc_offset, vmode);
+ hdmi_add_videomode(vmode, &pedid->modelist);
+ ddc_offset += 18;
+ }
+ kfree(vmode);
+}
+#endif
+ return E_HDMI_EDID_SUCCESS;
+}
+
+static int hdmi_edid_parse_extensions(unsigned char *buf,
+ struct hdmi_edid *pedid)
+{
+ int rc;
+
+ if (buf == NULL || pedid == NULL)
+ return E_HDMI_EDID_PARAM;
+
+ /* Checksum */
+ rc = hdmi_edid_checksum(buf);
+ if (rc != E_HDMI_EDID_SUCCESS) {
+ hdmi_edid_error("[EDID] extensions block checksum error\n");
+ return E_HDMI_EDID_CHECKSUM;
+ }
+
+ switch (buf[0]) {
+ case 0xF0:
+ hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n");
+ break;
+ case 0x02:
+ hdmi_edid_debug("[EDID-EXTEND] It is a CEA 861 Series Extension.\n");
+ hdmi_edid_parse_extensions_cea(buf, pedid);
+ break;
+ case 0x10:
+ hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n");
+ break;
+ case 0x40:
+ hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n");
+ break;
+ case 0x50:
+ hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n");
+ break;
+ case 0x60:
+ hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n");
+ break;
+ default:
+ hdmi_edid_error("[EDID-EXTEND] Unkowned extension.\n");
+ return E_HDMI_EDID_UNKOWNDATA;
+ }
+
+ return E_HDMI_EDID_SUCCESS;
+}
+
+
+int hdmi_sys_parse_edid(struct hdmi *hdmi)
+{
+ struct hdmi_edid *pedid;
+ unsigned char *buff = NULL;
+ int rc = HDMI_ERROR_SUCESS, extendblock = 0, i;
+
+ if (hdmi == NULL)
+ return HDMI_ERROR_FALSE;
+
+ pedid = &(hdmi->edid);
+ memset(pedid, 0, sizeof(struct hdmi_edid));
+ INIT_LIST_HEAD(&pedid->modelist);
+
+ buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL);
+ if (buff == NULL) {
+ hdmi_dbg(hdmi->dev,
+ "[%s] can not allocate memory for edid buff.\n",
+ __func__);
+ return -1;
+ }
+
+ /* Read base block edid. */
+ memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);
+ rc = hdmi->read_edid(hdmi, 0, buff);
+ if (rc) {
+ dev_err(hdmi->dev, "[HDMI] read edid base block error\n");
+ goto out;
+ }
+ rc = hdmi_edid_parse_base(buff, &extendblock, pedid);
+ if (rc) {
+ dev_err(hdmi->dev, "[HDMI] parse edid base block error\n");
+ goto out;
+ }
+ for (i = 1; i < extendblock + 1; i++) {
+ memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);
+ rc = hdmi->read_edid(hdmi, i, buff);
+ if (rc) {
+ printk("[HDMI] read edid block %d error\n", i);
+ goto out;
+ }
+ rc = hdmi_edid_parse_extensions(buff, pedid);
+ if (rc) {
+ dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n",
+ i);
+ continue;
+ }
+ }
+out:
+ kfree(buff);
+ rc = hdmi_ouputmode_select(hdmi, rc);
+ return rc;
+}