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
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/delay.h>
16#include <linux/gfp.h>
17#include <linux/pci.h>
18#include <linux/fb.h>
19
20#include <asm/io.h>
21#include "savagefb.h"
22
23#define SAVAGE_DDC 	0x50
24
25#define VGA_CR_IX	0x3d4
26#define VGA_CR_DATA	0x3d5
27
28#define CR_SERIAL1	0xa0	/* I2C serial communications interface */
29#define MM_SERIAL1	0xff20
30#define CR_SERIAL2	0xb1	/* DDC2 monitor communications interface */
31
32/* based on vt8365 documentation */
33#define PROSAVAGE_I2C_ENAB	0x10
34#define PROSAVAGE_I2C_SCL_OUT	0x01
35#define PROSAVAGE_I2C_SDA_OUT	0x02
36#define PROSAVAGE_I2C_SCL_IN	0x04
37#define PROSAVAGE_I2C_SDA_IN	0x08
38
39#define SAVAGE4_I2C_ENAB	0x00000020
40#define SAVAGE4_I2C_SCL_OUT	0x00000001
41#define SAVAGE4_I2C_SDA_OUT	0x00000002
42#define SAVAGE4_I2C_SCL_IN	0x00000008
43#define SAVAGE4_I2C_SDA_IN	0x00000010
44
45static void savage4_gpio_setscl(void *data, int val)
46{
47	struct savagefb_i2c_chan *chan = data;
48	unsigned int r;
49
50	r = readl(chan->ioaddr + chan->reg);
51	if(val)
52		r |= SAVAGE4_I2C_SCL_OUT;
53	else
54		r &= ~SAVAGE4_I2C_SCL_OUT;
55	writel(r, chan->ioaddr + chan->reg);
56	readl(chan->ioaddr + chan->reg);	/* flush posted write */
57}
58
59static void savage4_gpio_setsda(void *data, int val)
60{
61	struct savagefb_i2c_chan *chan = data;
62
63	unsigned int r;
64	r = readl(chan->ioaddr + chan->reg);
65	if(val)
66		r |= SAVAGE4_I2C_SDA_OUT;
67	else
68		r &= ~SAVAGE4_I2C_SDA_OUT;
69	writel(r, chan->ioaddr + chan->reg);
70	readl(chan->ioaddr + chan->reg);	/* flush posted write */
71}
72
73static int savage4_gpio_getscl(void *data)
74{
75	struct savagefb_i2c_chan *chan = data;
76
77	return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SCL_IN));
78}
79
80static int savage4_gpio_getsda(void *data)
81{
82	struct savagefb_i2c_chan *chan = data;
83
84	return (0 != (readl(chan->ioaddr + chan->reg) & SAVAGE4_I2C_SDA_IN));
85}
86
87static void prosavage_gpio_setscl(void* data, int val)
88{
89	struct savagefb_i2c_chan *chan = data;
90	u32			  r;
91
92	r = VGArCR(chan->reg, chan->par);
93	r |= PROSAVAGE_I2C_ENAB;
94	if (val) {
95		r |= PROSAVAGE_I2C_SCL_OUT;
96	} else {
97		r &= ~PROSAVAGE_I2C_SCL_OUT;
98	}
99
100	VGAwCR(chan->reg, r, chan->par);
101}
102
103static void prosavage_gpio_setsda(void* data, int val)
104{
105	struct savagefb_i2c_chan *chan = data;
106	unsigned int r;
107
108	r = VGArCR(chan->reg, chan->par);
109	r |= PROSAVAGE_I2C_ENAB;
110	if (val) {
111		r |= PROSAVAGE_I2C_SDA_OUT;
112	} else {
113		r &= ~PROSAVAGE_I2C_SDA_OUT;
114	}
115
116	VGAwCR(chan->reg, r, chan->par);
117}
118
119static int prosavage_gpio_getscl(void* data)
120{
121	struct savagefb_i2c_chan *chan = data;
122
123	return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SCL_IN) ? 1 : 0;
124}
125
126static int prosavage_gpio_getsda(void* data)
127{
128	struct savagefb_i2c_chan *chan = data;
129
130	return (VGArCR(chan->reg, chan->par) & PROSAVAGE_I2C_SDA_IN) ? 1 : 0;
131}
132
133static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan,
134				const char *name)
135{
136	int rc = 0;
137
138	if (chan->par) {
139		strcpy(chan->adapter.name, name);
140		chan->adapter.owner		= THIS_MODULE;
141		chan->adapter.algo_data		= &chan->algo;
142		chan->adapter.dev.parent	= &chan->par->pcidev->dev;
143		chan->algo.udelay		= 10;
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
154		rc = i2c_bit_add_bus(&chan->adapter);
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);
162	}
163
164	return rc;
165}
166
167void savagefb_create_i2c_busses(struct fb_info *info)
168{
169	struct savagefb_par *par = info->par;
170	par->chan.par	= par;
171
172	switch (par->chip) {
173	case S3_PROSAVAGE:
174	case S3_PROSAVAGEDDR:
175	case S3_TWISTER:
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 S3_SAVAGE4:
184		par->chan.reg = CR_SERIAL1;
185		if (par->pcidev->revision > 1 && !(VGArCR(0xa6, par) & 0x40))
186			par->chan.reg = CR_SERIAL2;
187		par->chan.ioaddr      = par->mmio.vbase;
188		par->chan.algo.setsda = prosavage_gpio_setsda;
189		par->chan.algo.setscl = prosavage_gpio_setscl;
190		par->chan.algo.getsda = prosavage_gpio_getsda;
191		par->chan.algo.getscl = prosavage_gpio_getscl;
192		break;
193	case S3_SAVAGE2000:
194		par->chan.reg         = MM_SERIAL1;
195		par->chan.ioaddr      = par->mmio.vbase;
196		par->chan.algo.setsda = savage4_gpio_setsda;
197		par->chan.algo.setscl = savage4_gpio_setscl;
198		par->chan.algo.getsda = savage4_gpio_getsda;
199		par->chan.algo.getscl = savage4_gpio_getscl;
200		break;
201	default:
202		par->chan.par = NULL;
203	}
204
205	savage_setup_i2c_bus(&par->chan, "SAVAGE DDC2");
206}
207
208void savagefb_delete_i2c_busses(struct fb_info *info)
209{
210	struct savagefb_par *par = info->par;
211
212	if (par->chan.par)
213		i2c_del_adapter(&par->chan.adapter);
214
215	par->chan.par = NULL;
216}
217
218int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid)
219{
220	struct savagefb_par *par = info->par;
221	u8 *edid;
222
223	if (par->chan.par)
224		edid = fb_ddc_read(&par->chan.adapter);
225	else
226		edid = NULL;
227
228	if (!edid) {
229		/* try to get from firmware */
230		const u8 *e = fb_firmware_edid(info->device);
231
232		if (e)
233			edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL);
234	}
235
236	*out_edid = edid;
237
238	return (edid) ? 0 : 1;
239}
240
241MODULE_LICENSE("GPL");
242