linux/arch/arm/plat-omap/fb.c
<<
>>
Prefs
   1/*
   2 * File: arch/arm/plat-omap/fb.c
   3 *
   4 * Framebuffer device registration for TI OMAP platforms
   5 *
   6 * Copyright (C) 2006 Nokia Corporation
   7 * Author: Imre Deak <imre.deak@nokia.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify it
  10 * under the terms of the GNU General Public License as published by the
  11 * Free Software Foundation; either version 2 of the License, or (at your
  12 * option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License along
  20 * with this program; if not, write to the Free Software Foundation, Inc.,
  21 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/kernel.h>
  26#include <linux/mm.h>
  27#include <linux/init.h>
  28#include <linux/platform_device.h>
  29#include <linux/bootmem.h>
  30#include <linux/io.h>
  31
  32#include <mach/hardware.h>
  33#include <asm/mach/map.h>
  34
  35#include <mach/board.h>
  36#include <mach/sram.h>
  37#include <mach/omapfb.h>
  38
  39#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
  40
  41static struct omapfb_platform_data omapfb_config;
  42static int config_invalid;
  43static int configured_regions;
  44
  45static u64 omap_fb_dma_mask = ~(u32)0;
  46
  47static struct platform_device omap_fb_device = {
  48        .name           = "omapfb",
  49        .id             = -1,
  50        .dev = {
  51                .dma_mask               = &omap_fb_dma_mask,
  52                .coherent_dma_mask      = ~(u32)0,
  53                .platform_data          = &omapfb_config,
  54        },
  55        .num_resources = 0,
  56};
  57
  58static inline int ranges_overlap(unsigned long start1, unsigned long size1,
  59                                 unsigned long start2, unsigned long size2)
  60{
  61        return (start1 >= start2 && start1 < start2 + size2) ||
  62               (start2 >= start1 && start2 < start1 + size1);
  63}
  64
  65static inline int range_included(unsigned long start1, unsigned long size1,
  66                                 unsigned long start2, unsigned long size2)
  67{
  68        return start1 >= start2 && start1 + size1 <= start2 + size2;
  69}
  70
  71
  72/* Check if there is an overlapping region. */
  73static int fbmem_region_reserved(unsigned long start, size_t size)
  74{
  75        struct omapfb_mem_region *rg;
  76        int i;
  77
  78        rg = &omapfb_config.mem_desc.region[0];
  79        for (i = 0; i < OMAPFB_PLANE_NUM; i++, rg++) {
  80                if (!rg->paddr)
  81                        /* Empty slot. */
  82                        continue;
  83                if (ranges_overlap(start, size, rg->paddr, rg->size))
  84                        return 1;
  85        }
  86        return 0;
  87}
  88
  89/*
  90 * Get the region_idx`th region from board config/ATAG and convert it to
  91 * our internal format.
  92 */
  93static int get_fbmem_region(int region_idx, struct omapfb_mem_region *rg)
  94{
  95        const struct omap_fbmem_config  *conf;
  96        u32                             paddr;
  97
  98        conf = omap_get_nr_config(OMAP_TAG_FBMEM,
  99                                  struct omap_fbmem_config, region_idx);
 100        if (conf == NULL)
 101                return -ENOENT;
 102
 103        paddr = conf->start;
 104        /*
 105         * Low bits encode the page allocation mode, if high bits
 106         * are zero. Otherwise we need a page aligned fixed
 107         * address.
 108         */
 109        memset(rg, 0, sizeof(*rg));
 110        rg->type = paddr & ~PAGE_MASK;
 111        rg->paddr = paddr & PAGE_MASK;
 112        rg->size = PAGE_ALIGN(conf->size);
 113        return 0;
 114}
 115
 116static int set_fbmem_region_type(struct omapfb_mem_region *rg, int mem_type,
 117                                  unsigned long mem_start,
 118                                  unsigned long mem_size)
 119{
 120        /*
 121         * Check if the configuration specifies the type explicitly.
 122         * type = 0 && paddr = 0, a default don't care case maps to
 123         * the SDRAM type.
 124         */
 125        if (rg->type || (!rg->type && !rg->paddr))
 126                return 0;
 127        if (ranges_overlap(rg->paddr, rg->size, mem_start, mem_size)) {
 128                rg->type = mem_type;
 129                return 0;
 130        }
 131        /* Can't determine it. */
 132        return -1;
 133}
 134
 135static int check_fbmem_region(int region_idx, struct omapfb_mem_region *rg,
 136                              unsigned long start_avail, unsigned size_avail)
 137{
 138        unsigned long   paddr = rg->paddr;
 139        size_t          size = rg->size;
 140
 141        if (rg->type > OMAPFB_MEMTYPE_MAX) {
 142                printk(KERN_ERR
 143                        "Invalid start address for FB region %d\n", region_idx);
 144                return -EINVAL;
 145        }
 146
 147        if (!rg->size) {
 148                printk(KERN_ERR "Zero size for FB region %d\n", region_idx);
 149                return -EINVAL;
 150        }
 151
 152        if (!paddr)
 153                /* Allocate this dynamically, leave paddr 0 for now. */
 154                return 0;
 155
 156        /*
 157         * Fixed region for the given RAM range. Check if it's already
 158         * reserved by the FB code or someone else.
 159         */
 160        if (fbmem_region_reserved(paddr, size) ||
 161            !range_included(paddr, size, start_avail, size_avail)) {
 162                printk(KERN_ERR "Trying to use reserved memory "
 163                        "for FB region %d\n", region_idx);
 164                return -EINVAL;
 165        }
 166
 167        return 0;
 168}
 169
 170/*
 171 * Called from map_io. We need to call to this early enough so that we
 172 * can reserve the fixed SDRAM regions before VM could get hold of them.
 173 */
 174void __init omapfb_reserve_sdram(void)
 175{
 176        struct bootmem_data     *bdata;
 177        unsigned long           sdram_start, sdram_size;
 178        unsigned long           reserved;
 179        int                     i;
 180
 181        if (config_invalid)
 182                return;
 183
 184        bdata = NODE_DATA(0)->bdata;
 185        sdram_start = bdata->node_min_pfn << PAGE_SHIFT;
 186        sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start;
 187        reserved = 0;
 188        for (i = 0; ; i++) {
 189                struct omapfb_mem_region        rg;
 190
 191                if (get_fbmem_region(i, &rg) < 0)
 192                        break;
 193                if (i == OMAPFB_PLANE_NUM) {
 194                        printk(KERN_ERR
 195                                "Extraneous FB mem configuration entries\n");
 196                        config_invalid = 1;
 197                        return;
 198                }
 199                /* Check if it's our memory type. */
 200                if (set_fbmem_region_type(&rg, OMAPFB_MEMTYPE_SDRAM,
 201                                          sdram_start, sdram_size) < 0 ||
 202                    (rg.type != OMAPFB_MEMTYPE_SDRAM))
 203                        continue;
 204                BUG_ON(omapfb_config.mem_desc.region[i].size);
 205                if (check_fbmem_region(i, &rg, sdram_start, sdram_size) < 0) {
 206                        config_invalid = 1;
 207                        return;
 208                }
 209                if (rg.paddr) {
 210                        reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT);
 211                        reserved += rg.size;
 212                }
 213                omapfb_config.mem_desc.region[i] = rg;
 214                configured_regions++;
 215        }
 216        omapfb_config.mem_desc.region_cnt = i;
 217        if (reserved)
 218                pr_info("Reserving %lu bytes SDRAM for frame buffer\n",
 219                         reserved);
 220}
 221
 222/*
 223 * Called at sram init time, before anything is pushed to the SRAM stack.
 224 * Because of the stack scheme, we will allocate everything from the
 225 * start of the lowest address region to the end of SRAM. This will also
 226 * include padding for page alignment and possible holes between regions.
 227 *
 228 * As opposed to the SDRAM case, we'll also do any dynamic allocations at
 229 * this point, since the driver built as a module would have problem with
 230 * freeing / reallocating the regions.
 231 */
 232unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
 233                                  unsigned long sram_vstart,
 234                                  unsigned long sram_size,
 235                                  unsigned long pstart_avail,
 236                                  unsigned long size_avail)
 237{
 238        struct omapfb_mem_region        rg;
 239        unsigned long                   pend_avail;
 240        unsigned long                   reserved;
 241        int                             i;
 242
 243        if (config_invalid)
 244                return 0;
 245
 246        reserved = 0;
 247        pend_avail = pstart_avail + size_avail;
 248        for (i = 0; ; i++) {
 249                if (get_fbmem_region(i, &rg) < 0)
 250                        break;
 251                if (i == OMAPFB_PLANE_NUM) {
 252                        printk(KERN_ERR
 253                                "Extraneous FB mem configuration entries\n");
 254                        config_invalid = 1;
 255                        return 0;
 256                }
 257
 258                /* Check if it's our memory type. */
 259                if (set_fbmem_region_type(&rg, OMAPFB_MEMTYPE_SRAM,
 260                                          sram_pstart, sram_size) < 0 ||
 261                    (rg.type != OMAPFB_MEMTYPE_SRAM))
 262                        continue;
 263                BUG_ON(omapfb_config.mem_desc.region[i].size);
 264
 265                if (check_fbmem_region(i, &rg, pstart_avail, size_avail) < 0) {
 266                        config_invalid = 1;
 267                        return 0;
 268                }
 269
 270                if (!rg.paddr) {
 271                        /* Dynamic allocation */
 272                        if ((size_avail & PAGE_MASK) < rg.size) {
 273                                printk("Not enough SRAM for FB region %d\n",
 274                                        i);
 275                                config_invalid = 1;
 276                                return 0;
 277                        }
 278                        size_avail = (size_avail - rg.size) & PAGE_MASK;
 279                        rg.paddr = pstart_avail + size_avail;
 280                }
 281                /* Reserve everything above the start of the region. */
 282                if (pend_avail - rg.paddr > reserved)
 283                        reserved = pend_avail - rg.paddr;
 284                size_avail = pend_avail - reserved - pstart_avail;
 285
 286                /*
 287                 * We have a kernel mapping for this already, so the
 288                 * driver won't have to make one.
 289                 */
 290                rg.vaddr = (void *)(sram_vstart + rg.paddr - sram_pstart);
 291                omapfb_config.mem_desc.region[i] = rg;
 292                configured_regions++;
 293        }
 294        omapfb_config.mem_desc.region_cnt = i;
 295        if (reserved)
 296                pr_info("Reserving %lu bytes SRAM for frame buffer\n",
 297                         reserved);
 298        return reserved;
 299}
 300
 301void omapfb_set_ctrl_platform_data(void *data)
 302{
 303        omapfb_config.ctrl_platform_data = data;
 304}
 305
 306static inline int omap_init_fb(void)
 307{
 308        const struct omap_lcd_config *conf;
 309
 310        if (config_invalid)
 311                return 0;
 312        if (configured_regions != omapfb_config.mem_desc.region_cnt) {
 313                printk(KERN_ERR "Invalid FB mem configuration entries\n");
 314                return 0;
 315        }
 316        conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
 317        if (conf == NULL) {
 318                if (configured_regions)
 319                        /* FB mem config, but no LCD config? */
 320                        printk(KERN_ERR "Missing LCD configuration\n");
 321                return 0;
 322        }
 323        omapfb_config.lcd = *conf;
 324
 325        return platform_device_register(&omap_fb_device);
 326}
 327
 328arch_initcall(omap_init_fb);
 329
 330#else
 331
 332void omapfb_reserve_sdram(void) {}
 333unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
 334                                  unsigned long sram_vstart,
 335                                  unsigned long sram_size,
 336                                  unsigned long start_avail,
 337                                  unsigned long size_avail)
 338{
 339        return 0;
 340}
 341
 342
 343#endif
 344