1 /* $OpenBSD: rkdwhdmi.c,v 1.5 2020/06/30 02:19:11 deraadt 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 46 #include <dev/ic/dwhdmi.h> 47 48 #define RK3399_GRF_SOC_CON20 0x6250 49 #define HDMI_LCDC_SEL (1 << 6) 50 51 const struct dwhdmi_mpll_config rkdwhdmi_mpll_config[] = { 52 { 40000, 0x00b3, 0x0000, 0x0018 }, 53 { 65000, 0x0072, 0x0001, 0x0028 }, 54 { 66000, 0x013e, 0x0003, 0x0038 }, 55 { 83500, 0x0072, 0x0001, 0x0028 }, 56 { 146250, 0x0051, 0x0002, 0x0038 }, 57 { 148500, 0x0051, 0x0003, 0x0000 }, 58 { 272000, 0x0040, 0x0003, 0x0000 }, 59 { 340000, 0x0040, 0x0003, 0x0000 }, 60 { 0, 0x0051, 0x0003, 0x0000 }, 61 }; 62 63 const struct dwhdmi_phy_config rkdwhdmi_phy_config[] = { 64 { 74250, 0x8009, 0x0004, 0x0272 }, 65 { 148500, 0x802b, 0x0004, 0x028d }, 66 { 297000, 0x8039, 0x0005, 0x028d }, 67 { 594000, 0x8039, 0x0000, 0x019d }, 68 { 0, 0x0000, 0x0000, 0x0000 } 69 }; 70 71 struct rkdwhdmi_softc { 72 struct dwhdmi_softc sc_base; 73 int sc_node; 74 int sc_clk_vpll; 75 76 struct drm_display_mode sc_curmode; 77 struct drm_encoder sc_encoder; 78 struct regmap *sc_grf; 79 80 int sc_activated; 81 82 struct device_ports sc_ports; 83 }; 84 85 #define to_rkdwhdmi_softc(x) container_of(x, struct rkdwhdmi_softc, sc_base) 86 #define to_rkdwhdmi_encoder(x) container_of(x, struct rkdwhdmi_softc, sc_encoder) 87 88 int rkdwhdmi_match(struct device *, void *, void *); 89 void rkdwhdmi_attach(struct device *, struct device *, void *); 90 91 void rkdwhdmi_select_input(struct rkdwhdmi_softc *, u_int); 92 void rkdwhdmi_encoder_enable(struct drm_encoder *); 93 94 int rkdwhdmi_ep_activate(void *, struct endpoint *, void *); 95 void *rkdwhdmi_ep_get_cookie(void *, struct endpoint *); 96 97 void rkdwhdmi_enable(struct dwhdmi_softc *); 98 void rkdwhdmi_mode_set(struct dwhdmi_softc *, const struct drm_display_mode *, 99 const struct drm_display_mode *); 100 enum drm_mode_status rkdwhdmi_mode_valid(struct dwhdmi_softc *, 101 const struct drm_display_mode *); 102 103 struct cfattach rkdwhdmi_ca = { 104 sizeof (struct rkdwhdmi_softc), rkdwhdmi_match, rkdwhdmi_attach 105 }; 106 107 struct cfdriver rkdwhdmi_cd = { 108 NULL, "rkdwhdmi", DV_DULL 109 }; 110 111 int 112 rkdwhdmi_match(struct device *parent, void *match, void *aux) 113 { 114 struct fdt_attach_args *faa = aux; 115 116 return OF_is_compatible(faa->fa_node, "rockchip,rk3399-dw-hdmi"); 117 } 118 119 void 120 rkdwhdmi_attach(struct device *parent, struct device *self, void *aux) 121 { 122 struct rkdwhdmi_softc *sc = (struct rkdwhdmi_softc *)self; 123 struct fdt_attach_args *faa = aux; 124 uint32_t grf; 125 bus_addr_t addr; 126 bus_size_t size; 127 uint32_t phandle; 128 129 if (faa->fa_nreg < 1) { 130 printf(": no registers\n"); 131 return; 132 } 133 134 pinctrl_byname(sc->sc_node, "default"); 135 136 clock_enable(faa->fa_node, "iahb"); 137 clock_enable(faa->fa_node, "isfr"); 138 clock_enable(faa->fa_node, "vpll"); 139 clock_enable(faa->fa_node, "grf"); 140 clock_enable(faa->fa_node, "cec"); 141 142 sc->sc_base.sc_reg_width = 143 OF_getpropint(faa->fa_node, "reg-io-width", 4); 144 145 sc->sc_base.sc_bst = faa->fa_iot; 146 if (bus_space_map(sc->sc_base.sc_bst, faa->fa_reg[0].addr, 147 faa->fa_reg[0].size, 0, &sc->sc_base.sc_bsh)) { 148 printf(": can't map registers\n"); 149 return; 150 } 151 152 sc->sc_node = faa->fa_node; 153 sc->sc_clk_vpll = OF_getindex(faa->fa_node, "vpll", "clock-names"); 154 155 grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0); 156 sc->sc_grf = regmap_byphandle(grf); 157 if (sc->sc_grf == NULL) { 158 printf(": can't get grf\n"); 159 return; 160 } 161 162 printf(": HDMI TX\n"); 163 164 phandle = OF_getpropint(faa->fa_node, "ddc-i2c-bus", 0); 165 sc->sc_base.sc_ic = i2c_byphandle(phandle); 166 if (phandle && sc->sc_base.sc_ic == NULL) { 167 printf("%s: couldn't find external I2C master\n", 168 self->dv_xname); 169 return; 170 } 171 172 sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY; 173 sc->sc_base.sc_detect = dwhdmi_phy_detect; 174 sc->sc_base.sc_enable = rkdwhdmi_enable; 175 sc->sc_base.sc_disable = dwhdmi_phy_disable; 176 sc->sc_base.sc_mode_set = rkdwhdmi_mode_set; 177 sc->sc_base.sc_mode_valid = rkdwhdmi_mode_valid; 178 sc->sc_base.sc_mpll_config = rkdwhdmi_mpll_config; 179 sc->sc_base.sc_phy_config = rkdwhdmi_phy_config; 180 181 if (dwhdmi_attach(&sc->sc_base) != 0) { 182 printf("%s: failed to attach driver\n", self->dv_xname); 183 return; 184 } 185 186 sc->sc_ports.dp_node = faa->fa_node; 187 sc->sc_ports.dp_cookie = sc; 188 sc->sc_ports.dp_ep_activate = rkdwhdmi_ep_activate; 189 sc->sc_ports.dp_ep_get_cookie = rkdwhdmi_ep_get_cookie; 190 device_ports_register(&sc->sc_ports, EP_DRM_ENCODER); 191 192 #ifdef notyet 193 fdtbus_register_dai_controller(self, phandle, &rkdwhdmi_dai_funcs); 194 #endif 195 } 196 197 void 198 rkdwhdmi_select_input(struct rkdwhdmi_softc *sc, u_int crtc_index) 199 { 200 const uint32_t write_mask = HDMI_LCDC_SEL << 16; 201 const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0; 202 203 regmap_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val); 204 } 205 206 void 207 rkdwhdmi_encoder_enable(struct drm_encoder *encoder) 208 { 209 struct rkdwhdmi_softc * const sc = to_rkdwhdmi_encoder(encoder); 210 const u_int crtc_index = drm_crtc_index(encoder->crtc); 211 212 rkdwhdmi_select_input(sc, crtc_index); 213 } 214 215 struct drm_encoder_funcs rkdwhdmi_encoder_funcs = { 216 .destroy = drm_encoder_cleanup, 217 }; 218 219 struct drm_encoder_helper_funcs rkdwhdmi_encoder_helper_funcs = { 220 .enable = rkdwhdmi_encoder_enable, 221 }; 222 223 int 224 rkdwhdmi_ep_activate(void *cookie, struct endpoint *ep, void *arg) 225 { 226 struct rkdwhdmi_softc *sc = cookie; 227 struct drm_crtc *crtc = NULL; 228 struct endpoint *rep; 229 int error; 230 231 if (sc->sc_activated) 232 return 0; 233 234 rep = endpoint_remote(ep); 235 if (rep && rep->ep_type == EP_DRM_CRTC) 236 crtc = endpoint_get_cookie(rep); 237 if (crtc == NULL) 238 return EINVAL; 239 240 sc->sc_encoder.possible_crtcs = 0x3; /* XXX */ 241 drm_encoder_init(crtc->dev, &sc->sc_encoder, &rkdwhdmi_encoder_funcs, 242 DRM_MODE_ENCODER_TMDS, NULL); 243 drm_encoder_helper_add(&sc->sc_encoder, &rkdwhdmi_encoder_helper_funcs); 244 245 sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_HDMIA; 246 error = dwhdmi_bind(&sc->sc_base, &sc->sc_encoder); 247 if (error != 0) 248 return error; 249 250 sc->sc_activated = 1; 251 return 0; 252 } 253 254 void * 255 rkdwhdmi_ep_get_cookie(void *cookie, struct endpoint *ep) 256 { 257 struct rkdwhdmi_softc *sc = cookie; 258 return &sc->sc_encoder; 259 } 260 261 void 262 rkdwhdmi_enable(struct dwhdmi_softc *dsc) 263 { 264 dwhdmi_phy_enable(dsc); 265 } 266 267 void 268 rkdwhdmi_mode_set(struct dwhdmi_softc *dsc, 269 const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) 270 { 271 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 272 int error; 273 274 if (sc->sc_clk_vpll != -1) { 275 error = clock_set_frequency(sc->sc_node, "vpll", 276 adjusted_mode->clock * 1000); 277 if (error != 0) 278 printf("%s: couldn't set pixel clock to %u Hz: %d\n", 279 dsc->sc_dev.dv_xname, adjusted_mode->clock * 1000, 280 error); 281 } 282 283 dwhdmi_phy_mode_set(dsc, mode, adjusted_mode); 284 } 285 286 enum drm_mode_status 287 rkdwhdmi_mode_valid(struct dwhdmi_softc *dsc, const struct drm_display_mode *mode) 288 { 289 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 290 int i; 291 292 for (i = 0; sc->sc_base.sc_mpll_config[i].pixel_clock != 0; i++) 293 if (mode->clock == sc->sc_base.sc_mpll_config[i].pixel_clock) 294 return MODE_OK; 295 296 return MODE_BAD; 297 } 298 299 #ifdef notyet 300 301 static audio_dai_tag_t 302 rkdwhdmi_dai_get_tag(device_t dev, const void *data, size_t len) 303 { 304 struct rkdwhdmi_softc * const sc = device_private(dev); 305 306 if (len != 4) 307 return NULL; 308 309 return &sc->sc_base.sc_dai; 310 } 311 312 static struct fdtbus_dai_controller_func rkdwhdmi_dai_funcs = { 313 .get_tag = rkdwhdmi_dai_get_tag 314 }; 315 316 #endif 317