diff options
author | Jason Chen <b02280@freescale.com> | 2011-01-17 13:51:24 +0800 |
---|---|---|
committer | Jason Chen <b02280@freescale.com> | 2011-01-17 13:51:24 +0800 |
commit | cf5347d7e8d18274f74bc7015a5dc237a8696751 (patch) | |
tree | 6e9ba1793509c1d5a60cd0416e4defbbbaec9996 | |
parent | c51ce7fdab4ea8a89b6a58c7fc41d3b697637412 (diff) |
ENGR00137991-2 mxc edid: add cea ext block parser
add cea ext block parser.
it provide more video modes and the info of device HDMI compatible.
Signed-off-by: Jason Chen <b02280@freescale.com>
-rw-r--r-- | drivers/video/mxc/mxc_edid.c | 246 | ||||
-rw-r--r-- | drivers/video/mxc/mxcfb_sii9022.c | 47 |
2 files changed, 274 insertions, 19 deletions
diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index 234a5d38c111..0289c5253ad7 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -1,5 +1,5 @@ /* - * Copyright 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -27,11 +27,237 @@ * Include files */ #include <linux/i2c.h> +#include <linux/fb.h> +#include <mach/mxc_edid.h> +#include "../edid.h" -#define EDID_LENGTH 128 +#undef DEBUG /* define this for verbose EDID parsing output */ + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk(fmt, ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +const struct fb_videomode cea_modes[64] = { + /* #1: 640x480p@59.94/60Hz */ + [1] = { + NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #3: 720x480p@59.94/60Hz */ + [3] = { + NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #5: 1920x1080i@59.94/60Hz */ + [5] = { + NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, 0, + }, + /* #7: 720(1440)x480iH@59.94/60Hz */ + [7] = { + NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, + FB_VMODE_INTERLACED, 0, + }, + /* #9: 720(1440)x240pH@59.94/60Hz */ + [9] = { + NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #18: 720x576pH@50Hz */ + [18] = { + NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, + FB_VMODE_NONINTERLACED, 0, + }, + /* #19: 1280x720p@50Hz */ + [19] = { + NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, 0, + }, + /* #20: 1920x1080i@50Hz */ + [20] = { + NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, 0, + }, + /* #32: 1920x1080p@23.98/24Hz */ + [32] = { + NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, 0, + }, + /* #35: (2880)x480p4x@59.94/60Hz */ + [35] = { + NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0, + FB_VMODE_NONINTERLACED, 0, + }, +}; + +static void get_detailed_timing(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; + + DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000); + DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, + H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); + DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, + V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING); + DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", + (VSYNC_POSITIVE) ? "+" : "-"); +} + +int mxc_edid_parse_ext_blk(unsigned char *edid, + struct mxc_edid_cfg *cfg, + struct fb_monspecs *specs) +{ + char detail_timming_desc_offset; + struct fb_videomode *mode, *m; + unsigned char index = 0x0; + unsigned char *block; + int i, num = 0; + + if (edid[index++] != 0x2) /* only support cea ext block now */ + return -1; + if (edid[index++] != 0x3) /* only support version 3*/ + return -1; + mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); + if (mode == NULL) + return -1; + + detail_timming_desc_offset = edid[index++]; + + cfg->cea_underscan = (edid[index] >> 7) & 0x1; + cfg->cea_basicaudio = (edid[index] >> 6) & 0x1; + cfg->cea_ycbcr444 = (edid[index] >> 5) & 0x1; + cfg->cea_ycbcr422 = (edid[index] >> 4) & 0x1; + + /* short desc */ + DPRINTK("CEA Short desc timmings\n"); + index++; + while (index < detail_timming_desc_offset) { + unsigned char tagcode, blklen; + + tagcode = (edid[index] >> 5) & 0x7; + blklen = (edid[index]) & 0x1f; + + DPRINTK("Tagcode %x Len %d\n", tagcode, blklen); + + switch (tagcode) { + case 0x2: /*Video data block*/ + { + int cea_idx; + i = 0; + while (i < blklen) { + index++; + cea_idx = edid[index] & 0x7f; + if (cea_idx < ARRAY_SIZE(cea_modes) && + (cea_modes[cea_idx].xres)) { + DPRINTK("Support CEA Format #%d\n", cea_idx); + mode[num] = cea_modes[cea_idx]; + mode[num].flag |= FB_MODE_IS_STANDARD; + num++; + } + i++; + } + break; + } + case 0x3: /*Vendor specific data*/ + { + unsigned char IEEE_reg_iden[3]; + IEEE_reg_iden[0] = edid[index+1]; + IEEE_reg_iden[1] = edid[index+2]; + IEEE_reg_iden[2] = edid[index+3]; + + if ((IEEE_reg_iden[0] == 0x03) && + (IEEE_reg_iden[1] == 0x0c) && + (IEEE_reg_iden[2] == 0x00)) + cfg->hdmi_cap = 1; + index += blklen; + break; + } + case 0x1: /*Audio data block*/ + case 0x4: /*Speaker allocation block*/ + case 0x7: /*User extended block*/ + default: + /* skip */ + index += blklen; + break; + } + + index++; + } + + /* long desc */ + DPRINTK("CEA long desc timmings\n"); + index = detail_timming_desc_offset; + block = edid + index; + while (index < (EDID_LENGTH - DETAILED_TIMING_DESCRIPTION_SIZE)) { + if (!(block[0] == 0x00 && block[1] == 0x00)) { + get_detailed_timing(block, &mode[num]); + num++; + } + block += DETAILED_TIMING_DESCRIPTION_SIZE; + index += DETAILED_TIMING_DESCRIPTION_SIZE; + } + + if (!num) { + kfree(mode); + return 0; + } + + m = kmalloc((num + specs->modedb_len) * + sizeof(struct fb_videomode), GFP_KERNEL); + if (!m) + return 0; + + if (specs->modedb_len) { + memmove(m, specs->modedb, + specs->modedb_len * sizeof(struct fb_videomode)); + kfree(specs->modedb); + } + memmove(m+specs->modedb_len, mode, + num * sizeof(struct fb_videomode)); + kfree(mode); + + specs->modedb_len += num; + specs->modedb = m; + + return 0; +} /* make sure edid has 256 bytes*/ -int read_edid(struct i2c_adapter *adp, unsigned char *edid) +int mxc_edid_read(struct i2c_adapter *adp, unsigned char *edid, + struct mxc_edid_cfg *cfg, struct fb_info *fbi) { u8 buf0[2] = {0, 0}; int dat = 0; @@ -53,6 +279,9 @@ int read_edid(struct i2c_adapter *adp, unsigned char *edid) if (adp == NULL) return -EINVAL; + memset(edid, 0, 256); + memset(cfg, 0, sizeof(struct mxc_edid_cfg)); + buf0[0] = 0x00; dat = i2c_transfer(adp, msg, 2); @@ -67,16 +296,23 @@ int read_edid(struct i2c_adapter *adp, unsigned char *edid) if (edid[1] == 0x00) return -ENOENT; + /* edid first block parsing */ + memset(&fbi->monspecs, 0, sizeof(fbi->monspecs)); + fb_edid_to_monspecs(edid, &fbi->monspecs); + /* need read ext block? Only support one more blk now*/ if (edid[0x7E]) { if (edid[0x7E] > 1) - printk(KERN_WARNING "Edid has %d ext block, \ - but now only support 1 ext blk\n", edid[0x7E]); + DPRINTK("Edid has %d ext block, \ + but now only support 1 ext blk\n", edid[0x7E]); buf0[0] = 0x80; msg[1].buf = edid + EDID_LENGTH; dat = i2c_transfer(adp, msg, 2); if (dat < 0) return dat; + + /* edid ext block parsing */ + mxc_edid_parse_ext_blk(edid + 128, cfg, &fbi->monspecs); } return 0; diff --git a/drivers/video/mxc/mxcfb_sii9022.c b/drivers/video/mxc/mxcfb_sii9022.c index b7104ce43796..273d04a20a6b 100644 --- a/drivers/video/mxc/mxcfb_sii9022.c +++ b/drivers/video/mxc/mxcfb_sii9022.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. */ /* @@ -56,6 +56,7 @@ struct sii9022_data { struct i2c_client *client; struct delayed_work det_work; struct fb_info *fbi; + struct mxc_edid_cfg edid_cfg; u8 cable_plugin; u8 edid[256]; } sii9022; @@ -127,27 +128,41 @@ static void sii9022_setup(struct fb_info *fbi) static int sii9022_read_edid(void) { - int dat, ret; + int old, dat, ret, cnt = 100; - dat = i2c_smbus_read_byte_data(sii9022.client, 0x1A); + old = i2c_smbus_read_byte_data(sii9022.client, 0x1A); - i2c_smbus_write_byte_data(sii9022.client, 0x1A, dat | 0x4); + i2c_smbus_write_byte_data(sii9022.client, 0x1A, old | 0x4); do { + cnt--; msleep(10); dat = i2c_smbus_read_byte_data(sii9022.client, 0x1A); - } while (!(dat & 0x2)); + } while ((!(dat & 0x2)) && cnt); - i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x06); + if (!cnt) { + ret = -1; + goto done; + } + + i2c_smbus_write_byte_data(sii9022.client, 0x1A, old | 0x06); /* edid reading */ - ret = read_edid(sii9022.client->adapter, sii9022.edid); + ret = mxc_edid_read(sii9022.client->adapter, sii9022.edid, + &sii9022.edid_cfg, sii9022.fbi); + cnt = 100; do { - i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x00); + cnt--; + i2c_smbus_write_byte_data(sii9022.client, 0x1A, old & ~0x6); msleep(10); dat = i2c_smbus_read_byte_data(sii9022.client, 0x1A); - } while (dat & 0x6); + } while ((dat & 0x6) && cnt); + + if (!cnt) + ret = -1; +done: + i2c_smbus_write_byte_data(sii9022.client, 0x1A, old); return ret; } @@ -167,9 +182,6 @@ static void det_worker(struct work_struct *work) dev_err(&sii9022.client->dev, "SII9022: read edid fail\n"); else { - /* change fbi modedb */ - memset(&sii9022.fbi->monspecs, 0, sizeof(sii9022.fbi->monspecs)); - fb_edid_to_monspecs(sii9022.edid, &(sii9022.fbi->monspecs)); if (sii9022.fbi->monspecs.modedb_len > 0) { int i; @@ -177,6 +189,7 @@ static void det_worker(struct work_struct *work) fb_add_videomode(&sii9022.fbi->monspecs.modedb[i], &sii9022.fbi->modelist); } + sii9022_poweron(); } } else { sii9022.cable_plugin = 0; @@ -331,14 +344,20 @@ static int sii9022_resume(struct i2c_client *client) static void sii9022_poweron(void) { /* Turn on DVI or HDMI */ - i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x01); + if (sii9022.edid_cfg.hdmi_cap) + i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x01); + else + i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x00); return; } static void sii9022_poweroff(void) { /* disable tmds before changing resolution */ - i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x11); + if (sii9022.edid_cfg.hdmi_cap) + i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x11); + else + i2c_smbus_write_byte_data(sii9022.client, 0x1A, 0x10); return; } |