1183000Smav/*
2183000Smav * Copyright �� 2009 Keith Packard
3162924Sjoel *
4162924Sjoel * Permission to use, copy, modify, distribute, and sell this software and its
5162924Sjoel * documentation for any purpose is hereby granted without fee, provided that
6162924Sjoel * the above copyright notice appear in all copies and that both that copyright
7162924Sjoel * notice and this permission notice appear in supporting documentation, and
8162924Sjoel * that the name of the copyright holders not be used in advertising or
9162924Sjoel * publicity pertaining to distribution of the software without specific,
10162924Sjoel * written prior permission.  The copyright holders make no representations
11162924Sjoel * about the suitability of this software for any purpose.  It is provided "as
12162924Sjoel * is" without express or implied warranty.
13162924Sjoel *
14162924Sjoel * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15162924Sjoel * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16162924Sjoel * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17162924Sjoel * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18162924Sjoel * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19162924Sjoel * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20162924Sjoel * OF THIS SOFTWARE.
21162924Sjoel */
22162924Sjoel
23162924Sjoel#include <sys/cdefs.h>
24162924Sjoel__FBSDID("$FreeBSD$");
25162924Sjoel
26162924Sjoel#include <sys/types.h>
27162924Sjoel#include <sys/kobj.h>
28230551Smav#include <sys/bus.h>
29162924Sjoel#include <dev/iicbus/iic.h>
30162924Sjoel#include "iicbus_if.h"
31162924Sjoel#include <dev/iicbus/iiconf.h>
32162924Sjoel#include <dev/drm2/drmP.h>
33162924Sjoel#include <dev/drm2/drm_dp_helper.h>
34162924Sjoel
35162924Sjoelstatic int
36162924Sjoeliic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte,
37162924Sjoel    uint8_t *read_byte)
38162924Sjoel{
39162924Sjoel	struct iic_dp_aux_data *aux_data;
40162924Sjoel	int ret;
41162924Sjoel
42162924Sjoel	aux_data = device_get_softc(idev);
43162924Sjoel	ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte);
44162924Sjoel	return (ret);
45162924Sjoel}
46162924Sjoel
47162924Sjoel/*
48162924Sjoel * I2C over AUX CH
49183000Smav */
50183000Smav
51183000Smav/*
52183000Smav * Send the address. If the I2C link is running, this 'restarts'
53183000Smav * the connection with the new address, this is used for doing
54162924Sjoel * a write followed by a read (as needed for DDC)
55162924Sjoel */
56230130Smavstatic int
57230130Smaviic_dp_aux_address(device_t idev, u16 address, bool reading)
58230130Smav{
59162924Sjoel	struct iic_dp_aux_data *aux_data;
60183000Smav	int mode, ret;
61184738Smav
62184751Smav	aux_data = device_get_softc(idev);
63199247Smav	mode = MODE_I2C_START;
64183000Smav	if (reading)
65162924Sjoel		mode |= MODE_I2C_READ;
66162924Sjoel	else
67162924Sjoel		mode |= MODE_I2C_WRITE;
68162924Sjoel	aux_data->address = address;
69162924Sjoel	aux_data->running = true;
70162924Sjoel	ret = iic_dp_aux_transaction(idev, mode, 0, NULL);
71183000Smav	return (ret);
72184738Smav}
73183000Smav
74183016Sjoel/*
75162924Sjoel * Stop the I2C transaction. This closes out the link, sending
76183016Sjoel * a bare address packet with the MOT bit turned off
77183016Sjoel */
78183016Sjoelstatic void
79183016Sjoeliic_dp_aux_stop(device_t idev, bool reading)
80184738Smav{
81230130Smav	struct iic_dp_aux_data *aux_data;
82230130Smav	int mode;
83230130Smav
84184738Smav	aux_data = device_get_softc(idev);
85184738Smav	mode = MODE_I2C_STOP;
86183564Smav	if (reading)
87184738Smav		mode |= MODE_I2C_READ;
88184738Smav	else
89184738Smav		mode |= MODE_I2C_WRITE;
90184738Smav	if (aux_data->running) {
91184751Smav		(void)iic_dp_aux_transaction(idev, mode, 0, NULL);
92166070Sjoel		aux_data->running = false;
93166070Sjoel	}
94166070Sjoel}
95166070Sjoel
96183000Smav/*
97183000Smav * Write a single byte to the current I2C address, the
98230130Smav * the I2C link must be running or this returns -EIO
99166070Sjoel */
100230130Smavstatic int
101169279Sjoeliic_dp_aux_put_byte(device_t idev, u8 byte)
102230130Smav{
103230130Smav	struct iic_dp_aux_data *aux_data;
104230130Smav	int ret;
105230130Smav
106230130Smav	aux_data = device_get_softc(idev);
107230130Smav
108230130Smav	if (!aux_data->running)
109230130Smav		return (EIO);
110230130Smav
111230130Smav	ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL);
112230130Smav	return (ret);
113230130Smav}
114230130Smav
115230130Smav/*
116230130Smav * Read a single byte from the current I2C address, the
117169279Sjoel * I2C link must be running or this returns -EIO
118169279Sjoel */
119169279Sjoelstatic int
120169279Sjoeliic_dp_aux_get_byte(device_t idev, u8 *byte_ret)
121169279Sjoel{
122166070Sjoel	struct iic_dp_aux_data *aux_data;
123166070Sjoel	int ret;
124169279Sjoel
125169279Sjoel	aux_data = device_get_softc(idev);
126169279Sjoel
127169279Sjoel	if (!aux_data->running)
128183000Smav		return (EIO);
129166070Sjoel
130166070Sjoel	ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret);
131166070Sjoel	return (ret);
132166070Sjoel}
133166070Sjoel
134166070Sjoelstatic int
135166094Sjoeliic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num)
136166070Sjoel{
137166070Sjoel	u8 *buf;
138202160Smav	int b, m, ret;
139230130Smav	u16 len;
140230130Smav	bool reading;
141230130Smav
142230130Smav	ret = 0;
143230130Smav	reading = false;
144230130Smav
145230130Smav	for (m = 0; m < num; m++) {
146230130Smav		len = msgs[m].len;
147230130Smav		buf = msgs[m].buf;
148230130Smav		reading = (msgs[m].flags & IIC_M_RD) != 0;
149230130Smav		ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading);
150230130Smav		if (ret != 0)
151230130Smav			break;
152230130Smav		if (reading) {
153230130Smav			for (b = 0; b < len; b++) {
154230130Smav				ret = iic_dp_aux_get_byte(idev, &buf[b]);
155230130Smav				if (ret != 0)
156230130Smav					break;
157230130Smav			}
158230130Smav		} else {
159230130Smav			for (b = 0; b < len; b++) {
160230130Smav				ret = iic_dp_aux_put_byte(idev, buf[b]);
161230130Smav				if (ret != 0)
162230130Smav					break;
163230130Smav			}
164230130Smav		}
165230130Smav		if (ret != 0)
166230130Smav			break;
167230130Smav	}
168230130Smav	iic_dp_aux_stop(idev, reading);
169230130Smav	DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
170230130Smav	return (ret);
171230130Smav}
172184738Smav
173184738Smavstatic void
174183000Smaviic_dp_aux_reset_bus(device_t idev)
175184738Smav{
176184738Smav
177184738Smav	(void)iic_dp_aux_address(idev, 0, false);
178230130Smav	(void)iic_dp_aux_stop(idev, false);
179230130Smav}
180184738Smav
181184738Smavstatic int
182184738Smaviic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr)
183184738Smav{
184184738Smav
185230551Smav	iic_dp_aux_reset_bus(idev);
186230551Smav	return (0);
187236443Sjoel}
188230551Smav
189230551Smavstatic int
190230551Smaviic_dp_aux_prepare_bus(device_t idev)
191230551Smav{
192230551Smav
193230551Smav	/* adapter->retries = 3; */
194166070Sjoel	iic_dp_aux_reset_bus(idev);
195230551Smav	return (0);
196230551Smav}
197230551Smav
198183000Smavstatic int
199183000Smaviic_dp_aux_probe(device_t idev)
200183000Smav{
201183000Smav
202183000Smav	return (BUS_PROBE_DEFAULT);
203183000Smav}
204183000Smav
205184738Smavstatic int
206184738Smaviic_dp_aux_attach(device_t idev)
207184738Smav{
208184738Smav	struct iic_dp_aux_data *aux_data;
209184738Smav
210183000Smav	aux_data = device_get_softc(idev);
211183000Smav	aux_data->port = device_add_child(idev, "iicbus", -1);
212183000Smav	if (aux_data->port == NULL)
213184738Smav		return (ENXIO);
214183000Smav	device_quiet(aux_data->port);
215230130Smav	bus_generic_attach(idev);
216183000Smav	return (0);
217184738Smav}
218184738Smav
219184738Smavint
220184738Smaviic_dp_aux_add_bus(device_t dev, const char *name,
221184738Smav    int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte),
222184738Smav    void *priv, device_t *bus, device_t *adapter)
223184738Smav{
224183000Smav	device_t ibus;
225183000Smav	struct iic_dp_aux_data *data;
226184738Smav	int idx, error;
227184738Smav	static int dp_bus_counter;
228184738Smav
229184738Smav	mtx_lock(&Giant);
230184738Smav
231184738Smav	idx = atomic_fetchadd_int(&dp_bus_counter, 1);
232184738Smav	ibus = device_add_child(dev, "drm_iic_dp_aux", idx);
233184738Smav	if (ibus == NULL) {
234184738Smav		mtx_unlock(&Giant);
235230130Smav		DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx);
236230130Smav		return (-ENXIO);
237230130Smav	}
238235317Sgjb	device_quiet(ibus);
239230130Smav	error = device_probe_and_attach(ibus);
240230130Smav	if (error != 0) {
241235317Sgjb		device_delete_child(dev, ibus);
242230130Smav		mtx_unlock(&Giant);
243230130Smav		DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n",
244230130Smav		    idx, error);
245230130Smav		return (-error);
246183000Smav	}
247183000Smav	data = device_get_softc(ibus);
248183000Smav	data->running = false;
249183000Smav	data->address = 0;
250183000Smav	data->aux_ch = ch;
251183000Smav	data->priv = priv;
252183000Smav	error = iic_dp_aux_prepare_bus(ibus);
253183000Smav	if (error == 0) {
254183000Smav		*bus = ibus;
255183000Smav		*adapter = data->port;
256183000Smav	}
257183000Smav	mtx_unlock(&Giant);
258183000Smav	return (error);
259183000Smav}
260183000Smav
261183000Smavstatic device_method_t drm_iic_dp_aux_methods[] = {
262183000Smav	DEVMETHOD(device_probe,		iic_dp_aux_probe),
263183000Smav	DEVMETHOD(device_attach,	iic_dp_aux_attach),
264183000Smav	DEVMETHOD(device_detach,	bus_generic_detach),
265183000Smav	DEVMETHOD(iicbus_reset,		iic_dp_aux_reset),
266183000Smav	DEVMETHOD(iicbus_transfer,	iic_dp_aux_xfer),
267183000Smav	DEVMETHOD_END
268183000Smav};
269184738Smavstatic driver_t drm_iic_dp_aux_driver = {
270183000Smav	"drm_iic_dp_aux",
271184738Smav	drm_iic_dp_aux_methods,
272183000Smav	sizeof(struct iic_dp_aux_data)
273183000Smav};
274184738Smavstatic devclass_t drm_iic_dp_aux_devclass;
275184738SmavDRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver,
276183000Smav    drm_iic_dp_aux_devclass, 0, 0, SI_ORDER_SECOND);
277183000Smav