1 /* $OpenBSD: rkdwhdmi.c,v 1.8 2024/08/21 11:24:12 jsg Exp $ */ 2 /* $NetBSD: rk_dwhdmi.c,v 1.4 2019/12/17 18:26:36 jakllsch Exp $ */ 3 4 /*- 5 * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/device.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 35 #include <machine/bus.h> 36 #include <machine/fdt.h> 37 38 #include <dev/ofw/openfirm.h> 39 #include <dev/ofw/ofw_clock.h> 40 #include <dev/ofw/ofw_misc.h> 41 #include <dev/ofw/ofw_pinctrl.h> 42 #include <dev/ofw/fdt.h> 43 44 #include <drm/drm_crtc_helper.h> 45 #include <drm/drm_atomic_helper.h> 46 47 #include <dev/ic/dwhdmi.h> 48 49 #define RK3399_GRF_SOC_CON20 0x6250 50 #define HDMI_LCDC_SEL (1 << 6) 51 52 const struct dwhdmi_mpll_config rkdwhdmi_mpll_config[] = { 53 { 40000, 0x00b3, 0x0000, 0x0018 }, 54 { 65000, 0x0072, 0x0001, 0x0028 }, 55 { 66000, 0x013e, 0x0003, 0x0038 }, 56 { 83500, 0x0072, 0x0001, 0x0028 }, 57 { 146250, 0x0051, 0x0002, 0x0038 }, 58 { 148500, 0x0051, 0x0003, 0x0000 }, 59 { 272000, 0x0040, 0x0003, 0x0000 }, 60 { 340000, 0x0040, 0x0003, 0x0000 }, 61 { 0, 0x0051, 0x0003, 0x0000 }, 62 }; 63 64 const struct dwhdmi_phy_config rkdwhdmi_phy_config[] = { 65 { 74250, 0x8009, 0x0004, 0x0272 }, 66 { 148500, 0x802b, 0x0004, 0x028d }, 67 { 297000, 0x8039, 0x0005, 0x028d }, 68 { 594000, 0x8039, 0x0000, 0x019d }, 69 { 0, 0x0000, 0x0000, 0x0000 } 70 }; 71 72 struct rkdwhdmi_softc { 73 struct dwhdmi_softc sc_base; 74 int sc_node; 75 int sc_clk_vpll; 76 77 struct drm_display_mode sc_curmode; 78 struct drm_encoder sc_encoder; 79 struct regmap *sc_grf; 80 81 int sc_activated; 82 83 struct device_ports sc_ports; 84 }; 85 86 #define to_rkdwhdmi_softc(x) container_of(x, struct rkdwhdmi_softc, sc_base) 87 #define to_rkdwhdmi_encoder(x) container_of(x, struct rkdwhdmi_softc, sc_encoder) 88 89 int rkdwhdmi_match(struct device *, void *, void *); 90 void rkdwhdmi_attach(struct device *, struct device *, void *); 91 92 void rkdwhdmi_select_input(struct rkdwhdmi_softc *, u_int); 93 void rkdwhdmi_encoder_enable(struct drm_encoder *); 94 95 int rkdwhdmi_ep_activate(void *, struct endpoint *, void *); 96 void *rkdwhdmi_ep_get_cookie(void *, struct endpoint *); 97 98 void rkdwhdmi_enable(struct dwhdmi_softc *); 99 void rkdwhdmi_mode_set(struct dwhdmi_softc *, const struct drm_display_mode *, 100 const struct drm_display_mode *); 101 enum drm_mode_status rkdwhdmi_mode_valid(struct dwhdmi_softc *, 102 const struct drm_display_mode *); 103 104 const struct cfattach rkdwhdmi_ca = { 105 sizeof (struct rkdwhdmi_softc), rkdwhdmi_match, rkdwhdmi_attach 106 }; 107 108 struct cfdriver rkdwhdmi_cd = { 109 NULL, "rkdwhdmi", DV_DULL 110 }; 111 112 int 113 rkdwhdmi_match(struct device *parent, void *match, void *aux) 114 { 115 struct fdt_attach_args *faa = aux; 116 117 return OF_is_compatible(faa->fa_node, "rockchip,rk3399-dw-hdmi"); 118 } 119 120 void 121 rkdwhdmi_attach(struct device *parent, struct device *self, void *aux) 122 { 123 struct rkdwhdmi_softc *sc = (struct rkdwhdmi_softc *)self; 124 struct fdt_attach_args *faa = aux; 125 uint32_t grf; 126 uint32_t phandle; 127 128 if (faa->fa_nreg < 1) { 129 printf(": no registers\n"); 130 return; 131 } 132 133 pinctrl_byname(sc->sc_node, "default"); 134 135 clock_enable(faa->fa_node, "iahb"); 136 clock_enable(faa->fa_node, "isfr"); 137 clock_enable(faa->fa_node, "vpll"); 138 clock_enable(faa->fa_node, "grf"); 139 clock_enable(faa->fa_node, "cec"); 140 141 sc->sc_base.sc_reg_width = 142 OF_getpropint(faa->fa_node, "reg-io-width", 4); 143 144 sc->sc_base.sc_bst = faa->fa_iot; 145 if (bus_space_map(sc->sc_base.sc_bst, faa->fa_reg[0].addr, 146 faa->fa_reg[0].size, 0, &sc->sc_base.sc_bsh)) { 147 printf(": can't map registers\n"); 148 return; 149 } 150 151 sc->sc_node = faa->fa_node; 152 sc->sc_clk_vpll = OF_getindex(faa->fa_node, "vpll", "clock-names"); 153 154 grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0); 155 sc->sc_grf = regmap_byphandle(grf); 156 if (sc->sc_grf == NULL) { 157 printf(": can't get grf\n"); 158 return; 159 } 160 161 printf(": HDMI TX\n"); 162 163 phandle = OF_getpropint(faa->fa_node, "ddc-i2c-bus", 0); 164 sc->sc_base.sc_ic = i2c_byphandle(phandle); 165 if (phandle && sc->sc_base.sc_ic == NULL) { 166 printf("%s: couldn't find external I2C master\n", 167 self->dv_xname); 168 return; 169 } 170 171 sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY; 172 sc->sc_base.sc_detect = dwhdmi_phy_detect; 173 sc->sc_base.sc_enable = rkdwhdmi_enable; 174 sc->sc_base.sc_disable = dwhdmi_phy_disable; 175 sc->sc_base.sc_mode_set = rkdwhdmi_mode_set; 176 sc->sc_base.sc_mode_valid = rkdwhdmi_mode_valid; 177 sc->sc_base.sc_mpll_config = rkdwhdmi_mpll_config; 178 sc->sc_base.sc_phy_config = rkdwhdmi_phy_config; 179 180 if (dwhdmi_attach(&sc->sc_base) != 0) { 181 printf("%s: failed to attach driver\n", self->dv_xname); 182 return; 183 } 184 185 sc->sc_ports.dp_node = faa->fa_node; 186 sc->sc_ports.dp_cookie = sc; 187 sc->sc_ports.dp_ep_activate = rkdwhdmi_ep_activate; 188 sc->sc_ports.dp_ep_get_cookie = rkdwhdmi_ep_get_cookie; 189 device_ports_register(&sc->sc_ports, EP_DRM_ENCODER); 190 191 #ifdef notyet 192 fdtbus_register_dai_controller(self, phandle, &rkdwhdmi_dai_funcs); 193 #endif 194 } 195 196 void 197 rkdwhdmi_select_input(struct rkdwhdmi_softc *sc, u_int crtc_index) 198 { 199 const uint32_t write_mask = HDMI_LCDC_SEL << 16; 200 const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0; 201 202 regmap_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val); 203 } 204 205 void 206 rkdwhdmi_encoder_enable(struct drm_encoder *encoder) 207 { 208 struct rkdwhdmi_softc * const sc = to_rkdwhdmi_encoder(encoder); 209 const u_int crtc_index = drm_crtc_index(encoder->crtc); 210 211 rkdwhdmi_select_input(sc, crtc_index); 212 } 213 214 struct drm_encoder_funcs rkdwhdmi_encoder_funcs = { 215 .destroy = drm_encoder_cleanup, 216 }; 217 218 struct drm_encoder_helper_funcs rkdwhdmi_encoder_helper_funcs = { 219 .enable = rkdwhdmi_encoder_enable, 220 }; 221 222 int 223 rkdwhdmi_ep_activate(void *cookie, struct endpoint *ep, void *arg) 224 { 225 struct rkdwhdmi_softc *sc = cookie; 226 struct drm_crtc *crtc = NULL; 227 struct endpoint *rep; 228 int error; 229 230 if (sc->sc_activated) 231 return 0; 232 233 rep = endpoint_remote(ep); 234 if (rep && rep->ep_type == EP_DRM_CRTC) 235 crtc = endpoint_get_cookie(rep); 236 if (crtc == NULL) 237 return EINVAL; 238 239 sc->sc_encoder.possible_crtcs = 0x3; /* XXX */ 240 drm_encoder_init(crtc->dev, &sc->sc_encoder, &rkdwhdmi_encoder_funcs, 241 DRM_MODE_ENCODER_TMDS, NULL); 242 drm_encoder_helper_add(&sc->sc_encoder, &rkdwhdmi_encoder_helper_funcs); 243 244 sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_HDMIA; 245 error = dwhdmi_bind(&sc->sc_base, &sc->sc_encoder); 246 if (error != 0) 247 return error; 248 249 sc->sc_activated = 1; 250 return 0; 251 } 252 253 void * 254 rkdwhdmi_ep_get_cookie(void *cookie, struct endpoint *ep) 255 { 256 struct rkdwhdmi_softc *sc = cookie; 257 return &sc->sc_encoder; 258 } 259 260 void 261 rkdwhdmi_enable(struct dwhdmi_softc *dsc) 262 { 263 dwhdmi_phy_enable(dsc); 264 } 265 266 void 267 rkdwhdmi_mode_set(struct dwhdmi_softc *dsc, 268 const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) 269 { 270 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 271 int error; 272 273 if (sc->sc_clk_vpll != -1) { 274 error = clock_set_frequency(sc->sc_node, "vpll", 275 adjusted_mode->clock * 1000); 276 if (error != 0) 277 printf("%s: couldn't set pixel clock to %u Hz: %d\n", 278 dsc->sc_dev.dv_xname, adjusted_mode->clock * 1000, 279 error); 280 } 281 282 dwhdmi_phy_mode_set(dsc, mode, adjusted_mode); 283 } 284 285 enum drm_mode_status 286 rkdwhdmi_mode_valid(struct dwhdmi_softc *dsc, const struct drm_display_mode *mode) 287 { 288 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 289 int i; 290 291 for (i = 0; sc->sc_base.sc_mpll_config[i].pixel_clock != 0; i++) 292 if (mode->clock == sc->sc_base.sc_mpll_config[i].pixel_clock) 293 return MODE_OK; 294 295 return MODE_BAD; 296 } 297 298 #ifdef notyet 299 300 static audio_dai_tag_t 301 rkdwhdmi_dai_get_tag(device_t dev, const void *data, size_t len) 302 { 303 struct rkdwhdmi_softc * const sc = device_private(dev); 304 305 if (len != 4) 306 return NULL; 307 308 return &sc->sc_base.sc_dai; 309 } 310 311 static struct fdtbus_dai_controller_func rkdwhdmi_dai_funcs = { 312 .get_tag = rkdwhdmi_dai_get_tag 313 }; 314 315 #endif 316