1 /* $NetBSD: sunxi_mixer.c,v 1.21 2026/04/21 10:32:16 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) 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_mixer.c,v 1.21 2026/04/21 10:32:16 jmcneill 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/sysctl.h> 39 #include <sys/systm.h> 40 41 #include <dev/fdt/fdt_port.h> 42 #include <dev/fdt/fdtvar.h> 43 44 #include <arm/sunxi/sunxi_drm.h> 45 46 #include <drm/drm_atomic.h> 47 #include <drm/drm_atomic_helper.h> 48 #include <drm/drm_crtc.h> 49 #include <drm/drm_crtc_helper.h> 50 #include <drm/drm_drv.h> 51 #include <drm/drm_fourcc.h> 52 #include <drm/drm_plane_helper.h> 53 #include <drm/drm_vblank.h> 54 55 #define MIXER_CURSOR_MAXWIDTH 256 56 #define MIXER_CURSOR_MAXHEIGHT 256 57 58 #define SUNXI_MIXER_FREQ 432000000 59 60 #define GLB_BASE 0x00000 61 #define BLD_BASE 0x01000 62 #define OVL_BASE(n) (0x02000 + (n) * 0x1000) 63 #define VSU_BASE 0x20000 64 #define CSC_BASE(n) ((n) == 0 ? 0xaa050 : 0xa0000) 65 66 /* GLB registers */ 67 #define GLB_CTL 0x000 68 #define GLB_CTL_EN __BIT(0) 69 #define GLB_STS 0x004 70 #define GLB_DBUFFER 0x008 71 #define GLB_DBUFFER_DOUBLE_BUFFER_RDY __BIT(0) 72 #define GLB_SIZE 0x00c 73 74 /* BLD registers */ 75 #define BLD_FILL_COLOR_CTL 0x000 76 #define BLD_FILL_COLOR_CTL_P3_EN __BIT(11) 77 #define BLD_FILL_COLOR_CTL_P2_EN __BIT(10) 78 #define BLD_FILL_COLOR_CTL_P1_EN __BIT(9) 79 #define BLD_FILL_COLOR_CTL_P0_EN __BIT(8) 80 #define BLD_FILL_COLOR_CTL_P3_FCEN __BIT(3) 81 #define BLD_FILL_COLOR_CTL_P2_FCEN __BIT(2) 82 #define BLD_FILL_COLOR_CTL_P1_FCEN __BIT(1) 83 #define BLD_FILL_COLOR_CTL_P0_FCEN __BIT(0) 84 #define BLD_FILL_COLOR(n) (0x004 + (n) * 0x10) 85 #define BLD_CH_ISIZE(n) (0x008 + (n) * 0x10) 86 #define BLD_CH_OFFSET(n) (0x00c + (n) * 0x10) 87 #define BLD_CH_RTCTL 0x080 88 #define BLD_CH_RTCTL_P3 __BITS(15,12) 89 #define BLD_CH_RTCTL_P2 __BITS(11,8) 90 #define BLD_CH_RTCTL_P1 __BITS(7,4) 91 #define BLD_CH_RTCTL_P0 __BITS(3,0) 92 #define BLD_SIZE 0x08c 93 #define BLD_CTL(n) (0x090 + (n) * 0x04) 94 95 /* OVL_V registers */ 96 #define OVL_V_ATTCTL(n) (0x000 + (n) * 0x30) 97 #define OVL_V_ATTCTL_VIDEO_UI_SEL __BIT(15) 98 #define OVL_V_ATTCTL_LAY_FBFMT __BITS(12,8) 99 #define OVL_V_ATTCTL_LAY_FBFMT_VYUY 0x00 100 #define OVL_V_ATTCTL_LAY_FBFMT_YVYU 0x01 101 #define OVL_V_ATTCTL_LAY_FBFMT_UYVY 0x02 102 #define OVL_V_ATTCTL_LAY_FBFMT_YUYV 0x03 103 #define OVL_V_ATTCTL_LAY_FBFMT_YUV422 0x06 104 #define OVL_V_ATTCTL_LAY_FBFMT_YUV420 0x0a 105 #define OVL_V_ATTCTL_LAY_FBFMT_YUV411 0x0e 106 #if BYTE_ORDER == BIG_ENDIAN 107 #define OVL_V_ATTCTL_LAY_FBFMT_ARGB_8888 0x03 108 #define OVL_V_ATTCTL_LAY_FBFMT_XRGB_8888 0x07 109 #else 110 #define OVL_V_ATTCTL_LAY_FBFMT_ARGB_8888 0x00 111 #define OVL_V_ATTCTL_LAY_FBFMT_XRGB_8888 0x04 112 #endif 113 #define OVL_V_ATTCTL_LAY0_EN __BIT(0) 114 #define OVL_V_MBSIZE(n) (0x004 + (n) * 0x30) 115 #define OVL_V_COOR(n) (0x008 + (n) * 0x30) 116 #define OVL_V_PITCH0(n) (0x00c + (n) * 0x30) 117 #define OVL_V_PITCH1(n) (0x010 + (n) * 0x30) 118 #define OVL_V_PITCH2(n) (0x014 + (n) * 0x30) 119 #define OVL_V_TOP_LADD0(n) (0x018 + (n) * 0x30) 120 #define OVL_V_TOP_LADD1(n) (0x01c + (n) * 0x30) 121 #define OVL_V_TOP_LADD2(n) (0x020 + (n) * 0x30) 122 #define OVL_V_FILL_COLOR(n) (0x0c0 + (n) * 0x4) 123 #define OVL_V_TOP_HADD0 0x0d0 124 #define OVL_V_TOP_HADD1 0x0d4 125 #define OVL_V_TOP_HADD2 0x0d8 126 #define OVL_V_TOP_HADD_LAYER0 __BITS(7,0) 127 #define OVL_V_SIZE 0x0e8 128 #define OVL_V_HDS_CTL0 0x0f0 129 #define OVL_V_HDS_CTL1 0x0f4 130 #define OVL_V_VDS_CTL0 0x0f8 131 #define OVL_V_VDS_CTL1 0x0fc 132 133 /* OVL_UI registers */ 134 #define OVL_UI_ATTR_CTL(n) (0x000 + (n) * 0x20) 135 #define OVL_UI_ATTR_CTL_LAY_FBFMT __BITS(12,8) 136 #if BYTE_ORDER == BIG_ENDIAN 137 #define OVL_UI_ATTR_CTL_LAY_FBFMT_ARGB_8888 0x03 138 #define OVL_UI_ATTR_CTL_LAY_FBFMT_XRGB_8888 0x07 139 #else 140 #define OVL_UI_ATTR_CTL_LAY_FBFMT_ARGB_8888 0x00 141 #define OVL_UI_ATTR_CTL_LAY_FBFMT_XRGB_8888 0x04 142 #endif 143 #define OVL_UI_ATTR_CTL_LAY_EN __BIT(0) 144 #define OVL_UI_MBSIZE(n) (0x004 + (n) * 0x20) 145 #define OVL_UI_COOR(n) (0x008 + (n) * 0x20) 146 #define OVL_UI_PITCH(n) (0x00c + (n) * 0x20) 147 #define OVL_UI_TOP_LADD(n) (0x010 + (n) * 0x20) 148 #define OVL_UI_FILL_COLOR(n) (0x018 + (n) * 0x20) 149 #define OVL_UI_TOP_HADD 0x080 150 #define OVL_UI_TOP_HADD_LAYER1 __BITS(15,8) 151 #define OVL_UI_TOP_HADD_LAYER0 __BITS(7,0) 152 #define OVL_UI_SIZE 0x088 153 154 /* VSU registers */ 155 #define VS_CTRL_REG 0x000 156 #define VS_CTRL_COEF_SWITCH_EN __BIT(4) 157 #define VS_CTRL_EN __BIT(0) 158 #define VS_STATUS_REG 0x008 159 #define VS_FIELD_CTRL_REG 0x00c 160 #define VS_OUT_SIZE_REG 0x040 161 #define VS_Y_SIZE_REG 0x080 162 #define VS_Y_HSTEP_REG 0x088 163 #define VS_Y_VSTEP_REG 0x08c 164 #define VS_Y_HPHASE_REG 0x090 165 #define VS_Y_VPHASE0_REG 0x098 166 #define VS_Y_VPHASE1_REG 0x09c 167 #define VS_C_SIZE_REG 0x0c0 168 #define VS_C_HSTEP_REG 0x0c8 169 #define VS_C_VSTEP_REG 0x0cc 170 #define VS_C_HPHASE_REG 0x0d0 171 #define VS_C_VPHASE0_REG 0x0d8 172 #define VS_C_VPHASE1_REG 0x0dc 173 #define VS_Y_HCOEF0_REG(n) (0x200 + (n) * 0x4) 174 #define VS_Y_HCOEF1_REG(n) (0x300 + (n) * 0x4) 175 #define VS_Y_VCOEF_REG(n) (0x400 + (n) * 0x4) 176 #define VS_C_HCOEF0_REG(n) (0x600 + (n) * 0x4) 177 #define VS_C_HCOEF1_REG(n) (0x700 + (n) * 0x4) 178 #define VS_C_VCOEF_REG(n) (0x800 + (n) * 0x4) 179 180 /* CSC registers */ 181 #define CSC_BYPASS_REG 0x000 182 #define CSC_BYPASS_DISABLE __BIT(0) 183 #define CSC_COEFF0_REG(n) (0x10 + 0x10 * (n)) 184 #define GLB_ALPHA_REG 0x040 185 186 enum { 187 MIXER_PORT_OUTPUT = 1, 188 }; 189 190 struct sunxi_mixer_compat_data { 191 uint8_t ovl_ui_count; 192 uint8_t mixer_index; 193 }; 194 195 struct sunxi_mixer_compat_data mixer0_data = { 196 .ovl_ui_count = 3, 197 .mixer_index = 0, 198 }; 199 200 struct sunxi_mixer_compat_data mixer1_data = { 201 .ovl_ui_count = 1, 202 .mixer_index = 1, 203 }; 204 205 static const struct device_compatible_entry compat_data[] = { 206 { .compat = "allwinner,sun8i-h3-de2-mixer-0", 207 .data = &mixer0_data }, 208 { .compat = "allwinner,sun8i-v3s-de2-mixer", 209 .data = &mixer0_data }, 210 { .compat = "allwinner,sun50i-a64-de2-mixer-0", 211 .data = &mixer0_data }, 212 { .compat = "allwinner,sun50i-a64-de2-mixer-1", 213 .data = &mixer1_data }, 214 215 DEVICE_COMPAT_EOL 216 }; 217 218 struct sunxi_mixer_softc; 219 220 struct sunxi_mixer_crtc { 221 struct drm_crtc base; 222 struct sunxi_mixer_softc *sc; 223 }; 224 225 struct sunxi_mixer_plane { 226 struct drm_plane base; 227 struct sunxi_mixer_softc *sc; 228 }; 229 230 struct sunxi_mixer_softc { 231 device_t sc_dev; 232 bus_space_tag_t sc_bst; 233 bus_space_handle_t sc_bsh; 234 int sc_phandle; 235 236 u_int sc_ovl_ui_count; 237 238 struct sunxi_mixer_crtc sc_crtc; 239 struct sunxi_mixer_plane sc_plane; 240 241 struct fdt_device_ports sc_ports; 242 }; 243 244 #define GLB_READ(sc, reg) \ 245 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, GLB_BASE + (reg)) 246 #define GLB_WRITE(sc, reg, val) \ 247 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, GLB_BASE + (reg), (val)) 248 249 #define BLD_READ(sc, reg) \ 250 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, BLD_BASE + (reg)) 251 #define BLD_WRITE(sc, reg, val) \ 252 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, BLD_BASE + (reg), (val)) 253 254 #define OVL_V_READ(sc, reg) \ 255 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE(0) + (reg)) 256 #define OVL_V_WRITE(sc, reg, val) \ 257 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE(0) + (reg), (val)) 258 259 #define OVL_UI_READ(sc, n, reg) \ 260 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE((n) + 1) + (reg)) 261 #define OVL_UI_WRITE(sc, n, reg, val) \ 262 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, OVL_BASE((n) + 1) + (reg), (val)) 263 264 #define VSU_READ(sc, reg) \ 265 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, VSU_BASE + (reg)) 266 #define VSU_WRITE(sc, reg, val) \ 267 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, VSU_BASE + (reg), (val)) 268 269 #define CSC_READ(sc, n, reg) \ 270 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, CSC_BASE(n) + (reg)) 271 #define CSC_WRITE(sc, n, reg, val) \ 272 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, CSC_BASE(n) + (reg), (val)) 273 274 #define to_sunxi_mixer_crtc(x) container_of(x, struct sunxi_mixer_crtc, base) 275 #define to_sunxi_mixer_plane(x) container_of(x, struct sunxi_mixer_plane, base) 276 277 static int 278 sunxi_mixer_plane_atomic_check(struct drm_plane *plane, 279 struct drm_plane_state *state) 280 { 281 struct drm_crtc_state *crtc_state; 282 283 if (state->crtc == NULL) { 284 return 0; 285 } 286 287 crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc); 288 if (IS_ERR(crtc_state)) { 289 return PTR_ERR(crtc_state); 290 } 291 292 return drm_atomic_helper_check_plane_state(state, crtc_state, 293 DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, 294 false, true); 295 } 296 297 static void 298 sunxi_mixer_plane_atomic_update(struct drm_plane *plane, 299 struct drm_plane_state *old_state) 300 { 301 struct sunxi_mixer_plane *mixer_plane = to_sunxi_mixer_plane(plane); 302 struct sunxi_mixer_softc * const sc = mixer_plane->sc; 303 struct sunxi_drm_framebuffer *sfb = 304 to_sunxi_drm_framebuffer(plane->state->fb); 305 uint32_t block_h, block_w, x, y, block_start_y, num_hblocks; 306 uint32_t val; 307 308 block_h = drm_format_info_block_height(sfb->base.format, 0); 309 block_w = drm_format_info_block_width(sfb->base.format, 0); 310 x = plane->state->src_x >> 16; 311 y = plane->state->src_y >> 16; 312 block_start_y = (y / block_h) * block_h; 313 num_hblocks = x / block_w; 314 315 uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr; 316 317 paddr += block_start_y * sfb->base.pitches[0]; 318 paddr += sfb->base.format->char_per_block[0] * num_hblocks; 319 320 uint32_t haddr = (paddr >> 32) & OVL_UI_TOP_HADD_LAYER0; 321 uint32_t laddr = paddr & 0xffffffff; 322 323 /* Enable UI overlay */ 324 val = OVL_UI_ATTR_CTL_LAY_EN | 325 __SHIFTIN(OVL_UI_ATTR_CTL_LAY_FBFMT_XRGB_8888, 326 OVL_UI_ATTR_CTL_LAY_FBFMT); 327 OVL_UI_WRITE(sc, 0, OVL_UI_ATTR_CTL(0), val); 328 329 /* Set UI overlay line size */ 330 OVL_UI_WRITE(sc, 0, OVL_UI_PITCH(0), sfb->base.pitches[0]); 331 332 /* Framebuffer start address */ 333 val = OVL_UI_READ(sc, 0, OVL_UI_TOP_HADD); 334 val &= ~OVL_UI_TOP_HADD_LAYER0; 335 val |= __SHIFTIN(haddr, OVL_UI_TOP_HADD_LAYER0); 336 OVL_UI_WRITE(sc, 0, OVL_UI_TOP_HADD, val); 337 OVL_UI_WRITE(sc, 0, OVL_UI_TOP_LADD(0), laddr); 338 339 /* Commit settings */ 340 GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY); 341 } 342 343 static void 344 sunxi_mixer_plane_atomic_disable(struct drm_plane *plane, 345 struct drm_plane_state *state) 346 { 347 struct sunxi_mixer_plane *mixer_plane = to_sunxi_mixer_plane(plane); 348 struct sunxi_mixer_softc * const sc = mixer_plane->sc; 349 350 /* Disable UI overlay */ 351 OVL_UI_WRITE(sc, 0, OVL_UI_ATTR_CTL(0), 0); 352 } 353 354 static const struct drm_plane_helper_funcs sunxi_mixer_plane_helper_funcs = { 355 .atomic_check = sunxi_mixer_plane_atomic_check, 356 .atomic_update = sunxi_mixer_plane_atomic_update, 357 .atomic_disable = sunxi_mixer_plane_atomic_disable, 358 }; 359 360 static bool 361 sunxi_mixer_format_mod_supported(struct drm_plane *plane, uint32_t format, 362 uint64_t modifier) 363 { 364 return modifier == DRM_FORMAT_MOD_LINEAR; 365 } 366 367 static const struct drm_plane_funcs sunxi_mixer_plane_funcs = { 368 .update_plane = drm_atomic_helper_update_plane, 369 .disable_plane = drm_atomic_helper_disable_plane, 370 .destroy = drm_plane_cleanup, 371 .reset = drm_atomic_helper_plane_reset, 372 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 373 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 374 .format_mod_supported = sunxi_mixer_format_mod_supported, 375 }; 376 377 static int 378 sunxi_mixer_crtc_atomic_check(struct drm_crtc *crtc, 379 struct drm_crtc_state *state) 380 { 381 bool enabled = state->plane_mask & drm_plane_mask(crtc->primary); 382 383 if (enabled != state->enable) { 384 return -EINVAL; 385 } 386 387 return drm_atomic_add_affected_planes(state->state, crtc); 388 } 389 390 static void 391 sunxi_mixer_crtc_atomic_enable(struct drm_crtc *crtc, 392 struct drm_crtc_state *state) 393 { 394 struct sunxi_mixer_crtc *mixer_crtc = to_sunxi_mixer_crtc(crtc); 395 struct sunxi_mixer_softc * const sc = mixer_crtc->sc; 396 struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; 397 uint32_t val; 398 399 const uint32_t size = ((adjusted_mode->vdisplay - 1) << 16) | 400 (adjusted_mode->hdisplay - 1); 401 402 /* Set global size */ 403 GLB_WRITE(sc, GLB_SIZE, size); 404 405 /* Enable pipe 0 */ 406 val = BLD_READ(sc, BLD_FILL_COLOR_CTL); 407 val |= BLD_FILL_COLOR_CTL_P0_EN; 408 BLD_WRITE(sc, BLD_FILL_COLOR_CTL, val); 409 410 /* Set blender 0 input size */ 411 BLD_WRITE(sc, BLD_CH_ISIZE(0), size); 412 /* Set blender 0 offset */ 413 BLD_WRITE(sc, BLD_CH_OFFSET(0), 0); 414 /* Route channel 1 to pipe 0 */ 415 val = BLD_READ(sc, BLD_CH_RTCTL); 416 val &= ~BLD_CH_RTCTL_P0; 417 val |= __SHIFTIN(1, BLD_CH_RTCTL_P0); 418 BLD_WRITE(sc, BLD_CH_RTCTL, val); 419 /* Set blender output size */ 420 BLD_WRITE(sc, BLD_SIZE, size); 421 422 /* Set UI overlay layer size */ 423 OVL_UI_WRITE(sc, 0, OVL_UI_MBSIZE(0), size); 424 /* Set UI overlay offset */ 425 OVL_UI_WRITE(sc, 0, OVL_UI_COOR(0), 0); 426 /* Set UI overlay window size */ 427 OVL_UI_WRITE(sc, 0, OVL_UI_SIZE, size); 428 429 /* Commit settings */ 430 GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY); 431 432 drm_crtc_vblank_on(crtc); 433 434 if (drm_crtc_index(crtc) == 0) { 435 pmf_event_inject(NULL, PMFE_DISPLAY_ON); 436 } 437 } 438 439 static void 440 sunxi_mixer_crtc_atomic_disable(struct drm_crtc *crtc, 441 struct drm_crtc_state *state) 442 { 443 drm_crtc_vblank_off(crtc); 444 445 if (crtc->state->event && !crtc->state->active) { 446 spin_lock(&crtc->dev->event_lock); 447 drm_crtc_send_vblank_event(crtc, crtc->state->event); 448 spin_unlock(&crtc->dev->event_lock); 449 450 crtc->state->event = NULL; 451 } 452 453 if (drm_crtc_index(crtc) == 0) { 454 pmf_event_inject(NULL, PMFE_DISPLAY_OFF); 455 } 456 } 457 458 static void 459 sunxi_mixer_crtc_atomic_flush(struct drm_crtc *crtc, 460 struct drm_crtc_state *state) 461 { 462 struct sunxi_mixer_crtc *mixer_crtc = to_sunxi_mixer_crtc(crtc); 463 struct sunxi_mixer_softc * const sc = mixer_crtc->sc; 464 int ret; 465 466 /* Commit settings */ 467 GLB_WRITE(sc, GLB_DBUFFER, GLB_DBUFFER_DOUBLE_BUFFER_RDY); 468 469 /* 470 * If caller wants a vblank event, tell the vblank interrupt 471 * handler to send it on the next interrupt. 472 */ 473 spin_lock(&crtc->dev->event_lock); 474 if (crtc->state->event) { 475 struct sunxi_drm_softc *drm_sc = device_private(crtc->dev->dev); 476 const int crtc_index = drm_crtc_index(crtc); 477 struct sunxi_drm_vblank *vbl = &drm_sc->sc_vbl[crtc_index]; 478 479 if ((ret = drm_crtc_vblank_get_locked(crtc)) != 0) { 480 aprint_error_dev(sc->sc_dev, 481 "drm_crtc_vblank_get: %d\n", ret); 482 } 483 if (vbl->event) { /* XXX leaky; KASSERT? */ 484 aprint_error_dev(sc->sc_dev, "unfinished vblank\n"); 485 } 486 vbl->event = crtc->state->event; 487 crtc->state->event = NULL; 488 } 489 spin_unlock(&crtc->dev->event_lock); 490 } 491 492 static int 493 sunxi_mixer_crtc_enable_vblank(struct drm_crtc *crtc) 494 { 495 struct drm_device *ddev = crtc->dev; 496 struct sunxi_drm_softc *drm_sc = device_private(ddev->dev); 497 const int crtc_index = drm_crtc_index(crtc); 498 499 if (drm_sc->sc_vbl[crtc_index].enable_vblank != NULL) { 500 drm_sc->sc_vbl[crtc_index].enable_vblank( 501 drm_sc->sc_vbl[crtc_index].priv); 502 } 503 504 return 0; 505 } 506 507 static void 508 sunxi_mixer_crtc_disable_vblank(struct drm_crtc *crtc) 509 { 510 struct drm_device *ddev = crtc->dev; 511 struct sunxi_drm_softc *drm_sc = device_private(ddev->dev); 512 const int crtc_index = drm_crtc_index(crtc); 513 514 if (drm_sc->sc_vbl[crtc_index].disable_vblank != NULL) { 515 drm_sc->sc_vbl[crtc_index].disable_vblank( 516 drm_sc->sc_vbl[crtc_index].priv); 517 } 518 } 519 520 static const struct drm_crtc_helper_funcs sunxi_mixer_crtc_helper_funcs = { 521 .atomic_check = sunxi_mixer_crtc_atomic_check, 522 .atomic_enable = sunxi_mixer_crtc_atomic_enable, 523 .atomic_disable = sunxi_mixer_crtc_atomic_disable, 524 .atomic_flush = sunxi_mixer_crtc_atomic_flush, 525 }; 526 527 static const struct drm_crtc_funcs sunxi_mixer_crtc_funcs = { 528 .set_config = drm_atomic_helper_set_config, 529 .destroy = drm_crtc_cleanup, 530 .page_flip = drm_atomic_helper_page_flip, 531 .reset = drm_atomic_helper_crtc_reset, 532 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 533 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 534 .enable_vblank = sunxi_mixer_crtc_enable_vblank, 535 .disable_vblank = sunxi_mixer_crtc_disable_vblank, 536 }; 537 538 static uint32_t sunxi_mixer_plane_formats[] = { 539 DRM_FORMAT_XRGB8888, 540 }; 541 542 static const uint64_t sunxi_mixer_plane_modifiers[] = { 543 DRM_FORMAT_MOD_LINEAR, 544 DRM_FORMAT_MOD_INVALID, 545 }; 546 547 static int 548 sunxi_mixer_ep_activate(device_t dev, struct fdt_endpoint *ep, bool activate) 549 { 550 struct sunxi_mixer_softc * const sc = device_private(dev); 551 struct drm_plane *plane, *cursor; 552 struct drm_device *ddev; 553 struct sunxi_drm_softc *drm_sc; 554 bus_size_t reg; 555 556 if (!activate) 557 return EINVAL; 558 559 ddev = sunxi_drm_endpoint_device(ep); 560 if (ddev == NULL) { 561 DRM_ERROR("couldn't find DRM device\n"); 562 return ENXIO; 563 } 564 drm_sc = device_private(ddev->dev); 565 566 sc->sc_crtc.sc = sc; 567 sc->sc_plane.sc = sc; 568 569 /* Initialize registers */ 570 for (reg = 0; reg < 0xc000; reg += 4) 571 bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, 0); 572 BLD_WRITE(sc, BLD_CTL(0), 0x03010301); 573 BLD_WRITE(sc, BLD_CTL(1), 0x03010301); 574 BLD_WRITE(sc, BLD_CTL(2), 0x03010301); 575 BLD_WRITE(sc, BLD_CTL(3), 0x03010301); 576 577 /* RT enable */ 578 GLB_WRITE(sc, GLB_CTL, GLB_CTL_EN); 579 580 drm_universal_plane_init(ddev, &sc->sc_plane.base, 581 0, &sunxi_mixer_plane_funcs, 582 sunxi_mixer_plane_formats, __arraycount(sunxi_mixer_plane_formats), 583 sunxi_mixer_plane_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL); 584 drm_plane_helper_add(&sc->sc_plane.base, &sunxi_mixer_plane_helper_funcs); 585 plane = &sc->sc_plane.base; 586 587 /* TODO: hardware cursor support */ 588 cursor = NULL; 589 590 drm_crtc_init_with_planes(ddev, &sc->sc_crtc.base, plane, cursor, 591 &sunxi_mixer_crtc_funcs, NULL); 592 drm_crtc_helper_add(&sc->sc_crtc.base, &sunxi_mixer_crtc_helper_funcs); 593 594 sc->sc_plane.base.possible_crtcs = 1 << drm_crtc_index(&sc->sc_crtc.base); 595 596 drm_sc->sc_vbl[drm_crtc_index(&sc->sc_crtc.base)].crtc = &sc->sc_crtc.base; 597 598 return fdt_endpoint_activate(ep, activate); 599 } 600 601 static void * 602 sunxi_mixer_ep_get_data(device_t dev, struct fdt_endpoint *ep) 603 { 604 struct sunxi_mixer_softc * const sc = device_private(dev); 605 606 return &sc->sc_crtc; 607 } 608 609 static int 610 sunxi_mixer_match(device_t parent, cfdata_t cf, void *aux) 611 { 612 struct fdt_attach_args * const faa = aux; 613 614 return of_compatible_match(faa->faa_phandle, compat_data); 615 } 616 617 static void 618 sunxi_mixer_attach(device_t parent, device_t self, void *aux) 619 { 620 struct sunxi_mixer_softc * const sc = device_private(self); 621 struct fdt_attach_args * const faa = aux; 622 struct fdt_endpoint *out_ep; 623 const int phandle = faa->faa_phandle; 624 const struct sunxi_mixer_compat_data * const cd = 625 of_compatible_lookup(phandle, compat_data)->data; 626 struct clk *clk_bus, *clk_mod; 627 struct fdtbus_reset *rst; 628 bus_addr_t addr; 629 bus_size_t size; 630 631 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 632 aprint_error(": couldn't get registers\n"); 633 return; 634 } 635 636 rst = fdtbus_reset_get_index(phandle, 0); 637 if (rst == NULL || fdtbus_reset_deassert(rst) != 0) { 638 aprint_error(": couldn't de-assert reset\n"); 639 return; 640 } 641 642 clk_bus = fdtbus_clock_get(phandle, "bus"); 643 if (clk_bus == NULL || clk_enable(clk_bus) != 0) { 644 aprint_error(": couldn't enable bus clock\n"); 645 return; 646 } 647 648 clk_mod = fdtbus_clock_get(phandle, "mod"); 649 if (clk_mod == NULL || 650 clk_set_rate(clk_mod, SUNXI_MIXER_FREQ) != 0 || 651 clk_enable(clk_mod) != 0) { 652 aprint_error(": couldn't enable mod clock\n"); 653 return; 654 } 655 656 sc->sc_dev = self; 657 sc->sc_bst = faa->faa_bst; 658 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 659 aprint_error(": couldn't map registers\n"); 660 return; 661 } 662 sc->sc_phandle = faa->faa_phandle; 663 sc->sc_ovl_ui_count = cd->ovl_ui_count; 664 665 aprint_naive("\n"); 666 aprint_normal(": Display Engine Mixer\n"); 667 668 sc->sc_ports.dp_ep_activate = sunxi_mixer_ep_activate; 669 sc->sc_ports.dp_ep_get_data = sunxi_mixer_ep_get_data; 670 fdt_ports_register(&sc->sc_ports, self, phandle, EP_DRM_CRTC); 671 672 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, 673 MIXER_PORT_OUTPUT, cd->mixer_index); 674 if (out_ep == NULL) { 675 /* Couldn't find new-style DE2 endpoint, try old style. */ 676 out_ep = fdt_endpoint_get_from_index(&sc->sc_ports, 677 MIXER_PORT_OUTPUT, 0); 678 } 679 680 if (out_ep != NULL) 681 sunxi_drm_register_endpoint(phandle, out_ep); 682 } 683 684 CFATTACH_DECL_NEW(sunxi_mixer, sizeof(struct sunxi_mixer_softc), 685 sunxi_mixer_match, sunxi_mixer_attach, NULL, NULL); 686