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.0/sys/arm/lpc/lpc_fb.c 239278 2012-08-15 05:37:10Z gonzo $");
   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/frame.h>
   56 #include <machine/intr.h>
   57 
   58 #include <dev/fdt/fdt_common.h>
   59 #include <dev/ofw/ofw_bus.h>
   60 #include <dev/ofw/ofw_bus_subr.h>
   61 
   62 #include <arm/lpc/lpcreg.h>
   63 #include <arm/lpc/lpcvar.h>
   64 
   65 
   66 struct lpc_fb_dmamap_arg {
   67         bus_addr_t              lf_dma_busaddr;
   68 };
   69 
   70 struct lpc_lcd_config {
   71         int                     lc_xres;
   72         int                     lc_yres;
   73         int                     lc_bpp;
   74         uint32_t                lc_pixelclock;
   75         int                     lc_left_margin;
   76         int                     lc_right_margin;
   77         int                     lc_upper_margin;
   78         int                     lc_lower_margin;
   79         int                     lc_hsync_len;
   80         int                     lc_vsync_len;
   81 };
   82 
   83 struct lpc_fb_softc {
   84         device_t                lf_dev;
   85         struct cdev *           lf_cdev;
   86         struct mtx              lf_mtx;
   87         struct resource *       lf_mem_res;
   88         struct resource *       lf_irq_res;
   89         bus_space_tag_t         lf_bst;
   90         bus_space_handle_t      lf_bsh;
   91         void *                  lf_intrhand;
   92         bus_dma_tag_t           lf_dma_tag;
   93         bus_dmamap_t            lf_dma_map;
   94         void *                  lf_buffer;
   95         bus_addr_t              lf_buffer_phys;
   96         bus_size_t              lf_buffer_size;
   97         struct lpc_lcd_config   lf_lcd_config;
   98         int                     lf_initialized;
   99         int                     lf_opened;
  100 };
  101 
  102 extern void ssd1289_configure(void);
  103 
  104 #define lpc_fb_lock(_sc)        mtx_lock(&(_sc)->lf_mtx)
  105 #define lpc_fb_unlock(_sc)      mtx_unlock(&(_sc)->lf_mtx)
  106 #define lpc_fb_lock_assert(sc)  mtx_assert(&(_sc)->lf_mtx, MA_OWNED)
  107 
  108 #define lpc_fb_read_4(_sc, _reg)                \
  109     bus_space_read_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg))
  110 #define lpc_fb_write_4(_sc, _reg, _val)         \
  111     bus_space_write_4((_sc)->lf_bst, (_sc)->lf_bsh, (_reg), (_val))
  112 
  113 
  114 
  115 static int lpc_fb_probe(device_t);
  116 static int lpc_fb_attach(device_t);
  117 static void lpc_fb_intr(void *);
  118 static void lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err);
  119 
  120 static int lpc_fb_fdt_read(phandle_t, const char *, uint32_t *);
  121 static int lpc_fb_read_lcd_config(phandle_t, struct lpc_lcd_config *);
  122 
  123 static int lpc_fb_open(struct cdev *, int, int, struct thread *);
  124 static int lpc_fb_close(struct cdev *, int, int, struct thread *);
  125 static int lpc_fb_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
  126 static int lpc_fb_mmap(struct cdev *, vm_ooffset_t, vm_paddr_t *, int, vm_memattr_t *);
  127 
  128 static void lpc_fb_blank(struct lpc_fb_softc *);
  129 
  130 static struct cdevsw lpc_fb_cdevsw = {
  131         .d_open         = lpc_fb_open,
  132         .d_close        = lpc_fb_close,
  133         .d_ioctl        = lpc_fb_ioctl,
  134         .d_mmap         = lpc_fb_mmap,
  135         .d_name         = "lpcfb",
  136         .d_version      = D_VERSION,
  137 };
  138 
  139 static int
  140 lpc_fb_probe(device_t dev)
  141 {
  142         if (!ofw_bus_is_compatible(dev, "lpc,fb"))
  143                 return (ENXIO);
  144 
  145         device_set_desc(dev, "LPC32x0 framebuffer device");
  146         return (BUS_PROBE_DEFAULT);
  147 }
  148 
  149 static int
  150 lpc_fb_attach(device_t dev)
  151 {
  152         struct lpc_fb_softc *sc = device_get_softc(dev);
  153         struct lpc_fb_dmamap_arg ctx;
  154         phandle_t node;
  155         int mode, rid, err = 0;
  156 
  157         sc->lf_dev = dev;
  158         mtx_init(&sc->lf_mtx, "lpcfb", "fb", MTX_DEF);
  159 
  160         rid = 0;
  161         sc->lf_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
  162             RF_ACTIVE);
  163         if (!sc->lf_mem_res) {
  164                 device_printf(dev, "cannot allocate memory window\n");
  165                 return (ENXIO);
  166         }
  167 
  168         sc->lf_bst = rman_get_bustag(sc->lf_mem_res);
  169         sc->lf_bsh = rman_get_bushandle(sc->lf_mem_res);
  170 
  171         rid = 0;
  172         sc->lf_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  173             RF_ACTIVE);
  174         if (!sc->lf_irq_res) {
  175                 device_printf(dev, "cannot allocate interrupt\n");
  176                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
  177                 return (ENXIO);
  178         }
  179 
  180         if (bus_setup_intr(dev, sc->lf_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
  181             NULL, lpc_fb_intr, sc, &sc->lf_intrhand))
  182         {
  183                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lf_mem_res);
  184                 bus_release_resource(dev, SYS_RES_IRQ, 1, sc->lf_irq_res);
  185                 device_printf(dev, "cannot setup interrupt handler\n");
  186                 return (ENXIO);
  187         }
  188 
  189         node = ofw_bus_get_node(dev);
  190 
  191         err = lpc_fb_read_lcd_config(node, &sc->lf_lcd_config);
  192         if (err) {
  193                 device_printf(dev, "cannot read LCD configuration\n");
  194                 goto fail;
  195         }
  196 
  197         sc->lf_buffer_size = sc->lf_lcd_config.lc_xres * 
  198             sc->lf_lcd_config.lc_yres *
  199             (sc->lf_lcd_config.lc_bpp == 24 ? 3 : 2);
  200 
  201         device_printf(dev, "%dx%d LCD, %d bits per pixel, %dkHz pixel clock\n",
  202             sc->lf_lcd_config.lc_xres, sc->lf_lcd_config.lc_yres,
  203             sc->lf_lcd_config.lc_bpp, sc->lf_lcd_config.lc_pixelclock / 1000);
  204 
  205         err = bus_dma_tag_create(
  206             bus_get_dma_tag(sc->lf_dev),
  207             4, 0,                       /* alignment, boundary */
  208             BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
  209             BUS_SPACE_MAXADDR,          /* highaddr */
  210             NULL, NULL,                 /* filter, filterarg */
  211             sc->lf_buffer_size, 1,      /* maxsize, nsegments */
  212             sc->lf_buffer_size, 0,      /* maxsegsize, flags */
  213             NULL, NULL,                 /* lockfunc, lockarg */
  214             &sc->lf_dma_tag);
  215 
  216         err = bus_dmamem_alloc(sc->lf_dma_tag, (void **)&sc->lf_buffer,
  217             0, &sc->lf_dma_map);
  218         if (err) {
  219                 device_printf(dev, "cannot allocate framebuffer\n");
  220                 goto fail;
  221         }
  222 
  223         err = bus_dmamap_load(sc->lf_dma_tag, sc->lf_dma_map, sc->lf_buffer,
  224             sc->lf_buffer_size, lpc_fb_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
  225         if (err) {
  226                 device_printf(dev, "cannot load DMA map\n");
  227                 goto fail;
  228         }
  229 
  230         switch (sc->lf_lcd_config.lc_bpp) {
  231         case 12:
  232                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_12;
  233                 break;
  234         case 15:
  235                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_15;
  236                 break;
  237         case 16:
  238                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_16;
  239                 break;
  240         case 24:
  241                 mode = LPC_CLKPWR_LCDCLK_CTRL_MODE_24;
  242                 break;
  243         default:
  244                 panic("unsupported bpp");
  245         }
  246 
  247         lpc_pwr_write(sc->lf_dev, LPC_CLKPWR_LCDCLK_CTRL,
  248             LPC_CLKPWR_LCDCLK_CTRL_MODE(mode) |
  249             LPC_CLKPWR_LCDCLK_CTRL_HCLKEN);
  250 
  251         sc->lf_buffer_phys = ctx.lf_dma_busaddr;
  252         sc->lf_cdev = make_dev(&lpc_fb_cdevsw, 0, UID_ROOT, GID_WHEEL,
  253             0600, "lpcfb");
  254 
  255         sc->lf_cdev->si_drv1 = sc;
  256 
  257         return (0);
  258 fail:
  259         return (ENXIO);
  260 }
  261 
  262 static void
  263 lpc_fb_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
  264 {
  265         struct lpc_fb_dmamap_arg *ctx;
  266 
  267         if (err)
  268                 return;
  269 
  270         ctx = (struct lpc_fb_dmamap_arg *)arg;
  271         ctx->lf_dma_busaddr = segs[0].ds_addr;
  272 }
  273 
  274 static void
  275 lpc_fb_intr(void *arg)
  276 {
  277 }
  278 
  279 static int
  280 lpc_fb_fdt_read(phandle_t node, const char *name, uint32_t *ret)
  281 {
  282         if (OF_getprop(node, name, ret, sizeof(uint32_t)) <= 0)
  283                 return (ENOENT);
  284 
  285         *ret = fdt32_to_cpu(*ret);
  286         return (0);
  287 }
  288 
  289 static int
  290 lpc_fb_read_lcd_config(phandle_t node, struct lpc_lcd_config *cfg)
  291 {
  292         if (lpc_fb_fdt_read(node, "horizontal-resolution", &cfg->lc_xres))
  293                 return (ENXIO);
  294 
  295         if (lpc_fb_fdt_read(node, "vertical-resolution", &cfg->lc_yres))
  296                 return (ENXIO);
  297 
  298         if (lpc_fb_fdt_read(node, "bits-per-pixel", &cfg->lc_bpp))
  299                 return (ENXIO);
  300 
  301         if (lpc_fb_fdt_read(node, "pixel-clock", &cfg->lc_pixelclock))
  302                 return (ENXIO);
  303 
  304         if (lpc_fb_fdt_read(node, "left-margin", &cfg->lc_left_margin))
  305                 return (ENXIO);
  306 
  307         if (lpc_fb_fdt_read(node, "right-margin", &cfg->lc_right_margin))
  308                 return (ENXIO);
  309 
  310         if (lpc_fb_fdt_read(node, "upper-margin", &cfg->lc_upper_margin))
  311                 return (ENXIO);
  312 
  313         if (lpc_fb_fdt_read(node, "lower-margin", &cfg->lc_lower_margin))
  314                 return (ENXIO);
  315 
  316         if (lpc_fb_fdt_read(node, "hsync-len", &cfg->lc_hsync_len))
  317                 return (ENXIO);
  318 
  319         if (lpc_fb_fdt_read(node, "vsync-len", &cfg->lc_vsync_len))
  320                 return (ENXIO);
  321 
  322         return (0);
  323 }
  324 
  325 static void
  326 lpc_fb_setup(struct lpc_fb_softc *sc)
  327 {
  328         struct lpc_lcd_config *cfg = &sc->lf_lcd_config;
  329         uint32_t bpp;
  330 
  331         lpc_fb_write_4(sc, LPC_LCD_TIMH,
  332             LPC_LCD_TIMH_PPL(cfg->lc_xres) |
  333             LPC_LCD_TIMH_HSW(cfg->lc_hsync_len - 1) |
  334             LPC_LCD_TIMH_HFP(cfg->lc_right_margin - 1) |
  335             LPC_LCD_TIMH_HBP(cfg->lc_left_margin - 1));
  336 
  337         lpc_fb_write_4(sc, LPC_LCD_TIMV,
  338             LPC_LCD_TIMV_LPP(cfg->lc_yres - 1) |
  339             LPC_LCD_TIMV_VSW(cfg->lc_vsync_len - 1) |
  340             LPC_LCD_TIMV_VFP(cfg->lc_lower_margin) |
  341             LPC_LCD_TIMV_VBP(cfg->lc_upper_margin));
  342 
  343         /* XXX LPC_LCD_POL_PCD_LO */
  344         lpc_fb_write_4(sc, LPC_LCD_POL,
  345             LPC_LCD_POL_IHS | LPC_LCD_POL_IVS |
  346             LPC_LCD_POL_CPL(cfg->lc_xres - 1) |
  347             LPC_LCD_POL_PCD_LO(4));
  348         
  349         lpc_fb_write_4(sc, LPC_LCD_UPBASE, sc->lf_buffer_phys);
  350         
  351         switch (cfg->lc_bpp) {
  352         case 1:
  353                 bpp = LPC_LCD_CTRL_BPP1;
  354                 break;
  355         case 2:
  356                 bpp = LPC_LCD_CTRL_BPP2;
  357                 break;
  358         case 4:
  359                 bpp = LPC_LCD_CTRL_BPP4;
  360                 break;
  361         case 8:
  362                 bpp = LPC_LCD_CTRL_BPP8;
  363                 break;
  364         case 12:
  365                 bpp = LPC_LCD_CTRL_BPP12_444;
  366                 break;
  367         case 15:
  368                 bpp = LPC_LCD_CTRL_BPP16;
  369                 break;
  370         case 16:
  371                 bpp = LPC_LCD_CTRL_BPP16_565;
  372                 break;
  373         case 24:
  374                 bpp = LPC_LCD_CTRL_BPP24;
  375                 break;
  376         default:
  377                 panic("LCD unknown bpp: %d", cfg->lc_bpp);
  378         }
  379 
  380         lpc_fb_write_4(sc, LPC_LCD_CTRL,
  381             LPC_LCD_CTRL_LCDVCOMP(1) |
  382             LPC_LCD_CTRL_LCDPWR |
  383             LPC_LCD_CTRL_BGR |
  384             LPC_LCD_CTRL_LCDTFT |
  385             LPC_LCD_CTRL_LCDBPP(bpp) |
  386             LPC_LCD_CTRL_LCDEN);
  387 }
  388 
  389 
  390 static int
  391 lpc_fb_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
  392 {
  393         struct lpc_fb_softc *sc = cdev->si_drv1;
  394 
  395         lpc_fb_lock(sc);
  396 
  397         if (sc->lf_opened)
  398                 return (EBUSY);
  399 
  400         sc->lf_opened = 1;
  401 
  402         lpc_fb_unlock(sc);
  403 
  404         if (!sc->lf_initialized) {
  405                 ssd1289_configure();
  406                 lpc_fb_setup(sc);
  407                 lpc_fb_blank(sc);
  408                 sc->lf_initialized = 1;
  409         }
  410 
  411         return (0);
  412 }
  413 
  414 static int
  415 lpc_fb_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
  416 {       
  417         struct lpc_fb_softc *sc = cdev->si_drv1;
  418 
  419         lpc_fb_lock(sc);
  420         sc->lf_opened = 0;
  421         lpc_fb_unlock(sc);
  422 
  423         return (0);
  424 }
  425 
  426 static int
  427 lpc_fb_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int x,
  428     struct thread *td)
  429 {
  430         
  431         return (EINVAL);
  432 }
  433 
  434 static int
  435 lpc_fb_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
  436     int nprot, vm_memattr_t *memattr)
  437 {
  438         struct lpc_fb_softc *sc = cdev->si_drv1;
  439 
  440         *paddr = (vm_paddr_t)(sc->lf_buffer_phys + offset);
  441         return (0);
  442 }
  443 
  444 static void
  445 lpc_fb_blank(struct lpc_fb_softc *sc)
  446 {
  447         memset(sc->lf_buffer, 0xffff, sc->lf_buffer_size);
  448 }
  449 
  450 static device_method_t lpc_fb_methods[] = {
  451         /* Device interface */
  452         DEVMETHOD(device_probe,         lpc_fb_probe),
  453         DEVMETHOD(device_attach,        lpc_fb_attach),
  454 
  455         { 0, 0 }
  456 };
  457 
  458 static devclass_t lpc_fb_devclass;
  459 
  460 static driver_t lpc_fb_driver = {
  461         "lpcfb",
  462         lpc_fb_methods,
  463         sizeof(struct lpc_fb_softc),
  464 };
  465 
  466 DRIVER_MODULE(lpcfb, simplebus, lpc_fb_driver, lpc_fb_devclass, 0, 0);

Cache object: 69fd920e119dd1b6a51b281a927fb9f3


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