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