hc
2025-02-14 bbb9540dc49f70f6b703d1c8d1b85fa5f602d86e
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");