1 /* $OpenBSD: rkdwhdmi.c,v 1.7 2024/01/16 23:37:50 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 bus_addr_t addr; 127 bus_size_t size; 128 uint32_t phandle; 129 130 if (faa->fa_nreg < 1) { 131 printf(": no registers\n"); 132 return; 133 } 134 135 pinctrl_byname(sc->sc_node, "default"); 136 137 clock_enable(faa->fa_node, "iahb"); 138 clock_enable(faa->fa_node, "isfr"); 139 clock_enable(faa->fa_node, "vpll"); 140 clock_enable(faa->fa_node, "grf"); 141 clock_enable(faa->fa_node, "cec"); 142 143 sc->sc_base.sc_reg_width = 144 OF_getpropint(faa->fa_node, "reg-io-width", 4); 145 146 sc->sc_base.sc_bst = faa->fa_iot; 147 if (bus_space_map(sc->sc_base.sc_bst, faa->fa_reg[0].addr, 148 faa->fa_reg[0].size, 0, &sc->sc_base.sc_bsh)) { 149 printf(": can't map registers\n"); 150 return; 151 } 152 153 sc->sc_node = faa->fa_node; 154 sc->sc_clk_vpll = OF_getindex(faa->fa_node, "vpll", "clock-names"); 155 156 grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0); 157 sc->sc_grf = regmap_byphandle(grf); 158 if (sc->sc_grf == NULL) { 159 printf(": can't get grf\n"); 160 return; 161 } 162 163 printf(": HDMI TX\n"); 164 165 phandle = OF_getpropint(faa->fa_node, "ddc-i2c-bus", 0); 166 sc->sc_base.sc_ic = i2c_byphandle(phandle); 167 if (phandle && sc->sc_base.sc_ic == NULL) { 168 printf("%s: couldn't find external I2C master\n", 169 self->dv_xname); 170 return; 171 } 172 173 sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY; 174 sc->sc_base.sc_detect = dwhdmi_phy_detect; 175 sc->sc_base.sc_enable = rkdwhdmi_enable; 176 sc->sc_base.sc_disable = dwhdmi_phy_disable; 177 sc->sc_base.sc_mode_set = rkdwhdmi_mode_set; 178 sc->sc_base.sc_mode_valid = rkdwhdmi_mode_valid; 179 sc->sc_base.sc_mpll_config = rkdwhdmi_mpll_config; 180 sc->sc_base.sc_phy_config = rkdwhdmi_phy_config; 181 182 if (dwhdmi_attach(&sc->sc_base) != 0) { 183 printf("%s: failed to attach driver\n", self->dv_xname); 184 return; 185 } 186 187 sc->sc_ports.dp_node = faa->fa_node; 188 sc->sc_ports.dp_cookie = sc; 189 sc->sc_ports.dp_ep_activate = rkdwhdmi_ep_activate; 190 sc->sc_ports.dp_ep_get_cookie = rkdwhdmi_ep_get_cookie; 191 device_ports_register(&sc->sc_ports, EP_DRM_ENCODER); 192 193 #ifdef notyet 194 fdtbus_register_dai_controller(self, phandle, &rkdwhdmi_dai_funcs); 195 #endif 196 } 197 198 void 199 rkdwhdmi_select_input(struct rkdwhdmi_softc *sc, u_int crtc_index) 200 { 201 const uint32_t write_mask = HDMI_LCDC_SEL << 16; 202 const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0; 203 204 regmap_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val); 205 } 206 207 void 208 rkdwhdmi_encoder_enable(struct drm_encoder *encoder) 209 { 210 struct rkdwhdmi_softc * const sc = to_rkdwhdmi_encoder(encoder); 211 const u_int crtc_index = drm_crtc_index(encoder->crtc); 212 213 rkdwhdmi_select_input(sc, crtc_index); 214 } 215 216 struct drm_encoder_funcs rkdwhdmi_encoder_funcs = { 217 .destroy = drm_encoder_cleanup, 218 }; 219 220 struct drm_encoder_helper_funcs rkdwhdmi_encoder_helper_funcs = { 221 .enable = rkdwhdmi_encoder_enable, 222 }; 223 224 int 225 rkdwhdmi_ep_activate(void *cookie, struct endpoint *ep, void *arg) 226 { 227 struct rkdwhdmi_softc *sc = cookie; 228 struct drm_crtc *crtc = NULL; 229 struct endpoint *rep; 230 int error; 231 232 if (sc->sc_activated) 233 return 0; 234 235 rep = endpoint_remote(ep); 236 if (rep && rep->ep_type == EP_DRM_CRTC) 237 crtc = endpoint_get_cookie(rep); 238 if (crtc == NULL) 239 return EINVAL; 240 241 sc->sc_encoder.possible_crtcs = 0x3; /* XXX */ 242 drm_encoder_init(crtc->dev, &sc->sc_encoder, &rkdwhdmi_encoder_funcs, 243 DRM_MODE_ENCODER_TMDS, NULL); 244 drm_encoder_helper_add(&sc->sc_encoder, &rkdwhdmi_encoder_helper_funcs); 245 246 sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_HDMIA; 247 error = dwhdmi_bind(&sc->sc_base, &sc->sc_encoder); 248 if (error != 0) 249 return error; 250 251 sc->sc_activated = 1; 252 return 0; 253 } 254 255 void * 256 rkdwhdmi_ep_get_cookie(void *cookie, struct endpoint *ep) 257 { 258 struct rkdwhdmi_softc *sc = cookie; 259 return &sc->sc_encoder; 260 } 261 262 void 263 rkdwhdmi_enable(struct dwhdmi_softc *dsc) 264 { 265 dwhdmi_phy_enable(dsc); 266 } 267 268 void 269 rkdwhdmi_mode_set(struct dwhdmi_softc *dsc, 270 const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) 271 { 272 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 273 int error; 274 275 if (sc->sc_clk_vpll != -1) { 276 error = clock_set_frequency(sc->sc_node, "vpll", 277 adjusted_mode->clock * 1000); 278 if (error != 0) 279 printf("%s: couldn't set pixel clock to %u Hz: %d\n", 280 dsc->sc_dev.dv_xname, adjusted_mode->clock * 1000, 281 error); 282 } 283 284 dwhdmi_phy_mode_set(dsc, mode, adjusted_mode); 285 } 286 287 enum drm_mode_status 288 rkdwhdmi_mode_valid(struct dwhdmi_softc *dsc, const struct drm_display_mode *mode) 289 { 290 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 291 int i; 292 293 for (i = 0; sc->sc_base.sc_mpll_config[i].pixel_clock != 0; i++) 294 if (mode->clock == sc->sc_base.sc_mpll_config[i].pixel_clock) 295 return MODE_OK; 296 297 return MODE_BAD; 298 } 299 300 #ifdef notyet 301 302 static audio_dai_tag_t 303 rkdwhdmi_dai_get_tag(device_t dev, const void *data, size_t len) 304 { 305 struct rkdwhdmi_softc * const sc = device_private(dev); 306 307 if (len != 4) 308 return NULL; 309 310 return &sc->sc_base.sc_dai; 311 } 312 313 static struct fdtbus_dai_controller_func rkdwhdmi_dai_funcs = { 314 .get_tag = rkdwhdmi_dai_get_tag 315 }; 316 317 #endif 318