The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/allwinner/a10_fb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   23  * SUCH DAMAGE.
   24  *
   25  * $FreeBSD$
   26  */
   27 
   28 /*
   29  * Allwinner A10/A20 Framebuffer
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/rman.h>
   39 #include <sys/condvar.h>
   40 #include <sys/eventhandler.h>
   41 #include <sys/kernel.h>
   42 #include <sys/module.h>
   43 #include <sys/fbio.h>
   44 #include <vm/vm.h>
   45 #include <vm/vm_extern.h>
   46 #include <vm/vm_kern.h>
   47 #include <vm/pmap.h>
   48 
   49 #include <machine/bus.h>
   50 
   51 #include <dev/ofw/ofw_bus.h>
   52 #include <dev/ofw/ofw_bus_subr.h>
   53 
   54 #include <dev/videomode/videomode.h>
   55 #include <dev/videomode/edidvar.h>
   56 
   57 #include <dev/extres/clk/clk.h>
   58 #include <dev/extres/hwreset/hwreset.h>
   59 
   60 #include "fb_if.h"
   61 #include "hdmi_if.h"
   62 
   63 #define FB_DEFAULT_W    800
   64 #define FB_DEFAULT_H    600
   65 #define FB_DEFAULT_REF  60
   66 #define FB_BPP          32
   67 #define FB_ALIGN        0x1000
   68 
   69 #define HDMI_ENABLE_DELAY       20000
   70 #define DEBE_FREQ               300000000
   71 
   72 #define DOT_CLOCK_TO_HZ(c)      ((c) * 1000)
   73 
   74 /* Display backend */
   75 #define DEBE_REG_START          0x800
   76 #define DEBE_REG_END            0x1000
   77 #define DEBE_REG_WIDTH          4
   78 #define DEBE_MODCTL             0x800
   79 #define MODCTL_ITLMOD_EN        (1 << 28)
   80 #define MODCTL_OUT_SEL_MASK     (0x7 << 20)
   81 #define MODCTL_OUT_SEL(sel)     ((sel) << 20)
   82 #define OUT_SEL_LCD             0
   83 #define MODCTL_LAY0_EN          (1 << 8)
   84 #define MODCTL_START_CTL        (1 << 1)
   85 #define MODCTL_EN               (1 << 0)
   86 #define DEBE_DISSIZE            0x808
   87 #define DIS_HEIGHT(h)           (((h) - 1) << 16)
   88 #define DIS_WIDTH(w)            (((w) - 1) << 0)
   89 #define DEBE_LAYSIZE0           0x810
   90 #define LAY_HEIGHT(h)           (((h) - 1) << 16)
   91 #define LAY_WIDTH(w)            (((w) - 1) << 0)
   92 #define DEBE_LAYCOOR0           0x820
   93 #define LAY_XCOOR(x)            ((x) << 16)
   94 #define LAY_YCOOR(y)            ((y) << 0)
   95 #define DEBE_LAYLINEWIDTH0      0x840
   96 #define DEBE_LAYFB_L32ADD0      0x850
   97 #define LAYFB_L32ADD(pa)        ((pa) << 3)
   98 #define DEBE_LAYFB_H4ADD        0x860
   99 #define LAY0FB_H4ADD(pa)        ((pa) >> 29)
  100 #define DEBE_REGBUFFCTL         0x870
  101 #define REGBUFFCTL_LOAD         (1 << 0)
  102 #define DEBE_ATTCTL1            0x8a0
  103 #define ATTCTL1_FBFMT(fmt)      ((fmt) << 8)
  104 #define FBFMT_XRGB8888          9
  105 #define ATTCTL1_FBPS(ps)        ((ps) << 0)
  106 #define FBPS_32BPP_ARGB         0
  107 
  108 /* Timing controller */
  109 #define TCON_GCTL               0x000
  110 #define GCTL_TCON_EN            (1 << 31)
  111 #define GCTL_IO_MAP_SEL_TCON1   (1 << 0)
  112 #define TCON_GINT1              0x008
  113 #define GINT1_TCON1_LINENO(n)   (((n) + 2) << 0)
  114 #define TCON0_DCLK              0x044
  115 #define DCLK_EN                 0xf0000000
  116 #define TCON1_CTL               0x090
  117 #define TCON1_EN                (1 << 31)
  118 #define INTERLACE_EN            (1 << 20)
  119 #define TCON1_SRC_SEL(src)      ((src) << 0)
  120 #define TCON1_SRC_CH1           0
  121 #define TCON1_SRC_CH2           1
  122 #define TCON1_SRC_BLUE          2
  123 #define TCON1_START_DELAY(sd)   ((sd) << 4)
  124 #define TCON1_BASIC0            0x094
  125 #define TCON1_BASIC1            0x098
  126 #define TCON1_BASIC2            0x09c
  127 #define TCON1_BASIC3            0x0a0
  128 #define TCON1_BASIC4            0x0a4
  129 #define TCON1_BASIC5            0x0a8
  130 #define BASIC_X(x)              (((x) - 1) << 16)
  131 #define BASIC_Y(y)              (((y) - 1) << 0)
  132 #define BASIC3_HT(ht)           (((ht) - 1) << 16)
  133 #define BASIC3_HBP(hbp)         (((hbp) - 1) << 0)
  134 #define BASIC4_VT(vt)           ((vt) << 16)
  135 #define BASIC4_VBP(vbp)         (((vbp) - 1) << 0)
  136 #define BASIC5_HSPW(hspw)       (((hspw) - 1) << 16)
  137 #define BASIC5_VSPW(vspw)       (((vspw) - 1) << 0)
  138 #define TCON1_IO_POL            0x0f0
  139 #define IO_POL_IO2_INV          (1 << 26)
  140 #define IO_POL_PHSYNC           (1 << 25)
  141 #define IO_POL_PVSYNC           (1 << 24)
  142 #define TCON1_IO_TRI            0x0f4
  143 #define IO0_OUTPUT_TRI_EN       (1 << 24)
  144 #define IO1_OUTPUT_TRI_EN       (1 << 25)
  145 #define IO_TRI_MASK             0xffffffff
  146 #define START_DELAY(vbl)        (MIN(32, (vbl)) - 2)
  147 #define VBLANK_LEN(vt, vd, i)   ((((vt) << (i)) >> 1) - (vd) - 2)
  148 #define VTOTAL(vt)              ((vt) * 2)
  149 #define DIVIDE(x, y)            (((x) + ((y) / 2)) / (y))
  150 
  151 struct a10fb_softc {
  152         device_t                dev;
  153         device_t                fbdev;
  154         struct resource         *res[2];
  155 
  156         /* Framebuffer */
  157         struct fb_info          info;
  158         size_t                  fbsize;
  159         bus_addr_t              paddr;
  160         vm_offset_t             vaddr;
  161 
  162         /* HDMI */
  163         eventhandler_tag        hdmi_evh;
  164 };
  165 
  166 static struct resource_spec a10fb_spec[] = {
  167         { SYS_RES_MEMORY,       0,      RF_ACTIVE },    /* DEBE */
  168         { SYS_RES_MEMORY,       1,      RF_ACTIVE },    /* TCON */
  169         { -1, 0 }
  170 };
  171 
  172 #define DEBE_READ(sc, reg)              bus_read_4((sc)->res[0], (reg))
  173 #define DEBE_WRITE(sc, reg, val)        bus_write_4((sc)->res[0], (reg), (val))
  174 
  175 #define TCON_READ(sc, reg)              bus_read_4((sc)->res[1], (reg))
  176 #define TCON_WRITE(sc, reg, val)        bus_write_4((sc)->res[1], (reg), (val))
  177 
  178 static int
  179 a10fb_allocfb(struct a10fb_softc *sc)
  180 {
  181         sc->vaddr = kmem_alloc_contig(sc->fbsize, M_NOWAIT | M_ZERO, 0, ~0,
  182             FB_ALIGN, 0, VM_MEMATTR_WRITE_COMBINING);
  183         if (sc->vaddr == 0) {
  184                 device_printf(sc->dev, "failed to allocate FB memory\n");
  185                 return (ENOMEM);
  186         }
  187         sc->paddr = pmap_kextract(sc->vaddr);
  188 
  189         return (0);
  190 }
  191 
  192 static void
  193 a10fb_freefb(struct a10fb_softc *sc)
  194 {
  195         kmem_free(sc->vaddr, sc->fbsize);
  196 }
  197 
  198 static int
  199 a10fb_setup_debe(struct a10fb_softc *sc, const struct videomode *mode)
  200 {
  201         int width, height, interlace, reg;
  202         clk_t clk_ahb, clk_dram, clk_debe;
  203         hwreset_t rst;
  204         uint32_t val;
  205         int error;
  206 
  207         interlace = !!(mode->flags & VID_INTERLACE);
  208         width = mode->hdisplay;
  209         height = mode->vdisplay << interlace;
  210 
  211         /* Leave reset */
  212         error = hwreset_get_by_ofw_name(sc->dev, 0, "de_be", &rst);
  213         if (error != 0) {
  214                 device_printf(sc->dev, "cannot find reset 'de_be'\n");
  215                 return (error);
  216         }
  217         error = hwreset_deassert(rst);
  218         if (error != 0) {
  219                 device_printf(sc->dev, "couldn't de-assert reset 'de_be'\n");
  220                 return (error);
  221         }
  222         /* Gating AHB clock for BE */
  223         error = clk_get_by_ofw_name(sc->dev, 0, "ahb_de_be", &clk_ahb);
  224         if (error != 0) {
  225                 device_printf(sc->dev, "cannot find clk 'ahb_de_be'\n");
  226                 return (error);
  227         }
  228         error = clk_enable(clk_ahb);
  229         if (error != 0) {
  230                 device_printf(sc->dev, "cannot enable clk 'ahb_de_be'\n");
  231                 return (error);
  232         }
  233         /* Enable DRAM clock to BE */
  234         error = clk_get_by_ofw_name(sc->dev, 0, "dram_de_be", &clk_dram);
  235         if (error != 0) {
  236                 device_printf(sc->dev, "cannot find clk 'dram_de_be'\n");
  237                 return (error);
  238         }
  239         error = clk_enable(clk_dram);
  240         if (error != 0) {
  241                 device_printf(sc->dev, "cannot enable clk 'dram_de_be'\n");
  242                 return (error);
  243         }
  244         /* Set BE clock to 300MHz and enable */
  245         error = clk_get_by_ofw_name(sc->dev, 0, "de_be", &clk_debe);
  246         if (error != 0) {
  247                 device_printf(sc->dev, "cannot find clk 'de_be'\n");
  248                 return (error);
  249         }
  250         error = clk_set_freq(clk_debe, DEBE_FREQ, CLK_SET_ROUND_DOWN);
  251         if (error != 0) {
  252                 device_printf(sc->dev, "cannot set 'de_be' frequency\n");
  253                 return (error);
  254         }
  255         error = clk_enable(clk_debe);
  256         if (error != 0) {
  257                 device_printf(sc->dev, "cannot enable clk 'de_be'\n");
  258                 return (error);
  259         }
  260 
  261         /* Initialize all registers to 0 */
  262         for (reg = DEBE_REG_START; reg < DEBE_REG_END; reg += DEBE_REG_WIDTH)
  263                 DEBE_WRITE(sc, reg, 0);
  264 
  265         /* Enable display backend */
  266         DEBE_WRITE(sc, DEBE_MODCTL, MODCTL_EN);
  267 
  268         /* Set display size */
  269         DEBE_WRITE(sc, DEBE_DISSIZE, DIS_HEIGHT(height) | DIS_WIDTH(width));
  270 
  271         /* Set layer 0 size, position, and stride */
  272         DEBE_WRITE(sc, DEBE_LAYSIZE0, LAY_HEIGHT(height) | LAY_WIDTH(width));
  273         DEBE_WRITE(sc, DEBE_LAYCOOR0, LAY_XCOOR(0) | LAY_YCOOR(0));
  274         DEBE_WRITE(sc, DEBE_LAYLINEWIDTH0, width * FB_BPP);
  275 
  276         /* Point layer 0 to FB memory */
  277         DEBE_WRITE(sc, DEBE_LAYFB_L32ADD0, LAYFB_L32ADD(sc->paddr));
  278         DEBE_WRITE(sc, DEBE_LAYFB_H4ADD, LAY0FB_H4ADD(sc->paddr));
  279 
  280         /* Set backend format and pixel sequence */
  281         DEBE_WRITE(sc, DEBE_ATTCTL1, ATTCTL1_FBFMT(FBFMT_XRGB8888) |
  282             ATTCTL1_FBPS(FBPS_32BPP_ARGB));
  283 
  284         /* Enable layer 0, output to LCD, setup interlace */
  285         val = DEBE_READ(sc, DEBE_MODCTL);
  286         val |= MODCTL_LAY0_EN;
  287         val &= ~MODCTL_OUT_SEL_MASK;
  288         val |= MODCTL_OUT_SEL(OUT_SEL_LCD);
  289         if (interlace)
  290                 val |= MODCTL_ITLMOD_EN;
  291         else
  292                 val &= ~MODCTL_ITLMOD_EN;
  293         DEBE_WRITE(sc, DEBE_MODCTL, val);
  294 
  295         /* Commit settings */
  296         DEBE_WRITE(sc, DEBE_REGBUFFCTL, REGBUFFCTL_LOAD);
  297 
  298         /* Start DEBE */
  299         val = DEBE_READ(sc, DEBE_MODCTL);
  300         val |= MODCTL_START_CTL;
  301         DEBE_WRITE(sc, DEBE_MODCTL, val);
  302 
  303         return (0);
  304 }
  305 
  306 static int
  307 a10fb_setup_pll(struct a10fb_softc *sc, uint64_t freq)
  308 {
  309         clk_t clk_sclk1, clk_sclk2;
  310         int error;
  311 
  312         error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk1", &clk_sclk1);
  313         if (error != 0) {
  314                 device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk1'\n");
  315                 return (error);
  316         }
  317         error = clk_get_by_ofw_name(sc->dev, 0, "lcd_ch1_sclk2", &clk_sclk2);
  318         if (error != 0) {
  319                 device_printf(sc->dev, "cannot find clk 'lcd_ch1_sclk2'\n");
  320                 return (error);
  321         }
  322 
  323         error = clk_set_freq(clk_sclk2, freq, 0);
  324         if (error != 0) {
  325                 device_printf(sc->dev, "cannot set lcd ch1 frequency\n");
  326                 return (error);
  327         }
  328         error = clk_enable(clk_sclk2);
  329         if (error != 0) {
  330                 device_printf(sc->dev, "cannot enable lcd ch1 sclk2\n");
  331                 return (error);
  332         }
  333         error = clk_enable(clk_sclk1);
  334         if (error != 0) {
  335                 device_printf(sc->dev, "cannot enable lcd ch1 sclk1\n");
  336                 return (error);
  337         }
  338 
  339         return (0);
  340 }
  341 
  342 static int
  343 a10fb_setup_tcon(struct a10fb_softc *sc, const struct videomode *mode)
  344 {
  345         u_int interlace, hspw, hbp, vspw, vbp, vbl, width, height, start_delay;
  346         u_int vtotal, framerate, clk;
  347         clk_t clk_ahb;
  348         hwreset_t rst;
  349         uint32_t val;
  350         int error;
  351 
  352         interlace = !!(mode->flags & VID_INTERLACE);
  353         width = mode->hdisplay;
  354         height = mode->vdisplay;
  355         hspw = mode->hsync_end - mode->hsync_start;
  356         hbp = mode->htotal - mode->hsync_start;
  357         vspw = mode->vsync_end - mode->vsync_start;
  358         vbp = mode->vtotal - mode->vsync_start;
  359         vbl = VBLANK_LEN(mode->vtotal, mode->vdisplay, interlace);
  360         start_delay = START_DELAY(vbl);
  361 
  362         /* Leave reset */
  363         error = hwreset_get_by_ofw_name(sc->dev, 0, "lcd", &rst);
  364         if (error != 0) {
  365                 device_printf(sc->dev, "cannot find reset 'lcd'\n");
  366                 return (error);
  367         }
  368         error = hwreset_deassert(rst);
  369         if (error != 0) {
  370                 device_printf(sc->dev, "couldn't de-assert reset 'lcd'\n");
  371                 return (error);
  372         }
  373         /* Gating AHB clock for LCD */
  374         error = clk_get_by_ofw_name(sc->dev, 0, "ahb_lcd", &clk_ahb);
  375         if (error != 0) {
  376                 device_printf(sc->dev, "cannot find clk 'ahb_lcd'\n");
  377                 return (error);
  378         }
  379         error = clk_enable(clk_ahb);
  380         if (error != 0) {
  381                 device_printf(sc->dev, "cannot enable clk 'ahb_lcd'\n");
  382                 return (error);
  383         }
  384 
  385         /* Disable TCON and TCON1 */
  386         TCON_WRITE(sc, TCON_GCTL, 0);
  387         TCON_WRITE(sc, TCON1_CTL, 0);
  388 
  389         /* Enable clocks */
  390         TCON_WRITE(sc, TCON0_DCLK, DCLK_EN);
  391 
  392         /* Disable IO and data output ports */
  393         TCON_WRITE(sc, TCON1_IO_TRI, IO_TRI_MASK);
  394 
  395         /* Disable TCON and select TCON1 */
  396         TCON_WRITE(sc, TCON_GCTL, GCTL_IO_MAP_SEL_TCON1);
  397 
  398         /* Source width and height */
  399         TCON_WRITE(sc, TCON1_BASIC0, BASIC_X(width) | BASIC_Y(height));
  400         /* Scaler width and height */
  401         TCON_WRITE(sc, TCON1_BASIC1, BASIC_X(width) | BASIC_Y(height));
  402         /* Output width and height */
  403         TCON_WRITE(sc, TCON1_BASIC2, BASIC_X(width) | BASIC_Y(height));
  404         /* Horizontal total and back porch */
  405         TCON_WRITE(sc, TCON1_BASIC3, BASIC3_HT(mode->htotal) | BASIC3_HBP(hbp));
  406         /* Vertical total and back porch */
  407         vtotal = VTOTAL(mode->vtotal);
  408         if (interlace) {
  409                 framerate = DIVIDE(DIVIDE(DOT_CLOCK_TO_HZ(mode->dot_clock),
  410                     mode->htotal), mode->vtotal);
  411                 clk = mode->htotal * (VTOTAL(mode->vtotal) + 1) * framerate;
  412                 if ((clk / 2) == DOT_CLOCK_TO_HZ(mode->dot_clock))
  413                         vtotal += 1;
  414         }
  415         TCON_WRITE(sc, TCON1_BASIC4, BASIC4_VT(vtotal) | BASIC4_VBP(vbp));
  416         /* Horizontal and vertical sync */
  417         TCON_WRITE(sc, TCON1_BASIC5, BASIC5_HSPW(hspw) | BASIC5_VSPW(vspw));
  418         /* Polarity */
  419         val = IO_POL_IO2_INV;
  420         if (mode->flags & VID_PHSYNC)
  421                 val |= IO_POL_PHSYNC;
  422         if (mode->flags & VID_PVSYNC)
  423                 val |= IO_POL_PVSYNC;
  424         TCON_WRITE(sc, TCON1_IO_POL, val);
  425 
  426         /* Set scan line for TCON1 line trigger */
  427         TCON_WRITE(sc, TCON_GINT1, GINT1_TCON1_LINENO(start_delay));
  428 
  429         /* Enable TCON1 */
  430         val = TCON1_EN;
  431         if (interlace)
  432                 val |= INTERLACE_EN;
  433         val |= TCON1_START_DELAY(start_delay);
  434         val |= TCON1_SRC_SEL(TCON1_SRC_CH1);
  435         TCON_WRITE(sc, TCON1_CTL, val);
  436 
  437         /* Setup PLL */
  438         return (a10fb_setup_pll(sc, DOT_CLOCK_TO_HZ(mode->dot_clock)));
  439 }
  440 
  441 static void
  442 a10fb_enable_tcon(struct a10fb_softc *sc, int onoff)
  443 {
  444         uint32_t val;
  445 
  446         /* Enable TCON */
  447         val = TCON_READ(sc, TCON_GCTL);
  448         if (onoff)
  449                 val |= GCTL_TCON_EN;
  450         else
  451                 val &= ~GCTL_TCON_EN;
  452         TCON_WRITE(sc, TCON_GCTL, val);
  453 
  454         /* Enable TCON1 IO0/IO1 outputs */
  455         val = TCON_READ(sc, TCON1_IO_TRI);
  456         if (onoff)
  457                 val &= ~(IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
  458         else
  459                 val |= (IO0_OUTPUT_TRI_EN | IO1_OUTPUT_TRI_EN);
  460         TCON_WRITE(sc, TCON1_IO_TRI, val);
  461 }
  462 
  463 static int
  464 a10fb_configure(struct a10fb_softc *sc, const struct videomode *mode)
  465 {
  466         size_t fbsize;
  467         int error;
  468 
  469         fbsize = round_page(mode->hdisplay * mode->vdisplay * (FB_BPP / NBBY));
  470 
  471         /* Detach the old FB device */
  472         if (sc->fbdev != NULL) {
  473                 device_delete_child(sc->dev, sc->fbdev);
  474                 sc->fbdev = NULL;
  475         }
  476 
  477         /* If the FB size has changed, free the old FB memory */
  478         if (sc->fbsize > 0 && sc->fbsize != fbsize) {
  479                 a10fb_freefb(sc);
  480                 sc->vaddr = 0;
  481         }
  482 
  483         /* Allocate the FB if necessary */
  484         sc->fbsize = fbsize;
  485         if (sc->vaddr == 0) {
  486                 error = a10fb_allocfb(sc);
  487                 if (error != 0) {
  488                         device_printf(sc->dev, "failed to allocate FB memory\n");
  489                         return (ENXIO);
  490                 }
  491         }
  492 
  493         /* Setup display backend */
  494         error = a10fb_setup_debe(sc, mode);
  495         if (error != 0)
  496                 return (error);
  497 
  498         /* Setup display timing controller */
  499         error = a10fb_setup_tcon(sc, mode);
  500         if (error != 0)
  501                 return (error);
  502 
  503         /* Attach framebuffer device */
  504         sc->info.fb_name = device_get_nameunit(sc->dev);
  505         sc->info.fb_vbase = (intptr_t)sc->vaddr;
  506         sc->info.fb_pbase = sc->paddr;
  507         sc->info.fb_size = sc->fbsize;
  508         sc->info.fb_bpp = sc->info.fb_depth = FB_BPP;
  509         sc->info.fb_stride = mode->hdisplay * (FB_BPP / NBBY);
  510         sc->info.fb_width = mode->hdisplay;
  511         sc->info.fb_height = mode->vdisplay;
  512 
  513         sc->fbdev = device_add_child(sc->dev, "fbd", device_get_unit(sc->dev));
  514         if (sc->fbdev == NULL) {
  515                 device_printf(sc->dev, "failed to add fbd child\n");
  516                 return (ENOENT);
  517         }
  518 
  519         error = device_probe_and_attach(sc->fbdev);
  520         if (error != 0) {
  521                 device_printf(sc->dev, "failed to attach fbd device\n");
  522                 return (error);
  523         }
  524 
  525         return (0);
  526 }
  527 
  528 static void
  529 a10fb_hdmi_event(void *arg, device_t hdmi_dev)
  530 {
  531         const struct videomode *mode;
  532         struct videomode hdmi_mode;
  533         struct a10fb_softc *sc;
  534         struct edid_info ei;
  535         uint8_t *edid;
  536         uint32_t edid_len;
  537         int error;
  538 
  539         sc = arg;
  540         edid = NULL;
  541         edid_len = 0;
  542         mode = NULL;
  543 
  544         error = HDMI_GET_EDID(hdmi_dev, &edid, &edid_len);
  545         if (error != 0) {
  546                 device_printf(sc->dev, "failed to get EDID: %d\n", error);
  547         } else {
  548                 error = edid_parse(edid, &ei);
  549                 if (error != 0) {
  550                         device_printf(sc->dev, "failed to parse EDID: %d\n",
  551                             error);
  552                 } else {
  553                         if (bootverbose)
  554                                 edid_print(&ei);
  555                         mode = ei.edid_preferred_mode;
  556                 }
  557         }
  558 
  559         /* If the preferred mode could not be determined, use the default */
  560         if (mode == NULL)
  561                 mode = pick_mode_by_ref(FB_DEFAULT_W, FB_DEFAULT_H,
  562                     FB_DEFAULT_REF);
  563 
  564         if (mode == NULL) {
  565                 device_printf(sc->dev, "failed to find usable video mode\n");
  566                 return;
  567         }
  568 
  569         if (bootverbose)
  570                 device_printf(sc->dev, "using %dx%d\n",
  571                     mode->hdisplay, mode->vdisplay);
  572 
  573         /* Disable HDMI */
  574         HDMI_ENABLE(hdmi_dev, 0);
  575 
  576         /* Disable timing controller */
  577         a10fb_enable_tcon(sc, 0);
  578 
  579         /* Configure DEBE and TCON */
  580         error = a10fb_configure(sc, mode);
  581         if (error != 0) {
  582                 device_printf(sc->dev, "failed to configure FB: %d\n", error);
  583                 return;
  584         }
  585 
  586         hdmi_mode = *mode;
  587         hdmi_mode.hskew = mode->hsync_end - mode->hsync_start;
  588         hdmi_mode.flags |= VID_HSKEW;
  589         HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
  590 
  591         /* Enable timing controller */
  592         a10fb_enable_tcon(sc, 1);
  593 
  594         DELAY(HDMI_ENABLE_DELAY);
  595 
  596         /* Enable HDMI */
  597         HDMI_ENABLE(hdmi_dev, 1);
  598 }
  599 
  600 static int
  601 a10fb_probe(device_t dev)
  602 {
  603         if (!ofw_bus_status_okay(dev))
  604                 return (ENXIO);
  605 
  606         if (!ofw_bus_is_compatible(dev, "allwinner,sun7i-a20-fb"))
  607                 return (ENXIO);
  608 
  609         device_set_desc(dev, "Allwinner Framebuffer");
  610         return (BUS_PROBE_DEFAULT);
  611 }
  612 
  613 static int
  614 a10fb_attach(device_t dev)
  615 {
  616         struct a10fb_softc *sc;
  617 
  618         sc = device_get_softc(dev);
  619 
  620         sc->dev = dev;
  621 
  622         if (bus_alloc_resources(dev, a10fb_spec, sc->res)) {
  623                 device_printf(dev, "cannot allocate resources for device\n");
  624                 return (ENXIO);
  625         }
  626 
  627         sc->hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
  628             a10fb_hdmi_event, sc, 0);
  629 
  630         return (0);
  631 }
  632 
  633 static struct fb_info *
  634 a10fb_fb_getinfo(device_t dev)
  635 {
  636         struct a10fb_softc *sc;
  637 
  638         sc = device_get_softc(dev);
  639 
  640         return (&sc->info);
  641 }
  642 
  643 static device_method_t a10fb_methods[] = {
  644         /* Device interface */
  645         DEVMETHOD(device_probe,         a10fb_probe),
  646         DEVMETHOD(device_attach,        a10fb_attach),
  647 
  648         /* FB interface */
  649         DEVMETHOD(fb_getinfo,           a10fb_fb_getinfo),
  650 
  651         DEVMETHOD_END
  652 };
  653 
  654 static driver_t a10fb_driver = {
  655         "fb",
  656         a10fb_methods,
  657         sizeof(struct a10fb_softc),
  658 };
  659 
  660 DRIVER_MODULE(fb, simplebus, a10fb_driver, 0, 0);

Cache object: 1cb94939d0674d294ec8c5846bef57b8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.