1 /* $NetBSD: sunxi_lcdc.c,v 1.15 2022/06/28 05:19:03 skrll 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.15 2022/06/28 05:19:03 skrll 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,sun8i-v3s-tcon", .value = TYPE_TCON0 }, 113 { .compat = "allwinner,sun50i-a64-tcon-lcd", .value = TYPE_TCON0 }, 114 { .compat = "allwinner,sun50i-a64-tcon-tv", .value = TYPE_TCON1 }, 115 DEVICE_COMPAT_EOL 116 }; 117 118 struct sunxi_lcdc_softc; 119 120 struct sunxi_lcdc_encoder { 121 struct drm_encoder base; 122 struct sunxi_lcdc_softc *sc; 123 struct drm_display_mode curmode; 124 }; 125 126 struct sunxi_lcdc_softc { 127 device_t sc_dev; 128 bus_space_tag_t sc_bst; 129 bus_space_handle_t sc_bsh; 130 int sc_phandle; 131 132 enum tcon_type sc_type; 133 134 struct clk *sc_clk_ch[2]; 135 136 struct sunxi_lcdc_encoder sc_encoder; 137 struct drm_connector sc_connector; 138 139 struct fdt_device_ports sc_ports; 140 141 uint32_t sc_vbl_counter; 142 }; 143 144 #define to_sunxi_lcdc_encoder(x) container_of(x, struct sunxi_lcdc_encoder, base) 145 146 #define TCON_READ(sc, reg) \ 147 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 148 #define TCON_WRITE(sc, reg, val) \ 149 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 150 151 static void 152 sunxi_lcdc_destroy(struct drm_encoder *encoder) 153 { 154 } 155 156 static const struct drm_encoder_funcs sunxi_lcdc_funcs = { 157 .destroy = sunxi_lcdc_destroy, 158 }; 159 160 static void 161 sunxi_lcdc_tcon_dpms(struct drm_encoder *encoder, int mode) 162 { 163 } 164 165 static bool 166 sunxi_lcdc_tcon_mode_fixup(struct drm_encoder *encoder, 167 const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 168 { 169 return true; 170 } 171 172 static void 173 sunxi_lcdc_tcon_mode_set(struct drm_encoder *encoder, 174 struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) 175 { 176 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 177 178 lcdc_encoder->curmode = *adjusted_mode; 179 } 180 181 static void 182 sunxi_lcdc_tcon0_prepare(struct drm_encoder *encoder) 183 { 184 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 185 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 186 uint32_t val; 187 188 val = TCON_READ(sc, TCON_GCTL_REG); 189 val |= TCON_GCTL_TCON_EN; 190 val &= ~TCON_GCTL_IO_MAP_SEL; 191 TCON_WRITE(sc, TCON_GCTL_REG, val); 192 193 TCON_WRITE(sc, TCON0_IO_TRI_REG, 0); 194 } 195 196 static void 197 sunxi_lcdc_tcon1_prepare(struct drm_encoder *encoder) 198 { 199 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 200 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 201 uint32_t val; 202 203 val = TCON_READ(sc, TCON_GCTL_REG); 204 val |= TCON_GCTL_TCON_EN; 205 TCON_WRITE(sc, TCON_GCTL_REG, val); 206 207 TCON_WRITE(sc, TCON1_IO_POL_REG, 0); 208 TCON_WRITE(sc, TCON1_IO_TRI_REG, 0xffffffff); 209 } 210 211 static void 212 sunxi_lcdc_tcon0_commit(struct drm_encoder *encoder) 213 { 214 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 215 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 216 struct drm_display_mode *mode = &lcdc_encoder->curmode; 217 uint32_t val; 218 int error; 219 220 const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0; 221 const u_int hspw = mode->crtc_hsync_end - mode->crtc_hsync_start; 222 const u_int hbp = mode->crtc_htotal - mode->crtc_hsync_start; 223 const u_int vspw = mode->crtc_vsync_end - mode->crtc_vsync_start; 224 const u_int vbp = mode->crtc_vtotal - mode->crtc_vsync_start; 225 const u_int vblank_len = (mode->crtc_vtotal - mode->crtc_vdisplay) >> interlace_p; 226 const u_int start_delay = uimin(vblank_len, 30); 227 228 val = TCON0_CTL_TCON0_EN | 229 __SHIFTIN(start_delay, TCON0_CTL_START_DELAY); 230 TCON_WRITE(sc, TCON0_CTL_REG, val); 231 232 TCON_WRITE(sc, TCON0_BASIC0_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 233 TCON_WRITE(sc, TCON0_BASIC1_REG, ((mode->crtc_htotal - 1) << 16) | (hbp - 1)); 234 TCON_WRITE(sc, TCON0_BASIC2_REG, ((mode->crtc_vtotal * 2) << 16) | (vbp - 1)); 235 TCON_WRITE(sc, TCON0_BASIC3_REG, ((hspw - 1) << 16) | (vspw - 1)); 236 237 val = TCON_READ(sc, TCON0_IO_POL_REG); 238 val &= ~(TCON0_IO_POL_IO3_INV|TCON0_IO_POL_IO2_INV| 239 TCON0_IO_POL_IO1_INV|TCON0_IO_POL_IO0_INV| 240 TCON0_IO_POL_DATA_INV); 241 if ((mode->flags & DRM_MODE_FLAG_PHSYNC) == 0) 242 val |= TCON0_IO_POL_IO1_INV; 243 if ((mode->flags & DRM_MODE_FLAG_PVSYNC) == 0) 244 val |= TCON0_IO_POL_IO0_INV; 245 TCON_WRITE(sc, TCON0_IO_POL_REG, val); 246 247 if (sc->sc_clk_ch[0] != NULL) { 248 error = clk_set_rate(sc->sc_clk_ch[0], mode->crtc_clock * 1000); 249 if (error != 0) { 250 device_printf(sc->sc_dev, "failed to set CH0 PLL rate to %u Hz: %d\n", 251 mode->crtc_clock * 1000, error); 252 return; 253 } 254 error = clk_enable(sc->sc_clk_ch[0]); 255 if (error != 0) { 256 device_printf(sc->sc_dev, "failed to enable CH0 PLL: %d\n", error); 257 return; 258 } 259 } else { 260 device_printf(sc->sc_dev, "no CH0 PLL configured\n"); 261 } 262 } 263 264 static void 265 sunxi_lcdc_tcon1_commit(struct drm_encoder *encoder) 266 { 267 struct sunxi_lcdc_encoder *lcdc_encoder = to_sunxi_lcdc_encoder(encoder); 268 struct sunxi_lcdc_softc * const sc = lcdc_encoder->sc; 269 struct drm_display_mode *mode = &lcdc_encoder->curmode; 270 uint32_t val; 271 int error; 272 273 const u_int interlace_p = (mode->flags & DRM_MODE_FLAG_INTERLACE) != 0; 274 const u_int hspw = mode->crtc_hsync_end - mode->crtc_hsync_start; 275 const u_int hbp = mode->crtc_htotal - mode->crtc_hsync_start; 276 const u_int vspw = mode->crtc_vsync_end - mode->crtc_vsync_start; 277 const u_int vbp = mode->crtc_vtotal - mode->crtc_vsync_start; 278 const u_int vblank_len = ((mode->crtc_vtotal - mode->crtc_vdisplay) >> interlace_p) - 2; 279 const u_int start_delay = uimin(vblank_len, 30); 280 281 val = TCON1_CTL_TCON1_EN | 282 __SHIFTIN(start_delay, TCON1_CTL_START_DELAY); 283 TCON_WRITE(sc, TCON1_CTL_REG, val); 284 285 TCON_WRITE(sc, TCON1_BASIC0_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 286 TCON_WRITE(sc, TCON1_BASIC1_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 287 TCON_WRITE(sc, TCON1_BASIC2_REG, ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); 288 TCON_WRITE(sc, TCON1_BASIC3_REG, ((mode->crtc_htotal - 1) << 16) | (hbp - 1)); 289 TCON_WRITE(sc, TCON1_BASIC4_REG, ((mode->crtc_vtotal * 2) << 16) | (vbp - 1)); 290 TCON_WRITE(sc, TCON1_BASIC5_REG, ((hspw - 1) << 16) | (vspw - 1)); 291 292 TCON_WRITE(sc, TCON_GINT1_REG, 293 __SHIFTIN(start_delay + 2, TCON_GINT1_TCON1_LINE_INT_NUM)); 294 295 if (sc->sc_clk_ch[1] != NULL) { 296 error = clk_set_rate(sc->sc_clk_ch[1], mode->crtc_clock * 1000); 297 if (error != 0) { 298 device_printf(sc->sc_dev, "failed to set CH1 PLL rate to %u Hz: %d\n", 299 mode->crtc_clock * 1000, error); 300 return; 301 } 302 error = clk_enable(sc->sc_clk_ch[1]); 303 if (error != 0) { 304 device_printf(sc->sc_dev, "failed to enable CH1 PLL: %d\n", error); 305 return; 306 } 307 } else { 308 device_printf(sc->sc_dev, "no CH1 PLL configured\n"); 309 } 310 } 311 312 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon0_helper_funcs = { 313 .dpms = sunxi_lcdc_tcon_dpms, 314 .mode_fixup = sunxi_lcdc_tcon_mode_fixup, 315 .prepare = sunxi_lcdc_tcon0_prepare, 316 .commit = sunxi_lcdc_tcon0_commit, 317 .mode_set = sunxi_lcdc_tcon_mode_set, 318 }; 319 320 static const struct drm_encoder_helper_funcs sunxi_lcdc_tcon1_helper_funcs = { 321 .dpms = sunxi_lcdc_tcon_dpms, 322 .mode_fixup = sunxi_lcdc_tcon_mode_fixup, 323 .prepare = sunxi_lcdc_tcon1_prepare, 324 .commit = sunxi_lcdc_tcon1_commit, 325 .mode_set = sunxi_lcdc_tcon_mode_set, 326 }; 327 328 static int 329 sunxi_lcdc_encoder_mode(struct fdt_endpoint *out_ep) 330 { 331 struct fdt_endpoint *remote_ep = fdt_endpoint_remote(out_ep); 332 333 if (remote_ep == NULL) 334 return DRM_MODE_ENCODER_NONE; 335 336 switch (fdt_endpoint_type(remote_ep)) { 337 case EP_DRM_BRIDGE: 338 return DRM_MODE_ENCODER_TMDS; 339 case EP_DRM_PANEL: 340 return DRM_MODE_ENCODER_LVDS; 341 default: 342 return DRM_MODE_ENCODER_NONE; 343 } 344 } 345 346 static uint32_t 347 sunxi_lcdc_get_vblank_counter(void *priv) 348 { 349 struct sunxi_lcdc_softc * const sc = priv; 350 351 return sc->sc_vbl_counter; 352 } 353 354 static void 355 sunxi_lcdc_enable_vblank(void *priv) 356 { 357 struct sunxi_lcdc_softc * const sc = priv; 358 const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1; 359 360 if (crtc_index == 0) 361 TCON_WRITE(sc, TCON_GINT0_REG, TCON_GINT0_TCON0_VB_INT_EN); 362 else 363 TCON_WRITE(sc, TCON_GINT0_REG, TCON_GINT0_TCON1_VB_INT_EN); 364 } 365 366 static void 367 sunxi_lcdc_disable_vblank(void *priv) 368 { 369 struct sunxi_lcdc_softc * const sc = priv; 370 371 TCON_WRITE(sc, TCON_GINT0_REG, 0); 372 } 373 374 static void 375 sunxi_lcdc_setup_vblank(struct sunxi_lcdc_softc *sc) 376 { 377 const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1; 378 struct drm_device *ddev = sc->sc_encoder.base.dev; 379 struct sunxi_drm_softc *drm_sc; 380 381 KASSERT(ddev != NULL); 382 383 drm_sc = device_private(ddev->dev); 384 drm_sc->sc_vbl[crtc_index].priv = sc; 385 drm_sc->sc_vbl[crtc_index].get_vblank_counter = sunxi_lcdc_get_vblank_counter; 386 drm_sc->sc_vbl[crtc_index].enable_vblank = sunxi_lcdc_enable_vblank; 387 drm_sc->sc_vbl[crtc_index].disable_vblank = sunxi_lcdc_disable_vblank; 388 } 389 390 static int 391 sunxi_lcdc_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) 392 { 393 struct sunxi_lcdc_softc * const sc = device_private(dev); 394 struct fdt_endpoint *in_ep = fdt_endpoint_remote(ep); 395 struct fdt_endpoint *out_ep; 396 struct drm_crtc *crtc; 397 398 if (!activate) 399 return EINVAL; 400 401 if (fdt_endpoint_port_index(ep) != TCON_PORT_INPUT) 402 return EINVAL; 403 404 if (fdt_endpoint_type(in_ep) != EP_DRM_CRTC) 405 return EINVAL; 406 407 crtc = fdt_endpoint_get_data(in_ep); 408 409 sc->sc_encoder.sc = sc; 410 sc->sc_encoder.base.possible_crtcs = 1 << drm_crtc_index(crtc); 411 412 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 0); 413 if (out_ep != NULL) { 414 drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs, 415 sunxi_lcdc_encoder_mode(out_ep), NULL); 416 drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon0_helper_funcs); 417 418 sunxi_lcdc_setup_vblank(sc); 419 420 return fdt_endpoint_activate(out_ep, activate); 421 } 422 423 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, TCON_PORT_OUTPUT, 1); 424 if (out_ep != NULL) { 425 drm_encoder_init(crtc->dev, &sc->sc_encoder.base, &sunxi_lcdc_funcs, 426 sunxi_lcdc_encoder_mode(out_ep), NULL); 427 drm_encoder_helper_add(&sc->sc_encoder.base, &sunxi_lcdc_tcon1_helper_funcs); 428 429 sunxi_lcdc_setup_vblank(sc); 430 431 return fdt_endpoint_activate(out_ep, activate); 432 } 433 434 return ENXIO; 435 } 436 437 static void * 438 sunxi_lcdc_ep_get_data(device_t dev, struct fdt_endpoint *ep) 439 { 440 struct sunxi_lcdc_softc * const sc = device_private(dev); 441 442 return &sc->sc_encoder; 443 } 444 445 static int 446 sunxi_lcdc_intr(void *priv) 447 { 448 struct sunxi_lcdc_softc * const sc = priv; 449 uint32_t val; 450 int rv = 0; 451 452 const int crtc_index = ffs32(sc->sc_encoder.base.possible_crtcs) - 1; 453 const uint32_t status_mask = crtc_index == 0 ? 454 TCON_GINT0_TCON0_VB_INT_FLAG : TCON_GINT0_TCON1_VB_INT_FLAG; 455 456 val = TCON_READ(sc, TCON_GINT0_REG); 457 if ((val & status_mask) != 0) { 458 TCON_WRITE(sc, TCON_GINT0_REG, val & ~status_mask); 459 atomic_inc_32(&sc->sc_vbl_counter); 460 drm_handle_vblank(sc->sc_encoder.base.dev, crtc_index); 461 rv = 1; 462 } 463 464 return rv; 465 } 466 467 static int 468 sunxi_lcdc_match(device_t parent, cfdata_t cf, void *aux) 469 { 470 struct fdt_attach_args * const faa = aux; 471 472 return of_compatible_match(faa->faa_phandle, compat_data); 473 } 474 475 static void 476 sunxi_lcdc_attach(device_t parent, device_t self, void *aux) 477 { 478 struct sunxi_lcdc_softc * const sc = device_private(self); 479 struct fdt_attach_args * const faa = aux; 480 const int phandle = faa->faa_phandle; 481 struct fdtbus_reset *rst; 482 char intrstr[128]; 483 struct clk *clk; 484 bus_addr_t addr; 485 bus_size_t size; 486 void *ih; 487 488 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 489 aprint_error(": couldn't get registers\n"); 490 return; 491 } 492 493 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 494 aprint_error(": couldn't decode interrupt\n"); 495 return; 496 } 497 498 rst = fdtbus_reset_get(phandle, "lcd"); 499 if (rst == NULL || fdtbus_reset_deassert(rst) != 0) { 500 aprint_error(": couldn't de-assert reset\n"); 501 return; 502 } 503 504 clk = fdtbus_clock_get(phandle, "ahb"); 505 if (clk == NULL || clk_enable(clk) != 0) { 506 aprint_error(": couldn't enable bus clock\n"); 507 return; 508 } 509 510 sc->sc_dev = self; 511 sc->sc_bst = faa->faa_bst; 512 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 513 aprint_error(": couldn't map registers\n"); 514 return; 515 } 516 sc->sc_phandle = faa->faa_phandle; 517 sc->sc_type = of_compatible_lookup(phandle, compat_data)->value; 518 sc->sc_clk_ch[0] = fdtbus_clock_get(phandle, "tcon-ch0"); 519 sc->sc_clk_ch[1] = fdtbus_clock_get(phandle, "tcon-ch1"); 520 521 aprint_naive("\n"); 522 switch (sc->sc_type) { 523 case TYPE_TCON0: 524 aprint_normal(": TCON0\n"); 525 break; 526 case TYPE_TCON1: 527 aprint_normal(": TCON1\n"); 528 break; 529 } 530 531 sc->sc_ports.dp_ep_activate = sunxi_lcdc_ep_activate; 532 sc->sc_ports.dp_ep_get_data = sunxi_lcdc_ep_get_data; 533 fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_ENCODER); 534 535 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, FDT_INTR_MPSAFE, 536 sunxi_lcdc_intr, sc, device_xname(self)); 537 if (ih == NULL) { 538 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 539 intrstr); 540 return; 541 } 542 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 543 } 544 545 CFATTACH_DECL_NEW(sunxi_lcdc, sizeof(struct sunxi_lcdc_softc), 546 sunxi_lcdc_match, sunxi_lcdc_attach, NULL, NULL); 547