|  | /* | 
|  | * driver/vide/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/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"); |