xref: /netbsd/sys/dev/i2c/anxedp.c (revision 67b4eb14)
1*67b4eb14Sriastradh /* $NetBSD: anxedp.c,v 1.8 2021/12/19 11:01:10 riastradh Exp $ */
2f7483fa6Sjmcneill 
3f7483fa6Sjmcneill /*-
4f7483fa6Sjmcneill  * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
5f7483fa6Sjmcneill  * All rights reserved.
6f7483fa6Sjmcneill  *
7f7483fa6Sjmcneill  * Redistribution and use in source and binary forms, with or without
8f7483fa6Sjmcneill  * modification, are permitted provided that the following conditions
9f7483fa6Sjmcneill  * are met:
10f7483fa6Sjmcneill  * 1. Redistributions of source code must retain the above copyright
11f7483fa6Sjmcneill  *    notice, this list of conditions and the following disclaimer.
12f7483fa6Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13f7483fa6Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
14f7483fa6Sjmcneill  *    documentation and/or other materials provided with the distribution.
15f7483fa6Sjmcneill  *
16f7483fa6Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17f7483fa6Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18f7483fa6Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19f7483fa6Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20f7483fa6Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21f7483fa6Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22f7483fa6Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23f7483fa6Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24f7483fa6Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f7483fa6Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f7483fa6Sjmcneill  * SUCH DAMAGE.
27f7483fa6Sjmcneill  */
28f7483fa6Sjmcneill 
29f7483fa6Sjmcneill #include <sys/cdefs.h>
30*67b4eb14Sriastradh __KERNEL_RCSID(0, "$NetBSD: anxedp.c,v 1.8 2021/12/19 11:01:10 riastradh Exp $");
31f7483fa6Sjmcneill 
32f7483fa6Sjmcneill #include <sys/param.h>
33f7483fa6Sjmcneill #include <sys/bus.h>
34*67b4eb14Sriastradh #include <sys/conf.h>
35f7483fa6Sjmcneill #include <sys/device.h>
36f7483fa6Sjmcneill #include <sys/intr.h>
37f7483fa6Sjmcneill #include <sys/kernel.h>
38*67b4eb14Sriastradh #include <sys/systm.h>
39f7483fa6Sjmcneill 
40f7483fa6Sjmcneill #include <dev/ic/dw_hdmi.h>
41f7483fa6Sjmcneill 
42f7483fa6Sjmcneill #include <dev/i2c/ddcreg.h>
43*67b4eb14Sriastradh #include <dev/i2c/ddcvar.h>
44*67b4eb14Sriastradh #include <dev/i2c/i2cvar.h>
45f7483fa6Sjmcneill #include <dev/videomode/edidvar.h>
46f7483fa6Sjmcneill 
47f7483fa6Sjmcneill #include <dev/fdt/fdt_port.h>
48*67b4eb14Sriastradh #include <dev/fdt/fdtvar.h>
49f7483fa6Sjmcneill 
50c7fa00edSriastradh #include <drm/drm_connector.h>
51f7483fa6Sjmcneill #include <drm/drm_crtc.h>
52f7483fa6Sjmcneill #include <drm/drm_crtc_helper.h>
53*67b4eb14Sriastradh #include <drm/drm_drv.h>
54f7483fa6Sjmcneill #include <drm/drm_edid.h>
55c7fa00edSriastradh #include <drm/drm_probe_helper.h>
56f7483fa6Sjmcneill 
57f7483fa6Sjmcneill #define	ANX_DP_AUX_CH_CTL_1	0xe5
58f7483fa6Sjmcneill #define	 ANX_AUX_LENGTH		__BITS(7,4)
59f7483fa6Sjmcneill #define	 ANX_AUX_TX_COMM	__BITS(3,0)
60f7483fa6Sjmcneill #define	  ANX_AUX_TX_COMM_MOT	4
61f7483fa6Sjmcneill #define	  ANX_AUX_TX_COMM_READ	1
62f7483fa6Sjmcneill #define	ANX_DP_AUX_ADDR(n)	(0xe6 + (n))
63f7483fa6Sjmcneill #define	ANX_DP_AUX_CH_CTL_2	0xe9
64f7483fa6Sjmcneill #define	 ANX_ADDR_ONLY		__BIT(1)
65f7483fa6Sjmcneill #define	 ANX_AUX_EN		__BIT(0)
66f7483fa6Sjmcneill #define	ANX_BUF_DATA(n)		(0xf0 + (n))
67f7483fa6Sjmcneill 
68f7483fa6Sjmcneill #define	ANX_DP_INT_STA		0xf7
69f7483fa6Sjmcneill #define	 ANX_RPLY_RECEIV	__BIT(1)
70f7483fa6Sjmcneill 
71f7483fa6Sjmcneill static const struct device_compatible_entry compat_data[] = {
723f1bcda8Sthorpej 	{ .compat = "analogix,anx6345" },
7301632a17Sthorpej 	DEVICE_COMPAT_EOL
74f7483fa6Sjmcneill };
75f7483fa6Sjmcneill 
76f7483fa6Sjmcneill struct anxedp_softc;
77f7483fa6Sjmcneill 
78f7483fa6Sjmcneill struct anxedp_connector {
79f7483fa6Sjmcneill 	struct drm_connector	base;
80f7483fa6Sjmcneill 	struct anxedp_softc	*sc;
81f7483fa6Sjmcneill };
82f7483fa6Sjmcneill 
83f7483fa6Sjmcneill struct anxedp_softc {
84f7483fa6Sjmcneill 	device_t		sc_dev;
85f7483fa6Sjmcneill 	i2c_tag_t		sc_i2c;
86f7483fa6Sjmcneill 	i2c_addr_t		sc_addr;
87f7483fa6Sjmcneill 	int			sc_phandle;
88f7483fa6Sjmcneill 
89f7483fa6Sjmcneill 	struct anxedp_connector sc_connector;
90f7483fa6Sjmcneill 	struct drm_bridge	sc_bridge;
91f7483fa6Sjmcneill 
92f7483fa6Sjmcneill 	struct fdt_device_ports	sc_ports;
93f7483fa6Sjmcneill 	struct drm_display_mode	sc_curmode;
94f7483fa6Sjmcneill };
95f7483fa6Sjmcneill 
96f7483fa6Sjmcneill #define	to_anxedp_connector(x)	container_of(x, struct anxedp_connector, base)
97f7483fa6Sjmcneill 
98f7483fa6Sjmcneill static uint8_t
anxedp_read(struct anxedp_softc * sc,u_int off,uint8_t reg)99f7483fa6Sjmcneill anxedp_read(struct anxedp_softc *sc, u_int off, uint8_t reg)
100f7483fa6Sjmcneill {
101f7483fa6Sjmcneill 	uint8_t val;
102f7483fa6Sjmcneill 
103e5b8f484Sthorpej 	if (iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr + off, reg, &val, 0) != 0)
104f7483fa6Sjmcneill 		val = 0xff;
105f7483fa6Sjmcneill 
106f7483fa6Sjmcneill 	return val;
107f7483fa6Sjmcneill }
108f7483fa6Sjmcneill 
109f7483fa6Sjmcneill static void
anxedp_write(struct anxedp_softc * sc,u_int off,uint8_t reg,uint8_t val)110f7483fa6Sjmcneill anxedp_write(struct anxedp_softc *sc, u_int off, uint8_t reg, uint8_t val)
111f7483fa6Sjmcneill {
112e5b8f484Sthorpej 	(void)iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr + off, reg, val, 0);
113f7483fa6Sjmcneill }
114f7483fa6Sjmcneill 
115c129e06eSjmcneill static int
anxedp_connector_dpms(struct drm_connector * connector,int mode)116c129e06eSjmcneill anxedp_connector_dpms(struct drm_connector *connector, int mode)
117c129e06eSjmcneill {
118c129e06eSjmcneill 	int error;
119c129e06eSjmcneill 
120c129e06eSjmcneill 	if (mode != DRM_MODE_DPMS_ON)
121c129e06eSjmcneill 		pmf_event_inject(NULL, PMFE_DISPLAY_OFF);
122c129e06eSjmcneill 
123c129e06eSjmcneill 	error = drm_helper_connector_dpms(connector, mode);
124c129e06eSjmcneill 
125c129e06eSjmcneill 	if (mode == DRM_MODE_DPMS_ON)
126c129e06eSjmcneill 		pmf_event_inject(NULL, PMFE_DISPLAY_ON);
127c129e06eSjmcneill 
128c129e06eSjmcneill 	return error;
129c129e06eSjmcneill }
130c129e06eSjmcneill 
131f7483fa6Sjmcneill static enum drm_connector_status
anxedp_connector_detect(struct drm_connector * connector,bool force)132f7483fa6Sjmcneill anxedp_connector_detect(struct drm_connector *connector, bool force)
133f7483fa6Sjmcneill {
134f7483fa6Sjmcneill 	return connector_status_connected;
135f7483fa6Sjmcneill }
136f7483fa6Sjmcneill 
137f7483fa6Sjmcneill static void
anxedp_connector_destroy(struct drm_connector * connector)138f7483fa6Sjmcneill anxedp_connector_destroy(struct drm_connector *connector)
139f7483fa6Sjmcneill {
140f7483fa6Sjmcneill 	drm_connector_unregister(connector);
141f7483fa6Sjmcneill 	drm_connector_cleanup(connector);
142f7483fa6Sjmcneill }
143f7483fa6Sjmcneill 
144f7483fa6Sjmcneill static const struct drm_connector_funcs anxedp_connector_funcs = {
145c129e06eSjmcneill 	.dpms = anxedp_connector_dpms,
146f7483fa6Sjmcneill 	.detect = anxedp_connector_detect,
147f7483fa6Sjmcneill 	.fill_modes = drm_helper_probe_single_connector_modes,
148f7483fa6Sjmcneill 	.destroy = anxedp_connector_destroy,
149f7483fa6Sjmcneill };
150f7483fa6Sjmcneill 
151f7483fa6Sjmcneill static int
anxedp_aux_wait(struct anxedp_softc * sc)152f7483fa6Sjmcneill anxedp_aux_wait(struct anxedp_softc *sc)
153f7483fa6Sjmcneill {
154f7483fa6Sjmcneill 	uint8_t val;
155f7483fa6Sjmcneill 	int retry;
156f7483fa6Sjmcneill 
157f7483fa6Sjmcneill 	for (retry = 1000; retry > 0; retry--) {
158f7483fa6Sjmcneill 		val = anxedp_read(sc, 0, ANX_DP_AUX_CH_CTL_2);
159f7483fa6Sjmcneill 		if ((val & ANX_AUX_EN) == 0)
160f7483fa6Sjmcneill 			break;
161f7483fa6Sjmcneill 		delay(100);
162f7483fa6Sjmcneill 	}
163f7483fa6Sjmcneill 	if (retry == 0) {
164f7483fa6Sjmcneill 		device_printf(sc->sc_dev, "aux transfer timeout\n");
165f7483fa6Sjmcneill 		return ETIMEDOUT;
166f7483fa6Sjmcneill 	}
167f7483fa6Sjmcneill 
168f7483fa6Sjmcneill 	for (retry = 1000; retry > 0; retry--) {
169f7483fa6Sjmcneill 		val = anxedp_read(sc, 1, ANX_DP_INT_STA);
170f7483fa6Sjmcneill 		if ((val & ANX_RPLY_RECEIV) != 0) {
171f7483fa6Sjmcneill 			anxedp_write(sc, 1, ANX_DP_INT_STA, val);
172f7483fa6Sjmcneill 			break;
173f7483fa6Sjmcneill 		}
174f7483fa6Sjmcneill 		delay(100);
175f7483fa6Sjmcneill 	}
176f7483fa6Sjmcneill 	if (retry == 0) {
177f7483fa6Sjmcneill 		device_printf(sc->sc_dev, "aux transfer timeout\n");
178f7483fa6Sjmcneill 		return ETIMEDOUT;
179f7483fa6Sjmcneill 	}
180f7483fa6Sjmcneill 
181f7483fa6Sjmcneill 	return 0;
182f7483fa6Sjmcneill }
183f7483fa6Sjmcneill 
184f7483fa6Sjmcneill static int
anxedp_aux_transfer(struct anxedp_softc * sc,uint8_t comm,uint32_t addr,uint8_t * buf,int buflen)185f7483fa6Sjmcneill anxedp_aux_transfer(struct anxedp_softc *sc, uint8_t comm, uint32_t addr,
186f7483fa6Sjmcneill     uint8_t *buf, int buflen)
187f7483fa6Sjmcneill {
188f7483fa6Sjmcneill 	uint8_t ctrl[2];
189f7483fa6Sjmcneill 	int n, error;
190f7483fa6Sjmcneill 
191f7483fa6Sjmcneill 	ctrl[0] = __SHIFTIN(comm, ANX_AUX_TX_COMM);
192f7483fa6Sjmcneill 	if (buflen > 0)
193f7483fa6Sjmcneill 		ctrl[0] |= __SHIFTIN(buflen - 1, ANX_AUX_LENGTH);
194f7483fa6Sjmcneill 	ctrl[1] = ANX_AUX_EN;
195f7483fa6Sjmcneill 	if (buflen == 0)
196f7483fa6Sjmcneill 		ctrl[1] |= ANX_ADDR_ONLY;
197f7483fa6Sjmcneill 
198f7483fa6Sjmcneill 	if (comm != ANX_AUX_TX_COMM_READ) {
199f7483fa6Sjmcneill 		for (n = 0; n < buflen; n++)
200f7483fa6Sjmcneill 			anxedp_write(sc, 0, ANX_BUF_DATA(n), buf[n]);
201f7483fa6Sjmcneill 	}
202f7483fa6Sjmcneill 
203f7483fa6Sjmcneill 	anxedp_write(sc, 0, ANX_DP_AUX_ADDR(0), addr & 0xff);
204f7483fa6Sjmcneill 	anxedp_write(sc, 0, ANX_DP_AUX_ADDR(1), (addr >> 8) & 0xff);
205f7483fa6Sjmcneill 	anxedp_write(sc, 0, ANX_DP_AUX_ADDR(2), (addr >> 16) & 0xf);
206f7483fa6Sjmcneill 	anxedp_write(sc, 0, ANX_DP_AUX_CH_CTL_1, ctrl[0]);
207f7483fa6Sjmcneill 	anxedp_write(sc, 0, ANX_DP_AUX_CH_CTL_2, ctrl[1]);
208f7483fa6Sjmcneill 
209f7483fa6Sjmcneill 	error = anxedp_aux_wait(sc);
210f7483fa6Sjmcneill 	if (error != 0)
211f7483fa6Sjmcneill 		return error;
212f7483fa6Sjmcneill 
213f7483fa6Sjmcneill 	if (comm == ANX_AUX_TX_COMM_READ) {
214f7483fa6Sjmcneill 		for (n = 0; n < buflen; n++)
215f7483fa6Sjmcneill 			buf[n] = anxedp_read(sc, 0, ANX_BUF_DATA(n));
216f7483fa6Sjmcneill 	}
217f7483fa6Sjmcneill 
218f7483fa6Sjmcneill 	return 0;
219f7483fa6Sjmcneill }
220f7483fa6Sjmcneill 
221f7483fa6Sjmcneill static int
anxedp_read_edid(struct anxedp_softc * sc,uint8_t * edid,int edidlen)222f7483fa6Sjmcneill anxedp_read_edid(struct anxedp_softc *sc, uint8_t *edid, int edidlen)
223f7483fa6Sjmcneill {
224f7483fa6Sjmcneill 	int error;
225f7483fa6Sjmcneill 	uint8_t n;
226f7483fa6Sjmcneill 
227f7483fa6Sjmcneill 	for (n = 0; n < edidlen; n += 16) {
228f7483fa6Sjmcneill 		const int xferlen = MIN(edidlen - n, 16);
229f7483fa6Sjmcneill 
230f7483fa6Sjmcneill 		error = anxedp_aux_transfer(sc, ANX_AUX_TX_COMM_MOT, DDC_ADDR, &n, 1);
231f7483fa6Sjmcneill 		if (error != 0)
232f7483fa6Sjmcneill 			return error;
233f7483fa6Sjmcneill 
234f7483fa6Sjmcneill 		error = anxedp_aux_transfer(sc, ANX_AUX_TX_COMM_READ, DDC_ADDR, &edid[n], xferlen);
235f7483fa6Sjmcneill 		if (error != 0)
236f7483fa6Sjmcneill 			return error;
237f7483fa6Sjmcneill 	}
238f7483fa6Sjmcneill 
239f7483fa6Sjmcneill 	return 0;
240f7483fa6Sjmcneill }
241f7483fa6Sjmcneill 
242f7483fa6Sjmcneill static int
anxedp_connector_get_modes(struct drm_connector * connector)243f7483fa6Sjmcneill anxedp_connector_get_modes(struct drm_connector *connector)
244f7483fa6Sjmcneill {
245f7483fa6Sjmcneill 	struct anxedp_connector *anxedp_connector = to_anxedp_connector(connector);
246f7483fa6Sjmcneill 	struct anxedp_softc * const sc = anxedp_connector->sc;
247f7483fa6Sjmcneill 	char edid[EDID_LENGTH];
248f7483fa6Sjmcneill 	struct edid *pedid = NULL;
249f7483fa6Sjmcneill 	int error;
250f7483fa6Sjmcneill 
251e5b8f484Sthorpej 	iic_acquire_bus(sc->sc_i2c, 0);
252f7483fa6Sjmcneill 	error = anxedp_read_edid(sc, edid, sizeof(edid));
253e5b8f484Sthorpej 	iic_release_bus(sc->sc_i2c, 0);
254f7483fa6Sjmcneill 	if (error == 0)
255f7483fa6Sjmcneill 		pedid = (struct edid *)edid;
256f7483fa6Sjmcneill 
257c7fa00edSriastradh 	drm_connector_update_edid_property(connector, pedid);
258f7483fa6Sjmcneill 	if (pedid == NULL)
259f7483fa6Sjmcneill 		return 0;
260f7483fa6Sjmcneill 
261c7fa00edSriastradh 	return drm_add_edid_modes(connector, pedid);
262f7483fa6Sjmcneill }
263f7483fa6Sjmcneill 
264f7483fa6Sjmcneill static const struct drm_connector_helper_funcs anxedp_connector_helper_funcs = {
265f7483fa6Sjmcneill 	.get_modes = anxedp_connector_get_modes,
266f7483fa6Sjmcneill };
267f7483fa6Sjmcneill 
268f7483fa6Sjmcneill static int
anxedp_bridge_attach(struct drm_bridge * bridge)269f7483fa6Sjmcneill anxedp_bridge_attach(struct drm_bridge *bridge)
270f7483fa6Sjmcneill {
271f7483fa6Sjmcneill 	struct anxedp_softc * const sc = bridge->driver_private;
272f7483fa6Sjmcneill 	struct anxedp_connector *anxedp_connector = &sc->sc_connector;
273f7483fa6Sjmcneill 	struct drm_connector *connector = &anxedp_connector->base;
274f7483fa6Sjmcneill 	int error;
275f7483fa6Sjmcneill 
276f7483fa6Sjmcneill 	anxedp_connector->sc = sc;
277f7483fa6Sjmcneill 
278f7483fa6Sjmcneill 	connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
279f7483fa6Sjmcneill 	connector->interlace_allowed = 0;
280f7483fa6Sjmcneill 	connector->doublescan_allowed = 0;
281f7483fa6Sjmcneill 
282f7483fa6Sjmcneill 	drm_connector_init(bridge->dev, connector, &anxedp_connector_funcs,
283f7483fa6Sjmcneill 	    connector->connector_type);
284f7483fa6Sjmcneill 	drm_connector_helper_add(connector, &anxedp_connector_helper_funcs);
285f7483fa6Sjmcneill 
286c7fa00edSriastradh 	error = drm_connector_attach_encoder(connector, bridge->encoder);
287f7483fa6Sjmcneill 	if (error != 0)
288f7483fa6Sjmcneill 		return error;
289f7483fa6Sjmcneill 
290f7483fa6Sjmcneill 	return drm_connector_register(connector);
291f7483fa6Sjmcneill }
292f7483fa6Sjmcneill 
293f7483fa6Sjmcneill static void
anxedp_bridge_enable(struct drm_bridge * bridge)294f7483fa6Sjmcneill anxedp_bridge_enable(struct drm_bridge *bridge)
295f7483fa6Sjmcneill {
296f7483fa6Sjmcneill }
297f7483fa6Sjmcneill 
298f7483fa6Sjmcneill static void
anxedp_bridge_pre_enable(struct drm_bridge * bridge)299f7483fa6Sjmcneill anxedp_bridge_pre_enable(struct drm_bridge *bridge)
300f7483fa6Sjmcneill {
301f7483fa6Sjmcneill }
302f7483fa6Sjmcneill 
303f7483fa6Sjmcneill static void
anxedp_bridge_disable(struct drm_bridge * bridge)304f7483fa6Sjmcneill anxedp_bridge_disable(struct drm_bridge *bridge)
305f7483fa6Sjmcneill {
306f7483fa6Sjmcneill }
307f7483fa6Sjmcneill 
308f7483fa6Sjmcneill static void
anxedp_bridge_post_disable(struct drm_bridge * bridge)309f7483fa6Sjmcneill anxedp_bridge_post_disable(struct drm_bridge *bridge)
310f7483fa6Sjmcneill {
311f7483fa6Sjmcneill }
312f7483fa6Sjmcneill 
313f7483fa6Sjmcneill static void
anxedp_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)314f7483fa6Sjmcneill anxedp_bridge_mode_set(struct drm_bridge *bridge,
315c7fa00edSriastradh     const struct drm_display_mode *mode,
316c7fa00edSriastradh     const struct drm_display_mode *adjusted_mode)
317f7483fa6Sjmcneill {
318f7483fa6Sjmcneill 	struct anxedp_softc * const sc = bridge->driver_private;
319f7483fa6Sjmcneill 
320f7483fa6Sjmcneill 	sc->sc_curmode = *adjusted_mode;
321f7483fa6Sjmcneill }
322f7483fa6Sjmcneill 
323f7483fa6Sjmcneill static bool
anxedp_bridge_mode_fixup(struct drm_bridge * bridge,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)324f7483fa6Sjmcneill anxedp_bridge_mode_fixup(struct drm_bridge *bridge,
325f7483fa6Sjmcneill     const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
326f7483fa6Sjmcneill {
327f7483fa6Sjmcneill 	return true;
328f7483fa6Sjmcneill }
329f7483fa6Sjmcneill 
330f7483fa6Sjmcneill static const struct drm_bridge_funcs anxedp_bridge_funcs = {
331f7483fa6Sjmcneill 	.attach = anxedp_bridge_attach,
332f7483fa6Sjmcneill 	.enable = anxedp_bridge_enable,
333f7483fa6Sjmcneill 	.pre_enable = anxedp_bridge_pre_enable,
334f7483fa6Sjmcneill 	.disable = anxedp_bridge_disable,
335f7483fa6Sjmcneill 	.post_disable = anxedp_bridge_post_disable,
336f7483fa6Sjmcneill 	.mode_set = anxedp_bridge_mode_set,
337f7483fa6Sjmcneill 	.mode_fixup = anxedp_bridge_mode_fixup,
338f7483fa6Sjmcneill };
339f7483fa6Sjmcneill 
340f7483fa6Sjmcneill static int
anxedp_ep_activate(device_t dev,struct fdt_endpoint * ep,bool activate)341f7483fa6Sjmcneill anxedp_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate)
342f7483fa6Sjmcneill {
343f7483fa6Sjmcneill 	struct anxedp_softc * const sc = device_private(dev);
344f7483fa6Sjmcneill 	struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep);
345f7483fa6Sjmcneill 	struct drm_encoder *encoder;
346f7483fa6Sjmcneill 	struct drm_bridge *bridge;
347f7483fa6Sjmcneill 	int error;
348f7483fa6Sjmcneill 
349f7483fa6Sjmcneill 	if (!activate)
350f7483fa6Sjmcneill 		return EINVAL;
351f7483fa6Sjmcneill 
352f7483fa6Sjmcneill 	if (fdt_endpoint_port_index(ep) != 0)
353f7483fa6Sjmcneill 		return EINVAL;
354f7483fa6Sjmcneill 
355f7483fa6Sjmcneill 	switch (fdt_endpoint_type(in_ep)) {
356f7483fa6Sjmcneill 	case EP_DRM_ENCODER:
357f7483fa6Sjmcneill 		encoder = fdt_endpoint_get_data(in_ep);
358f7483fa6Sjmcneill 		break;
359f7483fa6Sjmcneill 	case EP_DRM_BRIDGE:
360f7483fa6Sjmcneill 		bridge = fdt_endpoint_get_data(in_ep);
361f7483fa6Sjmcneill 		encoder = bridge->encoder;
362f7483fa6Sjmcneill 		break;
363f7483fa6Sjmcneill 	default:
364f7483fa6Sjmcneill 		encoder = NULL;
365f7483fa6Sjmcneill 		break;
366f7483fa6Sjmcneill 	}
367f7483fa6Sjmcneill 
368f7483fa6Sjmcneill 	if (encoder == NULL)
369f7483fa6Sjmcneill 		return EINVAL;
370f7483fa6Sjmcneill 
371f7483fa6Sjmcneill 	sc->sc_connector.base.connector_type = DRM_MODE_CONNECTOR_eDP;
372f7483fa6Sjmcneill 
373f7483fa6Sjmcneill 	sc->sc_bridge.driver_private = sc;
374f7483fa6Sjmcneill 	sc->sc_bridge.funcs = &anxedp_bridge_funcs;
375f7483fa6Sjmcneill 	sc->sc_bridge.encoder = encoder;
376f7483fa6Sjmcneill 
377c7fa00edSriastradh 	error = drm_bridge_attach(encoder, &sc->sc_bridge, NULL);
378f7483fa6Sjmcneill 	if (error != 0)
379f7483fa6Sjmcneill 		return EIO;
380f7483fa6Sjmcneill 
381f7483fa6Sjmcneill 	return 0;
382f7483fa6Sjmcneill }
383f7483fa6Sjmcneill 
384f7483fa6Sjmcneill static void *
anxedp_ep_get_data(device_t dev,struct fdt_endpoint * ep)385f7483fa6Sjmcneill anxedp_ep_get_data(device_t dev, struct fdt_endpoint *ep)
386f7483fa6Sjmcneill {
387f7483fa6Sjmcneill 	struct anxedp_softc * const sc = device_private(dev);
388f7483fa6Sjmcneill 
389f7483fa6Sjmcneill 	return &sc->sc_bridge;
390f7483fa6Sjmcneill }
391f7483fa6Sjmcneill 
392f7483fa6Sjmcneill static int
anxedp_match(device_t parent,cfdata_t match,void * aux)393f7483fa6Sjmcneill anxedp_match(device_t parent, cfdata_t match, void *aux)
394f7483fa6Sjmcneill {
395f7483fa6Sjmcneill 	struct i2c_attach_args *ia = aux;
396f7483fa6Sjmcneill 	int match_result;
397f7483fa6Sjmcneill 
398f7483fa6Sjmcneill 	if (iic_use_direct_match(ia, match, compat_data, &match_result))
399f7483fa6Sjmcneill 		return match_result;
400f7483fa6Sjmcneill 
401f7483fa6Sjmcneill 	/* This device is direct-config only */
402f7483fa6Sjmcneill 
403f7483fa6Sjmcneill 	return 0;
404f7483fa6Sjmcneill }
405f7483fa6Sjmcneill 
406f7483fa6Sjmcneill static void
anxedp_attach(device_t parent,device_t self,void * aux)407f7483fa6Sjmcneill anxedp_attach(device_t parent, device_t self, void *aux)
408f7483fa6Sjmcneill {
409f7483fa6Sjmcneill 	struct anxedp_softc * const sc = device_private(self);
410f7483fa6Sjmcneill 	struct i2c_attach_args * const ia = aux;
411f7483fa6Sjmcneill 
412f7483fa6Sjmcneill 	sc->sc_dev = self;
413f7483fa6Sjmcneill 	sc->sc_i2c = ia->ia_tag;
414f7483fa6Sjmcneill 	sc->sc_addr = ia->ia_addr;
415f7483fa6Sjmcneill 	sc->sc_phandle = ia->ia_cookie;
416f7483fa6Sjmcneill 
417f7483fa6Sjmcneill 	aprint_naive("\n");
418f7483fa6Sjmcneill 	aprint_normal(": eDP TX\n");
419f7483fa6Sjmcneill 
420f7483fa6Sjmcneill 	sc->sc_ports.dp_ep_activate = anxedp_ep_activate;
421f7483fa6Sjmcneill 	sc->sc_ports.dp_ep_get_data = anxedp_ep_get_data;
422f7483fa6Sjmcneill 	fdt_ports_register(&sc->sc_ports, self, sc->sc_phandle, EP_DRM_BRIDGE);
423f7483fa6Sjmcneill }
424f7483fa6Sjmcneill 
425f7483fa6Sjmcneill CFATTACH_DECL_NEW(anxedp, sizeof(struct anxedp_softc),
426f7483fa6Sjmcneill     anxedp_match, anxedp_attach, NULL, NULL);
427