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/lpc/lpc_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) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  */
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/10.2/sys/arm/lpc/lpc_fb.c 266152 2014-05-15 16:11:06Z ian $");
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/bio.h>
   33 #include <sys/bus.h>
   34 #include <sys/conf.h>
   35 #include <sys/endian.h>
   36 #include <sys/kernel.h>
   37 #include <sys/kthread.h>
   38 #include <sys/lock.h>
   39 #include <sys/malloc.h>
   40 #include <sys/module.h>
   41 #include <sys/mutex.h>
   42 #include <sys/queue.h>
   43 #include <sys/resource.h>
   44 #include <sys/rman.h>
   45 #include <sys/time.h>
   46 #include <sys/timetc.h>
   47 #include <sys/watchdog.h>
   48 
   49 #include <sys/kdb.h>
   50 
   51 #include <machine/bus.h>
   52 #include <machine/cpu.h>
   53 #include <machine/cpufunc.h>
   54 #include <machine/resource.h>
   55 #include <machine/intr.h>
   56 
   57 #include <dev/fdt/fdt_common.h>
   58 #include <dev/ofw/ofw_bus.h>
   59 #include <dev/ofw/ofw_bus_subr.h>
   60 
   61 #include <arm/lpc/lpcreg.h>
   62 #include <arm/lpc/lpcvar.h>
   63 
   64 
   65 struct lpc_fb_dmamap_arg {
   66         bus_addr_t              lf_dma_busaddr;
   67 };
   68 
   69 struct lpc_lcd_config {
   70         int                     lc_xres;
   71         int                     lc_yres;
   72         int                     lc_bpp;
   73         uint32_t                lc_pixelclock;
   74         int                     lc_left_margin;
   75         int                     lc_right_margin;
   76         int                     lc_upper_margin;
   77         int                     lc_lower_margin;
   78         int                     lc_hsync_len;
   79         int                     lc_vsync_len;
   80 };
   81 
   82 struct lpc_fb_softc {
   83         device_t                lf_dev;
   84         struct cdev *           lf_cdev;
   85         struct mtx              lf_mtx;
   86         struct resource *       lf_mem_res;
   87         struct resource *       lf_irq_res;
   88         bus_space_tag_t         lf_bst;
   89         bus_space_handle_t      lf_bsh;
   90         void *                  lf_intrhand;
   91         bus_dma_tag_t           lf_dma_tag;
   92         bus_dmamap_t            lf_dma_map;
   93         void *                  lf_buffer;
   94         bus_addr_t              lf_buffer_phys;
   95         bus_size_t              lf_buffer_size;
   96         struct lpc_lcd_config   lf_lcd_config;
   97         int                     lf_initialized;
   98         int                     lf_opened;
   99 };
  100 
  101 extern void ssd1289_configure(void);
  102 
  103 #define lpc_fb_lock(_sc)        mtx_lock(&(_sc)->lf_mtx)
  104 #define lpc_fb_unlock(_sc)      mtx_unlock(&(_sc)->lf_mtx)
  105 #define lpc_fb_lock_assert(sc)  mtx_assert(&(_sc)->lf_mtx, MA_OWNED)
  106 
  107 #define lpc_fb_read_4(_sc, _reg)                \
  108     bus_space_read_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg))
  109 #define lpc_fb_write_4(_sc, _reg, _val)         \
  110     bus_space_write_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg), (_val))
  111 
  112 
  113 
  114 static int lpc_fb_probe(device_t);
  115 static int lpc_fb_attach(device_t);
  116 static void lpc_fb_intr(void *);
  117 static void lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err);
  118 
  119 static int lpc_fb_fdt_read(phandle_t, const char *, uint32_t *);
  120 static int lpc_fb_read_lcd_config(phandle_t, struct lpc_lcd_config *);
  121 
  122 static int lpc_fb_open(struct cdev *, int, int, struct thread *);
  123 static int lpc_fb_close(struct cdev *, int, int, struct thread *);
  124 static int lpc_fb_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
  125 static int lpc_fb_mmap(struct cdev *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *);
  126 
  127 static void lpc_fb_blank(struct lpc_fb_softc *);
  128 
  129 static struct cdevsw lpc_fb_cdevsw = {
  130         .d_open         = lpc_fb_open,
  131         .d_close        = lpc_fb_close,
  132         .d_ioctl        = lpc_fb_ioctl,
  133         .d_mmap         = lpc_fb_mmap,
  134         .d_name         = "lpcfb",
  135         .d_version      = D_VERSION,
  136 };
  137 
  138 static int
  139 lpc_fb_probe(device_t dev)
  140 {
  141 
  142         if (!ofw_bus_status_okay(dev))
  143                 return (ENXIO);
  144 
  145         if (!ofw_bus_is_compatible(dev, "lpc,fb"))
  146                 return (ENXIO);
  147 
  148         device_set_desc(dev, "LPC32x0 framebuffer device");
  149         return (BUS_PROBE_DEFAULT);
  150 }
  151 
  152 static int
  153 lpc_fb_attach(device_t dev)
  154 {
  155         struct lpc_fb_softc *sc = device_get_softc(dev);
  156         struct lpc_fb_dmamap_arg ctx;
  157         phandle_t node;
  158         int mode, rid, err = 0;
  159 
  160         sc->lf_dev = dev;
  161         mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF);
  162 
  163         rid = 0;
  164         sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  165             RF_ACTIVE);
  166         if (!sc->lf_mem_res) {
  167                 device_printf(dev, "cannot allocate memory window\n");
  168                 return (ENXIO);
  169         }
  170 
  171         sc->lf_bst = rman_get_bustag(sc->lf_mem_res);
  172         sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res);
  173 
  174         rid = 0;
  175         sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  176             RF_ACTIVE);
  177         if (!sc->lf_irq_res) {
  178                 device_printf(dev, "cannot allocate interrupt\n");
  179                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
  180                 return (ENXIO);
  181         }
  182 
  183         if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  184             NULL, lpc_fb_intr, sc, &sc->lf_intrhand))
  185         {
  186                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
  187                 bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res);
  188                 device_printf(dev, "cannot setup interrupt handler\n");
  189                 return (ENXIO);
  190         }
  191 
  192         node = ofw_bus_get_node(dev);
  193 
  194         err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config);
  195         if (err) {
  196                 device_printf(dev, "cannot read LCD configuration\n");
  197                 goto fail;
  198         }
  199 
  200         sc->lf_buffer_size = sc->lf_lcd_config.lc_xres * 
  201             sc->lf_lcd_config.lc_yres *
  202             (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2);
  203 
  204         device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n",
  205             sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres,
  206             sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000);
  207 
  208         err = bus_dma_tag_create(
  209             bus_get_dma_tag(sc->lf_dev),
  210             4, 0,                       /* alignment, boundary */
  211             BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
  212             BUS_SPACE_MAXADDR,          /* highaddr */
  213             NULL, NULL,                 /* filter, filterarg */
  214             sc->lf_buffer_size, 1,      /* maxsize, nsegments */
  215             sc->lf_buffer_size, 0,      /* maxsegsize, flags */
  216             NULL, NULL,                 /* lockfunc, lockarg */
  217             &sc->lf_dma_tag);
  218 
  219         err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer,
  220             0, &sc->lf_dma_map);
  221         if (err) {
  222                 device_printf(dev, "cannot allocate framebuffer\n");
  223                 goto fail;
  224         }
  225 
  226         err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer,
  227             sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
  228         if (err) {
  229                 device_printf(dev, "cannot load DMA map\n");
  230                 goto fail;
  231         }
  232 
  233         switch (sc->lf_lcd_config.lc_bpp) {
  234         case 12:
  235                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12;
  236                 break;
  237         case 15:
  238                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15;
  239                 break;
  240         case 16:
  241                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16;
  242                 break;
  243         case 24:
  244                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24;
  245                 break;
  246         default:
  247                 panic("unsupported bpp");
  248         }
  249 
  250         lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL,
  251             LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) |
  252             LPC_CLKPWR_LCDCLK_CTRL_HCLKEN);
  253 
  254         sc->lf_buffer_phys = ctx.lf_dma_busaddr;
  255         sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL,
  256             0600, "lpcfb");
  257 
  258         sc->lf_cdev->si_drv1 = sc;
  259 
  260         return (0);
  261 fail:
  262         return (ENXIO);
  263 }
  264 
  265 static void
  266 lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
  267 {
  268         struct lpc_fb_dmamap_arg *ctx;
  269 
  270         if (err)
  271                 return;
  272 
  273         ctx = (struct lpc_fb_dmamap_arg *)arg;
  274         ctx->lf_dma_busaddr = segs[0].ds_addr;
  275 }
  276 
  277 static void
  278 lpc_fb_intr(void *arg)
  279 {
  280 }
  281 
  282 static int
  283 lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret)
  284 {
  285         if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0)
  286                 return (ENOENT);
  287 
  288         *ret = fdt32_to_cpu(*ret);
  289         return (0);
  290 }
  291 
  292 static int
  293 lpc_fb_read_lcd_config(phandle_t node, struct lpc_lcd_config *cfg)
  294 {
  295         if (lpc_fb_fdt_read(node, "horizontal-resolution", &cfg->lc_xres))
  296                 return (ENXIO);
  297 
  298         if (lpc_fb_fdt_read(node, "vertical-resolution", &cfg->lc_yres))
  299                 return (ENXIO);
  300 
  301         if (lpc_fb_fdt_read(node, "bits-per-pixel", &cfg->lc_bpp))
  302                 return (ENXIO);
  303 
  304         if (lpc_fb_fdt_read(node, "pixel-clock", &cfg->lc_pixelclock))
  305                 return (ENXIO);
  306 
  307         if (lpc_fb_fdt_read(node, "left-margin", &cfg->lc_left_margin))
  308                 return (ENXIO);
  309 
  310         if (lpc_fb_fdt_read(node, "right-margin", &cfg->lc_right_margin))
  311                 return (ENXIO);
  312 
  313         if (lpc_fb_fdt_read(node, "upper-margin", &cfg->lc_upper_margin))
  314                 return (ENXIO);
  315 
  316         if (lpc_fb_fdt_read(node, "lower-margin", &cfg->lc_lower_margin))
  317                 return (ENXIO);
  318 
  319         if (lpc_fb_fdt_read(node, "hsync-len", &cfg->lc_hsync_len))
  320                 return (ENXIO);
  321 
  322         if (lpc_fb_fdt_read(node, "vsync-len", &cfg->lc_vsync_len))
  323                 return (ENXIO);
  324 
  325         return (0);
  326 }
  327 
  328 static void
  329 lpc_fb_setup(struct lpc_fb_softc *sc)
  330 {
  331         struct lpc_lcd_config *cfg = &sc->lf_lcd_config;
  332         uint32_t bpp;
  333 
  334         lpc_fb_write_4(sc, LPC_LCD_TIMH,
  335             LPC_LCD_TIMH_PPL(cfg->lc_xres) |
  336             LPC_LCD_TIMH_HSW(cfg->lc_hsync_len - 1) |
  337             LPC_LCD_TIMH_HFP(cfg->lc_right_margin - 1) |
  338             LPC_LCD_TIMH_HBP(cfg->lc_left_margin - 1));
  339 
  340         lpc_fb_write_4(sc, LPC_LCD_TIMV,
  341             LPC_LCD_TIMV_LPP(cfg->lc_yres - 1) |
  342             LPC_LCD_TIMV_VSW(cfg->lc_vsync_len - 1) |
  343             LPC_LCD_TIMV_VFP(cfg->lc_lower_margin) |
  344             LPC_LCD_TIMV_VBP(cfg->lc_upper_margin));
  345 
  346         /* XXX LPC_LCD_POL_PCD_LO */
  347         lpc_fb_write_4(sc, LPC_LCD_POL,
  348             LPC_LCD_POL_IHS | LPC_LCD_POL_IVS |
  349             LPC_LCD_POL_CPL(cfg->lc_xres - 1) |
  350             LPC_LCD_POL_PCD_LO(4));
  351         
  352         lpc_fb_write_4(sc, LPC_LCD_UPBASE, sc->lf_buffer_phys);
  353         
  354         switch (cfg->lc_bpp) {
  355         case 1:
  356                 bpp = LPC_LCD_CTRL_BPP1;
  357                 break;
  358         case 2:
  359                 bpp = LPC_LCD_CTRL_BPP2;
  360                 break;
  361         case 4:
  362                 bpp = LPC_LCD_CTRL_BPP4;
  363                 break;
  364         case 8:
  365                 bpp = LPC_LCD_CTRL_BPP8;
  366                 break;
  367         case 12:
  368                 bpp = LPC_LCD_CTRL_BPP12_444;
  369                 break;
  370         case 15:
  371                 bpp = LPC_LCD_CTRL_BPP16;
  372                 break;
  373         case 16:
  374                 bpp = LPC_LCD_CTRL_BPP16_565;
  375                 break;
  376         case 24:
  377                 bpp = LPC_LCD_CTRL_BPP24;
  378                 break;
  379         default:
  380                 panic("LCD unknown bpp: %d", cfg->lc_bpp);
  381         }
  382 
  383         lpc_fb_write_4(sc, LPC_LCD_CTRL,
  384             LPC_LCD_CTRL_LCDVCOMP(1) |
  385             LPC_LCD_CTRL_LCDPWR |
  386             LPC_LCD_CTRL_BGR |
  387             LPC_LCD_CTRL_LCDTFT |
  388             LPC_LCD_CTRL_LCDBPP(bpp) |
  389             LPC_LCD_CTRL_LCDEN);
  390 }
  391 
  392 
  393 static int
  394 lpc_fb_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
  395 {
  396         struct lpc_fb_softc *sc = cdev->si_drv1;
  397 
  398         lpc_fb_lock(sc);
  399 
  400         if (sc->lf_opened)
  401                 return (EBUSY);
  402 
  403         sc->lf_opened = 1;
  404 
  405         lpc_fb_unlock(sc);
  406 
  407         if (!sc->lf_initialized) {
  408                 ssd1289_configure();
  409                 lpc_fb_setup(sc);
  410                 lpc_fb_blank(sc);
  411                 sc->lf_initialized = 1;
  412         }
  413 
  414         return (0);
  415 }
  416 
  417 static int
  418 lpc_fb_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
  419 {       
  420         struct lpc_fb_softc *sc = cdev->si_drv1;
  421 
  422         lpc_fb_lock(sc);
  423         sc->lf_opened = 0;
  424         lpc_fb_unlock(sc);
  425 
  426         return (0);
  427 }
  428 
  429 static int
  430 lpc_fb_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int x,
  431     struct thread *td)
  432 {
  433         
  434         return (EINVAL);
  435 }
  436 
  437 static int
  438 lpc_fb_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
  439     int nprot, vm_memattr_t *memattr)
  440 {
  441         struct lpc_fb_softc *sc = cdev->si_drv1;
  442 
  443         *paddr = (vm_paddr_t)(sc->lf_buffer_phys + offset);
  444         return (0);
  445 }
  446 
  447 static void
  448 lpc_fb_blank(struct lpc_fb_softc *sc)
  449 {
  450         memset(sc->lf_buffer, 0xffff, sc->lf_buffer_size);
  451 }
  452 
  453 static device_method_t lpc_fb_methods[] = {
  454         /* Device interface */
  455         DEVMETHOD(device_probe,         lpc_fb_probe),
  456         DEVMETHOD(device_attach,        lpc_fb_attach),
  457 
  458         { 0, 0 }
  459 };
  460 
  461 static devclass_t lpc_fb_devclass;
  462 
  463 static driver_t lpc_fb_driver = {
  464         "lpcfb",
  465         lpc_fb_methods,
  466         sizeof(struct lpc_fb_softc),
  467 };
  468 
  469 DRIVER_MODULE(lpcfb, simplebus, lpc_fb_driver, lpc_fb_devclass, 0, 0);

Cache object: 9cc41a76376dd368546e800d882d8bbb


[ 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.