diff options
Diffstat (limited to 'drivers/video/mxc/ldb.c')
-rw-r--r-- | drivers/video/mxc/ldb.c | 137 |
1 files changed, 135 insertions, 2 deletions
diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index 19de5c541fdd..278475c088f1 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -102,6 +102,8 @@ struct ldb_data { }; static int g_ldb_mode; +static struct i2c_client *ldb_i2c_client[2]; +static u8 g_edid[2][512]; static struct fb_videomode ldb_modedb[] = { { @@ -131,6 +133,8 @@ static struct fb_videomode ldb_modedb[] = { }; static int ldb_modedb_sz = ARRAY_SIZE(ldb_modedb); +static int mxc_ldb_edidread(struct i2c_client *client); + static int bits_per_pixel(int pixel_fmt) { switch (pixel_fmt) { @@ -412,10 +416,12 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, int ret = 0, i; struct ldb_data *ldb = mxc_dispdrv_getdata(disp); struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data; + struct i2c_client *i2c_dev; struct resource *res; uint32_t base_addr; uint32_t reg, setting_idx; uint32_t ch_mask = 0, ch_val = 0; + int lvds_channel = 0; uint32_t ipu_id, disp_id; /* if input format not valid, make RGB666 as default*/ @@ -428,7 +434,6 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, if (!ldb->inited) { char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; - int lvds_channel = 0; setting_idx = 0; res = platform_get_resource(ldb->pdev, IORESOURCE_MEM, 0); @@ -619,10 +624,12 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, } ldb->inited = true; + + i2c_dev = ldb_i2c_client[lvds_channel]; + } else { /* second time for separate mode */ char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; - int lvds_channel; if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1) || @@ -701,9 +708,16 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, return PTR_ERR(ldb->setting[setting_idx].di_clk); } + i2c_dev = ldb_i2c_client[lvds_channel]; + dev_dbg(&ldb->pdev->dev, "ldb_clk to di clk: %s -> %s\n", ldb_clk, di_clk); } + if (i2c_dev) + mxc_dispdrv_setdev(ldb->disp_ldb, &i2c_dev->dev); + else + mxc_dispdrv_setdev(ldb->disp_ldb, NULL); + ldb->setting[setting_idx].ch_mask = ch_mask; ldb->setting[setting_idx].ch_val = ch_val; @@ -734,6 +748,23 @@ static int ldb_disp_init(struct mxc_dispdrv_handle *disp, } } + /* get screen size in edid */ + if (i2c_dev) { + ret = mxc_ldb_edidread(i2c_dev); + if (ret > 0) { + fb_edid_to_monspecs(&g_edid[lvds_channel][0], + &setting->fbi->monspecs); + /* centimeter to millimeter */ + setting->fbi->var.width = + setting->fbi->monspecs.max_x * 10; + setting->fbi->var.height = + setting->fbi->monspecs.max_y * 10; + } else { + /* ignore i2c access failure */ + ret = 0; + } + } + /* save current ldb setting for fb notifier */ ldb->setting[setting_idx].active = true; ldb->setting[setting_idx].ipu = setting->dev_id; @@ -791,6 +822,108 @@ static int ldb_resume(struct platform_device *pdev) return 0; } + +static int mxc_ldb_edidread(struct i2c_client *client) +{ + int ret = 0; + unsigned char regaddr = 0; + struct i2c_adapter *adp = client->adapter; + int ldb_id = (int)client->dev.platform_data; + struct i2c_msg msg[2] = { + { + .addr = 0x50, + .flags = 0, + .len = 1, + .buf = ®addr, + }, { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 512, + .buf = &g_edid[ldb_id][0], + }, + }; + + ret = i2c_transfer(adp, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + return -EIO; + } + + return ret; +} + +static ssize_t mxc_ldb_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + + ret = mxc_ldb_edidread(client); + if (ret < 0) + strcpy(buf, "plugout\n"); + else + strcpy(buf, "plugin\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(cable_state, S_IRUGO, mxc_ldb_show_state, NULL); + +static int __devinit mxc_ldb_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + int ldb_id = (int)client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -ENODEV; + + ldb_i2c_client[ldb_id] = client; + + ret = device_create_file(&client->dev, &dev_attr_cable_state); + if (ret < 0) + dev_warn(&client->dev, + "cound not create sys node for cable state\n"); + + + return 0; +} + +static int __devexit mxc_ldb_i2c_remove(struct i2c_client *client) +{ + int ldb_id = (int)client->dev.platform_data; + ldb_i2c_client[ldb_id] = NULL; + return 0; +} + +static const struct i2c_device_id mxc_ldb_i2c_id[] = { + { "mxc_ldb_i2c", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mxc_ldb_i2c_id); + +static struct i2c_driver mxc_ldb_i2c_driver = { + .driver = { + .name = "mxc_ldb_i2c", + }, + .probe = mxc_ldb_i2c_probe, + .remove = mxc_ldb_i2c_remove, + .id_table = mxc_ldb_i2c_id, +}; + +static int __init mxc_ldb_i2c_init(void) +{ + return i2c_add_driver(&mxc_ldb_i2c_driver); +} + +static void __exit mxc_ldb_i2c_exit(void) +{ + i2c_del_driver(&mxc_ldb_i2c_driver); +} + +module_init(mxc_ldb_i2c_init); +module_exit(mxc_ldb_i2c_exit); + /*! * This function is called by the driver framework to initialize the LDB * device. |