1 /* $NetBSD: meson_genfb.c,v 1.1 2019/01/19 21:43:43 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015-2019 Jared 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Generic framebuffer console driver 31 */ 32 33 #include "opt_wsdisplay_compat.h" 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: meson_genfb.c,v 1.1 2019/01/19 21:43:43 jmcneill Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/types.h> 40 #include <sys/systm.h> 41 #include <sys/device.h> 42 #include <sys/conf.h> 43 #include <sys/bus.h> 44 #include <sys/kmem.h> 45 #include <sys/sysctl.h> 46 47 #include <dev/fdt/fdtvar.h> 48 49 #include <arm/amlogic/meson_canvasreg.h> 50 #include <arm/amlogic/meson_vpureg.h> 51 #include <arm/amlogic/meson_hdmireg.h> 52 53 #include <dev/wsfb/genfbvar.h> 54 55 static const char * const compatible[] = { 56 "amlogic,meson8b-fb", 57 NULL 58 }; 59 60 #define AMLOGIC_GENFB_DEFAULT_DEPTH 16 61 62 /* Map CEA-861-D video code (VIC) to framebuffer dimensions */ 63 static const struct meson_genfb_vic2mode { 64 u_int vic; 65 u_int width; 66 u_int height; 67 u_int flags; 68 #define INTERLACE __BIT(0) 69 #define DBLSCAN __BIT(1) 70 } meson_genfb_modes[] = { 71 { 1, 640, 480 }, 72 { 2, 720, 480 }, 73 { 3, 720, 480 }, 74 { 4, 1280, 720 }, 75 { 5, 1920, 1080, INTERLACE }, 76 { 6, 720, 480, DBLSCAN | INTERLACE }, 77 { 7, 720, 480, DBLSCAN | INTERLACE }, 78 { 8, 720, 240, DBLSCAN }, 79 { 9, 720, 240, DBLSCAN }, 80 { 16, 1920, 1080 }, 81 { 17, 720, 576 }, 82 { 18, 720, 576 }, 83 { 19, 1280, 720 }, 84 { 20, 1920, 1080, INTERLACE }, 85 { 31, 1920, 1080 }, 86 { 32, 1920, 1080 }, 87 { 33, 1920, 1080 }, 88 { 34, 1920, 1080 }, 89 { 39, 1920, 1080, INTERLACE }, 90 }; 91 92 struct meson_genfb_softc { 93 struct genfb_softc sc_gen; 94 bus_space_tag_t sc_bst; 95 bus_space_handle_t sc_cav_bsh; 96 bus_space_handle_t sc_hdmi_bsh; 97 bus_space_handle_t sc_vpu_bsh; 98 bus_dma_tag_t sc_dmat; 99 100 kmutex_t sc_lock; 101 102 u_int sc_scale; 103 104 bus_dma_segment_t sc_dmasegs[1]; 105 bus_size_t sc_dmasize; 106 bus_dmamap_t sc_dmamap; 107 void *sc_dmap; 108 109 uint32_t sc_wstype; 110 111 struct sysctllog *sc_sysctllog; 112 int sc_node_scale; 113 }; 114 115 static int meson_genfb_match(device_t, cfdata_t, void *); 116 static void meson_genfb_attach(device_t, device_t, void *); 117 118 static int meson_genfb_ioctl(void *, void *, u_long, void *, int, lwp_t *); 119 static paddr_t meson_genfb_mmap(void *, void *, off_t, int); 120 static bool meson_genfb_shutdown(device_t, int); 121 122 static void meson_genfb_canvas_config(struct meson_genfb_softc *); 123 static void meson_genfb_osd_config(struct meson_genfb_softc *); 124 static void meson_genfb_scaler_config(struct meson_genfb_softc *); 125 126 static void meson_genfb_init(struct meson_genfb_softc *); 127 static int meson_genfb_alloc_videomem(struct meson_genfb_softc *); 128 129 static int meson_genfb_scale_helper(SYSCTLFN_PROTO); 130 131 void meson_genfb_set_console_dev(device_t); 132 void meson_genfb_ddb_trap_callback(int); 133 134 static int meson_genfb_console_phandle = -1; 135 static device_t meson_genfb_console_dev = NULL; 136 137 CFATTACH_DECL_NEW(meson_genfb, sizeof(struct meson_genfb_softc), 138 meson_genfb_match, meson_genfb_attach, NULL, NULL); 139 140 static inline uint32_t 141 meson_genfb_hdmi_read_4(struct meson_genfb_softc *sc, uint32_t addr) 142 { 143 bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr); 144 bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr); 145 return bus_space_read_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_DATA_REG); 146 } 147 148 static __unused inline void 149 meson_genfb_hdmi_write_4(struct meson_genfb_softc *sc, uint32_t addr, 150 uint32_t data) 151 { 152 bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr); 153 bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_ADDR_REG, addr); 154 bus_space_write_4(sc->sc_bst, sc->sc_hdmi_bsh, HDMI_DATA_REG, data); 155 } 156 157 #define HDMI_READ meson_genfb_hdmi_read_4 158 #define HDMI_WRITE meson_genfb_hdmi_write_4 159 160 #define VPU_READ(sc, reg) \ 161 bus_space_read_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg)) 162 #define VPU_WRITE(sc, reg, val) \ 163 bus_space_write_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg), (val)) 164 165 #define CAV_READ(sc, reg) \ 166 bus_space_read_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg)) 167 #define CAV_WRITE(sc, reg, val) \ 168 bus_space_write_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg), (val)) 169 170 static int 171 meson_genfb_match(device_t parent, cfdata_t match, void *aux) 172 { 173 struct fdt_attach_args * const faa = aux; 174 175 return of_match_compatible(faa->faa_phandle, compatible); 176 } 177 178 static void 179 meson_genfb_attach(device_t parent, device_t self, void *aux) 180 { 181 struct meson_genfb_softc *sc = device_private(self); 182 struct fdt_attach_args * const faa = aux; 183 const int phandle = faa->faa_phandle; 184 prop_dictionary_t dict = device_properties(self); 185 static const struct genfb_ops zero_ops; 186 struct genfb_ops ops = zero_ops; 187 bus_addr_t addr[3]; 188 bus_size_t size[3]; 189 190 for (int i = 0; i < 3; i++) { 191 if (fdtbus_get_reg(phandle, i, &addr[i], &size[i]) != 0) { 192 aprint_error(": couldn't get register #%d\n", i); 193 return; 194 } 195 } 196 197 sc->sc_gen.sc_dev = self; 198 sc->sc_bst = faa->faa_bst; 199 sc->sc_dmat = faa->faa_dmat; 200 if (bus_space_map(sc->sc_bst, addr[0], size[0], 0, &sc->sc_cav_bsh) != 0 || 201 bus_space_map(sc->sc_bst, addr[1], size[1], 0, &sc->sc_hdmi_bsh) != 0 || 202 bus_space_map(sc->sc_bst, addr[2], size[2], 0, &sc->sc_vpu_bsh) != 0) { 203 aprint_error(": couldn't map registers\n"); 204 return; 205 } 206 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 207 208 meson_genfb_init(sc); 209 210 sc->sc_wstype = WSDISPLAY_TYPE_MESON; 211 212 aprint_naive("\n"); 213 aprint_normal("\n"); 214 215 genfb_init(&sc->sc_gen); 216 217 if (sc->sc_gen.sc_width == 0 || 218 sc->sc_gen.sc_fbsize == 0) { 219 aprint_normal_dev(self, "disabled\n"); 220 return; 221 } 222 223 pmf_device_register1(self, NULL, NULL, meson_genfb_shutdown); 224 225 #ifdef WSDISPLAY_MULTICONS 226 const bool is_console = true; 227 #else 228 const bool is_console = phandle == meson_genfb_console_phandle; 229 if (is_console) 230 aprint_normal_dev(self, "switching to framebuffer console\n"); 231 #endif 232 prop_dictionary_set_bool(dict, "is_console", is_console); 233 234 memset(&ops, 0, sizeof(ops)); 235 ops.genfb_ioctl = meson_genfb_ioctl; 236 ops.genfb_mmap = meson_genfb_mmap; 237 238 genfb_attach(&sc->sc_gen, &ops); 239 } 240 241 static int 242 meson_genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, lwp_t *l) 243 { 244 struct meson_genfb_softc *sc = v; 245 struct wsdisplayio_bus_id *busid; 246 247 switch (cmd) { 248 case WSDISPLAYIO_GTYPE: 249 *(u_int *)data = sc->sc_wstype; 250 return 0; 251 case WSDISPLAYIO_GET_BUSID: 252 busid = data; 253 busid->bus_type = WSDISPLAYIO_BUS_SOC; 254 return 0; 255 case WSDISPLAYIO_GET_FBINFO: 256 { 257 struct wsdisplayio_fbinfo *fbi = data; 258 struct rasops_info *ri = &sc->sc_gen.vd.active->scr_ri; 259 int ret; 260 261 ret = wsdisplayio_get_fbinfo(ri, fbi); 262 fbi->fbi_flags |= WSFB_VRAM_IS_RAM; 263 return ret; 264 } 265 default: 266 return EPASSTHROUGH; 267 } 268 } 269 270 static paddr_t 271 meson_genfb_mmap(void *v, void *vs, off_t offset, int prot) 272 { 273 struct meson_genfb_softc *sc = v; 274 275 if (offset < 0 || offset >= sc->sc_dmasegs[0].ds_len) 276 return -1; 277 278 return bus_dmamem_mmap(sc->sc_dmat, sc->sc_dmasegs, 1, 279 offset, prot, BUS_DMA_PREFETCHABLE); 280 } 281 282 static bool 283 meson_genfb_shutdown(device_t self, int flags) 284 { 285 genfb_enable_polling(self); 286 return true; 287 } 288 289 static void 290 meson_genfb_canvas_config(struct meson_genfb_softc *sc) 291 { 292 prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev); 293 const paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr; 294 uint32_t datal, datah, addr; 295 u_int width, height, depth; 296 297 prop_dictionary_get_uint32(cfg, "width", &width); 298 prop_dictionary_get_uint32(cfg, "height", &height); 299 prop_dictionary_get_uint32(cfg, "depth", &depth); 300 301 const uint32_t w = (width * (depth/8)) >> 3; 302 const uint32_t h = height; 303 304 datal = CAV_READ(sc, DC_CAV_LUT_DATAL_REG); 305 datah = CAV_READ(sc, DC_CAV_LUT_DATAH_REG); 306 addr = CAV_READ(sc, DC_CAV_LUT_ADDR_REG); 307 308 datal &= ~DC_CAV_LUT_DATAL_WIDTH_L; 309 datal |= __SHIFTIN(w & 7, DC_CAV_LUT_DATAL_WIDTH_L); 310 datal &= ~DC_CAV_LUT_DATAL_FBADDR; 311 datal |= __SHIFTIN(pa >> 3, DC_CAV_LUT_DATAL_FBADDR); 312 CAV_WRITE(sc, DC_CAV_LUT_DATAL_REG, datal); 313 314 datah &= ~DC_CAV_LUT_DATAH_BLKMODE; 315 datah |= __SHIFTIN(DC_CAV_LUT_DATAH_BLKMODE_LINEAR, 316 DC_CAV_LUT_DATAH_BLKMODE); 317 datah &= ~DC_CAV_LUT_DATAH_WIDTH_H; 318 datah |= __SHIFTIN(w >> 3, DC_CAV_LUT_DATAH_WIDTH_H); 319 datah &= ~DC_CAV_LUT_DATAH_HEIGHT; 320 datah |= __SHIFTIN(h, DC_CAV_LUT_DATAH_HEIGHT); 321 CAV_WRITE(sc, DC_CAV_LUT_DATAH_REG, datah); 322 323 addr |= DC_CAV_LUT_ADDR_WR_EN; 324 CAV_WRITE(sc, DC_CAV_LUT_ADDR_REG, addr); 325 } 326 327 static void 328 meson_genfb_osd_config(struct meson_genfb_softc *sc) 329 { 330 prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev); 331 uint32_t cs, tc, w0, w1, w2, w3, w4; 332 u_int width, height, depth; 333 bool interlace_p; 334 335 prop_dictionary_get_uint32(cfg, "width", &width); 336 prop_dictionary_get_uint32(cfg, "height", &height); 337 prop_dictionary_get_uint32(cfg, "depth", &depth); 338 prop_dictionary_get_bool(cfg, "interlace", &interlace_p); 339 340 cs = VPU_READ(sc, VIU_OSD2_CTRL_STAT_REG); 341 cs |= VIU_OSD_CTRL_STAT_ENABLE; 342 cs &= ~VIU_OSD_CTRL_STAT_GLOBAL_ALPHA; 343 cs |= __SHIFTIN(0xff, VIU_OSD_CTRL_STAT_GLOBAL_ALPHA); 344 cs |= VIU_OSD_CTRL_STAT_BLK0_ENABLE; 345 cs &= ~VIU_OSD_CTRL_STAT_BLK1_ENABLE; 346 cs &= ~VIU_OSD_CTRL_STAT_BLK2_ENABLE; 347 cs &= ~VIU_OSD_CTRL_STAT_BLK3_ENABLE; 348 VPU_WRITE(sc, VIU_OSD2_CTRL_STAT_REG, cs); 349 350 tc = __SHIFTIN(0, VIU_OSD_TCOLOR_R) | 351 __SHIFTIN(0, VIU_OSD_TCOLOR_G) | 352 __SHIFTIN(0, VIU_OSD_TCOLOR_B) | 353 __SHIFTIN(255, VIU_OSD_TCOLOR_A); 354 VPU_WRITE(sc, VIU_OSD2_TCOLOR_AG0_REG, tc); 355 356 w0 = VPU_READ(sc, VIU_OSD2_BLK0_CFG_W0_REG); 357 w0 |= VIU_OSD_BLK_CFG_W0_RGB_EN; 358 w0 &= ~VIU_OSD_BLK_CFG_W0_TC_ALPHA_EN; 359 w0 &= ~VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE; 360 w0 &= ~VIU_OSD_BLK_CFG_W0_COLOR_MATRIX; 361 switch (depth) { 362 case 32: 363 w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_32BPP, 364 VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE); 365 w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_ARGB, 366 VIU_OSD_BLK_CFG_W0_COLOR_MATRIX); 367 break; 368 case 24: 369 w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_24BPP, 370 VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE); 371 w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_RGB, 372 VIU_OSD_BLK_CFG_W0_COLOR_MATRIX); 373 break; 374 case 16: 375 w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE_16BPP, 376 VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE); 377 w0 |= __SHIFTIN(VIU_OSD_BLK_CFG_W0_COLOR_MATRIX_RGB565, 378 VIU_OSD_BLK_CFG_W0_COLOR_MATRIX); 379 break; 380 } 381 w0 |= VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN; 382 w0 &= ~VIU_OSD_BLK_CFG_W0_RPT_Y; 383 w0 &= ~VIU_OSD_BLK_CFG_W0_INTERP_CTRL; 384 if (interlace_p) { 385 w0 |= VIU_OSD_BLK_CFG_W0_INTERLACE_EN; 386 } else { 387 w0 &= ~VIU_OSD_BLK_CFG_W0_INTERLACE_EN; 388 } 389 VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W0_REG, w0); 390 391 w1 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W1_X_END) | 392 __SHIFTIN(0, VIU_OSD_BLK_CFG_W1_X_START); 393 w2 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W2_Y_END) | 394 __SHIFTIN(0, VIU_OSD_BLK_CFG_W2_Y_START); 395 w3 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W3_H_END) | 396 __SHIFTIN(0, VIU_OSD_BLK_CFG_W3_H_START); 397 w4 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W4_V_END) | 398 __SHIFTIN(0, VIU_OSD_BLK_CFG_W4_V_START); 399 400 VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W1_REG, w1); 401 VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W2_REG, w2); 402 VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W3_REG, w3); 403 VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W4_REG, w4); 404 } 405 406 static void 407 meson_genfb_scaler_config(struct meson_genfb_softc *sc) 408 { 409 prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev); 410 uint32_t scctl, sci_wh, sco_h, sco_v, hsc, vsc, hps, vps, hip, vip; 411 u_int width, height; 412 bool interlace_p; 413 414 prop_dictionary_get_uint32(cfg, "width", &width); 415 prop_dictionary_get_uint32(cfg, "height", &height); 416 prop_dictionary_get_bool(cfg, "interlace", &interlace_p); 417 418 const u_int scale = sc->sc_scale; 419 const u_int dst_w = (width * scale) / 100; 420 const u_int dst_h = (height * scale) / 100; 421 const u_int margin_w = (width - dst_w) / 2; 422 const u_int margin_h = (height - dst_h) / 2; 423 const bool scale_p = scale != 100; 424 425 VPU_WRITE(sc, VPP_OSD_SC_DUMMY_DATA_REG, 0x00808000); 426 427 scctl = VPU_READ(sc, VPP_OSD_SC_CTRL0_REG); 428 scctl |= VPP_OSD_SC_CTRL0_OSD_SC_PATH_EN; 429 scctl &= ~VPP_OSD_SC_CTRL0_OSD_SC_SEL; 430 scctl |= __SHIFTIN(1, VPP_OSD_SC_CTRL0_OSD_SC_SEL); /* OSD2 */ 431 scctl &= ~VPP_OSD_SC_CTRL0_DEFAULT_ALPHA; 432 scctl |= __SHIFTIN(0, VPP_OSD_SC_CTRL0_DEFAULT_ALPHA); 433 VPU_WRITE(sc, VPP_OSD_SC_CTRL0_REG, scctl); 434 435 sci_wh = __SHIFTIN(width - 1, VPP_OSD_SCI_WH_M1_WIDTH) | 436 __SHIFTIN((height >> interlace_p) - 1, VPP_OSD_SCI_WH_M1_HEIGHT); 437 sco_h = __SHIFTIN(margin_w, VPP_OSD_SCO_H_START) | 438 __SHIFTIN(width - margin_w - 1, VPP_OSD_SCO_H_END); 439 sco_v = __SHIFTIN(margin_h >> interlace_p, VPP_OSD_SCO_V_START) | 440 __SHIFTIN(((height - margin_h) >> interlace_p) - 1, 441 VPP_OSD_SCO_V_END); 442 443 VPU_WRITE(sc, VPP_OSD_SCI_WH_M1_REG, sci_wh); 444 VPU_WRITE(sc, VPP_OSD_SCO_H_REG, sco_h); 445 VPU_WRITE(sc, VPP_OSD_SCO_V_REG, sco_v); 446 447 /* horizontal scaling */ 448 hsc = VPU_READ(sc, VPP_OSD_HSC_CTRL0_REG); 449 if (scale_p) { 450 hsc &= ~VPP_OSD_HSC_CTRL0_BANK_LENGTH; 451 hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_BANK_LENGTH); 452 hsc &= ~VPP_OSD_HSC_CTRL0_INI_RCV_NUM0; 453 hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_INI_RCV_NUM0); 454 hsc &= ~VPP_OSD_HSC_CTRL0_RPT_P0_NUM0; 455 hsc |= __SHIFTIN(1, VPP_OSD_HSC_CTRL0_RPT_P0_NUM0); 456 hsc |= VPP_OSD_HSC_CTRL0_HSCALE_EN; 457 } else { 458 hsc &= ~VPP_OSD_HSC_CTRL0_HSCALE_EN; 459 } 460 VPU_WRITE(sc, VPP_OSD_HSC_CTRL0_REG, hsc); 461 462 /* vertical scaling */ 463 vsc = VPU_READ(sc, VPP_OSD_VSC_CTRL0_REG); 464 if (scale_p) { 465 vsc &= ~VPP_OSD_VSC_CTRL0_BANK_LENGTH; 466 vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_BANK_LENGTH); 467 vsc &= ~VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0; 468 vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0); 469 vsc &= ~VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0; 470 vsc |= __SHIFTIN(1, VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0); 471 vsc &= ~VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0; 472 vsc &= ~VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0; 473 vsc &= ~VPP_OSC_VSC_CTRL0_INTERLACE; 474 if (interlace_p) { 475 /* interlace */ 476 vsc |= VPP_OSC_VSC_CTRL0_INTERLACE; 477 vsc |= __SHIFTIN(6, VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0); 478 vsc |= __SHIFTIN(2, VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0); 479 } 480 vsc |= VPP_OSD_VSC_CTRL0_VSCALE_EN; 481 } else { 482 vsc &= ~VPP_OSD_VSC_CTRL0_VSCALE_EN; 483 } 484 VPU_WRITE(sc, VPP_OSD_VSC_CTRL0_REG, vsc); 485 486 /* free scale enable */ 487 if (scale_p) { 488 const u_int hf_phase_step = ((width << 18) / dst_w) << 6; 489 const u_int vf_phase_step = ((height << 20) / dst_h) << 4; 490 const u_int bot_ini_phase = 491 interlace_p ? ((vf_phase_step / 2) >> 8) : 0; 492 493 hps = VPU_READ(sc, VPP_OSD_HSC_PHASE_STEP_REG); 494 hps &= ~VPP_OSD_HSC_PHASE_STEP_FORMAT; 495 hps |= __SHIFTIN(hf_phase_step, VPP_OSD_HSC_PHASE_STEP_FORMAT); 496 VPU_WRITE(sc, VPP_OSD_HSC_PHASE_STEP_REG, hps); 497 498 hip = VPU_READ(sc, VPP_OSD_HSC_INI_PHASE_REG); 499 hip &= ~VPP_OSD_HSC_INI_PHASE_1; 500 VPU_WRITE(sc, VPP_OSD_HSC_INI_PHASE_REG, hip); 501 502 vps = VPU_READ(sc, VPP_OSD_VSC_PHASE_STEP_REG); 503 vps &= ~VPP_OSD_VSC_PHASE_STEP_FORMAT; 504 vps |= __SHIFTIN(hf_phase_step, VPP_OSD_VSC_PHASE_STEP_FORMAT); 505 VPU_WRITE(sc, VPP_OSD_VSC_PHASE_STEP_REG, vps); 506 507 vip = VPU_READ(sc, VPP_OSD_VSC_INI_PHASE_REG); 508 vip &= ~VPP_OSD_VSC_INI_PHASE_1; 509 vip |= __SHIFTIN(0, VPP_OSD_VSC_INI_PHASE_1); 510 vip &= ~VPP_OSD_VSC_INI_PHASE_0; 511 vip |= __SHIFTIN(bot_ini_phase, VPP_OSD_VSC_INI_PHASE_0); 512 VPU_WRITE(sc, VPP_OSD_VSC_INI_PHASE_REG, vip); 513 } 514 } 515 516 static void 517 meson_genfb_init(struct meson_genfb_softc *sc) 518 { 519 prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev); 520 const struct sysctlnode *node, *devnode; 521 u_int width = 0, height = 0, depth, flags, i, scale = 100; 522 int error; 523 524 /* 525 * Firmware has (maybe) setup HDMI TX for us. Read the VIC from 526 * the HDMI AVI InfoFrame (bits 6:0 in PB4) and map that to a 527 * framebuffer geometry. 528 */ 529 const uint32_t vic = HDMI_READ(sc, HDMITX_AVI_INFO_ADDR + 4) & 0x7f; 530 for (i = 0; i < __arraycount(meson_genfb_modes); i++) { 531 if (meson_genfb_modes[i].vic == vic) { 532 aprint_debug(" [HDMI VIC %d]", vic); 533 width = meson_genfb_modes[i].width; 534 height = meson_genfb_modes[i].height; 535 flags = meson_genfb_modes[i].flags; 536 break; 537 } 538 } 539 if (width == 0 || height == 0) { 540 aprint_error(" [UNSUPPORTED HDMI VIC %d]", vic); 541 return; 542 } 543 544 depth = AMLOGIC_GENFB_DEFAULT_DEPTH; 545 prop_dictionary_get_uint32(cfg, "depth", &depth); 546 switch (depth) { 547 case 16: 548 case 24: 549 break; 550 default: 551 aprint_error_dev(sc->sc_gen.sc_dev, 552 "unsupported depth %d, using %d\n", depth, 553 AMLOGIC_GENFB_DEFAULT_DEPTH); 554 depth = AMLOGIC_GENFB_DEFAULT_DEPTH; 555 break; 556 } 557 prop_dictionary_set_uint8(cfg, "depth", depth); 558 559 const uint32_t fbsize = width * height * (depth / 8); 560 sc->sc_dmasize = (fbsize + 3) & ~3; 561 562 error = meson_genfb_alloc_videomem(sc); 563 if (error) { 564 aprint_error_dev(sc->sc_gen.sc_dev, 565 "failed to allocate %u bytes of video memory: %d\n", 566 (u_int)sc->sc_dmasize, error); 567 return; 568 } 569 570 prop_dictionary_get_uint32(cfg, "scale", &scale); 571 if (scale > 100) { 572 scale = 100; 573 } else if (scale < 10) { 574 scale = 10; 575 } 576 sc->sc_scale = scale; 577 578 prop_dictionary_set_uint32(cfg, "width", width); 579 prop_dictionary_set_uint32(cfg, "height", height); 580 prop_dictionary_set_bool(cfg, "dblscan", !!(flags & DBLSCAN)); 581 prop_dictionary_set_bool(cfg, "interlace", !!(flags & INTERLACE)); 582 prop_dictionary_set_uint16(cfg, "linebytes", width * (depth / 8)); 583 prop_dictionary_set_uint32(cfg, "address", 0); 584 prop_dictionary_set_uint32(cfg, "virtual_address", 585 (uintptr_t)sc->sc_dmap); 586 587 meson_genfb_canvas_config(sc); 588 meson_genfb_osd_config(sc); 589 meson_genfb_scaler_config(sc); 590 591 /* sysctl setup */ 592 error = sysctl_createv(&sc->sc_sysctllog, 0, NULL, &node, 593 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL, 594 NULL, 0, NULL, 0, CTL_HW, CTL_EOL); 595 if (error) 596 goto sysctl_failed; 597 error = sysctl_createv(&sc->sc_sysctllog, 0, &node, &devnode, 598 0, CTLTYPE_NODE, device_xname(sc->sc_gen.sc_dev), NULL, 599 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 600 if (error) 601 goto sysctl_failed; 602 error = sysctl_createv(&sc->sc_sysctllog, 0, &devnode, &node, 603 CTLFLAG_READWRITE, CTLTYPE_INT, "scale", NULL, 604 meson_genfb_scale_helper, 0, (void *)sc, 0, 605 CTL_CREATE, CTL_EOL); 606 if (error) 607 goto sysctl_failed; 608 sc->sc_node_scale = node->sysctl_num; 609 610 return; 611 612 sysctl_failed: 613 aprint_error_dev(sc->sc_gen.sc_dev, 614 "couldn't create sysctl nodes (%d)\n", error); 615 sysctl_teardown(&sc->sc_sysctllog); 616 } 617 618 static int 619 meson_genfb_scale_helper(SYSCTLFN_ARGS) 620 { 621 struct meson_genfb_softc *sc; 622 struct sysctlnode node; 623 int scale, oldscale, error; 624 625 node = *rnode; 626 sc = node.sysctl_data; 627 scale = oldscale = sc->sc_scale; 628 node.sysctl_data = &scale; 629 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 630 if (error || newp == NULL) 631 return error; 632 633 if (scale > 100) { 634 scale = 100; 635 } else if (scale < 10) { 636 scale = 10; 637 } 638 639 if (scale == oldscale) { 640 return 0; 641 } 642 643 mutex_enter(&sc->sc_lock); 644 sc->sc_scale = scale; 645 meson_genfb_scaler_config(sc); 646 mutex_exit(&sc->sc_lock); 647 648 return 0; 649 } 650 651 static int 652 meson_genfb_alloc_videomem(struct meson_genfb_softc *sc) 653 { 654 int error, nsegs; 655 656 error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, 0x1000, 0, 657 sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK); 658 if (error) 659 return error; 660 error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs, 661 sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT); 662 if (error) 663 goto free; 664 error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1, 665 sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap); 666 if (error) 667 goto unmap; 668 error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap, 669 sc->sc_dmasize, NULL, BUS_DMA_WAITOK); 670 if (error) 671 goto destroy; 672 673 memset(sc->sc_dmap, 0, sc->sc_dmasize); 674 675 return 0; 676 677 destroy: 678 bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); 679 unmap: 680 bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize); 681 free: 682 bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs); 683 684 sc->sc_dmasize = 0; 685 sc->sc_dmap = NULL; 686 687 return error; 688 } 689 690 void 691 meson_genfb_set_console_dev(device_t dev) 692 { 693 KASSERT(meson_genfb_console_dev == NULL); 694 meson_genfb_console_dev = dev; 695 } 696 697 void 698 meson_genfb_ddb_trap_callback(int where) 699 { 700 if (meson_genfb_console_dev == NULL) 701 return; 702 703 if (where) { 704 genfb_enable_polling(meson_genfb_console_dev); 705 } else { 706 genfb_disable_polling(meson_genfb_console_dev); 707 } 708 } 709 710 static int 711 meson_genfb_console_match(int phandle) 712 { 713 return of_match_compatible(phandle, compatible); 714 } 715 716 static void 717 meson_genfb_console_consinit(struct fdt_attach_args *faa, u_int uart_freq) 718 { 719 meson_genfb_console_phandle = faa->faa_phandle; 720 genfb_cnattach(); 721 } 722 723 static const struct fdt_console meson_genfb_fdt_console = { 724 .match = meson_genfb_console_match, 725 .consinit = meson_genfb_console_consinit, 726 }; 727 728 FDT_CONSOLE(meson_genfb, &meson_genfb_fdt_console); 729