Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/video/savage/savagefb-i2c.c - S3 Savage DDC2 | |
3 | * | |
4 | * Copyright 2004 Antonino A. Daplas <adaplas @pol.net> | |
5 | * | |
6 | * Based partly on rivafb-i2c.c | |
7 | * | |
8 | * This file is subject to the terms and conditions of the GNU General Public | |
9 | * License. See the file COPYING in the main directory of this archive | |
10 | * for more details. | |
11 | */ | |
12 | ||
1da177e4 LT |
13 | #include <linux/module.h> |
14 | #include <linux/kernel.h> | |
1da177e4 LT |
15 | #include <linux/delay.h> |
16 | #include <linux/pci.h> | |
17 | #include <linux/fb.h> | |
18 | ||
19 | #include <asm/io.h> | |
20 | #include "savagefb.h" | |
21 | ||
22 | #define SAVAGE_DDC 0x50 | |
23 | ||
24 | #define VGA_CR_IX 0x3d4 | |
25 | #define VGA_CR_DATA 0x3d5 | |
26 | ||
27 | #define CR_SERIAL1 0xa0 /* I2C serial communications interface */ | |
28 | #define MM_SERIAL1 0xff20 | |
29 | #define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */ | |
30 | ||
31 | /* based on vt8365 documentation */ | |
32 | #define PROSAVAGE_I2C_ENAB 0x10 | |
33 | #define PROSAVAGE_I2C_SCL_OUT 0x01 | |
34 | #define PROSAVAGE_I2C_SDA_OUT 0x02 | |
35 | #define PROSAVAGE_I2C_SCL_IN 0x04 | |
36 | #define PROSAVAGE_I2C_SDA_IN 0x08 | |
37 | ||
38 | #define SAVAGE4_I2C_ENAB 0x00000020 | |
39 | #define SAVAGE4_I2C_SCL_OUT 0x00000001 | |
40 | #define SAVAGE4_I2C_SDA_OUT 0x00000002 | |
41 | #define SAVAGE4_I2C_SCL_IN 0x00000008 | |
42 | #define SAVAGE4_I2C_SDA_IN 0x00000010 | |
43 | ||
1da177e4 LT |
44 | static void savage4_gpio_setscl(void *data, int val) |
45 | { | |
b8901b09 | 46 | struct savagefb_i2c_chan *chan = data; |
1da177e4 LT |
47 | unsigned int r; |
48 | ||
49 | r = readl(chan->ioaddr + chan->reg); | |
50 | if(val) | |
51 | r |= SAVAGE4_I2C_SCL_OUT; | |
52 | else | |
53 | r &= ~SAVAGE4_I2C_SCL_OUT; | |
54 | writel(r, chan->ioaddr + chan->reg); | |
55 | readl(chan->ioaddr + chan->reg); /* flush posted write */ | |
56 | } | |
57 | ||
58 | static void savage4_gpio_setsda(void *data, int val) | |
59 | { | |
b8901b09 | 60 | struct savagefb_i2c_chan *chan = data; |
1da177e4 LT |
61 | |
62 | unsigned int r; | |
63 | r = readl(chan->ioaddr + chan->reg); | |
64 | if(val) | |
65 | r |= SAVAGE4_I2C_SDA_OUT; | |
66 | else | |
67 | r &= ~SAVAGE4_I2C_SDA_OUT; | |
68 | writel(r, chan->ioaddr + chan->reg); | |
69 | readl(chan->ioaddr + chan->reg); /* flush posted write */ | |
70 | } | |
71 | ||
72 | static int savage4_gpio_getscl(void *data) | |
73 | { | |
b8901b09 | 74 | struct savagefb_i2c_chan *chan = data; |
1da177e4 LT |
75 | |
76 | return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SCL_IN)); | |
77 | } | |
78 | ||
79 | static int savage4_gpio_getsda(void *data) | |
80 | { | |
b8901b09 | 81 | struct savagefb_i2c_chan *chan = data; |
1da177e4 LT |
82 | |
83 | return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SDA_IN)); | |
84 | } | |
85 | ||
86 | static void prosavage_gpio_setscl(void* data, int val) | |
87 | { | |
b8901b09 | 88 | struct savagefb_i2c_chan *chan = data; |
1da177e4 LT |
89 | u32 r; |
90 | ||
f7829158 | 91 | r = VGArCR(chan->reg, chan->par); |
1da177e4 LT |
92 | r |= PROSAVAGE_I2C_ENAB; |
93 | if (val) { | |
94 | r |= PROSAVAGE_I2C_SCL_OUT; | |
95 | } else { | |
96 | r &= ~PROSAVAGE_I2C_SCL_OUT; | |
97 | } | |
f7829158 AD |
98 | |
99 | VGAwCR(chan->reg, r, chan->par); | |
1da177e4 LT |
100 | } |
101 | ||
102 | static void prosavage_gpio_setsda(void* data, int val) | |
103 | { | |
b8901b09 | 104 | struct savagefb_i2c_chan *chan = data; |
1da177e4 LT |
105 | unsigned int r; |
106 | ||
f7829158 | 107 | r = VGArCR(chan->reg, chan->par); |
1da177e4 LT |
108 | r |= PROSAVAGE_I2C_ENAB; |
109 | if (val) { | |
110 | r |= PROSAVAGE_I2C_SDA_OUT; | |
111 | } else { | |
112 | r &= ~PROSAVAGE_I2C_SDA_OUT; | |
113 | } | |
f7829158 AD |
114 | |
115 | VGAwCR(chan->reg, r, chan->par); | |
1da177e4 LT |
116 | } |
117 | ||
118 | static int prosavage_gpio_getscl(void* data) | |
119 | { | |
b8901b09 | 120 | struct savagefb_i2c_chan *chan = data; |
1da177e4 | 121 | |
f7829158 | 122 | return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SCL_IN) ? 1 : 0; |
1da177e4 LT |
123 | } |
124 | ||
125 | static int prosavage_gpio_getsda(void* data) | |
126 | { | |
b8901b09 | 127 | struct savagefb_i2c_chan *chan = data; |
1da177e4 | 128 | |
f7829158 | 129 | return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SDA_IN) ? 1 : 0; |
1da177e4 LT |
130 | } |
131 | ||
1da177e4 LT |
132 | static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan, |
133 | const char *name) | |
134 | { | |
1da177e4 LT |
135 | int rc = 0; |
136 | ||
b8901b09 | 137 | if (chan->par) { |
1da177e4 LT |
138 | strcpy(chan->adapter.name, name); |
139 | chan->adapter.owner = THIS_MODULE; | |
1684a984 | 140 | chan->adapter.id = I2C_HW_B_SAVAGE; |
1da177e4 LT |
141 | chan->adapter.algo_data = &chan->algo; |
142 | chan->adapter.dev.parent = &chan->par->pcidev->dev; | |
9201a858 | 143 | chan->algo.udelay = 10; |
1da177e4 LT |
144 | chan->algo.timeout = 20; |
145 | chan->algo.data = chan; | |
146 | ||
147 | i2c_set_adapdata(&chan->adapter, chan); | |
148 | ||
149 | /* Raise SCL and SDA */ | |
150 | chan->algo.setsda(chan, 1); | |
151 | chan->algo.setscl(chan, 1); | |
152 | udelay(20); | |
153 | ||
b8901b09 | 154 | rc = i2c_bit_add_bus(&chan->adapter); |
1da177e4 LT |
155 | |
156 | if (rc == 0) | |
157 | dev_dbg(&chan->par->pcidev->dev, | |
158 | "I2C bus %s registered.\n", name); | |
159 | else | |
160 | dev_warn(&chan->par->pcidev->dev, | |
161 | "Failed to register I2C bus %s.\n", name); | |
1da177e4 LT |
162 | } else |
163 | chan->par = NULL; | |
164 | ||
165 | return rc; | |
166 | } | |
167 | ||
168 | void savagefb_create_i2c_busses(struct fb_info *info) | |
169 | { | |
b8901b09 | 170 | struct savagefb_par *par = info->par; |
1da177e4 LT |
171 | par->chan.par = par; |
172 | ||
173 | switch(info->fix.accel) { | |
174 | case FB_ACCEL_PROSAVAGE_DDRK: | |
175 | case FB_ACCEL_PROSAVAGE_PM: | |
176 | par->chan.reg = CR_SERIAL2; | |
177 | par->chan.ioaddr = par->mmio.vbase; | |
178 | par->chan.algo.setsda = prosavage_gpio_setsda; | |
179 | par->chan.algo.setscl = prosavage_gpio_setscl; | |
180 | par->chan.algo.getsda = prosavage_gpio_getsda; | |
181 | par->chan.algo.getscl = prosavage_gpio_getscl; | |
182 | break; | |
183 | case FB_ACCEL_SAVAGE4: | |
c35dba60 | 184 | case FB_ACCEL_SAVAGE2000: |
1da177e4 LT |
185 | par->chan.reg = 0xff20; |
186 | par->chan.ioaddr = par->mmio.vbase; | |
187 | par->chan.algo.setsda = savage4_gpio_setsda; | |
188 | par->chan.algo.setscl = savage4_gpio_setscl; | |
189 | par->chan.algo.getsda = savage4_gpio_getsda; | |
190 | par->chan.algo.getscl = savage4_gpio_getscl; | |
191 | break; | |
192 | default: | |
193 | par->chan.par = NULL; | |
194 | } | |
195 | ||
196 | savage_setup_i2c_bus(&par->chan, "SAVAGE DDC2"); | |
197 | } | |
198 | ||
199 | void savagefb_delete_i2c_busses(struct fb_info *info) | |
200 | { | |
b8901b09 | 201 | struct savagefb_par *par = info->par; |
1da177e4 | 202 | |
b8901b09 | 203 | if (par->chan.par) |
3269711b | 204 | i2c_del_adapter(&par->chan.adapter); |
1da177e4 LT |
205 | |
206 | par->chan.par = NULL; | |
207 | } | |
208 | ||
13776711 | 209 | int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid) |
1da177e4 | 210 | { |
13776711 | 211 | struct savagefb_par *par = info->par; |
946c4eab AD |
212 | u8 *edid; |
213 | ||
214 | if (par->chan.par) | |
215 | edid = fb_ddc_read(&par->chan.adapter); | |
216 | else | |
217 | edid = NULL; | |
13776711 AD |
218 | |
219 | if (!edid) { | |
220 | /* try to get from firmware */ | |
7b6a186d AD |
221 | const u8 *e = fb_firmware_edid(info->device); |
222 | ||
bfba7b37 AD |
223 | if (e) |
224 | edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL); | |
13776711 AD |
225 | } |
226 | ||
0e4be280 | 227 | *out_edid = edid; |
1da177e4 | 228 | |
13776711 | 229 | return (edid) ? 0 : 1; |
1da177e4 LT |
230 | } |
231 | ||
232 | MODULE_LICENSE("GPL"); |