diff options
author | Erik Gilling <konkers@android.com> | 2010-10-26 15:30:35 -0700 |
---|---|---|
committer | Erik Gilling <konkers@android.com> | 2010-10-26 18:20:13 -0700 |
commit | 157e610c36957f0887ec0974e3ab2242e91894ee (patch) | |
tree | 0a9aa8ff63c913c4fb3e53f5ecbb2dca98e99378 /drivers | |
parent | e4886b1e1b098af06720724770622caaa9721dc9 (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.c | 195 | ||||
-rw-r--r-- | drivers/video/tegra/dc/edid.h | 8 |
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); |