1 /* $NetBSD: sunxi_lcdc.c,v 1.14 2021/12/19 11:01:10 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: sunxi_lcdc.c,v 1.14 2021/12/19 11:01:10 riastradh Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/conf.h> 35 #include <sys/device.h> 36 #include <sys/intr.h> 37 #include <sys/kernel.h> 38 #include <sys/systm.h> 39 40 #include <dev/fdt/fdt_port.h> 41 #include <dev/fdt/fdtvar.h> 42 43 #include <arm/sunxi/sunxi_drm.h> 44 45 #include <drm/drm_crtc_helper.h> 46 #include <drm/drm_drv.h> 47 #include <drm/drm_vblank.h> 48 49 #define TCON_GCTL_REG 0x000 50 #define TCON_GCTL_TCON_EN __BIT(31) 51 #define TCON_GCTL_GAMMA_EN __BIT(30) 52 #define TCON_GCTL_IO_MAP_SEL __BIT(0) 53 #define TCON_GINT0_REG 0x004 54 #define TCON_GINT0_TCON0_VB_INT_EN __BIT(31) 55 #define TCON_GINT0_TCON1_VB_INT_EN __BIT(30) 56 #define TCON_GINT0_TCON0_VB_INT_FLAG __BIT(15) 57 #define TCON_GINT0_TCON1_VB_INT_FLAG __BIT(14) 58 #define TCON_GINT1_REG 0x008 59 #define TCON_GINT1_TCON1_LINE_INT_NUM __BITS(11,0) 60 61 #define TCON0_CTL_REG 0x040 62 #define TCON0_CTL_TCON0_EN __BIT(31) 63 #define TCON0_CTL_START_DELAY __BITS(8,4) 64 #define TCON0_CTL_TCON0_SRC_SEL __BITS(2,0) 65 #define TCON0_DCLK_REG 0x044 66 #define TCON0_DCLK_EN __BITS(31,28) 67 #define TCON0_DCLK_DIV __BITS(6,0) 68 #define TCON0_BASIC0_REG 0x048 69 #define TCON0_BASIC1_REG 0x04c 70 #define TCON0_BASIC2_REG 0x050 71 #define TCON0_BASIC3_REG 0x054 72 #define TCON0_IO_POL_REG 0x088 73 #define TCON0_IO_POL_IO_OUTPUT_SEL __BIT(31) 74 #define TCON0_IO_POL_DCLK_SEL __BITS(30,28) 75 #define TCON0_IO_POL_IO3_INV __BIT(27) 76 #define TCON0_IO_POL_IO2_INV __BIT(26) 77 #define TCON0_IO_POL_IO1_INV __BIT(25) 78 #define TCON0_IO_POL_IO0_INV __BIT(24) 79 #define TCON0_IO_POL_DATA_INV __BITS(23,0) 80 #define TCON0_IO_TRI_REG 0x08c 81 82 #define TCON1_CTL_REG 0x090 83 #define TCON1_CTL_TCON1_EN __BIT(31) 84 #define TCON1_CTL_START_DELAY __BITS(8,4) 85 #define TCON1_CTL_TCON1_SRC_SEL __BITS(1,0) 86 #define TCON1_BASIC0_REG 0x094 87 #define TCON1_BASIC1_REG 0x098 88 #define TCON1_BASIC2_REG 0x09c 89 #define TCON1_BASIC3_REG 0x0a0 90 #define TCON1_BASIC4_REG 0x0a4 91 #define TCON1_BASIC5_REG 0x0a8 92 #define TCON1_IO_POL_REG 0x0f0 93 #define TCON1_IO_POL_IO3_INV __BIT(27) 94 #define TCON1_IO_POL_IO2_INV __BIT(26) 95 #define TCON1_IO_POL_IO1_INV __BIT(25) 96 #define TCON1_IO_POL_IO0_INV __BIT(24) 97 #define TCON1_IO_POL_DATA_INV __BITS(23,0) 98 #define TCON1_IO_TRI_REG 0x0f4 99 100 enum { 101 TCON_PORT_INPUT = 0, 102 TCON_PORT_OUTPUT = 1, 103 }; 104 105 enum tcon_type { 106 TYPE_TCON0, 107 TYPE_TCON1, 108 }; 109 110 static const struct device_compatible_entry compat_data[] = { 111 { .compat = "allwinner,sun8i-h3-tcon-tv", .value = TYPE_TCON1 }, 112 { .compat = "allwinner,sun50i-a64-tcon-lcd", .value = TYPE_TCON0 }, 113 { .compat = "allwinner,sun50i-a64-tcon-tv", .value = TYPE_TCON1 }, 114 DEVICE_COMPAT_EOL 115 }; 116 117 struct sunxi_lcdc_softc; 118 119 struct sunxi_lcdc_encoder { 120 struct drm_encoder base; 121 struct sunxi_lcdc_softc *sc; 122 struct drm_display_mode curmode; 123 }; 124 125 struct sunxi_lcdc_softc { 126 device_t sc_dev; 127 bus_space_tag_t sc_bst; 128 bus_space_handle_t sc_bsh; 129 int sc_phandle; 130 131 enum tcon_type sc_type; 132 133 struct clk *sc_clk_ch[2]; 134 135 struct sunxi_lcdc_encoder sc_encoder; 136 struct drm_connector sc_connector; 137 138 struct fdt_device_ports sc_ports; 139 140 uint32_t sc_vbl_counter; 141 }; 142 143 #define to_sunxi_lcdc_encoder(x) container_of(x, struct sunxi_lcdc_encoder, base) 144 145 #define TCON_READ(sc, reg) \ 146 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 147 #define TCON_WRITE(sc, reg, val) \ 148 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 149 150 static void 151 sunxi_lcdc_destroy(struct drm_encoder *encoder) 152 { 153 } 154 155 static const struct drm_encoder_funcs sunxi_lcdc_funcs = { 156 .destroy = sunxi_lcdc_destroy, 157 }; 158 159 static void 160 sunxi_lcdc_tcon_dpms(struct drm_encoder *encoder, int mode) 161 { 162 } 163 164 static bool 165 sunxi_lcdc_tcon_mode_fixup(struct drm_encoder *encoder, 166 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 167 { 168 return true; 169 } 170 171 static void 172 sunxi_lcdc_tcon_mode_set(struct drm_encoder *encoder, 173 struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 174 { 175 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 176 177 lcdc_encoder->curmode = *adjusted_mode; 178 } 179 180 static void 181 sunxi_lcdc_tcon0_prepare(struct drm_encoder *encoder) 182 { 183 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 184 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 185 uint32_t val; 186 187 val = TCON_READ(sc, TCON_GCTL_REG); 188 val |= TCON_GCTL_TCON_EN; 189 val &= ~TCON_GCTL_IO_MAP_SEL; 190 TCON_WRITE(sc, TCON_GCTL_REG, val); 191 192 TCON_WRITE(sc, TCON0_IO_TRI_REG, 0); 193 } 194 195 static void 196 sunxi_lcdc_tcon1_prepare(struct drm_encoder *encoder) 197 { 198 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 199 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 200 uint32_t val; 201 202 val = TCON_READ(sc, TCON_GCTL_REG); 203 val |= TCON_GCTL_TCON_EN; 204 TCON_WRITE(sc, TCON_GCTL_REG, val); 205 206 TCON_WRITE(sc, TCON1_IO_POL_REG, 0); 207 TCON_WRITE(sc, TCON1_IO_TRI_REG, 0xffffffff); 208 } 209 210 static void 211 sunxi_lcdc_tcon0_commit(struct drm_encoder *encoder) 212 { 213 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 214 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 215 struct drm_display_mode *mode = &lcdc_encoder->curmode; 216 uint32_t val; 217 int error; 218 219 const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0; 220 const u_int hspw = mode->crtc_hsync_end - mode->crtc_hsync_start; 221 const u_int hbp = mode->crtc_htotal - mode->crtc_hsync_start; 222 const u_int vspw = mode->crtc_vsync_end - mode->crtc_vsync_start; 223 const u_int vbp = mode->crtc_vtotal - mode->crtc_vsync_start; 224 const u_int vblank_len = (mode->crtc_vtotal - mode->crtc_vdisplay) >> interlace_p; 225 const u_int start_delay = uimin(vblank_len, 30); 226 227 val = TCON0_CTL_TCON0_EN | 228 __SHIFTIN(start_delay, TCON0_CTL_START_DELAY); 229 TCON_WRITE(sc, TCON0_CTL_REG, val); 230 231 TCON_WRITE(sc, TCON0_BASIC0_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 232 TCON_WRITE(sc, TCON0_BASIC1_REG, ((mode->crtc_htotal - 1) << 16) | (hbp - 1)); 233 TCON_WRITE(sc, TCON0_BASIC2_REG, ((mode->crtc_vtotal * 2) << 16) | (vbp - 1)); 234 TCON_WRITE(sc, TCON0_BASIC3_REG, ((hspw - 1) << 16) | (vspw - 1)); 235 236 val = TCON_READ(sc, TCON0_IO_POL_REG); 237 val &= ~(TCON0_IO_POL_IO3_INV|TCON0_IO_POL_IO2_INV| 238 TCON0_IO_POL_IO1_INV|TCON0_IO_POL_IO0_INV| 239 TCON0_IO_POL_DATA_INV); 240 if ((mode->flags & DRM_MODE_FLAG_PHSYNC) == 0) 241 val |= TCON0_IO_POL_IO1_INV; 242 if ((mode->flags & DRM_MODE_FLAG_PVSYNC) == 0) 243 val |= TCON0_IO_POL_IO0_INV; 244 TCON_WRITE(sc, TCON0_IO_POL_REG, val); 245 246 if (sc->sc_clk_ch[0] != NULL) { 247 error = clk_set_rate(sc->sc_clk_ch[0], mode->crtc_clock * 1000); 248 if (error != 0) { 249 device_printf(sc->sc_dev, "failed to set CH0 PLL rate to %u Hz: %d\n", 250 mode->crtc_clock * 1000, error); 251 return; 252 } 253 error = clk_enable(sc->sc_clk_ch[0]); 254 if (error != 0) { 255 device_printf(sc->sc_dev, "failed to enable CH0 PLL: %d\n", error); 256 return; 257 } 258 } else { 259 device_printf(sc->sc_dev, "no CH0 PLL configured\n"); 260 } 261 } 262 263 static void 264 sunxi_lcdc_tcon1_commit(struct drm_encoder *encoder) 265 { 266 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 267 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 268 struct drm_display_mode *mode = &lcdc_encoder->curmode; 269 uint32_t val; 270 int error; 271 272 const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0; 273 const u_int hspw = mode->crtc_hsync_end - mode->crtc_hsync_start; 274 const u_int hbp = mode->crtc_htotal - mode->crtc_hsync_start; 275 const u_int vspw = mode->crtc_vsync_end - mode->crtc_vsync_start; 276 const u_int vbp = mode->crtc_vtotal - mode->crtc_vsync_start; 277 const u_int vblank_len = ((mode->crtc_vtotal - mode->crtc_vdisplay) >> interlace_p) - 2; 278 const u_int start_delay = uimin(vblank_len, 30); 279 280 val = TCON1_CTL_TCON1_EN | 281 __SHIFTIN(start_delay, TCON1_CTL_START_DELAY); 282 TCON_WRITE(sc, TCON1_CTL_REG, val); 283 284 TCON_WRITE(sc, TCON1_BASIC0_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 285 TCON_WRITE(sc, TCON1_BASIC1_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 286 TCON_WRITE(sc, TCON1_BASIC2_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 287 TCON_WRITE(sc, TCON1_BASIC3_REG, ((mode->crtc_htotal - 1) << 16) | (hbp - 1)); 288 TCON_WRITE(sc, TCON1_BASIC4_REG, ((mode->crtc_vtotal * 2) << 16) | (vbp - 1)); 289 TCON_WRITE(sc, TCON1_BASIC5_REG, ((hspw - 1) << 16) | (vspw - 1)); 290 291 TCON_WRITE(sc, TCON_GINT1_REG, 292 __SHIFTIN(start_delay + 2, TCON_GINT1_TCON1_LINE_INT_NUM)); 293 294 if (sc->sc_clk_ch[1] != NULL) { 295 error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000); 296 if (error != 0) { 297 device_printf(sc->sc_dev, "failed to set CH1 PLL rate to %u Hz: %d\n", 298 mode->crtc_clock * 1000, error); 299 return; 300 } 301 error = clk_enable(sc->sc_clk_ch[1]); 302 if (error != 0) { 303 device_printf(sc->sc_dev, "failed to enable CH1 PLL: %d\n", error); 304 return; 305 } 306 } else { 307 device_printf(sc->sc_dev, "no CH1 PLL configured\n"); 308 } 309 } 310 311 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon0_helper_funcs = { 312 .dpms = sunxi_lcdc_tcon_dpms, 313 .mode_fixup = sunxi_lcdc_tcon_mode_fixup, 314 .prepare = sunxi_lcdc_tcon0_prepare, 315 .commit = sunxi_lcdc_tcon0_commit, 316 .mode_set = sunxi_lcdc_tcon_mode_set, 317 }; 318 319 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon1_helper_funcs = { 320 .dpms = sunxi_lcdc_tcon_dpms, 321 .mode_fixup = sunxi_lcdc_tcon_mode_fixup, 322 .prepare = sunxi_lcdc_tcon1_prepare, 323 .commit = sunxi_lcdc_tcon1_commit, 324 .mode_set = sunxi_lcdc_tcon_mode_set, 325 }; 326 327 static int 328 sunxi_lcdc_encoder_mode(struct fdt_endpoint *out_ep) 329 { 330 struct fdt_endpoint *remote_ep = fdt_endpoint_remote(out_ep); 331 332 if (remote_ep == NULL) 333 return DRM_MODE_ENCODER_NONE; 334 335 switch (fdt_endpoint_type(remote_ep)) { 336 case EP_DRM_BRIDGE: 337 return DRM_MODE_ENCODER_TMDS; 338 case EP_DRM_PANEL: 339 return DRM_MODE_ENCODER_LVDS; 340 default: 341 return DRM_MODE_ENCODER_NONE; 342 } 343 } 344 345 static uint32_t 346 sunxi_lcdc_get_vblank_counter(void *priv) 347 { 348 struct sunxi_lcdc_softc * const sc = priv; 349 350 return sc->sc_vbl_counter; 351 } 352 353 static void 354 sunxi_lcdc_enable_vblank(void *priv) 355 { 356 struct sunxi_lcdc_softc * const sc = priv; 357 const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1; 358 359 if (crtc_index == 0) 360 TCON_WRITE(sc, TCON_GINT0_REG, TCON_GINT0_TCON0_VB_INT_EN); 361 else 362 TCON_WRITE(sc, TCON_GINT0_REG, TCON_GINT0_TCON1_VB_INT_EN); 363 } 364 365 static void 366 sunxi_lcdc_disable_vblank(void *priv) 367 { 368 struct sunxi_lcdc_softc * const sc = priv; 369 370 TCON_WRITE(sc, TCON_GINT0_REG, 0); 371 } 372 373 static void 374 sunxi_lcdc_setup_vblank(struct sunxi_lcdc_softc *sc) 375 { 376 const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1; 377 struct drm_device *ddev = sc->sc_encoder.base.dev; 378 struct sunxi_drm_softc *drm_sc; 379 380 KASSERT(ddev != NULL); 381 382 drm_sc = device_private(ddev->dev); 383 drm_sc->sc_vbl[crtc_index].priv = sc; 384 drm_sc->sc_vbl[crtc_index].get_vblank_counter = sunxi_lcdc_get_vblank_counter; 385 drm_sc->sc_vbl[crtc_index].enable_vblank = sunxi_lcdc_enable_vblank; 386 drm_sc->sc_vbl[crtc_index].disable_vblank = sunxi_lcdc_disable_vblank; 387 } 388 389 static int 390 sunxi_lcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) 391 { 392 struct sunxi_lcdc_softc * const sc = device_private(dev); 393 struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep); 394 struct fdt_endpoint *out_ep; 395 struct drm_crtc *crtc; 396 397 if (!activate) 398 return EINVAL; 399 400 if (fdt_endpoint_port_index(ep) != TCON_PORT_INPUT) 401 return EINVAL; 402 403 if (fdt_endpoint_type(in_ep) != EP_DRM_CRTC) 404 return EINVAL; 405 406 crtc = fdt_endpoint_get_data(in_ep); 407 408 sc->sc_encoder.sc = sc; 409 sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(crtc); 410 411 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 0); 412 if (out_ep != NULL) { 413 drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs, 414 sunxi_lcdc_encoder_mode(out_ep), NULL); 415 drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon0_helper_funcs); 416 417 sunxi_lcdc_setup_vblank(sc); 418 419 return fdt_endpoint_activate(out_ep, activate); 420 } 421 422 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 1); 423 if (out_ep != NULL) { 424 drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs, 425 sunxi_lcdc_encoder_mode(out_ep), NULL); 426 drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon1_helper_funcs); 427 428 sunxi_lcdc_setup_vblank(sc); 429 430 return fdt_endpoint_activate(out_ep, activate); 431 } 432 433 return ENXIO; 434 } 435 436 static void * 437 sunxi_lcdc_ep_get_data(device_t dev, struct fdt_endpoint *ep) 438 { 439 struct sunxi_lcdc_softc * const sc = device_private(dev); 440 441 return &sc->sc_encoder; 442 } 443 444 static int 445 sunxi_lcdc_intr(void *priv) 446 { 447 struct sunxi_lcdc_softc * const sc = priv; 448 uint32_t val; 449 int rv = 0; 450 451 const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1; 452 const uint32_t status_mask = crtc_index == 0 ? 453 TCON_GINT0_TCON0_VB_INT_FLAG : TCON_GINT0_TCON1_VB_INT_FLAG; 454 455 val = TCON_READ(sc, TCON_GINT0_REG); 456 if ((val & status_mask) != 0) { 457 TCON_WRITE(sc, TCON_GINT0_REG, val & ~status_mask); 458 atomic_inc_32(&sc->sc_vbl_counter); 459 drm_handle_vblank(sc->sc_encoder.base.dev, crtc_index); 460 rv = 1; 461 } 462 463 return rv; 464 } 465 466 static int 467 sunxi_lcdc_match(device_t parent, cfdata_t cf, void *aux) 468 { 469 struct fdt_attach_args * const faa = aux; 470 471 return of_compatible_match(faa->faa_phandle, compat_data); 472 } 473 474 static void 475 sunxi_lcdc_attach(device_t parent, device_t self, void *aux) 476 { 477 struct sunxi_lcdc_softc * const sc = device_private(self); 478 struct fdt_attach_args * const faa = aux; 479 const int phandle = faa->faa_phandle; 480 struct fdtbus_reset *rst; 481 char intrstr[128]; 482 struct clk *clk; 483 bus_addr_t addr; 484 bus_size_t size; 485 void *ih; 486 487 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 488 aprint_error(": couldn't get registers\n"); 489 return; 490 } 491 492 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 493 aprint_error(": couldn't decode interrupt\n"); 494 return; 495 } 496 497 rst = fdtbus_reset_get(phandle, "lcd"); 498 if (rst == NULL || fdtbus_reset_deassert(rst) != 0) { 499 aprint_error(": couldn't de-assert reset\n"); 500 return; 501 } 502 503 clk = fdtbus_clock_get(phandle, "ahb"); 504 if (clk == NULL || clk_enable(clk) != 0) { 505 aprint_error(": couldn't enable bus clock\n"); 506 return; 507 } 508 509 sc->sc_dev = self; 510 sc->sc_bst = faa->faa_bst; 511 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 512 aprint_error(": couldn't map registers\n"); 513 return; 514 } 515 sc->sc_phandle = faa->faa_phandle; 516 sc->sc_type = of_compatible_lookup(phandle, compat_data)->value; 517 sc->sc_clk_ch[0] = fdtbus_clock_get(phandle, "tcon-ch0"); 518 sc->sc_clk_ch[1] = fdtbus_clock_get(phandle, "tcon-ch1"); 519 520 aprint_naive("\n"); 521 switch (sc->sc_type) { 522 case TYPE_TCON0: 523 aprint_normal(": TCON0\n"); 524 break; 525 case TYPE_TCON1: 526 aprint_normal(": TCON1\n"); 527 break; 528 } 529 530 sc->sc_ports.dp_ep_activate = sunxi_lcdc_ep_activate; 531 sc->sc_ports.dp_ep_get_data = sunxi_lcdc_ep_get_data; 532 fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER); 533 534 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, FDT_INTR_MPSAFE, 535 sunxi_lcdc_intr, sc, device_xname(self)); 536 if (ih == NULL) { 537 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 538 intrstr); 539 return; 540 } 541 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 542 } 543 544 CFATTACH_DECL_NEW(sunxi_lcdc, sizeof(struct sunxi_lcdc_softc), 545 sunxi_lcdc_match, sunxi_lcdc_attach, NULL, NULL); 546