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