1 /* $OpenBSD: rkdwhdmi.c,v 1.2 2020/03/04 13:38:22 kettenis 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/drmP.h> 45 #include <drm/drm_crtc_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 enum { 73 DWHDMI_PORT_INPUT = 0, 74 DWHDMI_PORT_OUTPUT = 1, 75 }; 76 77 struct rkdwhdmi_port { 78 struct rkdwhdmi_softc *sc; 79 struct rkdwhdmi_ep *ep; 80 int nep; 81 }; 82 83 struct rkdwhdmi_ep { 84 struct rkdwhdmi_port *port; 85 struct video_device vd; 86 }; 87 88 struct rkdwhdmi_softc { 89 struct dwhdmi_softc sc_base; 90 int sc_node; 91 int sc_clk_vpll; 92 93 struct drm_display_mode sc_curmode; 94 struct drm_encoder sc_encoder; 95 struct regmap *sc_grf; 96 97 int sc_activated; 98 99 struct rkdwhdmi_port *sc_port; 100 int sc_nport; 101 }; 102 103 #define to_rkdwhdmi_softc(x) container_of(x, struct rkdwhdmi_softc, sc_base) 104 #define to_rkdwhdmi_encoder(x) container_of(x, struct rkdwhdmi_softc, sc_encoder) 105 106 int rkdwhdmi_match(struct device *, void *, void *); 107 void rkdwhdmi_attach(struct device *, struct device *, void *); 108 109 void rkdwhdmi_select_input(struct rkdwhdmi_softc *, u_int); 110 bool rkdwhdmi_encoder_mode_fixup(struct drm_encoder *, 111 const struct drm_display_mode *, struct drm_display_mode *); 112 void rkdwhdmi_encoder_mode_set(struct drm_encoder *, 113 struct drm_display_mode *, struct drm_display_mode *); 114 void rkdwhdmi_encoder_enable(struct drm_encoder *); 115 void rkdwhdmi_encoder_disable(struct drm_encoder *); 116 void rkdwhdmi_encoder_prepare(struct drm_encoder *); 117 void rkdwhdmi_encoder_commit(struct drm_encoder *); 118 void rkdwhdmi_encoder_dpms(struct drm_encoder *, int); 119 120 int rkdwhdmi_ep_activate(void *, struct drm_device *); 121 void *rkdwhdmi_ep_get_data(void *); 122 123 void rkdwhdmi_enable(struct dwhdmi_softc *); 124 void rkdwhdmi_mode_set(struct dwhdmi_softc *, struct drm_display_mode *, 125 struct drm_display_mode *); 126 enum drm_mode_status rkdwhdmi_mode_valid(struct dwhdmi_softc *, 127 struct drm_display_mode *); 128 129 struct cfattach rkdwhdmi_ca = { 130 sizeof (struct rkdwhdmi_softc), rkdwhdmi_match, rkdwhdmi_attach 131 }; 132 133 struct cfdriver rkdwhdmi_cd = { 134 NULL, "rkdwhdmi", DV_DULL 135 }; 136 137 int 138 rkdwhdmi_match(struct device *parent, void *match, void *aux) 139 { 140 struct fdt_attach_args *faa = aux; 141 142 return OF_is_compatible(faa->fa_node, "rockchip,rk3399-dw-hdmi"); 143 } 144 145 void 146 rkdwhdmi_attach(struct device *parent, struct device *self, void *aux) 147 { 148 struct rkdwhdmi_softc *sc = (struct rkdwhdmi_softc *)self; 149 struct fdt_attach_args *faa = aux; 150 int i, j, ep, port, ports, grf; 151 bus_addr_t addr; 152 bus_size_t size; 153 uint32_t phandle; 154 155 if (faa->fa_nreg < 1) { 156 printf(": no registers\n"); 157 return; 158 } 159 160 pinctrl_byname(sc->sc_node, "default"); 161 162 clock_enable(faa->fa_node, "iahb"); 163 clock_enable(faa->fa_node, "isfr"); 164 clock_enable(faa->fa_node, "vpll"); 165 clock_enable(faa->fa_node, "grf"); 166 clock_enable(faa->fa_node, "cec"); 167 168 sc->sc_base.sc_reg_width = 169 OF_getpropint(faa->fa_node, "reg-io-width", 4); 170 171 sc->sc_base.sc_bst = faa->fa_iot; 172 if (bus_space_map(sc->sc_base.sc_bst, faa->fa_reg[0].addr, 173 faa->fa_reg[0].size, 0, &sc->sc_base.sc_bsh)) { 174 printf(": can't map registers\n"); 175 return; 176 } 177 178 sc->sc_node = faa->fa_node; 179 sc->sc_clk_vpll = OF_getindex(faa->fa_node, "vpll", "clock-names"); 180 181 grf = OF_getpropint(faa->fa_node, "rockchip,grf", 0); 182 sc->sc_grf = regmap_byphandle(grf); 183 if (sc->sc_grf == NULL) { 184 printf(": can't get grf\n"); 185 return; 186 } 187 188 printf(": HDMI TX\n"); 189 190 phandle = OF_getpropint(faa->fa_node, "ddc-i2c-bus", 0); 191 sc->sc_base.sc_ic = i2c_byphandle(phandle); 192 if (phandle && sc->sc_base.sc_ic == NULL) { 193 printf("%s: couldn't find external I2C master\n", 194 self->dv_xname); 195 return; 196 } 197 198 sc->sc_base.sc_flags |= DWHDMI_USE_INTERNAL_PHY; 199 sc->sc_base.sc_detect = dwhdmi_phy_detect; 200 sc->sc_base.sc_enable = rkdwhdmi_enable; 201 sc->sc_base.sc_disable = dwhdmi_phy_disable; 202 sc->sc_base.sc_mode_set = rkdwhdmi_mode_set; 203 sc->sc_base.sc_mode_valid = rkdwhdmi_mode_valid; 204 sc->sc_base.sc_mpll_config = rkdwhdmi_mpll_config; 205 sc->sc_base.sc_phy_config = rkdwhdmi_phy_config; 206 207 if (dwhdmi_attach(&sc->sc_base) != 0) { 208 printf("%s: failed to attach driver\n", self->dv_xname); 209 return; 210 } 211 212 ports = OF_getnodebyname(faa->fa_node, "ports"); 213 if (!ports) 214 return; 215 216 for (port = OF_child(ports); port; port = OF_peer(port)) 217 sc->sc_nport++; 218 if (!sc->sc_nport) 219 return; 220 221 sc->sc_port = mallocarray(sc->sc_nport, sizeof(*sc->sc_port), M_DEVBUF, 222 M_WAITOK | M_ZERO); 223 for (i = 0, port = OF_child(ports); port; port = OF_peer(port), i++) { 224 for (ep = OF_child(port); ep; ep = OF_peer(ep)) 225 sc->sc_port[i].nep++; 226 if (!sc->sc_port[i].nep) 227 continue; 228 sc->sc_port[i].sc = sc; 229 sc->sc_port[i].ep = mallocarray(sc->sc_port[i].nep, 230 sizeof(*sc->sc_port[i].ep), M_DEVBUF, M_WAITOK | M_ZERO); 231 for (j = 0, ep = OF_child(port); ep; ep = OF_peer(ep), j++) { 232 sc->sc_port[i].ep[j].port = &sc->sc_port[i]; 233 sc->sc_port[i].ep[j].vd.vd_node = ep; 234 sc->sc_port[i].ep[j].vd.vd_cookie = 235 &sc->sc_port[i].ep[j]; 236 sc->sc_port[i].ep[j].vd.vd_ep_activate = 237 rkdwhdmi_ep_activate; 238 sc->sc_port[i].ep[j].vd.vd_ep_get_data = 239 rkdwhdmi_ep_get_data; 240 video_register(&sc->sc_port[i].ep[j].vd); 241 } 242 } 243 244 #ifdef notyet 245 fdtbus_register_dai_controller(self, phandle, &rkdwhdmi_dai_funcs); 246 #endif 247 } 248 249 void 250 rkdwhdmi_select_input(struct rkdwhdmi_softc *sc, u_int crtc_index) 251 { 252 const uint32_t write_mask = HDMI_LCDC_SEL << 16; 253 const uint32_t write_val = crtc_index == 0 ? HDMI_LCDC_SEL : 0; 254 255 regmap_write_4(sc->sc_grf, RK3399_GRF_SOC_CON20, write_mask | write_val); 256 } 257 258 bool 259 rkdwhdmi_encoder_mode_fixup(struct drm_encoder *encoder, 260 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 261 { 262 return true; 263 } 264 265 void 266 rkdwhdmi_encoder_mode_set(struct drm_encoder *encoder, 267 struct drm_display_mode *mode, struct drm_display_mode *adjusted) 268 { 269 } 270 271 void 272 rkdwhdmi_encoder_enable(struct drm_encoder *encoder) 273 { 274 } 275 276 void 277 rkdwhdmi_encoder_disable(struct drm_encoder *encoder) 278 { 279 } 280 281 void 282 rkdwhdmi_encoder_prepare(struct drm_encoder *encoder) 283 { 284 struct rkdwhdmi_softc * const sc = to_rkdwhdmi_encoder(encoder); 285 const u_int crtc_index = drm_crtc_index(encoder->crtc); 286 287 rkdwhdmi_select_input(sc, crtc_index); 288 } 289 290 void 291 rkdwhdmi_encoder_commit(struct drm_encoder *encoder) 292 { 293 } 294 295 struct drm_encoder_funcs rkdwhdmi_encoder_funcs = { 296 .destroy = drm_encoder_cleanup, 297 }; 298 299 struct drm_encoder_helper_funcs rkdwhdmi_encoder_helper_funcs = { 300 .prepare = rkdwhdmi_encoder_prepare, 301 .mode_fixup = rkdwhdmi_encoder_mode_fixup, 302 .mode_set = rkdwhdmi_encoder_mode_set, 303 .enable = rkdwhdmi_encoder_enable, 304 .disable = rkdwhdmi_encoder_disable, 305 .commit = rkdwhdmi_encoder_commit, 306 }; 307 308 int 309 rkdwhdmi_ep_activate(void *cookie, struct drm_device *ddev) 310 { 311 struct rkdwhdmi_ep *ep = cookie; 312 struct rkdwhdmi_port *port = ep->port; 313 struct rkdwhdmi_softc *sc = port->sc; 314 int error; 315 316 if (sc->sc_activated) 317 return 0; 318 319 if (OF_getpropint(OF_parent(ep->vd.vd_node), "reg", 0) != DWHDMI_PORT_INPUT) 320 return EINVAL; 321 322 sc->sc_encoder.possible_crtcs = 0x3; /* XXX */ 323 drm_encoder_init(ddev, &sc->sc_encoder, &rkdwhdmi_encoder_funcs, 324 DRM_MODE_ENCODER_TMDS, NULL); 325 drm_encoder_helper_add(&sc->sc_encoder, &rkdwhdmi_encoder_helper_funcs); 326 327 sc->sc_base.sc_connector.base.connector_type = DRM_MODE_CONNECTOR_HDMIA; 328 error = dwhdmi_bind(&sc->sc_base, &sc->sc_encoder); 329 if (error != 0) 330 return error; 331 332 sc->sc_activated = 1; 333 return 0; 334 } 335 336 void * 337 rkdwhdmi_ep_get_data(void *cookie) 338 { 339 struct rkdwhdmi_ep *ep = cookie; 340 struct rkdwhdmi_port *port = ep->port; 341 struct rkdwhdmi_softc *sc = port->sc; 342 343 return &sc->sc_encoder; 344 } 345 346 void 347 rkdwhdmi_enable(struct dwhdmi_softc *dsc) 348 { 349 dwhdmi_phy_enable(dsc); 350 } 351 352 void 353 rkdwhdmi_mode_set(struct dwhdmi_softc *dsc, 354 struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 355 { 356 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 357 int error; 358 359 if (sc->sc_clk_vpll != -1) { 360 error = clock_set_frequency(sc->sc_node, "vpll", 361 adjusted_mode->clock * 1000); 362 if (error != 0) 363 printf("%s: couldn't set pixel clock to %u Hz: %d\n", 364 dsc->sc_dev.dv_xname, adjusted_mode->clock * 1000, 365 error); 366 } 367 368 dwhdmi_phy_mode_set(dsc, mode, adjusted_mode); 369 } 370 371 enum drm_mode_status 372 rkdwhdmi_mode_valid(struct dwhdmi_softc *dsc, struct drm_display_mode *mode) 373 { 374 struct rkdwhdmi_softc *sc = to_rkdwhdmi_softc(dsc); 375 int i; 376 377 for (i = 0; sc->sc_base.sc_mpll_config[i].pixel_clock != 0; i++) 378 if (mode->clock == sc->sc_base.sc_mpll_config[i].pixel_clock) 379 return MODE_OK; 380 381 return MODE_BAD; 382 } 383 384 #ifdef notyet 385 386 static audio_dai_tag_t 387 rkdwhdmi_dai_get_tag(device_t dev, const void *data, size_t len) 388 { 389 struct rkdwhdmi_softc * const sc = device_private(dev); 390 391 if (len != 4) 392 return NULL; 393 394 return &sc->sc_base.sc_dai; 395 } 396 397 static struct fdtbus_dai_controller_func rkdwhdmi_dai_funcs = { 398 .get_tag = rkdwhdmi_dai_get_tag 399 }; 400 401 #endif 402