summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorErik Gilling <konkers@android.com>2010-10-26 15:30:35 -0700
committerErik Gilling <konkers@android.com>2010-10-26 18:20:13 -0700
commit157e610c36957f0887ec0974e3ab2242e91894ee (patch)
tree0a9aa8ff63c913c4fb3e53f5ecbb2dca98e99378 /drivers
parente4886b1e1b098af06720724770622caaa9721dc9 (diff)
video: tegra: flesh out edid support
* read blocks in a single command instead of byte at a time * allow reading past segment 0 (edid > 256 bytes) * handle mutiple extention blocks * add debugfs file for reading edid Change-Id: Iec8182cdbccdaa2142e4bbc892202d2e8d73c23b Signed-off-by: Erik Gilling <konkers@android.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/tegra/dc/edid.c195
-rw-r--r--drivers/video/tegra/dc/edid.h8
2 files changed, 182 insertions, 21 deletions
diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c
index 0b0b1a760a4e..812a0087a96d 100644
--- a/drivers/video/tegra/dc/edid.c
+++ b/drivers/video/tegra/dc/edid.c
@@ -15,31 +15,181 @@
*
*/
-#include <linux/i2c.h>
+#define DEBUG
+
+#include <linux/debugfs.h>
#include <linux/fb.h>
+#include <linux/i2c.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
#include "edid.h"
+struct tegra_edid {
+ struct i2c_client *client;
+ struct i2c_board_info info;
+ int bus;
+
+ u8 *data;
+ unsigned len;
+};
+
+#if defined(DEBUG) || defined(CONFIG_DEBUG_FS)
+static int tegra_edid_show(struct seq_file *s, void *unused)
+{
+ struct tegra_edid *edid = s->private;
+ int i;
+
+ for (i = 0; i < edid->len; i++) {
+ if (i % 16 == 0)
+ seq_printf(s, "edid[%03x] =", i);
+
+ seq_printf(s, " %02x", edid->data[i]);
+
+ if (i % 16 == 15)
+ seq_printf(s, "\n");
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_edid_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tegra_edid_show, inode->i_private);
+}
+
+static const struct file_operations tegra_edid_debug_fops = {
+ .open = tegra_edid_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void tegra_edid_debug_add(struct tegra_edid *edid)
+{
+ char name[] = "edidX";
+
+ snprintf(name, sizeof(name), "edid%1d", edid->bus);
+ debugfs_create_file(name, S_IRUGO, NULL, edid, &tegra_edid_debug_fops);
+}
+#else
+void tegra_edid_debug_add(struct tegra_edid *edid)
+{
+}
+#endif
+
+#ifdef DEBUG
+static char tegra_edid_dump_buff[16 * 1024];
+
+static void tegra_edid_dump(struct tegra_edid *edid)
+{
+ struct seq_file s;
+ int i;
+ char c;
+
+ memset(&s, 0x0, sizeof(s));
+
+ s.buf = tegra_edid_dump_buff;
+ s.size = sizeof(tegra_edid_dump_buff);
+ s.private = edid;
+
+ tegra_edid_show(&s, NULL);
+
+ i = 0;
+ while (i < s.count ) {
+ if ((s.count - i) > 256) {
+ c = s.buf[i + 256];
+ s.buf[i + 256] = 0;
+ printk("%s", s.buf + i);
+ s.buf[i + 256] = c;
+ } else {
+ printk("%s", s.buf + i);
+ }
+ i += 256;
+ }
+}
+#else
+static void tegra_edid_dump(struct tegra_edid *edid)
+{
+}
+#endif
+
+int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data)
+{
+ u8 block_buf[] = {block >> 1};
+ u8 cmd_buf[] = {(block & 0x1) * 128};
+ int status;
+ struct i2c_msg msg[] = {
+ {
+ .addr = 0x30,
+ .flags = 0,
+ .len = 1,
+ .buf = block_buf,
+ },
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .len = 1,
+ .buf = cmd_buf,
+ },
+ {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 128,
+ .buf = data,
+ }};
+ struct i2c_msg *m;
+ int msg_len;
+
+ if (block > 1) {
+ msg_len = 3;
+ m = msg;
+ } else {
+ msg_len = 2;
+ m = &msg[1];
+ }
+
+ status = i2c_transfer(edid->client->adapter, m, msg_len);
+
+ if (status < 0)
+ return status;
+
+ if (status != msg_len)
+ return -EIO;
+
+ return 0;
+}
+
+
int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs)
{
- u8 data[256];
int i;
int ret;
+ int extension_blocks;
+
+ ret = tegra_edid_read_block(edid, 0, edid->data);
+
+ memset(specs, 0x0, sizeof(struct fb_monspecs));
+ fb_edid_to_monspecs(edid->data, specs);
+ if (specs->modedb == NULL)
+ return -EINVAL;
+
+ extension_blocks = edid->data[0x7e];
- for (i = 0; i < 256; i++) {
- ret = i2c_smbus_read_byte_data(edid->client, i);
+ for (i = 1; i <= extension_blocks; i++) {
+ ret = tegra_edid_read_block(edid, i, edid->data + i * 128);
if (ret < 0)
break;
- data[i] = ret;
+ if (edid->data[i * 128] == 0x2)
+ fb_edid_add_monspecs(edid->data + i * 128, specs);
}
- if (i != 128 && i != 256)
- return ret;
+ edid->len = i * 128;
- fb_edid_to_monspecs(data, specs);
- if (i == 256)
- fb_edid_add_monspecs(data + 128, specs);
+ tegra_edid_dump(edid);
return 0;
}
@@ -48,36 +198,53 @@ struct tegra_edid *tegra_edid_create(int bus)
{
struct tegra_edid *edid;
struct i2c_adapter *adapter;
+ int err;
edid = kzalloc(sizeof(struct tegra_edid), GFP_KERNEL);
if (!edid)
return ERR_PTR(-ENOMEM);
+ edid->data = vmalloc(SZ_32K);
+ if (!edid->data) {
+ err = -ENOMEM;
+ goto free_edid;
+ }
strlcpy(edid->info.type, "tegra_edid", sizeof(edid->info.type));
+ edid->bus = bus;
edid->info.addr = 0x50;
edid->info.platform_data = edid;
- init_waitqueue_head(&edid->wq);
adapter = i2c_get_adapter(bus);
if (!adapter) {
pr_err("can't get adpater for bus %d\n", bus);
- return NULL;
+ err = -EBUSY;
+ goto free_edid;
}
- edid->client = i2c_new_device(adapter, &edid->info);
+ edid->client = i2c_new_device(adapter, &edid->info);
i2c_put_adapter(adapter);
if (!edid->client) {
pr_err("can't create new device\n");
- return NULL;
+ err = -EBUSY;
+ goto free_edid;
}
+ tegra_edid_debug_add(edid);
+
return edid;
+
+free_edid:
+ vfree(edid->data);
+ kfree(edid);
+
+ return ERR_PTR(err);
}
void tegra_edid_destroy(struct tegra_edid *edid)
{
i2c_release_client(edid->client);
+ vfree(edid->data);
kfree(edid);
}
diff --git a/drivers/video/tegra/dc/edid.h b/drivers/video/tegra/dc/edid.h
index eb78abb7e7d8..821da90a8b4f 100644
--- a/drivers/video/tegra/dc/edid.h
+++ b/drivers/video/tegra/dc/edid.h
@@ -21,13 +21,7 @@
#include <linux/i2c.h>
#include <linux/wait.h>
-struct tegra_edid {
- struct i2c_client *client;
- struct i2c_board_info info;
-
- int probed;
- wait_queue_head_t wq;
-};
+struct tegra_edid;
struct tegra_edid *tegra_edid_create(int bus);
void tegra_edid_destroy(struct tegra_edid *edid);