blob: 0fd4c784f8df0a776218ff9a896d6df91ab8f853 [file] [log] [blame]
York Sun9b53a9e2008-04-28 02:15:34 -07001/*
2 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
3 *
4 * Freescale DIU Frame Buffer device driver
5 *
6 * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
7 * Paul Widmer <paul.widmer@freescale.com>
8 * Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
9 * York Sun <yorksun@freescale.com>
10 *
11 * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17 *
18 */
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/errno.h>
23#include <linux/string.h>
24#include <linux/slab.h>
25#include <linux/fb.h>
26#include <linux/init.h>
27#include <linux/dma-mapping.h>
28#include <linux/platform_device.h>
29#include <linux/interrupt.h>
30#include <linux/clk.h>
31#include <linux/uaccess.h>
32#include <linux/vmalloc.h>
Timur Tabib715f9f2011-09-28 16:19:48 -050033#include <linux/spinlock.h>
York Sun9b53a9e2008-04-28 02:15:34 -070034
York Sun9b53a9e2008-04-28 02:15:34 -070035#include <sysdev/fsl_soc.h>
Anatolij Gustschin0814a972010-07-23 04:00:36 +000036#include <linux/fsl-diu-fb.h>
Anatolij Gustschin8b856f02010-07-23 04:00:39 +000037#include "edid.h"
York Sun9b53a9e2008-04-28 02:15:34 -070038
Timur Tabib715f9f2011-09-28 16:19:48 -050039#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */
40 /* 1 for plane 0, 2 for plane 1&2 each */
41
42/* HW cursor parameters */
43#define MAX_CURS 32
44
45/* INT_STATUS/INT_MASK field descriptions */
46#define INT_VSYNC 0x01 /* Vsync interrupt */
47#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */
48#define INT_UNDRUN 0x04 /* Under run exception interrupt */
49#define INT_PARERR 0x08 /* Display parameters error interrupt */
50#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */
51
Timur Tabib715f9f2011-09-28 16:19:48 -050052struct diu_hw {
53 struct diu __iomem *diu_reg;
54 spinlock_t reg_lock;
55 unsigned int mode; /* DIU operation mode */
56};
57
58struct diu_addr {
59 void *vaddr; /* Virtual address */
60 dma_addr_t paddr; /* Physical address */
61 __u32 offset;
62};
63
64struct diu_pool {
65 struct diu_addr ad;
66 struct diu_addr gamma;
67 struct diu_addr pallete;
68 struct diu_addr cursor;
69};
70
York Sun9b53a9e2008-04-28 02:15:34 -070071/*
Timur Tabi63cf8df2011-09-15 16:44:51 -050072 * List of supported video modes
73 *
Timur Tabi760af8f2011-09-28 16:19:50 -050074 * The first entry is the default video mode. The remain entries are in
75 * order if increasing resolution and frequency. The 320x240-60 mode is
76 * the initial AOI for the second and third planes.
York Sun9b53a9e2008-04-28 02:15:34 -070077 */
York Sun9b53a9e2008-04-28 02:15:34 -070078static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
79 {
York Sun9b53a9e2008-04-28 02:15:34 -070080 .refresh = 60,
81 .xres = 1024,
82 .yres = 768,
83 .pixclock = 15385,
84 .left_margin = 160,
85 .right_margin = 24,
86 .upper_margin = 29,
87 .lower_margin = 3,
88 .hsync_len = 136,
89 .vsync_len = 6,
90 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
91 .vmode = FB_VMODE_NONINTERLACED
92 },
93 {
Timur Tabi760af8f2011-09-28 16:19:50 -050094 .refresh = 60,
95 .xres = 320,
96 .yres = 240,
97 .pixclock = 79440,
98 .left_margin = 16,
99 .right_margin = 16,
100 .upper_margin = 16,
101 .lower_margin = 5,
102 .hsync_len = 48,
103 .vsync_len = 1,
104 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
105 .vmode = FB_VMODE_NONINTERLACED
106 },
107 {
108 .refresh = 60,
109 .xres = 640,
110 .yres = 480,
111 .pixclock = 39722,
112 .left_margin = 48,
113 .right_margin = 16,
114 .upper_margin = 33,
115 .lower_margin = 10,
116 .hsync_len = 96,
117 .vsync_len = 2,
118 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
119 .vmode = FB_VMODE_NONINTERLACED
120 },
121 {
122 .refresh = 72,
123 .xres = 640,
124 .yres = 480,
125 .pixclock = 32052,
126 .left_margin = 128,
127 .right_margin = 24,
128 .upper_margin = 28,
129 .lower_margin = 9,
130 .hsync_len = 40,
131 .vsync_len = 3,
132 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
133 .vmode = FB_VMODE_NONINTERLACED
134 },
135 {
136 .refresh = 75,
137 .xres = 640,
138 .yres = 480,
139 .pixclock = 31747,
140 .left_margin = 120,
141 .right_margin = 16,
142 .upper_margin = 16,
143 .lower_margin = 1,
144 .hsync_len = 64,
145 .vsync_len = 3,
146 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
147 .vmode = FB_VMODE_NONINTERLACED
148 },
149 {
150 .refresh = 90,
151 .xres = 640,
152 .yres = 480,
153 .pixclock = 25057,
154 .left_margin = 120,
155 .right_margin = 32,
156 .upper_margin = 14,
157 .lower_margin = 25,
158 .hsync_len = 40,
159 .vsync_len = 14,
160 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
161 .vmode = FB_VMODE_NONINTERLACED
162 },
163 {
164 .refresh = 100,
165 .xres = 640,
166 .yres = 480,
167 .pixclock = 22272,
168 .left_margin = 48,
169 .right_margin = 32,
170 .upper_margin = 17,
171 .lower_margin = 22,
172 .hsync_len = 128,
173 .vsync_len = 12,
174 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
175 .vmode = FB_VMODE_NONINTERLACED
176 },
177 {
178 .refresh = 60,
179 .xres = 800,
180 .yres = 480,
181 .pixclock = 33805,
182 .left_margin = 96,
183 .right_margin = 24,
184 .upper_margin = 10,
185 .lower_margin = 3,
186 .hsync_len = 72,
187 .vsync_len = 7,
188 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
189 .vmode = FB_VMODE_NONINTERLACED
190 },
191 {
192 .refresh = 60,
193 .xres = 800,
194 .yres = 600,
195 .pixclock = 25000,
196 .left_margin = 88,
197 .right_margin = 40,
198 .upper_margin = 23,
199 .lower_margin = 1,
200 .hsync_len = 128,
201 .vsync_len = 4,
202 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
203 .vmode = FB_VMODE_NONINTERLACED
204 },
205 {
206 .refresh = 60,
207 .xres = 854,
208 .yres = 480,
209 .pixclock = 31518,
210 .left_margin = 104,
211 .right_margin = 16,
212 .upper_margin = 13,
213 .lower_margin = 1,
214 .hsync_len = 88,
215 .vsync_len = 3,
216 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
217 .vmode = FB_VMODE_NONINTERLACED
218 },
219 {
York Sun9b53a9e2008-04-28 02:15:34 -0700220 .refresh = 70,
221 .xres = 1024,
222 .yres = 768,
223 .pixclock = 16886,
224 .left_margin = 3,
225 .right_margin = 3,
226 .upper_margin = 2,
227 .lower_margin = 2,
228 .hsync_len = 40,
229 .vsync_len = 18,
230 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
231 .vmode = FB_VMODE_NONINTERLACED
232 },
233 {
York Sun9b53a9e2008-04-28 02:15:34 -0700234 .refresh = 75,
235 .xres = 1024,
236 .yres = 768,
237 .pixclock = 15009,
238 .left_margin = 3,
239 .right_margin = 3,
240 .upper_margin = 2,
241 .lower_margin = 2,
242 .hsync_len = 80,
243 .vsync_len = 32,
244 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
245 .vmode = FB_VMODE_NONINTERLACED
246 },
247 {
Timur Tabi760af8f2011-09-28 16:19:50 -0500248 .refresh = 60,
249 .xres = 1280,
250 .yres = 480,
251 .pixclock = 18939,
252 .left_margin = 353,
253 .right_margin = 47,
254 .upper_margin = 39,
255 .lower_margin = 4,
256 .hsync_len = 8,
257 .vsync_len = 2,
258 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
259 .vmode = FB_VMODE_NONINTERLACED
260 },
261 {
262 .refresh = 60,
263 .xres = 1280,
264 .yres = 720,
265 .pixclock = 13426,
266 .left_margin = 192,
267 .right_margin = 64,
268 .upper_margin = 22,
269 .lower_margin = 1,
270 .hsync_len = 136,
271 .vsync_len = 3,
272 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
273 .vmode = FB_VMODE_NONINTERLACED
274 },
275 {
York Sun9b53a9e2008-04-28 02:15:34 -0700276 .refresh = 60,
277 .xres = 1280,
278 .yres = 1024,
279 .pixclock = 9375,
280 .left_margin = 38,
281 .right_margin = 128,
282 .upper_margin = 2,
283 .lower_margin = 7,
284 .hsync_len = 216,
285 .vsync_len = 37,
286 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
287 .vmode = FB_VMODE_NONINTERLACED
288 },
289 {
York Sun9b53a9e2008-04-28 02:15:34 -0700290 .refresh = 70,
291 .xres = 1280,
292 .yres = 1024,
293 .pixclock = 9380,
294 .left_margin = 6,
295 .right_margin = 6,
296 .upper_margin = 4,
297 .lower_margin = 4,
298 .hsync_len = 60,
299 .vsync_len = 94,
300 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
301 .vmode = FB_VMODE_NONINTERLACED
302 },
303 {
York Sun9b53a9e2008-04-28 02:15:34 -0700304 .refresh = 75,
305 .xres = 1280,
306 .yres = 1024,
307 .pixclock = 9380,
308 .left_margin = 6,
309 .right_margin = 6,
310 .upper_margin = 4,
311 .lower_margin = 4,
312 .hsync_len = 60,
313 .vsync_len = 15,
314 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
315 .vmode = FB_VMODE_NONINTERLACED
316 },
317 {
York Sun9b53a9e2008-04-28 02:15:34 -0700318 .refresh = 60,
Timur Tabi760af8f2011-09-28 16:19:50 -0500319 .xres = 1920,
320 .yres = 1080,
321 .pixclock = 5787,
322 .left_margin = 328,
323 .right_margin = 120,
324 .upper_margin = 34,
325 .lower_margin = 1,
326 .hsync_len = 208,
327 .vsync_len = 3,
York Sun9b53a9e2008-04-28 02:15:34 -0700328 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
329 .vmode = FB_VMODE_NONINTERLACED
330 },
331};
332
Timur Tabi760af8f2011-09-28 16:19:50 -0500333static char *fb_mode;
York Sun9b53a9e2008-04-28 02:15:34 -0700334static unsigned long default_bpp = 32;
Timur Tabi7653aaa2011-07-09 15:38:14 -0500335static enum fsl_diu_monitor_port monitor_port;
336static char *monitor_string;
York Sun9b53a9e2008-04-28 02:15:34 -0700337
338#if defined(CONFIG_NOT_COHERENT_CACHE)
339static u8 *coherence_data;
340static size_t coherence_data_size;
341static unsigned int d_cache_line_size;
342#endif
343
344static DEFINE_SPINLOCK(diu_lock);
345
346struct fsl_diu_data {
347 struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1];
348 /*FSL_AOI_NUM has one dummy AOI */
349 struct device_attribute dev_attr;
350 struct diu_ad *dummy_ad;
351 void *dummy_aoi_virt;
352 unsigned int irq;
353 int fb_enabled;
Timur Tabi7653aaa2011-07-09 15:38:14 -0500354 enum fsl_diu_monitor_port monitor_port;
York Sun9b53a9e2008-04-28 02:15:34 -0700355};
356
Timur Tabi2572df92011-09-28 16:19:51 -0500357enum mfb_index {
358 PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */
359 PLANE1_AOI0, /* Plane 1, first AOI */
360 PLANE1_AOI1, /* Plane 1, second AOI */
361 PLANE2_AOI0, /* Plane 2, first AOI */
362 PLANE2_AOI1, /* Plane 2, second AOI */
363};
364
York Sun9b53a9e2008-04-28 02:15:34 -0700365struct mfb_info {
Timur Tabi2572df92011-09-28 16:19:51 -0500366 enum mfb_index index;
York Sun9b53a9e2008-04-28 02:15:34 -0700367 char *id;
368 int registered;
York Sun9b53a9e2008-04-28 02:15:34 -0700369 unsigned long pseudo_palette[16];
370 struct diu_ad *ad;
371 int cursor_reset;
372 unsigned char g_alpha;
373 unsigned int count;
374 int x_aoi_d; /* aoi display x offset to physical screen */
375 int y_aoi_d; /* aoi display y offset to physical screen */
376 struct fsl_diu_data *parent;
Anatolij Gustschin8b856f02010-07-23 04:00:39 +0000377 u8 *edid_data;
York Sun9b53a9e2008-04-28 02:15:34 -0700378};
379
380
381static struct mfb_info mfb_template[] = {
Timur Tabi2572df92011-09-28 16:19:51 -0500382 {
383 .index = PLANE0,
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500384 .id = "Panel0",
385 .registered = 0,
386 .count = 0,
387 .x_aoi_d = 0,
388 .y_aoi_d = 0,
York Sun9b53a9e2008-04-28 02:15:34 -0700389 },
Timur Tabi2572df92011-09-28 16:19:51 -0500390 {
391 .index = PLANE1_AOI0,
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500392 .id = "Panel1 AOI0",
393 .registered = 0,
394 .g_alpha = 0xff,
395 .count = 0,
396 .x_aoi_d = 0,
397 .y_aoi_d = 0,
York Sun9b53a9e2008-04-28 02:15:34 -0700398 },
Timur Tabi2572df92011-09-28 16:19:51 -0500399 {
400 .index = PLANE1_AOI1,
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500401 .id = "Panel1 AOI1",
402 .registered = 0,
403 .g_alpha = 0xff,
404 .count = 0,
405 .x_aoi_d = 0,
406 .y_aoi_d = 480,
York Sun9b53a9e2008-04-28 02:15:34 -0700407 },
Timur Tabi2572df92011-09-28 16:19:51 -0500408 {
409 .index = PLANE2_AOI0,
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500410 .id = "Panel2 AOI0",
411 .registered = 0,
412 .g_alpha = 0xff,
413 .count = 0,
414 .x_aoi_d = 640,
415 .y_aoi_d = 0,
York Sun9b53a9e2008-04-28 02:15:34 -0700416 },
Timur Tabi2572df92011-09-28 16:19:51 -0500417 {
418 .index = PLANE2_AOI1,
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500419 .id = "Panel2 AOI1",
420 .registered = 0,
421 .g_alpha = 0xff,
422 .count = 0,
423 .x_aoi_d = 640,
424 .y_aoi_d = 480,
York Sun9b53a9e2008-04-28 02:15:34 -0700425 },
426};
427
428static struct diu_hw dr = {
429 .mode = MFB_MODE1,
430 .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock),
431};
432
433static struct diu_pool pool;
434
Timur Tabi6b51d512008-07-23 21:31:39 -0700435/**
Timur Tabi7653aaa2011-07-09 15:38:14 -0500436 * fsl_diu_name_to_port - convert a port name to a monitor port enum
437 *
438 * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns
439 * the enum fsl_diu_monitor_port that corresponds to that string.
440 *
441 * For compatibility with older versions, a number ("0", "1", or "2") is also
442 * supported.
443 *
444 * If the string is unknown, DVI is assumed.
445 *
446 * If the particular port is not supported by the platform, another port
447 * (platform-specific) is chosen instead.
448 */
449static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
450{
451 enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;
452 unsigned long val;
453
454 if (s) {
455 if (!strict_strtoul(s, 10, &val) && (val <= 2))
456 port = (enum fsl_diu_monitor_port) val;
457 else if (strncmp(s, "lvds", 4) == 0)
458 port = FSL_DIU_PORT_LVDS;
459 else if (strncmp(s, "dlvds", 5) == 0)
460 port = FSL_DIU_PORT_DLVDS;
461 }
462
463 return diu_ops.valid_monitor_port(port);
464}
465
466/**
Timur Tabi6b51d512008-07-23 21:31:39 -0700467 * fsl_diu_alloc - allocate memory for the DIU
468 * @size: number of bytes to allocate
469 * @param: returned physical address of memory
470 *
471 * This function allocates a physically-contiguous block of memory.
York Sun9b53a9e2008-04-28 02:15:34 -0700472 */
Timur Tabi6b51d512008-07-23 21:31:39 -0700473static void *fsl_diu_alloc(size_t size, phys_addr_t *phys)
York Sun9b53a9e2008-04-28 02:15:34 -0700474{
475 void *virt;
476
Timur Tabi6b51d512008-07-23 21:31:39 -0700477 virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO);
Timur Tabi154152a2011-09-15 16:44:47 -0500478 if (virt)
York Sun9b53a9e2008-04-28 02:15:34 -0700479 *phys = virt_to_phys(virt);
York Sun9b53a9e2008-04-28 02:15:34 -0700480
481 return virt;
482}
483
Timur Tabi6b51d512008-07-23 21:31:39 -0700484/**
485 * fsl_diu_free - release DIU memory
486 * @virt: pointer returned by fsl_diu_alloc()
487 * @size: number of bytes allocated by fsl_diu_alloc()
488 *
489 * This function releases memory allocated by fsl_diu_alloc().
490 */
491static void fsl_diu_free(void *virt, size_t size)
York Sun9b53a9e2008-04-28 02:15:34 -0700492{
Timur Tabi6b51d512008-07-23 21:31:39 -0700493 if (virt && size)
494 free_pages_exact(virt, size);
York Sun9b53a9e2008-04-28 02:15:34 -0700495}
496
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000497/*
498 * Workaround for failed writing desc register of planes.
499 * Needed with MPC5121 DIU rev 2.0 silicon.
500 */
501void wr_reg_wa(u32 *reg, u32 val)
502{
503 do {
504 out_be32(reg, val);
505 } while (in_be32(reg) != val);
506}
507
Timur Tabi7e47c212011-09-28 16:19:52 -0500508static void fsl_diu_enable_panel(struct fb_info *info)
York Sun9b53a9e2008-04-28 02:15:34 -0700509{
510 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
511 struct diu *hw = dr.diu_reg;
512 struct diu_ad *ad = mfbi->ad;
513 struct fsl_diu_data *machine_data = mfbi->parent;
York Sun9b53a9e2008-04-28 02:15:34 -0700514
Timur Tabi7e47c212011-09-28 16:19:52 -0500515 switch (mfbi->index) {
516 case PLANE0:
517 if (hw->desc[0] != ad->paddr)
518 wr_reg_wa(&hw->desc[0], ad->paddr);
519 break;
520 case PLANE1_AOI0:
521 cmfbi = machine_data->fsl_diu_info[2]->par;
522 if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
523 if (cmfbi->count > 0) /* AOI1 open */
524 ad->next_ad =
525 cpu_to_le32(cmfbi->ad->paddr);
526 else
527 ad->next_ad = 0;
528 wr_reg_wa(&hw->desc[1], ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700529 }
Timur Tabi7e47c212011-09-28 16:19:52 -0500530 break;
531 case PLANE2_AOI0:
532 cmfbi = machine_data->fsl_diu_info[4]->par;
533 if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
534 if (cmfbi->count > 0) /* AOI1 open */
535 ad->next_ad =
536 cpu_to_le32(cmfbi->ad->paddr);
537 else
538 ad->next_ad = 0;
539 wr_reg_wa(&hw->desc[2], ad->paddr);
540 }
541 break;
542 case PLANE1_AOI1:
543 pmfbi = machine_data->fsl_diu_info[1]->par;
544 ad->next_ad = 0;
545 if (hw->desc[1] == machine_data->dummy_ad->paddr)
546 wr_reg_wa(&hw->desc[1], ad->paddr);
547 else /* AOI0 open */
548 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
549 break;
550 case PLANE2_AOI1:
551 pmfbi = machine_data->fsl_diu_info[3]->par;
552 ad->next_ad = 0;
553 if (hw->desc[2] == machine_data->dummy_ad->paddr)
554 wr_reg_wa(&hw->desc[2], ad->paddr);
555 else /* AOI0 was open */
556 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
557 break;
558 }
York Sun9b53a9e2008-04-28 02:15:34 -0700559}
560
Timur Tabi2572df92011-09-28 16:19:51 -0500561static void fsl_diu_disable_panel(struct fb_info *info)
York Sun9b53a9e2008-04-28 02:15:34 -0700562{
563 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
564 struct diu *hw = dr.diu_reg;
565 struct diu_ad *ad = mfbi->ad;
566 struct fsl_diu_data *machine_data = mfbi->parent;
York Sun9b53a9e2008-04-28 02:15:34 -0700567
568 switch (mfbi->index) {
Timur Tabi2572df92011-09-28 16:19:51 -0500569 case PLANE0:
York Sun9b53a9e2008-04-28 02:15:34 -0700570 if (hw->desc[0] != machine_data->dummy_ad->paddr)
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000571 wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700572 break;
Timur Tabi2572df92011-09-28 16:19:51 -0500573 case PLANE1_AOI0:
York Sun9b53a9e2008-04-28 02:15:34 -0700574 cmfbi = machine_data->fsl_diu_info[2]->par;
575 if (cmfbi->count > 0) /* AOI1 is open */
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000576 wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700577 /* move AOI1 to the first */
578 else /* AOI1 was closed */
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000579 wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700580 /* close AOI 0 */
581 break;
Timur Tabi2572df92011-09-28 16:19:51 -0500582 case PLANE2_AOI0:
York Sun9b53a9e2008-04-28 02:15:34 -0700583 cmfbi = machine_data->fsl_diu_info[4]->par;
584 if (cmfbi->count > 0) /* AOI1 is open */
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000585 wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700586 /* move AOI1 to the first */
587 else /* AOI1 was closed */
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000588 wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700589 /* close AOI 0 */
590 break;
Timur Tabi2572df92011-09-28 16:19:51 -0500591 case PLANE1_AOI1:
York Sun9b53a9e2008-04-28 02:15:34 -0700592 pmfbi = machine_data->fsl_diu_info[1]->par;
593 if (hw->desc[1] != ad->paddr) {
594 /* AOI1 is not the first in the chain */
595 if (pmfbi->count > 0)
596 /* AOI0 is open, must be the first */
597 pmfbi->ad->next_ad = 0;
598 } else /* AOI1 is the first in the chain */
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000599 wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700600 /* close AOI 1 */
601 break;
Timur Tabi2572df92011-09-28 16:19:51 -0500602 case PLANE2_AOI1:
York Sun9b53a9e2008-04-28 02:15:34 -0700603 pmfbi = machine_data->fsl_diu_info[3]->par;
604 if (hw->desc[2] != ad->paddr) {
605 /* AOI1 is not the first in the chain */
606 if (pmfbi->count > 0)
607 /* AOI0 is open, must be the first */
608 pmfbi->ad->next_ad = 0;
609 } else /* AOI1 is the first in the chain */
Anatolij Gustschin0d9dab32010-07-23 04:00:35 +0000610 wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
York Sun9b53a9e2008-04-28 02:15:34 -0700611 /* close AOI 1 */
612 break;
York Sun9b53a9e2008-04-28 02:15:34 -0700613 }
York Sun9b53a9e2008-04-28 02:15:34 -0700614}
615
616static void enable_lcdc(struct fb_info *info)
617{
618 struct diu *hw = dr.diu_reg;
619 struct mfb_info *mfbi = info->par;
620 struct fsl_diu_data *machine_data = mfbi->parent;
621
622 if (!machine_data->fb_enabled) {
623 out_be32(&hw->diu_mode, dr.mode);
624 machine_data->fb_enabled++;
625 }
626}
627
628static void disable_lcdc(struct fb_info *info)
629{
630 struct diu *hw = dr.diu_reg;
631 struct mfb_info *mfbi = info->par;
632 struct fsl_diu_data *machine_data = mfbi->parent;
633
634 if (machine_data->fb_enabled) {
635 out_be32(&hw->diu_mode, 0);
636 machine_data->fb_enabled = 0;
637 }
638}
639
640static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
641 struct fb_info *info)
642{
643 struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
644 struct fsl_diu_data *machine_data = mfbi->parent;
Timur Tabi2572df92011-09-28 16:19:51 -0500645 int available_height, upper_aoi_bottom;
646 enum mfb_index index = mfbi->index;
York Sun9b53a9e2008-04-28 02:15:34 -0700647 int lower_aoi_is_open, upper_aoi_is_open;
648 __u32 base_plane_width, base_plane_height, upper_aoi_height;
649
650 base_plane_width = machine_data->fsl_diu_info[0]->var.xres;
651 base_plane_height = machine_data->fsl_diu_info[0]->var.yres;
652
York Sunfdfaa48332008-08-15 00:40:29 -0700653 if (mfbi->x_aoi_d < 0)
654 mfbi->x_aoi_d = 0;
655 if (mfbi->y_aoi_d < 0)
656 mfbi->y_aoi_d = 0;
York Sun9b53a9e2008-04-28 02:15:34 -0700657 switch (index) {
Timur Tabi2572df92011-09-28 16:19:51 -0500658 case PLANE0:
York Sun9b53a9e2008-04-28 02:15:34 -0700659 if (mfbi->x_aoi_d != 0)
660 mfbi->x_aoi_d = 0;
661 if (mfbi->y_aoi_d != 0)
662 mfbi->y_aoi_d = 0;
663 break;
Timur Tabi2572df92011-09-28 16:19:51 -0500664 case PLANE1_AOI0:
665 case PLANE2_AOI0:
York Sun9b53a9e2008-04-28 02:15:34 -0700666 lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par;
667 lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
668 if (var->xres > base_plane_width)
669 var->xres = base_plane_width;
670 if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
671 mfbi->x_aoi_d = base_plane_width - var->xres;
672
673 if (lower_aoi_is_open)
674 available_height = lower_aoi_mfbi->y_aoi_d;
675 else
676 available_height = base_plane_height;
677 if (var->yres > available_height)
678 var->yres = available_height;
679 if ((mfbi->y_aoi_d + var->yres) > available_height)
680 mfbi->y_aoi_d = available_height - var->yres;
681 break;
Timur Tabi2572df92011-09-28 16:19:51 -0500682 case PLANE1_AOI1:
683 case PLANE2_AOI1:
York Sun9b53a9e2008-04-28 02:15:34 -0700684 upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par;
685 upper_aoi_height =
686 machine_data->fsl_diu_info[index-1]->var.yres;
687 upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
688 upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
689 if (var->xres > base_plane_width)
690 var->xres = base_plane_width;
691 if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
692 mfbi->x_aoi_d = base_plane_width - var->xres;
693 if (mfbi->y_aoi_d < 0)
694 mfbi->y_aoi_d = 0;
695 if (upper_aoi_is_open) {
696 if (mfbi->y_aoi_d < upper_aoi_bottom)
697 mfbi->y_aoi_d = upper_aoi_bottom;
698 available_height = base_plane_height
699 - upper_aoi_bottom;
700 } else
701 available_height = base_plane_height;
702 if (var->yres > available_height)
703 var->yres = available_height;
704 if ((mfbi->y_aoi_d + var->yres) > base_plane_height)
705 mfbi->y_aoi_d = base_plane_height - var->yres;
706 break;
707 }
708}
709/*
710 * Checks to see if the hardware supports the state requested by var passed
711 * in. This function does not alter the hardware state! If the var passed in
712 * is slightly off by what the hardware can support then we alter the var
713 * PASSED in to what we can do. If the hardware doesn't support mode change
714 * a -EINVAL will be returned by the upper layers.
715 */
716static int fsl_diu_check_var(struct fb_var_screeninfo *var,
717 struct fb_info *info)
718{
York Sun9b53a9e2008-04-28 02:15:34 -0700719 if (var->xres_virtual < var->xres)
720 var->xres_virtual = var->xres;
721 if (var->yres_virtual < var->yres)
722 var->yres_virtual = var->yres;
723
724 if (var->xoffset < 0)
725 var->xoffset = 0;
726
727 if (var->yoffset < 0)
728 var->yoffset = 0;
729
730 if (var->xoffset + info->var.xres > info->var.xres_virtual)
731 var->xoffset = info->var.xres_virtual - info->var.xres;
732
733 if (var->yoffset + info->var.yres > info->var.yres_virtual)
734 var->yoffset = info->var.yres_virtual - info->var.yres;
735
736 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
737 (var->bits_per_pixel != 16))
738 var->bits_per_pixel = default_bpp;
739
740 switch (var->bits_per_pixel) {
741 case 16:
742 var->red.length = 5;
743 var->red.offset = 11;
744 var->red.msb_right = 0;
745
746 var->green.length = 6;
747 var->green.offset = 5;
748 var->green.msb_right = 0;
749
750 var->blue.length = 5;
751 var->blue.offset = 0;
752 var->blue.msb_right = 0;
753
754 var->transp.length = 0;
755 var->transp.offset = 0;
756 var->transp.msb_right = 0;
757 break;
758 case 24:
759 var->red.length = 8;
760 var->red.offset = 0;
761 var->red.msb_right = 0;
762
763 var->green.length = 8;
764 var->green.offset = 8;
765 var->green.msb_right = 0;
766
767 var->blue.length = 8;
768 var->blue.offset = 16;
769 var->blue.msb_right = 0;
770
771 var->transp.length = 0;
772 var->transp.offset = 0;
773 var->transp.msb_right = 0;
774 break;
775 case 32:
776 var->red.length = 8;
777 var->red.offset = 16;
778 var->red.msb_right = 0;
779
780 var->green.length = 8;
781 var->green.offset = 8;
782 var->green.msb_right = 0;
783
784 var->blue.length = 8;
785 var->blue.offset = 0;
786 var->blue.msb_right = 0;
787
788 var->transp.length = 8;
789 var->transp.offset = 24;
790 var->transp.msb_right = 0;
791
792 break;
793 }
York Sun9b53a9e2008-04-28 02:15:34 -0700794
795 var->height = -1;
796 var->width = -1;
797 var->grayscale = 0;
798
799 /* Copy nonstd field to/from sync for fbset usage */
800 var->sync |= var->nonstd;
801 var->nonstd |= var->sync;
802
803 adjust_aoi_size_position(var, info);
804 return 0;
805}
806
807static void set_fix(struct fb_info *info)
808{
809 struct fb_fix_screeninfo *fix = &info->fix;
810 struct fb_var_screeninfo *var = &info->var;
811 struct mfb_info *mfbi = info->par;
812
Timur Tabiec02dd22011-09-15 16:44:54 -0500813 strncpy(fix->id, mfbi->id, sizeof(fix->id));
York Sun9b53a9e2008-04-28 02:15:34 -0700814 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
815 fix->type = FB_TYPE_PACKED_PIXELS;
816 fix->accel = FB_ACCEL_NONE;
817 fix->visual = FB_VISUAL_TRUECOLOR;
818 fix->xpanstep = 1;
819 fix->ypanstep = 1;
820}
821
822static void update_lcdc(struct fb_info *info)
823{
824 struct fb_var_screeninfo *var = &info->var;
825 struct mfb_info *mfbi = info->par;
826 struct fsl_diu_data *machine_data = mfbi->parent;
827 struct diu *hw;
828 int i, j;
829 char __iomem *cursor_base, *gamma_table_base;
830
831 u32 temp;
832
833 hw = dr.diu_reg;
834
York Sun9b53a9e2008-04-28 02:15:34 -0700835 diu_ops.set_monitor_port(machine_data->monitor_port);
836 gamma_table_base = pool.gamma.vaddr;
837 cursor_base = pool.cursor.vaddr;
838 /* Prep for DIU init - gamma table, cursor table */
839
840 for (i = 0; i <= 2; i++)
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500841 for (j = 0; j <= 255; j++)
842 *gamma_table_base++ = j;
York Sun9b53a9e2008-04-28 02:15:34 -0700843
844 diu_ops.set_gamma_table(machine_data->monitor_port, pool.gamma.vaddr);
845
York Sun9b53a9e2008-04-28 02:15:34 -0700846 disable_lcdc(info);
847
848 /* Program DIU registers */
849
850 out_be32(&hw->gamma, pool.gamma.paddr);
851 out_be32(&hw->cursor, pool.cursor.paddr);
852
853 out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */
854 out_be32(&hw->bgnd_wb, 0); /* BGND_WB */
855 out_be32(&hw->disp_size, (var->yres << 16 | var->xres));
856 /* DISP SIZE */
York Sun9b53a9e2008-04-28 02:15:34 -0700857 out_be32(&hw->wb_size, 0); /* WB SIZE */
858 out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */
859
860 /* Horizontal and vertical configuration register */
861 temp = var->left_margin << 22 | /* BP_H */
862 var->hsync_len << 11 | /* PW_H */
863 var->right_margin; /* FP_H */
864
865 out_be32(&hw->hsyn_para, temp);
866
867 temp = var->upper_margin << 22 | /* BP_V */
868 var->vsync_len << 11 | /* PW_V */
869 var->lower_margin; /* FP_V */
870
871 out_be32(&hw->vsyn_para, temp);
872
York Sun9b53a9e2008-04-28 02:15:34 -0700873 diu_ops.set_pixel_clock(var->pixclock);
874
875 out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */
876 out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */
877 out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */
878 out_be32(&hw->plut, 0x01F5F666);
879
880 /* Enable the DIU */
881 enable_lcdc(info);
882}
883
884static int map_video_memory(struct fb_info *info)
885{
886 phys_addr_t phys;
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700887 u32 smem_len = info->fix.line_length * info->var.yres_virtual;
York Sun9b53a9e2008-04-28 02:15:34 -0700888
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700889 info->screen_base = fsl_diu_alloc(smem_len, &phys);
Anton Vorontsov05946bc2008-07-04 09:59:38 -0700890 if (info->screen_base == NULL) {
Timur Tabi154152a2011-09-15 16:44:47 -0500891 dev_err(info->dev, "unable to allocate fb memory\n");
York Sun9b53a9e2008-04-28 02:15:34 -0700892 return -ENOMEM;
893 }
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700894 mutex_lock(&info->mm_lock);
York Sun9b53a9e2008-04-28 02:15:34 -0700895 info->fix.smem_start = (unsigned long) phys;
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700896 info->fix.smem_len = smem_len;
897 mutex_unlock(&info->mm_lock);
York Sun9b53a9e2008-04-28 02:15:34 -0700898 info->screen_size = info->fix.smem_len;
899
York Sun9b53a9e2008-04-28 02:15:34 -0700900 return 0;
901}
902
903static void unmap_video_memory(struct fb_info *info)
904{
905 fsl_diu_free(info->screen_base, info->fix.smem_len);
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700906 mutex_lock(&info->mm_lock);
Anton Vorontsov05946bc2008-07-04 09:59:38 -0700907 info->screen_base = NULL;
York Sun9b53a9e2008-04-28 02:15:34 -0700908 info->fix.smem_start = 0;
909 info->fix.smem_len = 0;
Krzysztof Helt537a1bf2009-06-30 11:41:29 -0700910 mutex_unlock(&info->mm_lock);
York Sun9b53a9e2008-04-28 02:15:34 -0700911}
912
913/*
York Sunae5591e2008-08-15 00:40:28 -0700914 * Using the fb_var_screeninfo in fb_info we set the aoi of this
915 * particular framebuffer. It is a light version of fsl_diu_set_par.
916 */
917static int fsl_diu_set_aoi(struct fb_info *info)
918{
919 struct fb_var_screeninfo *var = &info->var;
920 struct mfb_info *mfbi = info->par;
921 struct diu_ad *ad = mfbi->ad;
922
923 /* AOI should not be greater than display size */
924 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
925 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
926 return 0;
927}
928
929/*
York Sun9b53a9e2008-04-28 02:15:34 -0700930 * Using the fb_var_screeninfo in fb_info we set the resolution of this
931 * particular framebuffer. This function alters the fb_fix_screeninfo stored
932 * in fb_info. It does not alter var in fb_info since we are using that
933 * data. This means we depend on the data in var inside fb_info to be
934 * supported by the hardware. fsl_diu_check_var is always called before
935 * fsl_diu_set_par to ensure this.
936 */
937static int fsl_diu_set_par(struct fb_info *info)
938{
939 unsigned long len;
940 struct fb_var_screeninfo *var = &info->var;
941 struct mfb_info *mfbi = info->par;
942 struct fsl_diu_data *machine_data = mfbi->parent;
943 struct diu_ad *ad = mfbi->ad;
944 struct diu *hw;
945
946 hw = dr.diu_reg;
947
948 set_fix(info);
949 mfbi->cursor_reset = 1;
950
951 len = info->var.yres_virtual * info->fix.line_length;
952 /* Alloc & dealloc each time resolution/bpp change */
953 if (len != info->fix.smem_len) {
954 if (info->fix.smem_start)
955 unmap_video_memory(info);
York Sun9b53a9e2008-04-28 02:15:34 -0700956
957 /* Memory allocation for framebuffer */
958 if (map_video_memory(info)) {
Timur Tabi154152a2011-09-15 16:44:47 -0500959 dev_err(info->dev, "unable to allocate fb memory 1\n");
York Sun9b53a9e2008-04-28 02:15:34 -0700960 return -ENOMEM;
961 }
962 }
963
Timur Tabi7653aaa2011-07-09 15:38:14 -0500964 ad->pix_fmt = diu_ops.get_pixel_format(machine_data->monitor_port,
965 var->bits_per_pixel);
York Sun9b53a9e2008-04-28 02:15:34 -0700966 ad->addr = cpu_to_le32(info->fix.smem_start);
York Sunae5591e2008-08-15 00:40:28 -0700967 ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
968 var->xres_virtual) | mfbi->g_alpha;
969 /* AOI should not be greater than display size */
York Sun9b53a9e2008-04-28 02:15:34 -0700970 ad->aoi_size = cpu_to_le32((var->yres << 16) | var->xres);
York Sunae5591e2008-08-15 00:40:28 -0700971 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
York Sun9b53a9e2008-04-28 02:15:34 -0700972 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
973
974 /* Disable chroma keying function */
975 ad->ckmax_r = 0;
976 ad->ckmax_g = 0;
977 ad->ckmax_b = 0;
978
979 ad->ckmin_r = 255;
980 ad->ckmin_g = 255;
981 ad->ckmin_b = 255;
982
Timur Tabi2572df92011-09-28 16:19:51 -0500983 if (mfbi->index == PLANE0)
York Sun9b53a9e2008-04-28 02:15:34 -0700984 update_lcdc(info);
985 return 0;
986}
987
988static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
989{
Timur Tabi4a85dc8b2011-09-15 16:44:46 -0500990 return ((val << width) + 0x7FFF - val) >> 16;
York Sun9b53a9e2008-04-28 02:15:34 -0700991}
992
993/*
994 * Set a single color register. The values supplied have a 16 bit magnitude
995 * which needs to be scaled in this function for the hardware. Things to take
996 * into consideration are how many color registers, if any, are supported with
997 * the current color visual. With truecolor mode no color palettes are
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300998 * supported. Here a pseudo palette is created which we store the value in
York Sun9b53a9e2008-04-28 02:15:34 -0700999 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
1000 * color palette.
1001 */
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001002static int fsl_diu_setcolreg(unsigned int regno, unsigned int red,
1003 unsigned int green, unsigned int blue,
1004 unsigned int transp, struct fb_info *info)
York Sun9b53a9e2008-04-28 02:15:34 -07001005{
1006 int ret = 1;
1007
1008 /*
1009 * If greyscale is true, then we convert the RGB value
1010 * to greyscale no matter what visual we are using.
1011 */
1012 if (info->var.grayscale)
1013 red = green = blue = (19595 * red + 38470 * green +
1014 7471 * blue) >> 16;
1015 switch (info->fix.visual) {
1016 case FB_VISUAL_TRUECOLOR:
1017 /*
1018 * 16-bit True Colour. We encode the RGB value
1019 * according to the RGB bitfield information.
1020 */
1021 if (regno < 16) {
1022 u32 *pal = info->pseudo_palette;
1023 u32 v;
1024
1025 red = CNVT_TOHW(red, info->var.red.length);
1026 green = CNVT_TOHW(green, info->var.green.length);
1027 blue = CNVT_TOHW(blue, info->var.blue.length);
1028 transp = CNVT_TOHW(transp, info->var.transp.length);
1029
1030 v = (red << info->var.red.offset) |
1031 (green << info->var.green.offset) |
1032 (blue << info->var.blue.offset) |
1033 (transp << info->var.transp.offset);
1034
1035 pal[regno] = v;
1036 ret = 0;
1037 }
1038 break;
York Sun9b53a9e2008-04-28 02:15:34 -07001039 }
1040
1041 return ret;
1042}
1043
1044/*
1045 * Pan (or wrap, depending on the `vmode' field) the display using the
1046 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
1047 * don't fit, return -EINVAL.
1048 */
1049static int fsl_diu_pan_display(struct fb_var_screeninfo *var,
1050 struct fb_info *info)
1051{
1052 if ((info->var.xoffset == var->xoffset) &&
1053 (info->var.yoffset == var->yoffset))
1054 return 0; /* No change, do nothing */
1055
1056 if (var->xoffset < 0 || var->yoffset < 0
1057 || var->xoffset + info->var.xres > info->var.xres_virtual
1058 || var->yoffset + info->var.yres > info->var.yres_virtual)
1059 return -EINVAL;
1060
1061 info->var.xoffset = var->xoffset;
1062 info->var.yoffset = var->yoffset;
1063
1064 if (var->vmode & FB_VMODE_YWRAP)
1065 info->var.vmode |= FB_VMODE_YWRAP;
1066 else
1067 info->var.vmode &= ~FB_VMODE_YWRAP;
1068
York Sunae5591e2008-08-15 00:40:28 -07001069 fsl_diu_set_aoi(info);
1070
York Sun9b53a9e2008-04-28 02:15:34 -07001071 return 0;
1072}
1073
York Sun9b53a9e2008-04-28 02:15:34 -07001074static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
1075 unsigned long arg)
1076{
1077 struct mfb_info *mfbi = info->par;
1078 struct diu_ad *ad = mfbi->ad;
1079 struct mfb_chroma_key ck;
1080 unsigned char global_alpha;
1081 struct aoi_display_offset aoi_d;
1082 __u32 pix_fmt;
1083 void __user *buf = (void __user *)arg;
1084
1085 if (!arg)
1086 return -EINVAL;
1087 switch (cmd) {
Timur Tabi36b0b1d2011-10-04 19:36:44 -05001088 case MFB_SET_PIXFMT_OLD:
1089 dev_warn(info->dev,
1090 "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n",
1091 MFB_SET_PIXFMT_OLD);
York Sun9b53a9e2008-04-28 02:15:34 -07001092 case MFB_SET_PIXFMT:
1093 if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
1094 return -EFAULT;
1095 ad->pix_fmt = pix_fmt;
York Sun9b53a9e2008-04-28 02:15:34 -07001096 break;
Timur Tabi36b0b1d2011-10-04 19:36:44 -05001097 case MFB_GET_PIXFMT_OLD:
1098 dev_warn(info->dev,
1099 "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n",
1100 MFB_GET_PIXFMT_OLD);
York Sun9b53a9e2008-04-28 02:15:34 -07001101 case MFB_GET_PIXFMT:
1102 pix_fmt = ad->pix_fmt;
1103 if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
1104 return -EFAULT;
York Sun9b53a9e2008-04-28 02:15:34 -07001105 break;
1106 case MFB_SET_AOID:
1107 if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
1108 return -EFAULT;
1109 mfbi->x_aoi_d = aoi_d.x_aoi_d;
1110 mfbi->y_aoi_d = aoi_d.y_aoi_d;
York Sun9b53a9e2008-04-28 02:15:34 -07001111 fsl_diu_check_var(&info->var, info);
York Sunae5591e2008-08-15 00:40:28 -07001112 fsl_diu_set_aoi(info);
York Sun9b53a9e2008-04-28 02:15:34 -07001113 break;
1114 case MFB_GET_AOID:
1115 aoi_d.x_aoi_d = mfbi->x_aoi_d;
1116 aoi_d.y_aoi_d = mfbi->y_aoi_d;
1117 if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
1118 return -EFAULT;
York Sun9b53a9e2008-04-28 02:15:34 -07001119 break;
1120 case MFB_GET_ALPHA:
1121 global_alpha = mfbi->g_alpha;
1122 if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
1123 return -EFAULT;
York Sun9b53a9e2008-04-28 02:15:34 -07001124 break;
1125 case MFB_SET_ALPHA:
1126 /* set panel information */
1127 if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
1128 return -EFAULT;
1129 ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
1130 (global_alpha & 0xff);
1131 mfbi->g_alpha = global_alpha;
York Sun9b53a9e2008-04-28 02:15:34 -07001132 break;
1133 case MFB_SET_CHROMA_KEY:
1134 /* set panel winformation */
1135 if (copy_from_user(&ck, buf, sizeof(ck)))
1136 return -EFAULT;
1137
1138 if (ck.enable &&
1139 (ck.red_max < ck.red_min ||
1140 ck.green_max < ck.green_min ||
1141 ck.blue_max < ck.blue_min))
1142 return -EINVAL;
1143
1144 if (!ck.enable) {
1145 ad->ckmax_r = 0;
1146 ad->ckmax_g = 0;
1147 ad->ckmax_b = 0;
1148 ad->ckmin_r = 255;
1149 ad->ckmin_g = 255;
1150 ad->ckmin_b = 255;
1151 } else {
1152 ad->ckmax_r = ck.red_max;
1153 ad->ckmax_g = ck.green_max;
1154 ad->ckmax_b = ck.blue_max;
1155 ad->ckmin_r = ck.red_min;
1156 ad->ckmin_g = ck.green_min;
1157 ad->ckmin_b = ck.blue_min;
1158 }
York Sun9b53a9e2008-04-28 02:15:34 -07001159 break;
York Sun9b53a9e2008-04-28 02:15:34 -07001160 default:
Timur Tabi154152a2011-09-15 16:44:47 -05001161 dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
York Sun9b53a9e2008-04-28 02:15:34 -07001162 return -ENOIOCTLCMD;
1163 }
1164
1165 return 0;
1166}
1167
1168/* turn on fb if count == 1
1169 */
1170static int fsl_diu_open(struct fb_info *info, int user)
1171{
1172 struct mfb_info *mfbi = info->par;
1173 int res = 0;
1174
Anatolij Gustschin4b5006e2010-07-23 04:00:37 +00001175 /* free boot splash memory on first /dev/fb0 open */
Timur Tabi2572df92011-09-28 16:19:51 -05001176 if ((mfbi->index == PLANE0) && diu_ops.release_bootmem)
Anatolij Gustschin4b5006e2010-07-23 04:00:37 +00001177 diu_ops.release_bootmem();
1178
York Sun9b53a9e2008-04-28 02:15:34 -07001179 spin_lock(&diu_lock);
1180 mfbi->count++;
1181 if (mfbi->count == 1) {
York Sun9b53a9e2008-04-28 02:15:34 -07001182 fsl_diu_check_var(&info->var, info);
1183 res = fsl_diu_set_par(info);
1184 if (res < 0)
1185 mfbi->count--;
Timur Tabi7e47c212011-09-28 16:19:52 -05001186 else
1187 fsl_diu_enable_panel(info);
York Sun9b53a9e2008-04-28 02:15:34 -07001188 }
1189
1190 spin_unlock(&diu_lock);
1191 return res;
1192}
1193
1194/* turn off fb if count == 0
1195 */
1196static int fsl_diu_release(struct fb_info *info, int user)
1197{
1198 struct mfb_info *mfbi = info->par;
1199 int res = 0;
1200
1201 spin_lock(&diu_lock);
1202 mfbi->count--;
Timur Tabi2572df92011-09-28 16:19:51 -05001203 if (mfbi->count == 0)
1204 fsl_diu_disable_panel(info);
1205
York Sun9b53a9e2008-04-28 02:15:34 -07001206 spin_unlock(&diu_lock);
1207 return res;
1208}
1209
1210static struct fb_ops fsl_diu_ops = {
1211 .owner = THIS_MODULE,
1212 .fb_check_var = fsl_diu_check_var,
1213 .fb_set_par = fsl_diu_set_par,
1214 .fb_setcolreg = fsl_diu_setcolreg,
York Sun9b53a9e2008-04-28 02:15:34 -07001215 .fb_pan_display = fsl_diu_pan_display,
1216 .fb_fillrect = cfb_fillrect,
1217 .fb_copyarea = cfb_copyarea,
1218 .fb_imageblit = cfb_imageblit,
1219 .fb_ioctl = fsl_diu_ioctl,
1220 .fb_open = fsl_diu_open,
1221 .fb_release = fsl_diu_release,
1222};
1223
1224static int init_fbinfo(struct fb_info *info)
1225{
1226 struct mfb_info *mfbi = info->par;
1227
1228 info->device = NULL;
1229 info->var.activate = FB_ACTIVATE_NOW;
1230 info->fbops = &fsl_diu_ops;
1231 info->flags = FBINFO_FLAG_DEFAULT;
1232 info->pseudo_palette = &mfbi->pseudo_palette;
1233
1234 /* Allocate colormap */
1235 fb_alloc_cmap(&info->cmap, 16, 0);
1236 return 0;
1237}
1238
Anton Vorontsov05946bc2008-07-04 09:59:38 -07001239static int __devinit install_fb(struct fb_info *info)
York Sun9b53a9e2008-04-28 02:15:34 -07001240{
1241 int rc;
1242 struct mfb_info *mfbi = info->par;
1243 const char *aoi_mode, *init_aoi_mode = "320x240";
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001244 struct fb_videomode *db = fsl_diu_mode_db;
1245 unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
1246 int has_default_mode = 1;
York Sun9b53a9e2008-04-28 02:15:34 -07001247
1248 if (init_fbinfo(info))
1249 return -EINVAL;
1250
Timur Tabi2572df92011-09-28 16:19:51 -05001251 if (mfbi->index == PLANE0) {
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001252 if (mfbi->edid_data) {
1253 /* Now build modedb from EDID */
1254 fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs);
1255 fb_videomode_to_modelist(info->monspecs.modedb,
1256 info->monspecs.modedb_len,
1257 &info->modelist);
1258 db = info->monspecs.modedb;
1259 dbsize = info->monspecs.modedb_len;
1260 }
York Sun9b53a9e2008-04-28 02:15:34 -07001261 aoi_mode = fb_mode;
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001262 } else {
York Sun9b53a9e2008-04-28 02:15:34 -07001263 aoi_mode = init_aoi_mode;
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001264 }
Timur Tabi63cf8df2011-09-15 16:44:51 -05001265 rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,
1266 default_bpp);
Timur Tabi154152a2011-09-15 16:44:47 -05001267 if (!rc) {
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001268 /*
1269 * For plane 0 we continue and look into
1270 * driver's internal modedb.
1271 */
Timur Tabi2572df92011-09-28 16:19:51 -05001272 if ((mfbi->index == PLANE0) && mfbi->edid_data)
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001273 has_default_mode = 0;
1274 else
1275 return -EINVAL;
York Sun9b53a9e2008-04-28 02:15:34 -07001276 }
1277
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001278 if (!has_default_mode) {
1279 rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
Timur Tabi63cf8df2011-09-15 16:44:51 -05001280 ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);
1281 if (rc)
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001282 has_default_mode = 1;
1283 }
1284
1285 /* Still not found, use preferred mode from database if any */
1286 if (!has_default_mode && info->monspecs.modedb) {
1287 struct fb_monspecs *specs = &info->monspecs;
1288 struct fb_videomode *modedb = &specs->modedb[0];
1289
1290 /*
1291 * Get preferred timing. If not found,
1292 * first mode in database will be used.
1293 */
1294 if (specs->misc & FB_MISC_1ST_DETAIL) {
1295 int i;
1296
1297 for (i = 0; i < specs->modedb_len; i++) {
1298 if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
1299 modedb = &specs->modedb[i];
1300 break;
1301 }
1302 }
1303 }
1304
1305 info->var.bits_per_pixel = default_bpp;
1306 fb_videomode_to_var(&info->var, modedb);
1307 }
1308
York Sun9b53a9e2008-04-28 02:15:34 -07001309 if (fsl_diu_check_var(&info->var, info)) {
Timur Tabi154152a2011-09-15 16:44:47 -05001310 dev_err(info->dev, "fsl_diu_check_var failed\n");
Timur Tabi589c7972011-09-15 16:44:55 -05001311 unmap_video_memory(info);
York Sun9b53a9e2008-04-28 02:15:34 -07001312 fb_dealloc_cmap(&info->cmap);
1313 return -EINVAL;
1314 }
1315
York Sun9b53a9e2008-04-28 02:15:34 -07001316 if (register_framebuffer(info) < 0) {
Timur Tabi154152a2011-09-15 16:44:47 -05001317 dev_err(info->dev, "register_framebuffer failed\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001318 unmap_video_memory(info);
1319 fb_dealloc_cmap(&info->cmap);
1320 return -EINVAL;
1321 }
1322
1323 mfbi->registered = 1;
Timur Tabi154152a2011-09-15 16:44:47 -05001324 dev_info(info->dev, "%s registered successfully\n", mfbi->id);
York Sun9b53a9e2008-04-28 02:15:34 -07001325
1326 return 0;
1327}
1328
Anton Vorontsov05946bc2008-07-04 09:59:38 -07001329static void uninstall_fb(struct fb_info *info)
York Sun9b53a9e2008-04-28 02:15:34 -07001330{
1331 struct mfb_info *mfbi = info->par;
1332
1333 if (!mfbi->registered)
1334 return;
1335
Timur Tabi2572df92011-09-28 16:19:51 -05001336 if (mfbi->index == PLANE0)
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001337 kfree(mfbi->edid_data);
1338
York Sun9b53a9e2008-04-28 02:15:34 -07001339 unregister_framebuffer(info);
1340 unmap_video_memory(info);
1341 if (&info->cmap)
1342 fb_dealloc_cmap(&info->cmap);
1343
1344 mfbi->registered = 0;
1345}
1346
1347static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
1348{
1349 struct diu *hw = dr.diu_reg;
1350 unsigned int status = in_be32(&hw->int_status);
1351
1352 if (status) {
1353 /* This is the workaround for underrun */
1354 if (status & INT_UNDRUN) {
1355 out_be32(&hw->diu_mode, 0);
York Sun9b53a9e2008-04-28 02:15:34 -07001356 udelay(1);
1357 out_be32(&hw->diu_mode, 1);
1358 }
1359#if defined(CONFIG_NOT_COHERENT_CACHE)
1360 else if (status & INT_VSYNC) {
1361 unsigned int i;
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001362
York Sun9b53a9e2008-04-28 02:15:34 -07001363 for (i = 0; i < coherence_data_size;
1364 i += d_cache_line_size)
1365 __asm__ __volatile__ (
1366 "dcbz 0, %[input]"
1367 ::[input]"r"(&coherence_data[i]));
1368 }
1369#endif
1370 return IRQ_HANDLED;
1371 }
1372 return IRQ_NONE;
1373}
1374
1375static int request_irq_local(int irq)
1376{
Timur Tabibada04f2011-09-15 16:44:52 -05001377 u32 ints;
York Sun9b53a9e2008-04-28 02:15:34 -07001378 struct diu *hw;
1379 int ret;
1380
1381 hw = dr.diu_reg;
1382
1383 /* Read to clear the status */
Timur Tabibada04f2011-09-15 16:44:52 -05001384 in_be32(&hw->int_status);
York Sun9b53a9e2008-04-28 02:15:34 -07001385
Timur Tabif8c6bf62011-09-15 16:44:53 -05001386 ret = request_irq(irq, fsl_diu_isr, 0, "fsl-diu-fb", NULL);
Timur Tabi154152a2011-09-15 16:44:47 -05001387 if (!ret) {
York Sun9b53a9e2008-04-28 02:15:34 -07001388 ints = INT_PARERR | INT_LS_BF_VS;
1389#if !defined(CONFIG_NOT_COHERENT_CACHE)
1390 ints |= INT_VSYNC;
1391#endif
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001392
York Sun9b53a9e2008-04-28 02:15:34 -07001393 if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3)
1394 ints |= INT_VSYNC_WB;
1395
1396 /* Read to clear the status */
Timur Tabibada04f2011-09-15 16:44:52 -05001397 in_be32(&hw->int_status);
York Sun9b53a9e2008-04-28 02:15:34 -07001398 out_be32(&hw->int_mask, ints);
1399 }
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001400
York Sun9b53a9e2008-04-28 02:15:34 -07001401 return ret;
1402}
1403
1404static void free_irq_local(int irq)
1405{
1406 struct diu *hw = dr.diu_reg;
1407
1408 /* Disable all LCDC interrupt */
1409 out_be32(&hw->int_mask, 0x1f);
1410
Anton Vorontsov05946bc2008-07-04 09:59:38 -07001411 free_irq(irq, NULL);
York Sun9b53a9e2008-04-28 02:15:34 -07001412}
1413
1414#ifdef CONFIG_PM
1415/*
1416 * Power management hooks. Note that we won't be called from IRQ context,
1417 * unlike the blank functions above, so we may sleep.
1418 */
Grant Likely2dc11582010-08-06 09:25:50 -06001419static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
York Sun9b53a9e2008-04-28 02:15:34 -07001420{
1421 struct fsl_diu_data *machine_data;
1422
Takashi Iwai48948a32008-07-08 18:41:17 +02001423 machine_data = dev_get_drvdata(&ofdev->dev);
York Sun9b53a9e2008-04-28 02:15:34 -07001424 disable_lcdc(machine_data->fsl_diu_info[0]);
1425
1426 return 0;
1427}
1428
Grant Likely2dc11582010-08-06 09:25:50 -06001429static int fsl_diu_resume(struct platform_device *ofdev)
York Sun9b53a9e2008-04-28 02:15:34 -07001430{
1431 struct fsl_diu_data *machine_data;
1432
Takashi Iwai48948a32008-07-08 18:41:17 +02001433 machine_data = dev_get_drvdata(&ofdev->dev);
York Sun9b53a9e2008-04-28 02:15:34 -07001434 enable_lcdc(machine_data->fsl_diu_info[0]);
1435
1436 return 0;
1437}
1438
1439#else
1440#define fsl_diu_suspend NULL
1441#define fsl_diu_resume NULL
1442#endif /* CONFIG_PM */
1443
1444/* Align to 64-bit(8-byte), 32-byte, etc. */
Anton Vorontsovf3791882009-04-04 22:31:20 +04001445static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size,
1446 u32 bytes_align)
York Sun9b53a9e2008-04-28 02:15:34 -07001447{
Timur Tabibada04f2011-09-15 16:44:52 -05001448 u32 offset;
1449 dma_addr_t mask;
York Sun9b53a9e2008-04-28 02:15:34 -07001450
Timur Tabibada04f2011-09-15 16:44:52 -05001451 buf->vaddr =
1452 dma_alloc_coherent(dev, size + bytes_align, &buf->paddr,
1453 GFP_DMA | __GFP_ZERO);
York Sun9b53a9e2008-04-28 02:15:34 -07001454 if (!buf->vaddr)
1455 return -ENOMEM;
1456
York Sun9b53a9e2008-04-28 02:15:34 -07001457 mask = bytes_align - 1;
Timur Tabibada04f2011-09-15 16:44:52 -05001458 offset = buf->paddr & mask;
York Sun9b53a9e2008-04-28 02:15:34 -07001459 if (offset) {
1460 buf->offset = bytes_align - offset;
Timur Tabibada04f2011-09-15 16:44:52 -05001461 buf->paddr = buf->paddr + offset;
York Sun9b53a9e2008-04-28 02:15:34 -07001462 } else
1463 buf->offset = 0;
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001464
York Sun9b53a9e2008-04-28 02:15:34 -07001465 return 0;
1466}
1467
Anton Vorontsovf3791882009-04-04 22:31:20 +04001468static void free_buf(struct device *dev, struct diu_addr *buf, u32 size,
1469 u32 bytes_align)
York Sun9b53a9e2008-04-28 02:15:34 -07001470{
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001471 dma_free_coherent(dev, size + bytes_align, buf->vaddr,
1472 buf->paddr - buf->offset);
York Sun9b53a9e2008-04-28 02:15:34 -07001473}
1474
1475static ssize_t store_monitor(struct device *device,
1476 struct device_attribute *attr, const char *buf, size_t count)
1477{
Timur Tabi7653aaa2011-07-09 15:38:14 -05001478 enum fsl_diu_monitor_port old_monitor_port;
York Sun9b53a9e2008-04-28 02:15:34 -07001479 struct fsl_diu_data *machine_data =
1480 container_of(attr, struct fsl_diu_data, dev_attr);
1481
York Sun9b53a9e2008-04-28 02:15:34 -07001482 old_monitor_port = machine_data->monitor_port;
Timur Tabi7653aaa2011-07-09 15:38:14 -05001483 machine_data->monitor_port = fsl_diu_name_to_port(buf);
York Sun9b53a9e2008-04-28 02:15:34 -07001484
1485 if (old_monitor_port != machine_data->monitor_port) {
1486 /* All AOIs need adjust pixel format
1487 * fsl_diu_set_par only change the pixsel format here
1488 * unlikely to fail. */
1489 fsl_diu_set_par(machine_data->fsl_diu_info[0]);
1490 fsl_diu_set_par(machine_data->fsl_diu_info[1]);
1491 fsl_diu_set_par(machine_data->fsl_diu_info[2]);
1492 fsl_diu_set_par(machine_data->fsl_diu_info[3]);
1493 fsl_diu_set_par(machine_data->fsl_diu_info[4]);
1494 }
1495 return count;
1496}
1497
1498static ssize_t show_monitor(struct device *device,
1499 struct device_attribute *attr, char *buf)
1500{
1501 struct fsl_diu_data *machine_data =
1502 container_of(attr, struct fsl_diu_data, dev_attr);
Timur Tabi7653aaa2011-07-09 15:38:14 -05001503
1504 switch (machine_data->monitor_port) {
1505 case FSL_DIU_PORT_DVI:
1506 return sprintf(buf, "DVI\n");
1507 case FSL_DIU_PORT_LVDS:
1508 return sprintf(buf, "Single-link LVDS\n");
1509 case FSL_DIU_PORT_DLVDS:
1510 return sprintf(buf, "Dual-link LVDS\n");
1511 }
1512
1513 return 0;
York Sun9b53a9e2008-04-28 02:15:34 -07001514}
1515
Timur Tabi9e52ba62011-09-15 16:44:50 -05001516static int __devinit fsl_diu_probe(struct platform_device *pdev)
York Sun9b53a9e2008-04-28 02:15:34 -07001517{
Timur Tabi9e52ba62011-09-15 16:44:50 -05001518 struct device_node *np = pdev->dev.of_node;
York Sun9b53a9e2008-04-28 02:15:34 -07001519 struct mfb_info *mfbi;
Timur Tabi89f08e32011-09-15 16:44:49 -05001520 phys_addr_t dummy_ad_addr = 0;
York Sun9b53a9e2008-04-28 02:15:34 -07001521 int ret, i, error = 0;
York Sun9b53a9e2008-04-28 02:15:34 -07001522 struct fsl_diu_data *machine_data;
Anatolij Gustschin4b5006e2010-07-23 04:00:37 +00001523 int diu_mode;
York Sun9b53a9e2008-04-28 02:15:34 -07001524
1525 machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
1526 if (!machine_data)
1527 return -ENOMEM;
1528
1529 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
1530 machine_data->fsl_diu_info[i] =
Timur Tabi9e52ba62011-09-15 16:44:50 -05001531 framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev);
York Sun9b53a9e2008-04-28 02:15:34 -07001532 if (!machine_data->fsl_diu_info[i]) {
Timur Tabi9e52ba62011-09-15 16:44:50 -05001533 dev_err(&pdev->dev, "cannot allocate memory\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001534 ret = -ENOMEM;
1535 goto error2;
1536 }
1537 mfbi = machine_data->fsl_diu_info[i]->par;
1538 memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
1539 mfbi->parent = machine_data;
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001540
Timur Tabi2572df92011-09-28 16:19:51 -05001541 if (mfbi->index == PLANE0) {
Anatolij Gustschin8b856f02010-07-23 04:00:39 +00001542 const u8 *prop;
1543 int len;
1544
1545 /* Get EDID */
1546 prop = of_get_property(np, "edid", &len);
1547 if (prop && len == EDID_LENGTH)
1548 mfbi->edid_data = kmemdup(prop, EDID_LENGTH,
1549 GFP_KERNEL);
1550 }
York Sun9b53a9e2008-04-28 02:15:34 -07001551 }
1552
Timur Tabi9e52ba62011-09-15 16:44:50 -05001553 dr.diu_reg = of_iomap(np, 0);
York Sun9b53a9e2008-04-28 02:15:34 -07001554 if (!dr.diu_reg) {
Timur Tabi9e52ba62011-09-15 16:44:50 -05001555 dev_err(&pdev->dev, "cannot map DIU registers\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001556 ret = -EFAULT;
1557 goto error2;
1558 }
1559
Anatolij Gustschin4b5006e2010-07-23 04:00:37 +00001560 diu_mode = in_be32(&dr.diu_reg->diu_mode);
1561 if (diu_mode != MFB_MODE1)
1562 out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU */
York Sun9b53a9e2008-04-28 02:15:34 -07001563
1564 /* Get the IRQ of the DIU */
1565 machine_data->irq = irq_of_parse_and_map(np, 0);
1566
1567 if (!machine_data->irq) {
Timur Tabi9e52ba62011-09-15 16:44:50 -05001568 dev_err(&pdev->dev, "could not get DIU IRQ\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001569 ret = -EINVAL;
1570 goto error;
1571 }
1572 machine_data->monitor_port = monitor_port;
1573
1574 /* Area descriptor memory pool aligns to 64-bit boundary */
Timur Tabi9e52ba62011-09-15 16:44:50 -05001575 if (allocate_buf(&pdev->dev, &pool.ad,
Anton Vorontsovf3791882009-04-04 22:31:20 +04001576 sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
York Sun9b53a9e2008-04-28 02:15:34 -07001577 return -ENOMEM;
1578
1579 /* Get memory for Gamma Table - 32-byte aligned memory */
Timur Tabi9e52ba62011-09-15 16:44:50 -05001580 if (allocate_buf(&pdev->dev, &pool.gamma, 768, 32)) {
York Sun9b53a9e2008-04-28 02:15:34 -07001581 ret = -ENOMEM;
1582 goto error;
1583 }
1584
1585 /* For performance, cursor bitmap buffer aligns to 32-byte boundary */
Timur Tabi9e52ba62011-09-15 16:44:50 -05001586 if (allocate_buf(&pdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
Anton Vorontsovf3791882009-04-04 22:31:20 +04001587 32)) {
York Sun9b53a9e2008-04-28 02:15:34 -07001588 ret = -ENOMEM;
1589 goto error;
1590 }
1591
1592 i = ARRAY_SIZE(machine_data->fsl_diu_info);
1593 machine_data->dummy_ad = (struct diu_ad *)
1594 ((u32)pool.ad.vaddr + pool.ad.offset) + i;
1595 machine_data->dummy_ad->paddr = pool.ad.paddr +
1596 i * sizeof(struct diu_ad);
1597 machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
1598 if (!machine_data->dummy_aoi_virt) {
1599 ret = -ENOMEM;
1600 goto error;
1601 }
1602 machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr);
1603 machine_data->dummy_ad->pix_fmt = 0x88882317;
1604 machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
1605 machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) | 2);
1606 machine_data->dummy_ad->offset_xyi = 0;
1607 machine_data->dummy_ad->offset_xyd = 0;
1608 machine_data->dummy_ad->next_ad = 0;
1609
Anatolij Gustschin4b5006e2010-07-23 04:00:37 +00001610 /*
1611 * Let DIU display splash screen if it was pre-initialized
1612 * by the bootloader, set dummy area descriptor otherwise.
1613 */
1614 if (diu_mode != MFB_MODE1)
1615 out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr);
1616
York Sun9b53a9e2008-04-28 02:15:34 -07001617 out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr);
1618 out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr);
1619
1620 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
1621 machine_data->fsl_diu_info[i]->fix.smem_start = 0;
1622 mfbi = machine_data->fsl_diu_info[i]->par;
1623 mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr
1624 + pool.ad.offset) + i;
1625 mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad);
1626 ret = install_fb(machine_data->fsl_diu_info[i]);
1627 if (ret) {
Timur Tabi9e52ba62011-09-15 16:44:50 -05001628 dev_err(&pdev->dev, "could not register fb %d\n", i);
York Sun9b53a9e2008-04-28 02:15:34 -07001629 goto error;
1630 }
1631 }
1632
1633 if (request_irq_local(machine_data->irq)) {
Timur Tabi9e52ba62011-09-15 16:44:50 -05001634 dev_err(&pdev->dev, "could not claim irq\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001635 goto error;
1636 }
1637
Wolfram Sang12765512010-04-06 14:34:52 -07001638 sysfs_attr_init(&machine_data->dev_attr.attr);
York Sun9b53a9e2008-04-28 02:15:34 -07001639 machine_data->dev_attr.attr.name = "monitor";
1640 machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
1641 machine_data->dev_attr.show = show_monitor;
1642 machine_data->dev_attr.store = store_monitor;
1643 error = device_create_file(machine_data->fsl_diu_info[0]->dev,
1644 &machine_data->dev_attr);
1645 if (error) {
Timur Tabi9e52ba62011-09-15 16:44:50 -05001646 dev_err(&pdev->dev, "could not create sysfs file %s\n",
York Sun9b53a9e2008-04-28 02:15:34 -07001647 machine_data->dev_attr.attr.name);
1648 }
1649
Timur Tabi9e52ba62011-09-15 16:44:50 -05001650 dev_set_drvdata(&pdev->dev, machine_data);
York Sun9b53a9e2008-04-28 02:15:34 -07001651 return 0;
1652
1653error:
Timur Tabi3f78bbd2011-09-15 16:44:56 -05001654 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1655 uninstall_fb(machine_data->fsl_diu_info[i]);
1656
York Sun9b53a9e2008-04-28 02:15:34 -07001657 if (pool.ad.vaddr)
Timur Tabi9e52ba62011-09-15 16:44:50 -05001658 free_buf(&pdev->dev, &pool.ad,
Anton Vorontsovf3791882009-04-04 22:31:20 +04001659 sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
York Sun9b53a9e2008-04-28 02:15:34 -07001660 if (pool.gamma.vaddr)
Timur Tabi9e52ba62011-09-15 16:44:50 -05001661 free_buf(&pdev->dev, &pool.gamma, 768, 32);
York Sun9b53a9e2008-04-28 02:15:34 -07001662 if (pool.cursor.vaddr)
Timur Tabi9e52ba62011-09-15 16:44:50 -05001663 free_buf(&pdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2,
Anton Vorontsovf3791882009-04-04 22:31:20 +04001664 32);
York Sun9b53a9e2008-04-28 02:15:34 -07001665 if (machine_data->dummy_aoi_virt)
1666 fsl_diu_free(machine_data->dummy_aoi_virt, 64);
1667 iounmap(dr.diu_reg);
1668
1669error2:
1670 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1671 if (machine_data->fsl_diu_info[i])
1672 framebuffer_release(machine_data->fsl_diu_info[i]);
1673 kfree(machine_data);
1674
1675 return ret;
1676}
1677
Timur Tabi9e52ba62011-09-15 16:44:50 -05001678static int fsl_diu_remove(struct platform_device *pdev)
York Sun9b53a9e2008-04-28 02:15:34 -07001679{
1680 struct fsl_diu_data *machine_data;
1681 int i;
1682
Timur Tabi9e52ba62011-09-15 16:44:50 -05001683 machine_data = dev_get_drvdata(&pdev->dev);
York Sun9b53a9e2008-04-28 02:15:34 -07001684 disable_lcdc(machine_data->fsl_diu_info[0]);
1685 free_irq_local(machine_data->irq);
Timur Tabi3f78bbd2011-09-15 16:44:56 -05001686 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1687 uninstall_fb(machine_data->fsl_diu_info[i]);
York Sun9b53a9e2008-04-28 02:15:34 -07001688 if (pool.ad.vaddr)
Timur Tabi9e52ba62011-09-15 16:44:50 -05001689 free_buf(&pdev->dev, &pool.ad,
Anton Vorontsovf3791882009-04-04 22:31:20 +04001690 sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
York Sun9b53a9e2008-04-28 02:15:34 -07001691 if (pool.gamma.vaddr)
Timur Tabi9e52ba62011-09-15 16:44:50 -05001692 free_buf(&pdev->dev, &pool.gamma, 768, 32);
York Sun9b53a9e2008-04-28 02:15:34 -07001693 if (pool.cursor.vaddr)
Timur Tabi9e52ba62011-09-15 16:44:50 -05001694 free_buf(&pdev->dev, &pool.cursor, MAX_CURS * MAX_CURS * 2, 32);
York Sun9b53a9e2008-04-28 02:15:34 -07001695 if (machine_data->dummy_aoi_virt)
1696 fsl_diu_free(machine_data->dummy_aoi_virt, 64);
1697 iounmap(dr.diu_reg);
1698 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1699 if (machine_data->fsl_diu_info[i])
1700 framebuffer_release(machine_data->fsl_diu_info[i]);
1701 kfree(machine_data);
1702
1703 return 0;
1704}
1705
1706#ifndef MODULE
1707static int __init fsl_diu_setup(char *options)
1708{
1709 char *opt;
1710 unsigned long val;
1711
1712 if (!options || !*options)
1713 return 0;
1714
1715 while ((opt = strsep(&options, ",")) != NULL) {
1716 if (!*opt)
1717 continue;
1718 if (!strncmp(opt, "monitor=", 8)) {
Timur Tabi7653aaa2011-07-09 15:38:14 -05001719 monitor_port = fsl_diu_name_to_port(opt + 8);
York Sun9b53a9e2008-04-28 02:15:34 -07001720 } else if (!strncmp(opt, "bpp=", 4)) {
1721 if (!strict_strtoul(opt + 4, 10, &val))
1722 default_bpp = val;
1723 } else
1724 fb_mode = opt;
1725 }
1726
1727 return 0;
1728}
1729#endif
1730
1731static struct of_device_id fsl_diu_match[] = {
Anatolij Gustschind24720a2010-02-17 07:33:22 -07001732#ifdef CONFIG_PPC_MPC512x
1733 {
1734 .compatible = "fsl,mpc5121-diu",
1735 },
1736#endif
York Sun9b53a9e2008-04-28 02:15:34 -07001737 {
1738 .compatible = "fsl,diu",
1739 },
1740 {}
1741};
1742MODULE_DEVICE_TABLE(of, fsl_diu_match);
1743
Grant Likely28541d02011-02-22 21:07:43 -07001744static struct platform_driver fsl_diu_driver = {
Grant Likely40182942010-04-13 16:13:02 -07001745 .driver = {
Timur Tabif8c6bf62011-09-15 16:44:53 -05001746 .name = "fsl-diu-fb",
Grant Likely40182942010-04-13 16:13:02 -07001747 .owner = THIS_MODULE,
1748 .of_match_table = fsl_diu_match,
1749 },
York Sun9b53a9e2008-04-28 02:15:34 -07001750 .probe = fsl_diu_probe,
1751 .remove = fsl_diu_remove,
1752 .suspend = fsl_diu_suspend,
1753 .resume = fsl_diu_resume,
1754};
1755
1756static int __init fsl_diu_init(void)
1757{
1758#ifdef CONFIG_NOT_COHERENT_CACHE
1759 struct device_node *np;
1760 const u32 *prop;
1761#endif
1762 int ret;
1763#ifndef MODULE
1764 char *option;
1765
1766 /*
1767 * For kernel boot options (in 'video=xxxfb:<options>' format)
1768 */
1769 if (fb_get_options("fslfb", &option))
1770 return -ENODEV;
1771 fsl_diu_setup(option);
Timur Tabi7653aaa2011-07-09 15:38:14 -05001772#else
1773 monitor_port = fsl_diu_name_to_port(monitor_string);
York Sun9b53a9e2008-04-28 02:15:34 -07001774#endif
Timur Tabi154152a2011-09-15 16:44:47 -05001775 pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001776
1777#ifdef CONFIG_NOT_COHERENT_CACHE
1778 np = of_find_node_by_type(NULL, "cpu");
1779 if (!np) {
Timur Tabi154152a2011-09-15 16:44:47 -05001780 pr_err("fsl-diu-fb: can't find 'cpu' device node\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001781 return -ENODEV;
1782 }
1783
1784 prop = of_get_property(np, "d-cache-size", NULL);
Julia Lawall5394ba02008-08-05 13:01:28 -07001785 if (prop == NULL) {
Timur Tabi154152a2011-09-15 16:44:47 -05001786 pr_err("fsl-diu-fb: missing 'd-cache-size' property' "
1787 "in 'cpu' node\n");
Julia Lawall5394ba02008-08-05 13:01:28 -07001788 of_node_put(np);
York Sun9b53a9e2008-04-28 02:15:34 -07001789 return -ENODEV;
Julia Lawall5394ba02008-08-05 13:01:28 -07001790 }
York Sun9b53a9e2008-04-28 02:15:34 -07001791
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001792 /*
1793 * Freescale PLRU requires 13/8 times the cache size to do a proper
1794 * displacement flush
York Sun9b53a9e2008-04-28 02:15:34 -07001795 */
Timur Tabi9e52ba62011-09-15 16:44:50 -05001796 coherence_data_size = be32_to_cpup(prop) * 13;
York Sun9b53a9e2008-04-28 02:15:34 -07001797 coherence_data_size /= 8;
1798
1799 prop = of_get_property(np, "d-cache-line-size", NULL);
Julia Lawall5394ba02008-08-05 13:01:28 -07001800 if (prop == NULL) {
Timur Tabi154152a2011-09-15 16:44:47 -05001801 pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
1802 "in 'cpu' node\n");
Julia Lawall5394ba02008-08-05 13:01:28 -07001803 of_node_put(np);
York Sun9b53a9e2008-04-28 02:15:34 -07001804 return -ENODEV;
Julia Lawall5394ba02008-08-05 13:01:28 -07001805 }
Timur Tabi9e52ba62011-09-15 16:44:50 -05001806 d_cache_line_size = be32_to_cpup(prop);
York Sun9b53a9e2008-04-28 02:15:34 -07001807
1808 of_node_put(np);
1809 coherence_data = vmalloc(coherence_data_size);
1810 if (!coherence_data)
1811 return -ENOMEM;
1812#endif
Timur Tabi4a85dc8b2011-09-15 16:44:46 -05001813
Grant Likely28541d02011-02-22 21:07:43 -07001814 ret = platform_driver_register(&fsl_diu_driver);
York Sun9b53a9e2008-04-28 02:15:34 -07001815 if (ret) {
Timur Tabi154152a2011-09-15 16:44:47 -05001816 pr_err("fsl-diu-fb: failed to register platform driver\n");
York Sun9b53a9e2008-04-28 02:15:34 -07001817#if defined(CONFIG_NOT_COHERENT_CACHE)
1818 vfree(coherence_data);
1819#endif
1820 iounmap(dr.diu_reg);
1821 }
1822 return ret;
1823}
1824
1825static void __exit fsl_diu_exit(void)
1826{
Grant Likely28541d02011-02-22 21:07:43 -07001827 platform_driver_unregister(&fsl_diu_driver);
York Sun9b53a9e2008-04-28 02:15:34 -07001828#if defined(CONFIG_NOT_COHERENT_CACHE)
1829 vfree(coherence_data);
1830#endif
1831}
1832
1833module_init(fsl_diu_init);
1834module_exit(fsl_diu_exit);
1835
1836MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
1837MODULE_DESCRIPTION("Freescale DIU framebuffer driver");
1838MODULE_LICENSE("GPL");
1839
1840module_param_named(mode, fb_mode, charp, 0);
1841MODULE_PARM_DESC(mode,
1842 "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
1843module_param_named(bpp, default_bpp, ulong, 0);
Timur Tabi154152a2011-09-15 16:44:47 -05001844MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");
Timur Tabi7653aaa2011-07-09 15:38:14 -05001845module_param_named(monitor, monitor_string, charp, 0);
1846MODULE_PARM_DESC(monitor, "Specify the monitor port "
1847 "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");
York Sun9b53a9e2008-04-28 02:15:34 -07001848