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