Linux Audio

Check our new training course

Loading...
  1/*
  2 * linux/drivers/video/riva/fbdev-i2c.c - nVidia i2c
  3 *
  4 * Maintained by Ani Joshi <ajoshi@shell.unixbox.com>
  5 *
  6 * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
  7 *
  8 * Based on radeonfb-i2c.c
  9 *
 10 * This file is subject to the terms and conditions of the GNU General Public
 11 * License.  See the file COPYING in the main directory of this archive
 12 * for more details.
 13 */
 14
 15#include <linux/module.h>
 16#include <linux/kernel.h>
 17#include <linux/delay.h>
 18#include <linux/pci.h>
 19#include <linux/fb.h>
 20#include <linux/jiffies.h>
 21
 22#include <asm/io.h>
 23
 24#include "rivafb.h"
 25#include "../edid.h"
 26
 27static void riva_gpio_setscl(void* data, int state)
 28{
 29	struct riva_i2c_chan 	*chan = data;
 30	struct riva_par 	*par = chan->par;
 31	u32			val;
 32
 33	VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
 34	val = VGA_RD08(par->riva.PCIO, 0x3d5) & 0xf0;
 35
 36	if (state)
 37		val |= 0x20;
 38	else
 39		val &= ~0x20;
 40
 41	VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
 42	VGA_WR08(par->riva.PCIO, 0x3d5, val | 0x1);
 43}
 44
 45static void riva_gpio_setsda(void* data, int state)
 46{
 47	struct riva_i2c_chan 	*chan = data;
 48	struct riva_par 	*par = chan->par;
 49	u32			val;
 50
 51	VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
 52	val = VGA_RD08(par->riva.PCIO, 0x3d5) & 0xf0;
 53
 54	if (state)
 55		val |= 0x10;
 56	else
 57		val &= ~0x10;
 58
 59	VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
 60	VGA_WR08(par->riva.PCIO, 0x3d5, val | 0x1);
 61}
 62
 63static int riva_gpio_getscl(void* data)
 64{
 65	struct riva_i2c_chan 	*chan = data;
 66	struct riva_par 	*par = chan->par;
 67	u32			val = 0;
 68
 69	VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base);
 70	if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x04)
 71		val = 1;
 72
 73	return val;
 74}
 75
 76static int riva_gpio_getsda(void* data)
 77{
 78	struct riva_i2c_chan 	*chan = data;
 79	struct riva_par 	*par = chan->par;
 80	u32			val = 0;
 81
 82	VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base);
 83	if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x08)
 84		val = 1;
 85
 86	return val;
 87}
 88
 89static int riva_setup_i2c_bus(struct riva_i2c_chan *chan, const char *name,
 90			      unsigned int i2c_class)
 91{
 92	int rc;
 93
 94	strcpy(chan->adapter.name, name);
 95	chan->adapter.owner		= THIS_MODULE;
 96	chan->adapter.class		= i2c_class;
 97	chan->adapter.algo_data		= &chan->algo;
 98	chan->adapter.dev.parent	= &chan->par->pdev->dev;
 99	chan->algo.setsda		= riva_gpio_setsda;
100	chan->algo.setscl		= riva_gpio_setscl;
101	chan->algo.getsda		= riva_gpio_getsda;
102	chan->algo.getscl		= riva_gpio_getscl;
103	chan->algo.udelay		= 40;
104	chan->algo.timeout		= msecs_to_jiffies(2);
105	chan->algo.data 		= chan;
106
107	i2c_set_adapdata(&chan->adapter, chan);
108
109	/* Raise SCL and SDA */
110	riva_gpio_setsda(chan, 1);
111	riva_gpio_setscl(chan, 1);
112	udelay(20);
113
114	rc = i2c_bit_add_bus(&chan->adapter);
115	if (rc == 0)
116		dev_dbg(&chan->par->pdev->dev, "I2C bus %s registered.\n", name);
117	else {
118		dev_warn(&chan->par->pdev->dev,
119			 "Failed to register I2C bus %s.\n", name);
120		chan->par = NULL;
121	}
122
123	return rc;
124}
125
126void riva_create_i2c_busses(struct riva_par *par)
127{
128	par->chan[0].par	= par;
129	par->chan[1].par	= par;
130	par->chan[2].par        = par;
131
132	par->chan[0].ddc_base = 0x36;
133	par->chan[1].ddc_base = 0x3e;
134	par->chan[2].ddc_base = 0x50;
135	riva_setup_i2c_bus(&par->chan[0], "BUS1", I2C_CLASS_HWMON);
136	riva_setup_i2c_bus(&par->chan[1], "BUS2", 0);
137	riva_setup_i2c_bus(&par->chan[2], "BUS3", 0);
138}
139
140void riva_delete_i2c_busses(struct riva_par *par)
141{
142	int i;
143
144	for (i = 0; i < 3; i++) {
145		if (!par->chan[i].par)
146			continue;
147		i2c_del_adapter(&par->chan[i].adapter);
148		par->chan[i].par = NULL;
149	}
150}
151
152int riva_probe_i2c_connector(struct riva_par *par, int conn, u8 **out_edid)
153{
154	u8 *edid = NULL;
155
156	if (par->chan[conn].par)
157		edid = fb_ddc_read(&par->chan[conn].adapter);
158
159	if (out_edid)
160		*out_edid = edid;
161	if (!edid)
162		return 1;
163
164	return 0;
165}
166