1*d66736b0Sjsg /* $OpenBSD: rkdwhdmi.c,v 1.8 2024/08/21 11:24:12 jsg Exp $ */ 26a2cdf39Skettenis /* $NetBSD: rk_dwhdmi.c,v 1.4 2019/12/17 18:26:36 jakllsch Exp $ */ 36a2cdf39Skettenis 46a2cdf39Skettenis /*- 56a2cdf39Skettenis * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 66a2cdf39Skettenis * All rights reserved. 76a2cdf39Skettenis * 86a2cdf39Skettenis * Redistribution and use in source and binary forms, with or without 96a2cdf39Skettenis * modification, are permitted provided that the following conditions 106a2cdf39Skettenis * are met: 116a2cdf39Skettenis * 1. Redistributions of source code must retain the above copyright 126a2cdf39Skettenis * notice, this list of conditions and the following disclaimer. 136a2cdf39Skettenis * 2. Redistributions in binary form must reproduce the above copyright 146a2cdf39Skettenis * notice, this list of conditions and the following disclaimer in the 156a2cdf39Skettenis * documentation and/or other materials provided with the distribution. 166a2cdf39Skettenis * 176a2cdf39Skettenis * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 186a2cdf39Skettenis * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 196a2cdf39Skettenis * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 206a2cdf39Skettenis * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 216a2cdf39Skettenis * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 226a2cdf39Skettenis * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 236a2cdf39Skettenis * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 246a2cdf39Skettenis * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 256a2cdf39Skettenis * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 266a2cdf39Skettenis * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 276a2cdf39Skettenis * SUCH DAMAGE. 286a2cdf39Skettenis */ 296a2cdf39Skettenis 306a2cdf39Skettenis #include <sys/param.h> 316a2cdf39Skettenis #include <sys/device.h> 326a2cdf39Skettenis #include <sys/systm.h> 336a2cdf39Skettenis #include <sys/kernel.h> 346a2cdf39Skettenis 356a2cdf39Skettenis #include <machine/bus.h> 366a2cdf39Skettenis #include <machine/fdt.h> 376a2cdf39Skettenis 386a2cdf39Skettenis #include <dev/ofw/openfirm.h> 396a2cdf39Skettenis #include <dev/ofw/ofw_clock.h> 406a2cdf39Skettenis #include <dev/ofw/ofw_misc.h> 416a2cdf39Skettenis #include <dev/ofw/ofw_pinctrl.h> 426a2cdf39Skettenis #include <dev/ofw/fdt.h> 436a2cdf39Skettenis 446a2cdf39Skettenis #include <drm/drm_crtc_helper.h> 45f005ef32Sjsg #include <drm/drm_atomic_helper.h> 466a2cdf39Skettenis 476a2cdf39Skettenis #include <dev/ic/dwhdmi.h> 486a2cdf39Skettenis 496a2cdf39Skettenis #define RK3399_GRF_SOC_CON20 0x6250 506a2cdf39Skettenis #define HDMI_LCDC_SEL (1 << 6) 516a2cdf39Skettenis 526a2cdf39Skettenis const struct dwhdmi_mpll_config rkdwhdmi_mpll_config[] = { 536a2cdf39Skettenis { 40000, 0x00b3, 0x0000, 0x0018 }, 546a2cdf39Skettenis { 65000, 0x0072, 0x0001, 0x0028 }, 556a2cdf39Skettenis { 66000, 0x013e, 0x0003, 0x0038 }, 566a2cdf39Skettenis { 83500, 0x0072, 0x0001, 0x0028 }, 576a2cdf39Skettenis { 146250, 0x0051, 0x0002, 0x0038 }, 586a2cdf39Skettenis { 148500, 0x0051, 0x0003, 0x0000 }, 596a2cdf39Skettenis { 272000, 0x0040, 0x0003, 0x0000 }, 606a2cdf39Skettenis { 340000, 0x0040, 0x0003, 0x0000 }, 616a2cdf39Skettenis { 0, 0x0051, 0x0003, 0x0000 }, 626a2cdf39Skettenis }; 636a2cdf39Skettenis 646a2cdf39Skettenis const struct dwhdmi_phy_config rkdwhdmi_phy_config[] = { 656a2cdf39Skettenis { 74250, 0x8009, 0x0004, 0x0272 }, 666a2cdf39Skettenis { 148500, 0x802b, 0x0004, 0x028d }, 676a2cdf39Skettenis { 297000, 0x8039, 0x0005, 0x028d }, 686a2cdf39Skettenis { 594000, 0x8039, 0x0000, 0x019d }, 696a2cdf39Skettenis { 0, 0x0000, 0x0000, 0x0000 } 706a2cdf39Skettenis }; 716a2cdf39Skettenis 726a2cdf39Skettenis struct rkdwhdmi_softc { 736a2cdf39Skettenis struct dwhdmi_softc sc_base; 746a2cdf39Skettenis int sc_node; 756a2cdf39Skettenis int sc_clk_vpll; 766a2cdf39Skettenis 776a2cdf39Skettenis struct drm_display_mode sc_curmode; 786a2cdf39Skettenis struct drm_encoder sc_encoder; 796a2cdf39Skettenis struct regmap *sc_grf; 806a2cdf39Skettenis 816a2cdf39Skettenis int sc_activated; 826a2cdf39Skettenis 83c2490375Skettenis struct device_ports sc_ports; 846a2cdf39Skettenis }; 856a2cdf39Skettenis 866a2cdf39Skettenis #define to_rkdwhdmi_softc(x) container_of(x, struct rkdwhdmi_softc, sc_base) 876a2cdf39Skettenis #define to_rkdwhdmi_encoder(x) container_of(x, struct rkdwhdmi_softc, sc_encoder) 886a2cdf39Skettenis 896a2cdf39Skettenis int rkdwhdmi_match(struct device *, void *, void *); 906a2cdf39Skettenis void rkdwhdmi_attach(struct device *, struct device *, void *); 916a2cdf39Skettenis 926a2cdf39Skettenis void rkdwhdmi_select_input(struct rkdwhdmi_softc *, u_int); 936a2cdf39Skettenis void rkdwhdmi_encoder_enable(struct drm_encoder *); 946a2cdf39Skettenis 95c2490375Skettenis int rkdwhdmi_ep_activate(void *, struct endpoint *, void *); 96c2490375Skettenis void *rkdwhdmi_ep_get_cookie(void *, struct endpoint *); 976a2cdf39Skettenis 986a2cdf39Skettenis void rkdwhdmi_enable(struct dwhdmi_softc *); 997137d9bbSderaadt void rkdwhdmi_mode_set(struct dwhdmi_softc *, const struct drm_display_mode *, 1007137d9bbSderaadt const struct drm_display_mode *); 10113a9a14fSkettenis enum drm_mode_status rkdwhdmi_mode_valid(struct dwhdmi_softc *, 1027137d9bbSderaadt const struct drm_display_mode *); 1036a2cdf39Skettenis 104471aeecfSnaddy const struct cfattach rkdwhdmi_ca = { 1056a2cdf39Skettenis sizeof (struct rkdwhdmi_softc), rkdwhdmi_match, rkdwhdmi_attach 1066a2cdf39Skettenis }; 1076a2cdf39Skettenis 1086a2cdf39Skettenis struct cfdriver rkdwhdmi_cd = { 1096a2cdf39Skettenis NULL, "rkdwhdmi", DV_DULL 1106a2cdf39Skettenis }; 1116a2cdf39Skettenis 1126a2cdf39Skettenis int 1136a2cdf39Skettenis rkdwhdmi_match(struct device *parent, void *match, void *aux) 1146a2cdf39Skettenis { 1156a2cdf39Skettenis struct fdt_attach_args *faa = aux; 1166a2cdf39Skettenis 1176a2cdf39Skettenis return OF_is_compatible(faa->fa_node, "rockchip,rk3399-dw-hdmi"); 1186a2cdf39Skettenis } 1196a2cdf39Skettenis 1206a2cdf39Skettenis void 1216a2cdf39Skettenis rkdwhdmi_attach(struct device *parent, struct device *self, void *aux) 1226a2cdf39Skettenis { 1236a2cdf39Skettenis struct rkdwhdmi_softc *sc = (struct rkdwhdmi_softc *)self; 1246a2cdf39Skettenis struct fdt_attach_args *faa = aux; 125c2490375Skettenis uint32_t grf; 1266a2cdf39Skettenis uint32_t phandle; 1276a2cdf39Skettenis 1286a2cdf39Skettenis if (faa->fa_nreg < 1) { 1296a2cdf39Skettenis printf(": no registers\n"); 1306a2cdf39Skettenis return; 1316a2cdf39Skettenis } 1326a2cdf39Skettenis 1336a2cdf39Skettenis pinctrl_byname(sc->sc_node, "default"); 1346a2cdf39Skettenis 1356a2cdf39Skettenis clock_enable(faa->fa_node, "iahb"); 1366a2cdf39Skettenis clock_enable(faa->fa_node, "isfr"); 1376a2cdf39Skettenis clock_enable(faa->fa_node, "vpll"); 1386a2cdf39Skettenis clock_enable(faa->fa_node, "grf"); 1396a2cdf39Skettenis clock_enable(faa->fa_node, "cec"); 1406a2cdf39Skettenis 1416a2cdf39Skettenis sc->sc_base.sc_reg_width = 1426a2cdf39Skettenis OF_getpropint(faa->fa_node, "reg-io-width", 4); 1436a2cdf39Skettenis 1446a2cdf39Skettenis sc->sc_base.sc_bst = faa->fa_iot; 1456a2cdf39Skettenis if (bus_space_map(sc->sc_base.sc_bst, faa->fa_reg[0].addr, 1466a2cdf39Skettenis faa->fa_reg[0].size, 0, &sc->sc_base.sc_bsh)) { 1476a2cdf39Skettenis printf(": can't map registers\n"); 1486a2cdf39Skettenis return; 1496a2cdf39Skettenis } 1506a2cdf39Skettenis 1516a2cdf39Skettenis sc->sc_node = faa->fa_node; 1526a2cdf39Skettenis sc->sc_clk_vpll = OF_getindex(faa->fa_node, "vpll", "clock-names"); 1536a2cdf39Skettenis 1546a2cdf39Skettenis grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0); 1556a2cdf39Skettenis sc->sc_grf = regmap_byphandle(grf); 1566a2cdf39Skettenis if (sc->sc_grf == NULL) { 1576a2cdf39Skettenis printf(": can't get grf\n"); 1586a2cdf39Skettenis return; 1596a2cdf39Skettenis } 1606a2cdf39Skettenis 1616a2cdf39Skettenis printf(": HDMI TX\n"); 1626a2cdf39Skettenis 1636a2cdf39Skettenis phandle = OF_getpropint(faa->fa_node, "ddc-i2c-bus", 0); 1646a2cdf39Skettenis sc->sc_base.sc_ic = i2c_byphandle(phandle); 1656a2cdf39Skettenis if (phandle && sc->sc_base.sc_ic == NULL) { 1666a2cdf39Skettenis printf("%s: couldn't find external I2C master\n", 1676a2cdf39Skettenis self->dv_xname); 1686a2cdf39Skettenis return; 1696a2cdf39Skettenis } 1706a2cdf39Skettenis 1716a2cdf39Skettenis sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY; 1726a2cdf39Skettenis sc->sc_base.sc_detect = dwhdmi_phy_detect; 1736a2cdf39Skettenis sc->sc_base.sc_enable = rkdwhdmi_enable; 1746a2cdf39Skettenis sc->sc_base.sc_disable = dwhdmi_phy_disable; 1756a2cdf39Skettenis sc->sc_base.sc_mode_set = rkdwhdmi_mode_set; 17613a9a14fSkettenis sc->sc_base.sc_mode_valid = rkdwhdmi_mode_valid; 1776a2cdf39Skettenis sc->sc_base.sc_mpll_config = rkdwhdmi_mpll_config; 1786a2cdf39Skettenis sc->sc_base.sc_phy_config = rkdwhdmi_phy_config; 1796a2cdf39Skettenis 1806a2cdf39Skettenis if (dwhdmi_attach(&sc->sc_base) != 0) { 1816a2cdf39Skettenis printf("%s: failed to attach driver\n", self->dv_xname); 1826a2cdf39Skettenis return; 1836a2cdf39Skettenis } 1846a2cdf39Skettenis 185c2490375Skettenis sc->sc_ports.dp_node = faa->fa_node; 186c2490375Skettenis sc->sc_ports.dp_cookie = sc; 187c2490375Skettenis sc->sc_ports.dp_ep_activate = rkdwhdmi_ep_activate; 188c2490375Skettenis sc->sc_ports.dp_ep_get_cookie = rkdwhdmi_ep_get_cookie; 189c2490375Skettenis device_ports_register(&sc->sc_ports, EP_DRM_ENCODER); 1906a2cdf39Skettenis 1916a2cdf39Skettenis #ifdef notyet 1926a2cdf39Skettenis fdtbus_register_dai_controller(self, phandle, &rkdwhdmi_dai_funcs); 1936a2cdf39Skettenis #endif 1946a2cdf39Skettenis } 1956a2cdf39Skettenis 1966a2cdf39Skettenis void 1976a2cdf39Skettenis rkdwhdmi_select_input(struct rkdwhdmi_softc *sc, u_int crtc_index) 1986a2cdf39Skettenis { 1996a2cdf39Skettenis const uint32_t write_mask = HDMI_LCDC_SEL << 16; 2006a2cdf39Skettenis const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0; 2016a2cdf39Skettenis 2026a2cdf39Skettenis regmap_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val); 2036a2cdf39Skettenis } 2046a2cdf39Skettenis 2056a2cdf39Skettenis void 2066a2cdf39Skettenis rkdwhdmi_encoder_enable(struct drm_encoder *encoder) 2076a2cdf39Skettenis { 2086a2cdf39Skettenis struct rkdwhdmi_softc * const sc = to_rkdwhdmi_encoder(encoder); 2096a2cdf39Skettenis const u_int crtc_index = drm_crtc_index(encoder->crtc); 2106a2cdf39Skettenis 2116a2cdf39Skettenis rkdwhdmi_select_input(sc, crtc_index); 2126a2cdf39Skettenis } 2136a2cdf39Skettenis 2146a2cdf39Skettenis struct drm_encoder_funcs rkdwhdmi_encoder_funcs = { 2156a2cdf39Skettenis .destroy = drm_encoder_cleanup, 2166a2cdf39Skettenis }; 2176a2cdf39Skettenis 2186a2cdf39Skettenis struct drm_encoder_helper_funcs rkdwhdmi_encoder_helper_funcs = { 2196a2cdf39Skettenis .enable = rkdwhdmi_encoder_enable, 2206a2cdf39Skettenis }; 2216a2cdf39Skettenis 2226a2cdf39Skettenis int 223c2490375Skettenis rkdwhdmi_ep_activate(void *cookie, struct endpoint *ep, void *arg) 2246a2cdf39Skettenis { 225c2490375Skettenis struct rkdwhdmi_softc *sc = cookie; 226c2490375Skettenis struct drm_crtc *crtc = NULL; 227c2490375Skettenis struct endpoint *rep; 2286a2cdf39Skettenis int error; 2296a2cdf39Skettenis 2306a2cdf39Skettenis if (sc->sc_activated) 2316a2cdf39Skettenis return 0; 2326a2cdf39Skettenis 233c2490375Skettenis rep = endpoint_remote(ep); 234c2490375Skettenis if (rep && rep->ep_type == EP_DRM_CRTC) 235c2490375Skettenis crtc = endpoint_get_cookie(rep); 236c2490375Skettenis if (crtc == NULL) 2376a2cdf39Skettenis return EINVAL; 2386a2cdf39Skettenis 2396a2cdf39Skettenis sc->sc_encoder.possible_crtcs = 0x3; /* XXX */ 240c2490375Skettenis drm_encoder_init(crtc->dev, &sc->sc_encoder, &rkdwhdmi_encoder_funcs, 2416a2cdf39Skettenis DRM_MODE_ENCODER_TMDS, NULL); 2426a2cdf39Skettenis drm_encoder_helper_add(&sc->sc_encoder, &rkdwhdmi_encoder_helper_funcs); 2436a2cdf39Skettenis 2446a2cdf39Skettenis sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_HDMIA; 2456a2cdf39Skettenis error = dwhdmi_bind(&sc->sc_base, &sc->sc_encoder); 2466a2cdf39Skettenis if (error != 0) 2476a2cdf39Skettenis return error; 2486a2cdf39Skettenis 2496a2cdf39Skettenis sc->sc_activated = 1; 2506a2cdf39Skettenis return 0; 2516a2cdf39Skettenis } 2526a2cdf39Skettenis 2536a2cdf39Skettenis void * 254c2490375Skettenis rkdwhdmi_ep_get_cookie(void *cookie, struct endpoint *ep) 2556a2cdf39Skettenis { 256c2490375Skettenis struct rkdwhdmi_softc *sc = cookie; 2576a2cdf39Skettenis return &sc->sc_encoder; 2586a2cdf39Skettenis } 2596a2cdf39Skettenis 2606a2cdf39Skettenis void 2616a2cdf39Skettenis rkdwhdmi_enable(struct dwhdmi_softc *dsc) 2626a2cdf39Skettenis { 2636a2cdf39Skettenis dwhdmi_phy_enable(dsc); 2646a2cdf39Skettenis } 2656a2cdf39Skettenis 2666a2cdf39Skettenis void 2676a2cdf39Skettenis rkdwhdmi_mode_set(struct dwhdmi_softc *dsc, 2687137d9bbSderaadt const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) 2696a2cdf39Skettenis { 2706a2cdf39Skettenis struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 2716a2cdf39Skettenis int error; 2726a2cdf39Skettenis 2736a2cdf39Skettenis if (sc->sc_clk_vpll != -1) { 2746a2cdf39Skettenis error = clock_set_frequency(sc->sc_node, "vpll", 2756a2cdf39Skettenis adjusted_mode->clock * 1000); 2766a2cdf39Skettenis if (error != 0) 2776a2cdf39Skettenis printf("%s: couldn't set pixel clock to %u Hz: %d\n", 2786a2cdf39Skettenis dsc->sc_dev.dv_xname, adjusted_mode->clock * 1000, 2796a2cdf39Skettenis error); 2806a2cdf39Skettenis } 2816a2cdf39Skettenis 2826a2cdf39Skettenis dwhdmi_phy_mode_set(dsc, mode, adjusted_mode); 2836a2cdf39Skettenis } 2846a2cdf39Skettenis 28513a9a14fSkettenis enum drm_mode_status 2867137d9bbSderaadt rkdwhdmi_mode_valid(struct dwhdmi_softc *dsc, const struct drm_display_mode *mode) 28713a9a14fSkettenis { 28813a9a14fSkettenis struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 28913a9a14fSkettenis int i; 29013a9a14fSkettenis 29113a9a14fSkettenis for (i = 0; sc->sc_base.sc_mpll_config[i].pixel_clock != 0; i++) 29213a9a14fSkettenis if (mode->clock == sc->sc_base.sc_mpll_config[i].pixel_clock) 29313a9a14fSkettenis return MODE_OK; 29413a9a14fSkettenis 29513a9a14fSkettenis return MODE_BAD; 29613a9a14fSkettenis } 29713a9a14fSkettenis 2986a2cdf39Skettenis #ifdef notyet 2996a2cdf39Skettenis 3006a2cdf39Skettenis static audio_dai_tag_t 3016a2cdf39Skettenis rkdwhdmi_dai_get_tag(device_t dev, const void *data, size_t len) 3026a2cdf39Skettenis { 3036a2cdf39Skettenis struct rkdwhdmi_softc * const sc = device_private(dev); 3046a2cdf39Skettenis 3056a2cdf39Skettenis if (len != 4) 3066a2cdf39Skettenis return NULL; 3076a2cdf39Skettenis 3086a2cdf39Skettenis return &sc->sc_base.sc_dai; 3096a2cdf39Skettenis } 3106a2cdf39Skettenis 3116a2cdf39Skettenis static struct fdtbus_dai_controller_func rkdwhdmi_dai_funcs = { 3126a2cdf39Skettenis .get_tag = rkdwhdmi_dai_get_tag 3136a2cdf39Skettenis }; 3146a2cdf39Skettenis 3156a2cdf39Skettenis #endif 316