| 1 | |
| 2 | * drivers/video/fb_ddc.c - DDC/EDID read support.
|
| 3 | *
|
| 4 | * Copyright (C) 2006 Dennis Munsie <[email protected]>
|
| 5 | *
|
| 6 | * This file is subject to the terms and conditions of the GNU General Public
|
| 7 | * License. See the file COPYING in the main directory of this archive
|
| 8 | * for more details.
|
| 9 |
|
| 10 |
|
| 11 | #include <linux/delay.h>
|
| 12 | #include <linux/device.h>
|
| 13 | #include <linux/export.h>
|
| 14 | #include <linux/module.h>
|
| 15 | #include <linux/fb.h>
|
| 16 | #include <linux/i2c-algo-bit.h>
|
| 17 | #include <linux/slab.h>
|
| 18 |
|
| 19 | #include "../edid.h"
|
| 20 |
|
| 21 | #define DDC_ADDR 0x50
|
| 22 |
|
| 23 | static unsigned char *fb_do_probe_ddc_edid(struct i2c_adapter *adapter)
|
| 24 | {
|
| 25 | unsigned char start = 0x0;
|
| 26 | unsigned char *buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
| 27 | struct i2c_msg msgs[] = {
|
| 28 | {
|
| 29 | .addr = DDC_ADDR,
|
| 30 | .flags = 0,
|
| 31 | .len = 1,
|
| 32 | .buf = &start,
|
| 33 | }, {
|
| 34 | .addr = DDC_ADDR,
|
| 35 | .flags = I2C_M_RD,
|
| 36 | .len = EDID_LENGTH,
|
| 37 | .buf = buf,
|
| 38 | }
|
| 39 | };
|
| 40 |
|
| 41 | if (!buf) {
|
| 42 | dev_warn(&adapter->dev, "unable to allocate memory for EDID "
|
| 43 | "block.\n");
|
| 44 | return NULL;
|
| 45 | }
|
| 46 |
|
| 47 | if (i2c_transfer(adapter, msgs, 2) == 2)
|
| 48 | return buf;
|
| 49 |
|
| 50 | dev_warn(&adapter->dev, "unable to read EDID block.\n");
|
| 51 | kfree(buf);
|
| 52 | return NULL;
|
| 53 | }
|
| 54 |
|
| 55 | unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
|
| 56 | {
|
| 57 | struct i2c_algo_bit_data *algo_data = adapter->algo_data;
|
| 58 | unsigned char *edid = NULL;
|
| 59 | int i, j;
|
| 60 |
|
| 61 | algo_data->setscl(algo_data->data, 1);
|
| 62 |
|
| 63 | for (i = 0; i < 3; i++) {
|
| 64 | |
| 65 | * following process to initialize/stop DDC
|
| 66 |
|
| 67 | algo_data->setsda(algo_data->data, 1);
|
| 68 | msleep(13);
|
| 69 |
|
| 70 | algo_data->setscl(algo_data->data, 1);
|
| 71 | if (algo_data->getscl) {
|
| 72 | for (j = 0; j < 5; j++) {
|
| 73 | msleep(10);
|
| 74 | if (algo_data->getscl(algo_data->data))
|
| 75 | break;
|
| 76 | }
|
| 77 | if (j == 5)
|
| 78 | continue;
|
| 79 | } else {
|
| 80 | udelay(algo_data->udelay);
|
| 81 | }
|
| 82 |
|
| 83 | algo_data->setsda(algo_data->data, 0);
|
| 84 | msleep(15);
|
| 85 | algo_data->setscl(algo_data->data, 0);
|
| 86 | msleep(15);
|
| 87 | algo_data->setsda(algo_data->data, 1);
|
| 88 | msleep(15);
|
| 89 |
|
| 90 |
|
| 91 | edid = fb_do_probe_ddc_edid(adapter);
|
| 92 | algo_data->setsda(algo_data->data, 0);
|
| 93 | algo_data->setscl(algo_data->data, 0);
|
| 94 | msleep(15);
|
| 95 |
|
| 96 | algo_data->setscl(algo_data->data, 1);
|
| 97 | if (algo_data->getscl) {
|
| 98 | for (j = 0; j < 10; j++) {
|
| 99 | msleep(10);
|
| 100 | if (algo_data->getscl(algo_data->data))
|
| 101 | break;
|
| 102 | }
|
| 103 | } else {
|
| 104 | udelay(algo_data->udelay);
|
| 105 | }
|
| 106 |
|
| 107 | algo_data->setsda(algo_data->data, 1);
|
| 108 | msleep(15);
|
| 109 | algo_data->setscl(algo_data->data, 0);
|
| 110 | algo_data->setsda(algo_data->data, 0);
|
| 111 | if (edid)
|
| 112 | break;
|
| 113 | }
|
| 114 | |
| 115 | * will switch off
|
| 116 |
|
| 117 | algo_data->setsda(algo_data->data, 1);
|
| 118 | algo_data->setscl(algo_data->data, 1);
|
| 119 |
|
| 120 | return edid;
|
| 121 | }
|
| 122 |
|
| 123 | EXPORT_SYMBOL_GPL(fb_ddc_read);
|
| 124 |
|
| 125 | MODULE_AUTHOR("Dennis Munsie <[email protected]>");
|
| 126 | MODULE_DESCRIPTION("DDC/EDID reading support");
|
| 127 | MODULE_LICENSE("GPL");
|