drm_dp_iic_helper.c revision 249249
1235783Skib/*
2235783Skib * Copyright �� 2009 Keith Packard
3235783Skib *
4235783Skib * Permission to use, copy, modify, distribute, and sell this software and its
5235783Skib * documentation for any purpose is hereby granted without fee, provided that
6235783Skib * the above copyright notice appear in all copies and that both that copyright
7235783Skib * notice and this permission notice appear in supporting documentation, and
8235783Skib * that the name of the copyright holders not be used in advertising or
9235783Skib * publicity pertaining to distribution of the software without specific,
10235783Skib * written prior permission.  The copyright holders make no representations
11235783Skib * about the suitability of this software for any purpose.  It is provided "as
12235783Skib * is" without express or implied warranty.
13235783Skib *
14235783Skib * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15235783Skib * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16235783Skib * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17235783Skib * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18235783Skib * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19235783Skib * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20235783Skib * OF THIS SOFTWARE.
21235783Skib */
22235783Skib
23235783Skib#include <sys/cdefs.h>
24235783Skib__FBSDID("$FreeBSD: head/sys/dev/drm2/drm_dp_iic_helper.c 249249 2013-04-08 08:37:57Z dumbbell $");
25235783Skib
26235783Skib#include <sys/types.h>
27235783Skib#include <sys/kobj.h>
28235783Skib#include <sys/bus.h>
29235783Skib#include <dev/iicbus/iic.h>
30235783Skib#include "iicbus_if.h"
31235783Skib#include <dev/iicbus/iiconf.h>
32235783Skib#include <dev/drm2/drmP.h>
33235783Skib#include <dev/drm2/drm_dp_helper.h>
34235783Skib
35235783Skibstatic int
36235783Skibiic_dp_aux_transaction(device_t idev, int mode, uint8_t write_byte,
37235783Skib    uint8_t *read_byte)
38235783Skib{
39235783Skib	struct iic_dp_aux_data *aux_data;
40235783Skib	int ret;
41235783Skib
42235783Skib	aux_data = device_get_softc(idev);
43235783Skib	ret = (*aux_data->aux_ch)(idev, mode, write_byte, read_byte);
44235783Skib	return (ret);
45235783Skib}
46235783Skib
47235783Skib/*
48235783Skib * I2C over AUX CH
49235783Skib */
50235783Skib
51235783Skib/*
52235783Skib * Send the address. If the I2C link is running, this 'restarts'
53235783Skib * the connection with the new address, this is used for doing
54235783Skib * a write followed by a read (as needed for DDC)
55235783Skib */
56235783Skibstatic int
57235783Skibiic_dp_aux_address(device_t idev, u16 address, bool reading)
58235783Skib{
59235783Skib	struct iic_dp_aux_data *aux_data;
60235783Skib	int mode, ret;
61235783Skib
62235783Skib	aux_data = device_get_softc(idev);
63235783Skib	mode = MODE_I2C_START;
64235783Skib	if (reading)
65235783Skib		mode |= MODE_I2C_READ;
66235783Skib	else
67235783Skib		mode |= MODE_I2C_WRITE;
68235783Skib	aux_data->address = address;
69235783Skib	aux_data->running = true;
70235783Skib	ret = iic_dp_aux_transaction(idev, mode, 0, NULL);
71235783Skib	return (ret);
72235783Skib}
73235783Skib
74235783Skib/*
75235783Skib * Stop the I2C transaction. This closes out the link, sending
76235783Skib * a bare address packet with the MOT bit turned off
77235783Skib */
78235783Skibstatic void
79235783Skibiic_dp_aux_stop(device_t idev, bool reading)
80235783Skib{
81235783Skib	struct iic_dp_aux_data *aux_data;
82235783Skib	int mode;
83235783Skib
84235783Skib	aux_data = device_get_softc(idev);
85235783Skib	mode = MODE_I2C_STOP;
86235783Skib	if (reading)
87235783Skib		mode |= MODE_I2C_READ;
88235783Skib	else
89235783Skib		mode |= MODE_I2C_WRITE;
90235783Skib	if (aux_data->running) {
91235783Skib		(void)iic_dp_aux_transaction(idev, mode, 0, NULL);
92235783Skib		aux_data->running = false;
93235783Skib	}
94235783Skib}
95235783Skib
96235783Skib/*
97235783Skib * Write a single byte to the current I2C address, the
98235783Skib * the I2C link must be running or this returns -EIO
99235783Skib */
100235783Skibstatic int
101235783Skibiic_dp_aux_put_byte(device_t idev, u8 byte)
102235783Skib{
103235783Skib	struct iic_dp_aux_data *aux_data;
104235783Skib	int ret;
105235783Skib
106235783Skib	aux_data = device_get_softc(idev);
107235783Skib
108235783Skib	if (!aux_data->running)
109235783Skib		return (EIO);
110235783Skib
111235783Skib	ret = iic_dp_aux_transaction(idev, MODE_I2C_WRITE, byte, NULL);
112235783Skib	return (ret);
113235783Skib}
114235783Skib
115235783Skib/*
116235783Skib * Read a single byte from the current I2C address, the
117235783Skib * I2C link must be running or this returns -EIO
118235783Skib */
119235783Skibstatic int
120235783Skibiic_dp_aux_get_byte(device_t idev, u8 *byte_ret)
121235783Skib{
122235783Skib	struct iic_dp_aux_data *aux_data;
123235783Skib	int ret;
124235783Skib
125235783Skib	aux_data = device_get_softc(idev);
126235783Skib
127235783Skib	if (!aux_data->running)
128235783Skib		return (EIO);
129235783Skib
130235783Skib	ret = iic_dp_aux_transaction(idev, MODE_I2C_READ, 0, byte_ret);
131235783Skib	return (ret);
132235783Skib}
133235783Skib
134235783Skibstatic int
135235783Skibiic_dp_aux_xfer(device_t idev, struct iic_msg *msgs, uint32_t num)
136235783Skib{
137235783Skib	u8 *buf;
138235783Skib	int b, m, ret;
139235783Skib	u16 len;
140235783Skib	bool reading;
141235783Skib
142235783Skib	ret = 0;
143235783Skib	reading = false;
144235783Skib
145235783Skib	for (m = 0; m < num; m++) {
146235783Skib		len = msgs[m].len;
147235783Skib		buf = msgs[m].buf;
148235783Skib		reading = (msgs[m].flags & IIC_M_RD) != 0;
149249249Sdumbbell		ret = iic_dp_aux_address(idev, msgs[m].slave >> 1, reading);
150235783Skib		if (ret != 0)
151235783Skib			break;
152235783Skib		if (reading) {
153235783Skib			for (b = 0; b < len; b++) {
154235783Skib				ret = iic_dp_aux_get_byte(idev, &buf[b]);
155235783Skib				if (ret != 0)
156235783Skib					break;
157235783Skib			}
158235783Skib		} else {
159235783Skib			for (b = 0; b < len; b++) {
160235783Skib				ret = iic_dp_aux_put_byte(idev, buf[b]);
161235783Skib				if (ret != 0)
162235783Skib					break;
163235783Skib			}
164235783Skib		}
165235783Skib		if (ret != 0)
166235783Skib			break;
167235783Skib	}
168235783Skib	iic_dp_aux_stop(idev, reading);
169235783Skib	DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
170235783Skib	return (ret);
171235783Skib}
172235783Skib
173235783Skibstatic void
174235783Skibiic_dp_aux_reset_bus(device_t idev)
175235783Skib{
176235783Skib
177235783Skib	(void)iic_dp_aux_address(idev, 0, false);
178235783Skib	(void)iic_dp_aux_stop(idev, false);
179235783Skib}
180235783Skib
181235783Skibstatic int
182235783Skibiic_dp_aux_reset(device_t idev, u_char speed, u_char addr, u_char *oldaddr)
183235783Skib{
184235783Skib
185235783Skib	iic_dp_aux_reset_bus(idev);
186235783Skib	return (0);
187235783Skib}
188235783Skib
189235783Skibstatic int
190235783Skibiic_dp_aux_prepare_bus(device_t idev)
191235783Skib{
192235783Skib
193235783Skib	/* adapter->retries = 3; */
194235783Skib	iic_dp_aux_reset_bus(idev);
195235783Skib	return (0);
196235783Skib}
197235783Skib
198235783Skibstatic int
199235783Skibiic_dp_aux_probe(device_t idev)
200235783Skib{
201235783Skib
202235783Skib	return (BUS_PROBE_DEFAULT);
203235783Skib}
204235783Skib
205235783Skibstatic int
206235783Skibiic_dp_aux_attach(device_t idev)
207235783Skib{
208235783Skib	struct iic_dp_aux_data *aux_data;
209235783Skib
210235783Skib	aux_data = device_get_softc(idev);
211235783Skib	aux_data->port = device_add_child(idev, "iicbus", -1);
212235783Skib	if (aux_data->port == NULL)
213235783Skib		return (ENXIO);
214235783Skib	device_quiet(aux_data->port);
215235783Skib	bus_generic_attach(idev);
216235783Skib	return (0);
217235783Skib}
218235783Skib
219235783Skibstatic int
220235783Skibiic_dp_aux_detach(device_t idev)
221235783Skib{
222235783Skib	struct iic_dp_aux_data *aux_data;
223235783Skib	device_t port;
224235783Skib
225235783Skib	aux_data = device_get_softc(idev);
226235783Skib
227235783Skib	port = aux_data->port;
228235783Skib	bus_generic_detach(idev);
229235783Skib	if (port != NULL)
230235783Skib		device_delete_child(idev, port);
231235783Skib
232235783Skib	return (0);
233235783Skib}
234235783Skib
235235783Skibint
236235783Skibiic_dp_aux_add_bus(device_t dev, const char *name,
237235783Skib    int (*ch)(device_t idev, int mode, uint8_t write_byte, uint8_t *read_byte),
238235783Skib    void *priv, device_t *bus, device_t *adapter)
239235783Skib{
240235783Skib	device_t ibus;
241235783Skib	struct iic_dp_aux_data *data;
242235783Skib	int idx, error;
243235783Skib	static int dp_bus_counter;
244235783Skib
245235783Skib	mtx_lock(&Giant);
246235783Skib
247235783Skib	idx = atomic_fetchadd_int(&dp_bus_counter, 1);
248235783Skib	ibus = device_add_child(dev, "drm_iic_dp_aux", idx);
249235783Skib	if (ibus == NULL) {
250235783Skib		mtx_unlock(&Giant);
251235783Skib		DRM_ERROR("drm_iic_dp_aux bus %d creation error\n", idx);
252235783Skib		return (-ENXIO);
253235783Skib	}
254235783Skib	device_quiet(ibus);
255235783Skib	error = device_probe_and_attach(ibus);
256235783Skib	if (error != 0) {
257235783Skib		device_delete_child(dev, ibus);
258235783Skib		mtx_unlock(&Giant);
259235783Skib		DRM_ERROR("drm_iic_dp_aux bus %d attach failed, %d\n",
260235783Skib		    idx, error);
261235783Skib		return (-error);
262235783Skib	}
263235783Skib	data = device_get_softc(ibus);
264235783Skib	data->running = false;
265235783Skib	data->address = 0;
266235783Skib	data->aux_ch = ch;
267235783Skib	data->priv = priv;
268235783Skib	error = iic_dp_aux_prepare_bus(ibus);
269235783Skib	if (error == 0) {
270235783Skib		*bus = ibus;
271235783Skib		*adapter = data->port;
272235783Skib	}
273235783Skib	mtx_unlock(&Giant);
274235783Skib	return (error);
275235783Skib}
276235783Skib
277235783Skibstatic device_method_t drm_iic_dp_aux_methods[] = {
278235783Skib	DEVMETHOD(device_probe,		iic_dp_aux_probe),
279235783Skib	DEVMETHOD(device_attach,	iic_dp_aux_attach),
280235783Skib	DEVMETHOD(device_detach,	iic_dp_aux_detach),
281235783Skib	DEVMETHOD(iicbus_reset,		iic_dp_aux_reset),
282235783Skib	DEVMETHOD(iicbus_transfer,	iic_dp_aux_xfer),
283235783Skib	DEVMETHOD_END
284235783Skib};
285235783Skibstatic driver_t drm_iic_dp_aux_driver = {
286235783Skib	"drm_iic_dp_aux",
287235783Skib	drm_iic_dp_aux_methods,
288235783Skib	sizeof(struct iic_dp_aux_data)
289235783Skib};
290235783Skibstatic devclass_t drm_iic_dp_aux_devclass;
291235783SkibDRIVER_MODULE_ORDERED(drm_iic_dp_aux, drmn, drm_iic_dp_aux_driver,
292235783Skib    drm_iic_dp_aux_devclass, 0, 0, SI_ORDER_SECOND);
293