Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /* * 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); if (algo_data->getscl) { for (j = 0; j < 5; j++) { msleep(10); if (algo_data->getscl(algo_data->data)) break; } if (j == 5) continue; } else { udelay(algo_data->udelay); } 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); if (algo_data->getscl) { for (j = 0; j < 10; j++) { msleep(10); if (algo_data->getscl(algo_data->data)) break; } } else { udelay(algo_data->udelay); } 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"); |