/* * drivers/video/fb_ddc.c - DDC/EDID read support. * * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */ #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> #include <linux/fb.h> #include <linux/i2c-algo-bit.h> #include <linux/slab.h> #include "../edid.h" #define DDC_ADDR 0x50 static unsigned char *fb_do_probe_ddc_edid(struct i2c_adapter *adapter) { unsigned char start = 0x0; unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL); struct i2c_msg msgs[] = { { .addr = DDC_ADDR, .flags = 0, .len = 1, .buf = &start, }, { .addr = DDC_ADDR, .flags = I2C_M_RD, .len = EDID_LENGTH, .buf = buf, } }; if (!buf) { dev_warn(&adapter->dev, "unable to allocate memory for EDID " "block.\n"); return NULL; } if (i2c_transfer(adapter, msgs, 2) == 2) return buf; dev_warn(&adapter->dev, "unable to read EDID block.\n"); kfree(buf); return NULL; } unsigned char *fb_ddc_read(struct i2c_adapter *adapter) { struct i2c_algo_bit_data *algo_data = adapter->algo_data; unsigned char *edid = NULL; int i, j; algo_data->setscl(algo_data->data, 1); for (i = 0; i < 3; i++) { /* For some old monitors we need the * following process to initialize/stop DDC */ algo_data->setsda(algo_data->data, 1); msleep(13); algo_data->setscl(algo_data->data, 1); for (j = 0; j < 5; j++) { msleep(10); if (algo_data->getscl(algo_data->data)) break; } if (j == 5) continue; algo_data->setsda(algo_data->data, 0); msleep(15); algo_data->setscl(algo_data->data, 0); msleep(15); algo_data->setsda(algo_data->data, 1); msleep(15); /* Do the real work */ edid = fb_do_probe_ddc_edid(adapter); algo_data->setsda(algo_data->data, 0); algo_data->setscl(algo_data->data, 0); msleep(15); algo_data->setscl(algo_data->data, 1); for (j = 0; j < 10; j++) { msleep(10); if (algo_data->getscl(algo_data->data)) break; } algo_data->setsda(algo_data->data, 1); msleep(15); algo_data->setscl(algo_data->data, 0); algo_data->setsda(algo_data->data, 0); if (edid) break; } /* Release the DDC lines when done or the Apple Cinema HD display * will switch off */ algo_data->setsda(algo_data->data, 1); algo_data->setscl(algo_data->data, 1); adapter->class |= I2C_CLASS_DDC; return edid; } EXPORT_SYMBOL_GPL(fb_ddc_read); MODULE_AUTHOR("Dennis Munsie <dmunsie@cecropia.com>"); MODULE_DESCRIPTION("DDC/EDID reading support"); MODULE_LICENSE("GPL");