blob: d3eea4f47533050618ce2a0e489a79f5f2725088 [file] [log] [blame]
Tony Lindgrenc40fae92006-12-07 13:58:10 -08001/*
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
Tony Lindgren0dc5e772006-04-02 17:46:26 +010024#include <linux/module.h>
25#include <linux/kernel.h>
Andrea Righi27ac7922008-07-23 21:28:13 -070026#include <linux/mm.h>
Tony Lindgren0dc5e772006-04-02 17:46:26 +010027#include <linux/init.h>
28#include <linux/platform_device.h>
29#include <linux/bootmem.h>
Russell Kingfced80c2008-09-06 12:10:45 +010030#include <linux/io.h>
Tomi Valkeinen91773a02009-08-03 15:06:36 +030031#include <linux/omapfb.h>
Tony Lindgren0dc5e772006-04-02 17:46:26 +010032
Russell Kinga09e64f2008-08-05 16:14:15 +010033#include <mach/hardware.h>
Tony Lindgren0dc5e772006-04-02 17:46:26 +010034#include <asm/mach/map.h>
35
Tony Lindgrence491cf2009-10-20 09:40:47 -070036#include <plat/board.h>
37#include <plat/sram.h>
Tony Lindgren0dc5e772006-04-02 17:46:26 +010038
39#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
40
41static struct omapfb_platform_data omapfb_config;
Imre Deakb7cc6d42007-03-06 03:16:36 -080042static int config_invalid;
43static int configured_regions;
Tony Lindgren0dc5e772006-04-02 17:46:26 +010044
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
Tomi Valkeinenb39a982d2009-08-04 16:12:50 +030058void omapfb_set_platform_data(struct omapfb_platform_data *data)
59{
60}
61
Imre Deakb7cc6d42007-03-06 03:16:36 -080062static inline int ranges_overlap(unsigned long start1, unsigned long size1,
63 unsigned long start2, unsigned long size2)
Tony Lindgren0dc5e772006-04-02 17:46:26 +010064{
Imre Deakb7cc6d42007-03-06 03:16:36 -080065 return (start1 >= start2 && start1 < start2 + size2) ||
66 (start2 >= start1 && start2 < start1 + size1);
67}
68
69static inline int range_included(unsigned long start1, unsigned long size1,
70 unsigned long start2, unsigned long size2)
71{
72 return start1 >= start2 && start1 + size1 <= start2 + size2;
73}
74
75
76/* Check if there is an overlapping region. */
77static int fbmem_region_reserved(unsigned long start, size_t size)
78{
79 struct omapfb_mem_region *rg;
Tony Lindgrenc40fae92006-12-07 13:58:10 -080080 int i;
Tony Lindgren0dc5e772006-04-02 17:46:26 +010081
Imre Deakb7cc6d42007-03-06 03:16:36 -080082 rg = &omapfb_config.mem_desc.region[0];
83 for (i = 0; i < OMAPFB_PLANE_NUM; i++, rg++) {
84 if (!rg->paddr)
85 /* Empty slot. */
86 continue;
87 if (ranges_overlap(start, size, rg->paddr, rg->size))
88 return 1;
89 }
90 return 0;
91}
92
93/*
94 * Get the region_idx`th region from board config/ATAG and convert it to
95 * our internal format.
96 */
97static int get_fbmem_region(int region_idx, struct omapfb_mem_region *rg)
98{
99 const struct omap_fbmem_config *conf;
100 u32 paddr;
101
102 conf = omap_get_nr_config(OMAP_TAG_FBMEM,
103 struct omap_fbmem_config, region_idx);
104 if (conf == NULL)
105 return -ENOENT;
106
107 paddr = conf->start;
108 /*
109 * Low bits encode the page allocation mode, if high bits
110 * are zero. Otherwise we need a page aligned fixed
111 * address.
112 */
113 memset(rg, 0, sizeof(*rg));
114 rg->type = paddr & ~PAGE_MASK;
115 rg->paddr = paddr & PAGE_MASK;
116 rg->size = PAGE_ALIGN(conf->size);
117 return 0;
118}
119
120static int set_fbmem_region_type(struct omapfb_mem_region *rg, int mem_type,
121 unsigned long mem_start,
122 unsigned long mem_size)
123{
124 /*
125 * Check if the configuration specifies the type explicitly.
126 * type = 0 && paddr = 0, a default don't care case maps to
127 * the SDRAM type.
128 */
129 if (rg->type || (!rg->type && !rg->paddr))
130 return 0;
131 if (ranges_overlap(rg->paddr, rg->size, mem_start, mem_size)) {
132 rg->type = mem_type;
133 return 0;
134 }
135 /* Can't determine it. */
136 return -1;
137}
138
139static int check_fbmem_region(int region_idx, struct omapfb_mem_region *rg,
140 unsigned long start_avail, unsigned size_avail)
141{
142 unsigned long paddr = rg->paddr;
143 size_t size = rg->size;
144
145 if (rg->type > OMAPFB_MEMTYPE_MAX) {
146 printk(KERN_ERR
147 "Invalid start address for FB region %d\n", region_idx);
148 return -EINVAL;
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100149 }
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800150
Imre Deakb7cc6d42007-03-06 03:16:36 -0800151 if (!rg->size) {
152 printk(KERN_ERR "Zero size for FB region %d\n", region_idx);
153 return -EINVAL;
154 }
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800155
Imre Deakb7cc6d42007-03-06 03:16:36 -0800156 if (!paddr)
157 /* Allocate this dynamically, leave paddr 0 for now. */
158 return 0;
159
160 /*
161 * Fixed region for the given RAM range. Check if it's already
162 * reserved by the FB code or someone else.
163 */
164 if (fbmem_region_reserved(paddr, size) ||
165 !range_included(paddr, size, start_avail, size_avail)) {
166 printk(KERN_ERR "Trying to use reserved memory "
167 "for FB region %d\n", region_idx);
168 return -EINVAL;
169 }
170
171 return 0;
172}
173
174/*
175 * Called from map_io. We need to call to this early enough so that we
176 * can reserve the fixed SDRAM regions before VM could get hold of them.
177 */
David Brownell342fb3d2007-10-26 21:48:37 +0100178void __init omapfb_reserve_sdram(void)
Imre Deakb7cc6d42007-03-06 03:16:36 -0800179{
180 struct bootmem_data *bdata;
181 unsigned long sdram_start, sdram_size;
182 unsigned long reserved;
183 int i;
184
185 if (config_invalid)
186 return;
187
188 bdata = NODE_DATA(0)->bdata;
Johannes Weiner3560e242008-07-23 21:28:09 -0700189 sdram_start = bdata->node_min_pfn << PAGE_SHIFT;
Imre Deakb7cc6d42007-03-06 03:16:36 -0800190 sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start;
191 reserved = 0;
192 for (i = 0; ; i++) {
193 struct omapfb_mem_region rg;
194
195 if (get_fbmem_region(i, &rg) < 0)
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800196 break;
Imre Deakb7cc6d42007-03-06 03:16:36 -0800197 if (i == OMAPFB_PLANE_NUM) {
198 printk(KERN_ERR
199 "Extraneous FB mem configuration entries\n");
200 config_invalid = 1;
201 return;
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800202 }
Imre Deakb7cc6d42007-03-06 03:16:36 -0800203 /* Check if it's our memory type. */
204 if (set_fbmem_region_type(&rg, OMAPFB_MEMTYPE_SDRAM,
205 sdram_start, sdram_size) < 0 ||
206 (rg.type != OMAPFB_MEMTYPE_SDRAM))
207 continue;
208 BUG_ON(omapfb_config.mem_desc.region[i].size);
209 if (check_fbmem_region(i, &rg, sdram_start, sdram_size) < 0) {
210 config_invalid = 1;
211 return;
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800212 }
Tomi Valkeinen72af2b32009-05-11 09:58:19 -0700213 if (rg.paddr) {
Bernhard Walle72a7fe32008-02-07 00:15:17 -0800214 reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT);
Tomi Valkeinen72af2b32009-05-11 09:58:19 -0700215 reserved += rg.size;
216 }
Imre Deakb7cc6d42007-03-06 03:16:36 -0800217 omapfb_config.mem_desc.region[i] = rg;
218 configured_regions++;
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800219 }
220 omapfb_config.mem_desc.region_cnt = i;
Imre Deakb7cc6d42007-03-06 03:16:36 -0800221 if (reserved)
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800222 pr_info("Reserving %lu bytes SDRAM for frame buffer\n",
Imre Deakb7cc6d42007-03-06 03:16:36 -0800223 reserved);
224}
Tony Lindgrenc40fae92006-12-07 13:58:10 -0800225
Imre Deakb7cc6d42007-03-06 03:16:36 -0800226/*
227 * Called at sram init time, before anything is pushed to the SRAM stack.
228 * Because of the stack scheme, we will allocate everything from the
229 * start of the lowest address region to the end of SRAM. This will also
230 * include padding for page alignment and possible holes between regions.
231 *
232 * As opposed to the SDRAM case, we'll also do any dynamic allocations at
233 * this point, since the driver built as a module would have problem with
234 * freeing / reallocating the regions.
235 */
236unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
237 unsigned long sram_vstart,
238 unsigned long sram_size,
239 unsigned long pstart_avail,
240 unsigned long size_avail)
241{
242 struct omapfb_mem_region rg;
243 unsigned long pend_avail;
244 unsigned long reserved;
245 int i;
246
247 if (config_invalid)
248 return 0;
249
250 reserved = 0;
251 pend_avail = pstart_avail + size_avail;
252 for (i = 0; ; i++) {
253 if (get_fbmem_region(i, &rg) < 0)
254 break;
255 if (i == OMAPFB_PLANE_NUM) {
256 printk(KERN_ERR
257 "Extraneous FB mem configuration entries\n");
258 config_invalid = 1;
259 return 0;
260 }
261
262 /* Check if it's our memory type. */
263 if (set_fbmem_region_type(&rg, OMAPFB_MEMTYPE_SRAM,
264 sram_pstart, sram_size) < 0 ||
265 (rg.type != OMAPFB_MEMTYPE_SRAM))
266 continue;
267 BUG_ON(omapfb_config.mem_desc.region[i].size);
268
269 if (check_fbmem_region(i, &rg, pstart_avail, size_avail) < 0) {
270 config_invalid = 1;
271 return 0;
272 }
273
274 if (!rg.paddr) {
275 /* Dynamic allocation */
276 if ((size_avail & PAGE_MASK) < rg.size) {
277 printk("Not enough SRAM for FB region %d\n",
278 i);
279 config_invalid = 1;
280 return 0;
281 }
282 size_avail = (size_avail - rg.size) & PAGE_MASK;
283 rg.paddr = pstart_avail + size_avail;
284 }
285 /* Reserve everything above the start of the region. */
286 if (pend_avail - rg.paddr > reserved)
287 reserved = pend_avail - rg.paddr;
288 size_avail = pend_avail - reserved - pstart_avail;
289
290 /*
291 * We have a kernel mapping for this already, so the
292 * driver won't have to make one.
293 */
294 rg.vaddr = (void *)(sram_vstart + rg.paddr - sram_pstart);
295 omapfb_config.mem_desc.region[i] = rg;
296 configured_regions++;
297 }
298 omapfb_config.mem_desc.region_cnt = i;
299 if (reserved)
300 pr_info("Reserving %lu bytes SRAM for frame buffer\n",
301 reserved);
302 return reserved;
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100303}
304
Imre Deak771af222006-12-06 17:13:50 -0800305void omapfb_set_ctrl_platform_data(void *data)
306{
307 omapfb_config.ctrl_platform_data = data;
308}
309
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100310static inline int omap_init_fb(void)
311{
312 const struct omap_lcd_config *conf;
313
Imre Deakb7cc6d42007-03-06 03:16:36 -0800314 if (config_invalid)
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100315 return 0;
Imre Deakb7cc6d42007-03-06 03:16:36 -0800316 if (configured_regions != omapfb_config.mem_desc.region_cnt) {
317 printk(KERN_ERR "Invalid FB mem configuration entries\n");
318 return 0;
319 }
320 conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
321 if (conf == NULL) {
322 if (configured_regions)
323 /* FB mem config, but no LCD config? */
324 printk(KERN_ERR "Missing LCD configuration\n");
325 return 0;
326 }
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100327 omapfb_config.lcd = *conf;
328
329 return platform_device_register(&omap_fb_device);
330}
331
332arch_initcall(omap_init_fb);
333
Tomi Valkeinenb39a982d2009-08-04 16:12:50 +0300334#elif defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
335
336static u64 omap_fb_dma_mask = ~(u32)0;
337static struct omapfb_platform_data omapfb_config;
338
339static struct platform_device omap_fb_device = {
340 .name = "omapfb",
341 .id = -1,
342 .dev = {
343 .dma_mask = &omap_fb_dma_mask,
344 .coherent_dma_mask = ~(u32)0,
345 .platform_data = &omapfb_config,
346 },
347 .num_resources = 0,
348};
349
350void omapfb_set_platform_data(struct omapfb_platform_data *data)
351{
352 omapfb_config = *data;
353}
354
355static inline int omap_init_fb(void)
356{
357 return platform_device_register(&omap_fb_device);
358}
359
360arch_initcall(omap_init_fb);
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100361
Imre Deakb7cc6d42007-03-06 03:16:36 -0800362void omapfb_reserve_sdram(void) {}
363unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
364 unsigned long sram_vstart,
365 unsigned long sram_size,
366 unsigned long start_avail,
David Brownellcc150b02007-03-28 16:38:14 -0700367 unsigned long size_avail)
368{
369 return 0;
370}
Imre Deakb7cc6d42007-03-06 03:16:36 -0800371
Tomi Valkeinenb39a982d2009-08-04 16:12:50 +0300372#else
373
374void omapfb_set_platform_data(struct omapfb_platform_data *data)
375{
376}
377
378void omapfb_reserve_sdram(void) {}
379unsigned long omapfb_reserve_sram(unsigned long sram_pstart,
380 unsigned long sram_vstart,
381 unsigned long sram_size,
382 unsigned long start_avail,
383 unsigned long size_avail)
384{
385 return 0;
386}
Tony Lindgren0dc5e772006-04-02 17:46:26 +0100387
388#endif