xref: /freebsd/sys/arm/freescale/imx/imx6_hdmi.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1a0becfc9SOleksandr Tymoshenko /*-
2a0becfc9SOleksandr Tymoshenko  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
3a0becfc9SOleksandr Tymoshenko  * All rights reserved.
4a0becfc9SOleksandr Tymoshenko  *
5a0becfc9SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
6a0becfc9SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
7a0becfc9SOleksandr Tymoshenko  * are met:
8a0becfc9SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
9a0becfc9SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
10a0becfc9SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
11a0becfc9SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
12a0becfc9SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
13a0becfc9SOleksandr Tymoshenko  *
14a0becfc9SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15a0becfc9SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16a0becfc9SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17a0becfc9SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18a0becfc9SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19a0becfc9SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20a0becfc9SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21a0becfc9SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22a0becfc9SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23a0becfc9SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24a0becfc9SOleksandr Tymoshenko  * SUCH DAMAGE.
25a0becfc9SOleksandr Tymoshenko  */
26a0becfc9SOleksandr Tymoshenko 
27a0becfc9SOleksandr Tymoshenko #include <sys/cdefs.h>
28a0becfc9SOleksandr Tymoshenko /*
29a0becfc9SOleksandr Tymoshenko  * HDMI core module
30a0becfc9SOleksandr Tymoshenko  */
31a0becfc9SOleksandr Tymoshenko 
32a0becfc9SOleksandr Tymoshenko #include <sys/param.h>
33a0becfc9SOleksandr Tymoshenko #include <sys/systm.h>
347e7ef416SIan Lepore #include <sys/eventhandler.h>
35a0becfc9SOleksandr Tymoshenko #include <sys/kernel.h>
36a0becfc9SOleksandr Tymoshenko #include <sys/module.h>
37a0becfc9SOleksandr Tymoshenko #include <sys/bus.h>
38a0becfc9SOleksandr Tymoshenko #include <sys/rman.h>
39a0becfc9SOleksandr Tymoshenko 
40a0becfc9SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
41a0becfc9SOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
42a0becfc9SOleksandr Tymoshenko 
43a0becfc9SOleksandr Tymoshenko #include <machine/bus.h>
44a0becfc9SOleksandr Tymoshenko 
45a0becfc9SOleksandr Tymoshenko #include <dev/videomode/videomode.h>
466443acaaSJared McNeill #include <dev/videomode/edidvar.h>
47a0becfc9SOleksandr Tymoshenko 
48a0becfc9SOleksandr Tymoshenko #include <arm/freescale/imx/imx_ccmvar.h>
49a0becfc9SOleksandr Tymoshenko #include <arm/freescale/imx/imx_iomuxvar.h>
50a0becfc9SOleksandr Tymoshenko #include <arm/freescale/imx/imx_iomuxreg.h>
5106785ff6SJared McNeill 
5206785ff6SJared McNeill #include <dev/hdmi/dwc_hdmi.h>
53a0becfc9SOleksandr Tymoshenko 
54*00e84f52SEmmanuel Vadot #include "crtc_if.h"
55a0becfc9SOleksandr Tymoshenko 
56a0becfc9SOleksandr Tymoshenko struct imx_hdmi_softc {
5706785ff6SJared McNeill 	struct dwc_hdmi_softc	base;
5806785ff6SJared McNeill 	phandle_t		i2c_xref;
597e7ef416SIan Lepore 	eventhandler_tag	eh_tag;
60a0becfc9SOleksandr Tymoshenko };
61a0becfc9SOleksandr Tymoshenko 
62a0becfc9SOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = {
63a0becfc9SOleksandr Tymoshenko 	{"fsl,imx6dl-hdmi", 1},
64a0becfc9SOleksandr Tymoshenko 	{"fsl,imx6q-hdmi",  1},
65a0becfc9SOleksandr Tymoshenko 	{NULL,	            0}
66a0becfc9SOleksandr Tymoshenko };
67a0becfc9SOleksandr Tymoshenko 
6806785ff6SJared McNeill static device_t
imx_hdmi_get_i2c_dev(device_t dev)6906785ff6SJared McNeill imx_hdmi_get_i2c_dev(device_t dev)
70a0becfc9SOleksandr Tymoshenko {
71a0becfc9SOleksandr Tymoshenko 	struct imx_hdmi_softc *sc;
72a0becfc9SOleksandr Tymoshenko 
7306785ff6SJared McNeill 	sc = device_get_softc(dev);
7406785ff6SJared McNeill 
7506785ff6SJared McNeill 	if (sc->i2c_xref == 0)
7606785ff6SJared McNeill 		return (NULL);
7706785ff6SJared McNeill 
7806785ff6SJared McNeill 	return (OF_device_from_xref(sc->i2c_xref));
79a0becfc9SOleksandr Tymoshenko }
80a0becfc9SOleksandr Tymoshenko 
817e7ef416SIan Lepore /*
827e7ef416SIan Lepore  * Deferred HDMI init.  dwc_hdmi_init() does i2c transfers for DDC/EDID. The imx
837e7ef416SIan Lepore  * i2c devices also use a config_intrhook function to finish their init, because
847e7ef416SIan Lepore  * they require interrupts to perform transfers.  There is no way to control
857e7ef416SIan Lepore  * whether the i2c or our hdmi intrhook function runs first.  If we go first we
867e7ef416SIan Lepore  * have to continue waiting until after the i2c driver is ready to do transfers
877e7ef416SIan Lepore  * and has registered its phandle.
887e7ef416SIan Lepore  *
897e7ef416SIan Lepore  * This function is used as both a config_intrhook function and after that as an
907e7ef416SIan Lepore  * eventhandler callback function (if necessary), to see if our i2c device is
917e7ef416SIan Lepore  * ready yet.  When it is, continue with hdmi init.  When first called as an
927e7ef416SIan Lepore  * intrhook function the i2c devices might be ready, in which case we never
937e7ef416SIan Lepore  * register as an eventhandler at all.  Otherwise we register to see newbus
947e7ef416SIan Lepore  * attach events, and as each device attaches we check to see whether it was the
957e7ef416SIan Lepore  * i2c device we care about.  Once we have our i2c device we unregister from
967e7ef416SIan Lepore  * seeing further attach events.
977e7ef416SIan Lepore  */
987e7ef416SIan Lepore static void
imx_hdmi_init(void * dev)997e7ef416SIan Lepore imx_hdmi_init(void *dev)
1007e7ef416SIan Lepore {
1017e7ef416SIan Lepore 	struct imx_hdmi_softc *sc;
1027e7ef416SIan Lepore 
1037e7ef416SIan Lepore 	sc = device_get_softc((device_t)dev);
1047e7ef416SIan Lepore 
1057e7ef416SIan Lepore 	if (OF_device_from_xref(sc->i2c_xref) != NULL) {
1067e7ef416SIan Lepore 		if (sc->eh_tag != NULL) {
1077e7ef416SIan Lepore 			EVENTHANDLER_DEREGISTER_NOWAIT(device_attach,
1087e7ef416SIan Lepore 			    sc->eh_tag);
1097e7ef416SIan Lepore 		}
1107e7ef416SIan Lepore 		dwc_hdmi_init(dev);
1117e7ef416SIan Lepore 		return;
1127e7ef416SIan Lepore 	}
1137e7ef416SIan Lepore 
1147e7ef416SIan Lepore 	if (bootverbose)
1157e7ef416SIan Lepore 		device_printf((device_t)dev, "Waiting for DDC i2c device\n");
1167e7ef416SIan Lepore 
1177e7ef416SIan Lepore 	if (sc->eh_tag == NULL) {
1187e7ef416SIan Lepore 		sc->eh_tag = EVENTHANDLER_REGISTER(device_attach,
1197e7ef416SIan Lepore 		    imx_hdmi_init, dev, EVENTHANDLER_PRI_ANY);
1207e7ef416SIan Lepore 	}
1217e7ef416SIan Lepore }
1227e7ef416SIan Lepore 
123a0becfc9SOleksandr Tymoshenko static int
imx_hdmi_detach(device_t dev)124a0becfc9SOleksandr Tymoshenko imx_hdmi_detach(device_t dev)
125a0becfc9SOleksandr Tymoshenko {
126a0becfc9SOleksandr Tymoshenko 	struct imx_hdmi_softc *sc;
127a0becfc9SOleksandr Tymoshenko 
128a0becfc9SOleksandr Tymoshenko 	sc = device_get_softc(dev);
129a0becfc9SOleksandr Tymoshenko 
13006785ff6SJared McNeill 	if (sc->base.sc_mem_res != NULL)
131a0becfc9SOleksandr Tymoshenko 		bus_release_resource(dev, SYS_RES_MEMORY,
13206785ff6SJared McNeill 		    sc->base.sc_mem_rid, sc->base.sc_mem_res);
133a0becfc9SOleksandr Tymoshenko 
134a0becfc9SOleksandr Tymoshenko 	return (0);
135a0becfc9SOleksandr Tymoshenko }
136a0becfc9SOleksandr Tymoshenko 
137a0becfc9SOleksandr Tymoshenko static int
imx_hdmi_attach(device_t dev)138a0becfc9SOleksandr Tymoshenko imx_hdmi_attach(device_t dev)
139a0becfc9SOleksandr Tymoshenko {
140a0becfc9SOleksandr Tymoshenko 	struct imx_hdmi_softc *sc;
141a0becfc9SOleksandr Tymoshenko 	int err;
142a0becfc9SOleksandr Tymoshenko 	uint32_t gpr3;
143a0becfc9SOleksandr Tymoshenko 	phandle_t node, i2c_xref;
144a0becfc9SOleksandr Tymoshenko 
145a0becfc9SOleksandr Tymoshenko 	sc = device_get_softc(dev);
14606785ff6SJared McNeill 	sc->base.sc_dev = dev;
14706785ff6SJared McNeill 	sc->base.sc_get_i2c_dev = imx_hdmi_get_i2c_dev;
148a0becfc9SOleksandr Tymoshenko 	err = 0;
149a0becfc9SOleksandr Tymoshenko 
150a0becfc9SOleksandr Tymoshenko 	/* Allocate memory resources. */
15106785ff6SJared McNeill 	sc->base.sc_mem_rid = 0;
15206785ff6SJared McNeill 	sc->base.sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
15306785ff6SJared McNeill 	    &sc->base.sc_mem_rid, RF_ACTIVE);
15406785ff6SJared McNeill 	if (sc->base.sc_mem_res == NULL) {
155a0becfc9SOleksandr Tymoshenko 		device_printf(dev, "Cannot allocate memory resources\n");
156a0becfc9SOleksandr Tymoshenko 		err = ENXIO;
157a0becfc9SOleksandr Tymoshenko 		goto out;
158a0becfc9SOleksandr Tymoshenko 	}
159a0becfc9SOleksandr Tymoshenko 
160a0becfc9SOleksandr Tymoshenko 	node = ofw_bus_get_node(dev);
161a0becfc9SOleksandr Tymoshenko 	if (OF_getencprop(node, "ddc-i2c-bus", &i2c_xref, sizeof(i2c_xref)) == -1)
16206785ff6SJared McNeill 		sc->i2c_xref = 0;
163a0becfc9SOleksandr Tymoshenko 	else
16406785ff6SJared McNeill 		sc->i2c_xref = i2c_xref;
165a0becfc9SOleksandr Tymoshenko 
166a0becfc9SOleksandr Tymoshenko 	imx_ccm_hdmi_enable();
167a0becfc9SOleksandr Tymoshenko 
168a0becfc9SOleksandr Tymoshenko 	gpr3 = imx_iomux_gpr_get(IOMUXC_GPR3);
169a0becfc9SOleksandr Tymoshenko 	gpr3 &= ~(IOMUXC_GPR3_HDMI_MASK);
170a0becfc9SOleksandr Tymoshenko 	gpr3 |= IOMUXC_GPR3_HDMI_IPU1_DI0;
171a0becfc9SOleksandr Tymoshenko 	imx_iomux_gpr_set(IOMUXC_GPR3, gpr3);
172a0becfc9SOleksandr Tymoshenko 
1737e7ef416SIan Lepore 	/* Further HDMI init requires interrupts for i2c transfers. */
1747e7ef416SIan Lepore 	config_intrhook_oneshot(imx_hdmi_init, dev);
1757e7ef416SIan Lepore 	return (0);
176a0becfc9SOleksandr Tymoshenko 
177a0becfc9SOleksandr Tymoshenko out:
178a0becfc9SOleksandr Tymoshenko 	imx_hdmi_detach(dev);
179a0becfc9SOleksandr Tymoshenko 
180a0becfc9SOleksandr Tymoshenko 	return (err);
181a0becfc9SOleksandr Tymoshenko }
182a0becfc9SOleksandr Tymoshenko 
183a0becfc9SOleksandr Tymoshenko static int
imx_hdmi_probe(device_t dev)184a0becfc9SOleksandr Tymoshenko imx_hdmi_probe(device_t dev)
185a0becfc9SOleksandr Tymoshenko {
186a0becfc9SOleksandr Tymoshenko 
187a0becfc9SOleksandr Tymoshenko 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
188a0becfc9SOleksandr Tymoshenko 		return (ENXIO);
189a0becfc9SOleksandr Tymoshenko 
190a0becfc9SOleksandr Tymoshenko 	device_set_desc(dev, "Freescale i.MX6 HDMI core");
191a0becfc9SOleksandr Tymoshenko 
192a0becfc9SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
193a0becfc9SOleksandr Tymoshenko }
194a0becfc9SOleksandr Tymoshenko 
195a0becfc9SOleksandr Tymoshenko static device_method_t imx_hdmi_methods[] = {
196a0becfc9SOleksandr Tymoshenko 	/* Device interface */
197a0becfc9SOleksandr Tymoshenko 	DEVMETHOD(device_probe,  imx_hdmi_probe),
198a0becfc9SOleksandr Tymoshenko 	DEVMETHOD(device_attach, imx_hdmi_attach),
199a0becfc9SOleksandr Tymoshenko 	DEVMETHOD(device_detach, imx_hdmi_detach),
200a0becfc9SOleksandr Tymoshenko 
201*00e84f52SEmmanuel Vadot 	/* CRTC methods */
202*00e84f52SEmmanuel Vadot 	DEVMETHOD(crtc_get_edid,	dwc_hdmi_get_edid),
203*00e84f52SEmmanuel Vadot 	DEVMETHOD(crtc_set_videomode,	dwc_hdmi_set_videomode),
204a0becfc9SOleksandr Tymoshenko 
205a0becfc9SOleksandr Tymoshenko 	DEVMETHOD_END
206a0becfc9SOleksandr Tymoshenko };
207a0becfc9SOleksandr Tymoshenko 
208a0becfc9SOleksandr Tymoshenko static driver_t imx_hdmi_driver = {
209a0becfc9SOleksandr Tymoshenko 	"hdmi",
210a0becfc9SOleksandr Tymoshenko 	imx_hdmi_methods,
211a0becfc9SOleksandr Tymoshenko 	sizeof(struct imx_hdmi_softc)
212a0becfc9SOleksandr Tymoshenko };
213a0becfc9SOleksandr Tymoshenko 
214ea538dabSJohn Baldwin DRIVER_MODULE(hdmi, simplebus, imx_hdmi_driver, 0, 0);
215