xref: /linux/drivers/video/fbdev/core/fb_ddc.c (revision 260f6f4fda93c8485c8037865c941b42b9cba5d2)
1 /*
2  * drivers/video/fb_ddc.c - DDC/EDID read support.
3  *
4  *  Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
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 
fb_do_probe_ddc_edid(struct i2c_adapter * adapter)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 
fb_ddc_read(struct i2c_adapter * adapter)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 		/* For some old monitors we need the
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 		/* Do the real work */
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 	/* Release the DDC lines when done or the Apple Cinema HD display
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 <dmunsie@cecropia.com>");
126 MODULE_DESCRIPTION("DDC/EDID reading support");
127 MODULE_LICENSE("GPL");
128