From 91f9b96f04be06907f7963a90a59f59b101f3342 Mon Sep 17 00:00:00 2001
From: Tomi Valkeinen <tomi.valkeinen@nokia.com>
Date: Thu, 5 Feb 2009 14:10:00 +0200
Subject: [PATCH] HACK: DSS2: Hack for EDID testing

---
 arch/arm/mach-omap2/board-omap3beagle.c      |    9 ++-
 drivers/video/omap2/displays/Kconfig         |    1 +
 drivers/video/omap2/displays/panel-generic.c |  165 +++++++++++++++++++++++++-
 3 files changed, 173 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 484a343..9c5b052 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -223,6 +223,12 @@ static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = {
 	},
 };
 
+static struct i2c_board_info __initdata beagle_i2c3_boardinfo[] = {
+	{
+		I2C_BOARD_INFO("panel-generic", 0x50),
+	},
+};
+
 static int __init omap3_beagle_i2c_init(void)
 {
 	omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo,
@@ -230,7 +236,8 @@ static int __init omap3_beagle_i2c_init(void)
 #ifdef CONFIG_I2C2_OMAP_BEAGLE
 	omap_register_i2c_bus(2, 400, NULL, 0);
 #endif
-	omap_register_i2c_bus(3, 400, NULL, 0);
+	omap_register_i2c_bus(3, 400, beagle_i2c3_boardinfo,
+			ARRAY_SIZE(beagle_i2c3_boardinfo));
 	return 0;
 }
 
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index d9a5def..8cf45b9 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -3,6 +3,7 @@ menu "OMAP2/3 Display Device Drivers"
 
 config PANEL_GENERIC
         tristate "Generic Panel"
+        select FB_DDC
         help
 	  Generic panel driver.
 	  Used for DVI output for Beagle and OMAP3 SDP.
diff --git a/drivers/video/omap2/displays/panel-generic.c b/drivers/video/omap2/displays/panel-generic.c
index 5c8fecd..06266a3 100644
--- a/drivers/video/omap2/displays/panel-generic.c
+++ b/drivers/video/omap2/displays/panel-generic.c
@@ -19,6 +19,9 @@
 
 #include <linux/module.h>
 #include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/fb.h>
 
 #include <mach/display.h>
 
@@ -80,15 +83,175 @@ static struct omap_panel generic_panel = {
 	.config		= OMAP_DSS_LCD_TFT,
 };
 
+static void setsda(void *data, int state)
+{
+}
 
-static int __init generic_panel_drv_init(void)
+static void setscl(void *data, int state)
+{
+}
+
+static int getscl(void *data)
+{
+	return 1;
+}
+
+static struct i2c_algo_bit_data foo = {
+	.setsda = setsda,
+	.setscl = setscl,
+	.getscl = getscl,
+};
+
+
+static int mode2timings(struct fb_videomode *mode,
+		struct omap_video_timings *timings)
+{
+	unsigned hsw;
+	unsigned hfp, hbp;
+	unsigned extra;
+
+	printk("%dx%d, %lukHz, h:%d/%d v:%d/%d s:%d/%d, %x\n",
+			mode->xres,
+			mode->yres,
+			PICOS2KHZ(mode->pixclock),
+			mode->left_margin,
+			mode->right_margin,
+			mode->upper_margin,
+			mode->lower_margin,
+			mode->hsync_len,
+			mode->vsync_len,
+			mode->sync);
+
+	if (PICOS2KHZ(mode->pixclock) > 80000)
+		return -EINVAL;
+
+	if (mode->vmode & FB_VMODE_INTERLACED)
+		return -EINVAL;
+
+	hsw = mode->hsync_len;
+
+	if (hsw > 64) {
+		extra = hsw - 64;
+		hsw = 64;
+	} else {
+		hsw = mode->hsync_len;
+		extra = 0;
+	}
+
+	hfp = mode->left_margin + extra / 2;
+	hbp = mode->right_margin + extra / 2;
+
+	timings->x_res = mode->xres;
+	timings->y_res = mode->yres;
+	timings->pixel_clock = PICOS2KHZ(mode->pixclock);
+	timings->hfp = hfp;
+	timings->hsw = hsw;
+	timings->hbp = hbp;
+	timings->vfp = mode->upper_margin;
+	timings->vsw = mode->vsync_len;
+	timings->vbp = mode->lower_margin;
+/*
+	if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+		generic_panel.config |= OMAP_DSS_LCD_IHS;
+
+	if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+		generic_panel.config |= OMAP_DSS_LCD_IVS;
+*/
+	return 0;
+}
+
+static int generic_panel_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
 {
+	unsigned char *p;
+
+	printk("probe\n");
+
+	client->adapter->algo_data = &foo;
+
+	p = fb_ddc_read(client->adapter);
+	if (p) {
+		int i;
+		struct fb_monspecs specs;
+		struct omap_video_timings best_timings;
+
+		memset(&best_timings, 0, sizeof(best_timings));
+
+		fb_edid_to_monspecs(p, &specs);
+
+		for (i = 0; i < specs.modedb_len; ++i) {
+			struct fb_videomode *mode;
+			struct omap_video_timings timings;
+
+			mode = &specs.modedb[i];
+
+			if (mode2timings(mode, &timings))
+				continue;
+
+			if (best_timings.pixel_clock == 0) {
+				best_timings = timings;
+				continue;
+			}
+
+			if (best_timings.x_res < timings.x_res ||
+					best_timings.y_res < timings.y_res) {
+				best_timings = timings;
+			}
+		}
+
+		if (best_timings.pixel_clock) {
+			generic_panel.timings = best_timings;
+			printk("selected %dx%d, %ukHz\n",
+					best_timings.x_res,
+					best_timings.y_res,
+					best_timings.pixel_clock);
+		}
+
+		fb_destroy_modedb(specs.modedb);
+		kfree(p);
+	}
+
 	omap_dss_register_panel(&generic_panel);
+
+	return 0;
+}
+
+static int generic_panel_i2c_remove(struct i2c_client *client)
+{
+	printk("remove\n");
+	return 0;
+}
+
+static const struct i2c_device_id generic_panel_i2c_dev_id[] = {
+        { "panel-generic", 0 },
+        { }
+};
+
+static struct i2c_driver generic_panel_i2c_driver = {
+	.driver = {
+		.name = "panel-generic",
+	},
+	.probe = &generic_panel_i2c_probe,
+	.remove = &generic_panel_i2c_remove,
+	.id_table = generic_panel_i2c_dev_id,
+};
+
+static int __init generic_panel_drv_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&generic_panel_i2c_driver);
+	if (r) {
+		printk(KERN_ERR "i2c registration failed\n");
+		return r;
+	}
+
 	return 0;
 }
 
 static void __exit generic_panel_drv_exit(void)
 {
+	i2c_del_driver(&generic_panel_i2c_driver);
 	omap_dss_unregister_panel(&generic_panel);
 }
 
-- 
1.6.1.2

