Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch Use the hardware scaler to do overscan compensation...



details:   https://anonhg.NetBSD.org/src/rev/21db86b58f69
branches:  trunk
changeset: 336827:21db86b58f69
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sun Mar 22 13:53:33 2015 +0000

description:
Use the hardware scaler to do overscan compensation. You can set the
scaling value as a percentage in two ways -- either as a kernel cmdline
parameter (fb.scale=<pct>) or at runtime with sysctl (hw.genfb0.scale=<pct>).

Setting scale=100 disables the scaler, any other value enables it. For
the cheap TV on my desk, scale=95 gives me a fully visible framebuffer.

diffstat:

 sys/arch/arm/amlogic/amlogic_genfb.c      |  372 ++++++++++++++++++++++++-----
 sys/arch/arm/amlogic/amlogic_vpureg.h     |   52 ++++-
 sys/arch/evbarm/amlogic/amlogic_machdep.c |    9 +-
 3 files changed, 358 insertions(+), 75 deletions(-)

diffs (truncated from 583 to 300 lines):

diff -r da63d88b7bec -r 21db86b58f69 sys/arch/arm/amlogic/amlogic_genfb.c
--- a/sys/arch/arm/amlogic/amlogic_genfb.c      Sun Mar 22 12:48:18 2015 +0000
+++ b/sys/arch/arm/amlogic/amlogic_genfb.c      Sun Mar 22 13:53:33 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: amlogic_genfb.c,v 1.1 2015/03/21 01:17:00 jmcneill Exp $ */
+/* $NetBSD: amlogic_genfb.c,v 1.2 2015/03/22 13:53:33 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: amlogic_genfb.c,v 1.1 2015/03/21 01:17:00 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: amlogic_genfb.c,v 1.2 2015/03/22 13:53:33 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -40,6 +40,7 @@
 #include <sys/conf.h>
 #include <sys/bus.h>
 #include <sys/kmem.h>
+#include <sys/sysctl.h>
 
 #include <arm/amlogic/amlogic_reg.h>
 #include <arm/amlogic/amlogic_var.h>
@@ -60,6 +61,10 @@
        { 3, 720, 480 },
        { 4, 1280, 720 },
        { 5, 1920, 1080 },
+       { 6, 720, 480 },
+       { 7, 720, 480 },
+       { 8, 720, 240 },
+       { 9, 720, 240 },
        { 16, 1920, 1080 },
        { 17, 720, 576 },
        { 18, 720, 576 },
@@ -75,17 +80,24 @@
 struct amlogic_genfb_softc {
        struct genfb_softc      sc_gen;
        bus_space_tag_t         sc_bst;
-       bus_space_handle_t      sc_bsh;
+       bus_space_handle_t      sc_cav_bsh;
        bus_space_handle_t      sc_hdmi_bsh;
        bus_space_handle_t      sc_vpu_bsh;
        bus_dma_tag_t           sc_dmat;
 
+       kmutex_t                sc_lock;
+
+       u_int                   sc_scale;
+
        bus_dma_segment_t       sc_dmasegs[1];
        bus_size_t              sc_dmasize;
        bus_dmamap_t            sc_dmamap;
        void                    *sc_dmap;
 
        uint32_t                sc_wstype;
+
+       struct sysctllog        *sc_sysctllog;
+       int                     sc_node_scale;
 };
 
 static int     amlogic_genfb_match(device_t, cfdata_t, void *);
@@ -95,9 +107,15 @@
 static paddr_t amlogic_genfb_mmap(void *, void *, off_t, int);
 static bool    amlogic_genfb_shutdown(device_t, int);
 
-static void    amlogic_genfb_probe(struct amlogic_genfb_softc *);
+static void    amlogic_genfb_canvas_config(struct amlogic_genfb_softc *);
+static void    amlogic_genfb_osd_config(struct amlogic_genfb_softc *);
+static void    amlogic_genfb_scaler_config(struct amlogic_genfb_softc *);
+
+static void    amlogic_genfb_init(struct amlogic_genfb_softc *);
 static int     amlogic_genfb_alloc_videomem(struct amlogic_genfb_softc *);
 
+static int     amlogic_genfb_scale_helper(SYSCTLFN_PROTO);
+
 void           amlogic_genfb_set_console_dev(device_t);
 void           amlogic_genfb_ddb_trap_callback(int);
 
@@ -131,6 +149,11 @@
 #define VPU_WRITE(sc, reg, val) \
     bus_space_write_4((sc)->sc_bst, (sc)->sc_vpu_bsh, (reg), (val))
 
+#define CAV_READ(sc, reg) \
+    bus_space_read_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg))
+#define CAV_WRITE(sc, reg, val) \
+    bus_space_write_4((sc)->sc_bst, (sc)->sc_cav_bsh, (reg), (val))
+
 static int
 amlogic_genfb_match(device_t parent, cfdata_t match, void *aux)
 {
@@ -152,13 +175,14 @@
        sc->sc_bst = aio->aio_core_bst;
        sc->sc_dmat = aio->aio_dmat;
        bus_space_subregion(aio->aio_core_bst, aio->aio_bsh,
-           loc->loc_offset, loc->loc_size, &sc->sc_bsh);
+           loc->loc_offset, loc->loc_size, &sc->sc_cav_bsh);
        bus_space_subregion(aio->aio_core_bst, aio->aio_bsh,
            AMLOGIC_HDMI_OFFSET, AMLOGIC_HDMI_SIZE, &sc->sc_hdmi_bsh);
        bus_space_subregion(aio->aio_core_bst, aio->aio_bsh,
            AMLOGIC_VPU_OFFSET, AMLOGIC_VPU_SIZE, &sc->sc_vpu_bsh);
+       mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
 
-       amlogic_genfb_probe(sc);
+       amlogic_genfb_init(sc);
 
        sc->sc_wstype = WSDISPLAY_TYPE_MESON;
        prop_dictionary_get_bool(dict, "is_console", &is_console);
@@ -236,40 +260,207 @@
 }
 
 static void
-amlogic_genfb_probe(struct amlogic_genfb_softc *sc)
+amlogic_genfb_canvas_config(struct amlogic_genfb_softc *sc)
+{
+       prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
+       const paddr_t pa = sc->sc_dmamap->dm_segs[0].ds_addr;
+       uint32_t datal, datah, addr;
+       u_int width, height;
+
+       prop_dictionary_get_uint32(cfg, "width", &width);
+       prop_dictionary_get_uint32(cfg, "height", &height);
+
+       const uint32_t w = (width * 3) >> 3;
+       const uint32_t h = height;
+
+       datal = CAV_READ(sc, DC_CAV_LUT_DATAL_REG);
+       datah = CAV_READ(sc, DC_CAV_LUT_DATAH_REG);
+       addr = CAV_READ(sc, DC_CAV_LUT_ADDR_REG);
+
+       datal &= ~DC_CAV_LUT_DATAL_WIDTH_L;
+       datal |= __SHIFTIN(w & 7, DC_CAV_LUT_DATAL_WIDTH_L);
+       datal &= ~DC_CAV_LUT_DATAL_FBADDR;
+       datal |= __SHIFTIN(pa >> 3, DC_CAV_LUT_DATAL_FBADDR);
+       CAV_WRITE(sc, DC_CAV_LUT_DATAL_REG, datal);
+
+       datah &= ~DC_CAV_LUT_DATAH_BLKMODE;
+       datah |= __SHIFTIN(DC_CAV_LUT_DATAH_BLKMODE_LINEAR,
+                          DC_CAV_LUT_DATAH_BLKMODE);
+       datah &= ~DC_CAV_LUT_DATAH_WIDTH_H;
+       datah |= __SHIFTIN(w >> 3, DC_CAV_LUT_DATAH_WIDTH_H);
+       datah &= ~DC_CAV_LUT_DATAH_HEIGHT;
+       datah |= __SHIFTIN(h, DC_CAV_LUT_DATAH_HEIGHT);
+       CAV_WRITE(sc, DC_CAV_LUT_DATAH_REG, datah);
+
+       addr |= DC_CAV_LUT_ADDR_WR_EN;
+       CAV_WRITE(sc, DC_CAV_LUT_ADDR_REG, addr);
+}
+
+static void
+amlogic_genfb_osd_config(struct amlogic_genfb_softc *sc)
+{
+       prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
+       uint32_t w0, w1, w2, w3, w4;
+       u_int width, height;
+
+       prop_dictionary_get_uint32(cfg, "width", &width);
+       prop_dictionary_get_uint32(cfg, "height", &height);
+
+       w0 = VPU_READ(sc, VIU_OSD2_BLK0_CFG_W0_REG);
+       w0 &= ~VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE;
+       w0 |= __SHIFTIN(7, VIU_OSD_BLK_CFG_W0_OSD_BLK_MODE);
+       w0 |= VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN;
+       w0 &= ~VIU_OSD_BLK_CFG_W0_INTERP_CTRL;
+       w0 &= ~VIU_OSD_BLK_CFG_W0_INTERLACE_EN;
+       w0 |= VIU_OSD_BLK_CFG_W0_RGB_EN;
+       w0 &= ~VIU_OSD_BLK_CFG_W0_COLOR_MATRIX;
+       VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W0_REG, w0);
+
+       w1 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W1_X_END) |
+            __SHIFTIN(0, VIU_OSD_BLK_CFG_W1_X_START);
+       w2 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W2_Y_END) |
+            __SHIFTIN(0, VIU_OSD_BLK_CFG_W2_Y_START);
+       w3 = __SHIFTIN(width - 1, VIU_OSD_BLK_CFG_W3_H_END) |
+            __SHIFTIN(0, VIU_OSD_BLK_CFG_W3_H_START);
+       w4 = __SHIFTIN(height - 1, VIU_OSD_BLK_CFG_W4_V_END) |
+            __SHIFTIN(0, VIU_OSD_BLK_CFG_W4_V_START);
+
+       VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W1_REG, w1);
+       VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W2_REG, w2);
+       VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W3_REG, w3);
+       VPU_WRITE(sc, VIU_OSD2_BLK0_CFG_W4_REG, w4);
+}
+
+static void
+amlogic_genfb_scaler_config(struct amlogic_genfb_softc *sc)
 {
        prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);
-       u_int width = 0, height = 0, i;
-       uint32_t datal, datah, addr;
-       uint32_t w1, w2, w3, w4;
+       uint32_t scctl, sci_wh, sco_h, sco_v, hsc, vsc, hps, vps, hip, vip;
+       u_int width, height;
+
+       prop_dictionary_get_uint32(cfg, "width", &width);
+       prop_dictionary_get_uint32(cfg, "height", &height);
+
+       const u_int scale = sc->sc_scale;
+       const u_int dst_w = (width * scale) / 100;
+       const u_int dst_h = (height * scale) / 100;
+       const u_int margin_w = (width - dst_w) / 2;
+       const u_int margin_h = (height - dst_h) / 2;
+       const bool interlace_p = false; /* TODO */
+       const bool scale_p = scale != 100;
+
+       VPU_WRITE(sc, VPP_OSD_SC_DUMMY_DATA_REG, 0x00808000);
+
+       scctl = VPU_READ(sc, VPP_OSD_SC_CTRL0_REG);
+       scctl |= VPP_OSD_SC_CTRL0_OSD_SC_PATH_EN;
+       scctl &= ~VPP_OSD_SC_CTRL0_OSD_SC_SEL;
+       scctl |= __SHIFTIN(1, VPP_OSD_SC_CTRL0_OSD_SC_SEL); /* OSD2 */
+       scctl &= ~VPP_OSD_SC_CTRL0_DEFAULT_ALPHA;
+       scctl |= __SHIFTIN(0, VPP_OSD_SC_CTRL0_DEFAULT_ALPHA);
+       VPU_WRITE(sc, VPP_OSD_SC_CTRL0_REG, scctl);
+
+       sci_wh = __SHIFTIN(width - 1, VPP_OSD_SCI_WH_M1_WIDTH) |
+                __SHIFTIN(height - 1, VPP_OSD_SCI_WH_M1_HEIGHT);
+       sco_h = __SHIFTIN(margin_w, VPP_OSD_SCO_H_START) |
+               __SHIFTIN(width - margin_w - 1, VPP_OSD_SCO_H_END);
+       sco_v = __SHIFTIN(margin_h, VPP_OSD_SCO_V_START) |
+               __SHIFTIN(height - margin_h - 1, VPP_OSD_SCO_V_END);
+
+       VPU_WRITE(sc, VPP_OSD_SCI_WH_M1_REG, sci_wh);
+       VPU_WRITE(sc, VPP_OSD_SCO_H_REG, sco_h);
+       VPU_WRITE(sc, VPP_OSD_SCO_V_REG, sco_v);
+
+       /* horizontal scaling */
+       hsc = VPU_READ(sc, VPP_OSD_HSC_CTRL0_REG);
+       if (scale_p) {
+               hsc &= ~VPP_OSD_HSC_CTRL0_BANK_LENGTH;
+               hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_BANK_LENGTH);
+               hsc &= ~VPP_OSD_HSC_CTRL0_INI_RCV_NUM0;
+               hsc |= __SHIFTIN(4, VPP_OSD_HSC_CTRL0_INI_RCV_NUM0);
+               hsc &= ~VPP_OSD_HSC_CTRL0_RPT_P0_NUM0;
+               hsc |= __SHIFTIN(1, VPP_OSD_HSC_CTRL0_RPT_P0_NUM0);
+               hsc |= VPP_OSD_HSC_CTRL0_HSCALE_EN;
+       } else {
+               hsc &= ~VPP_OSD_HSC_CTRL0_HSCALE_EN;
+       }
+       VPU_WRITE(sc, VPP_OSD_HSC_CTRL0_REG, hsc);
+
+       /* vertical scaling */
+       vsc = VPU_READ(sc, VPP_OSD_VSC_CTRL0_REG);
+       if (scale_p) {
+               vsc &= ~VPP_OSD_VSC_CTRL0_BANK_LENGTH;
+               vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_BANK_LENGTH);
+               vsc &= ~VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0;
+               vsc |= __SHIFTIN(4, VPP_OSD_VSC_CTRL0_TOP_INI_RCV_NUM0);
+               vsc &= ~VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0;
+               vsc |= __SHIFTIN(1, VPP_OSD_VSC_CTRL0_TOP_RPT_P0_NUM0);
+               vsc &= ~VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0;
+               vsc &= ~VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0;
+               vsc &= ~VPP_OSC_VSC_CTRL0_INTERLACE;
+               if (interlace_p) {
+                       /* interlace */
+                       vsc |= VPP_OSC_VSC_CTRL0_INTERLACE;
+                       vsc |= __SHIFTIN(6, VPP_OSD_VSC_CTRL0_BOT_INI_RCV_NUM0);
+                       vsc |= __SHIFTIN(2, VPP_OSD_VSC_CTRL0_BOT_RPT_P0_NUM0);
+               }
+               vsc |= VPP_OSD_VSC_CTRL0_VSCALE_EN;
+       } else {
+               vsc &= ~VPP_OSD_VSC_CTRL0_VSCALE_EN;
+       }
+       VPU_WRITE(sc, VPP_OSD_VSC_CTRL0_REG, vsc);
+
+       /* free scale enable */
+       if (scale_p) {
+               const u_int hf_phase_step = ((width << 18) / dst_w) << 6;
+               const u_int vf_phase_step = ((height << 20) / dst_h) << 4;
+               const u_int bot_ini_phase =
+                   interlace_p ? ((vf_phase_step / 2) >> 8) : 0;
+
+               hps = VPU_READ(sc, VPP_OSD_HSC_PHASE_STEP_REG);
+               hps &= ~VPP_OSD_HSC_PHASE_STEP_FORMAT;
+               hps |= __SHIFTIN(hf_phase_step, VPP_OSD_HSC_PHASE_STEP_FORMAT);
+               VPU_WRITE(sc, VPP_OSD_HSC_PHASE_STEP_REG, hps);
+
+               hip = VPU_READ(sc, VPP_OSD_HSC_INI_PHASE_REG);
+               hip &= ~VPP_OSD_HSC_INI_PHASE_1;
+               VPU_WRITE(sc, VPP_OSD_HSC_INI_PHASE_REG, hip);
+
+               vps = VPU_READ(sc, VPP_OSD_VSC_PHASE_STEP_REG);
+               vps &= ~VPP_OSD_VSC_PHASE_STEP_FORMAT;
+               vps |= __SHIFTIN(hf_phase_step, VPP_OSD_VSC_PHASE_STEP_FORMAT);
+               VPU_WRITE(sc, VPP_OSD_VSC_PHASE_STEP_REG, vps);
+
+               vip = VPU_READ(sc, VPP_OSD_VSC_INI_PHASE_REG);
+               vip &= ~VPP_OSD_VSC_INI_PHASE_1;
+               vip |= __SHIFTIN(0, VPP_OSD_VSC_INI_PHASE_1);
+               vip &= ~VPP_OSD_VSC_INI_PHASE_0;
+               vip |= __SHIFTIN(bot_ini_phase, VPP_OSD_VSC_INI_PHASE_0);
+               VPU_WRITE(sc, VPP_OSD_VSC_INI_PHASE_REG, vip);
+       }
+}
+
+static void
+amlogic_genfb_init(struct amlogic_genfb_softc *sc)
+{
+       prop_dictionary_t cfg = device_properties(sc->sc_gen.sc_dev);



Home | Main Index | Thread Index | Old Index