blob: f16bb882e0c5fd2604b89007a5de2a039adde85b [file] [log] [blame]
Liviu Dudaucaf003f2012-01-18 16:52:04 +00001/*
2 * drivers/video/arm-hdlcd.c
3 *
4 * Copyright (C) 2011 ARM Limited
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 *
10 * ARM HDLCD Controller
11 */
12
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/errno.h>
16#include <linux/string.h>
17#include <linux/ctype.h>
18#include <linux/mm.h>
19#include <linux/delay.h>
20#include <linux/of.h>
21#include <linux/fb.h>
22#include <linux/clk.h>
23#include <linux/init.h>
24#include <linux/ioport.h>
25#include <linux/dma-mapping.h>
26#include <linux/platform_device.h>
27#include <linux/memblock.h>
28#include <linux/arm-hdlcd.h>
Chris Redpath7195cb12012-06-29 16:07:08 +010029#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
30#include <linux/proc_fs.h>
31#include <linux/seq_file.h>
32#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +000033
34#include "edid.h"
35
Liviu Dudaucaf003f2012-01-18 16:52:04 +000036/* set the DVI output mode using the firmware */
Jon Medhurst51f19112012-07-13 13:32:48 +010037extern int set_dvi_mode(int mode);
38
39#ifdef CONFIG_SERIAL_AMBA_PCU_UART
Liviu Dudaucaf003f2012-01-18 16:52:04 +000040int get_edid(u8 *msgbuf);
Jon Medhurst51f19112012-07-13 13:32:48 +010041#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +000042#endif
43
44#define to_hdlcd_device(info) container_of(info, struct hdlcd_device, fb)
45
46static struct of_device_id hdlcd_of_matches[] = {
47 { .compatible = "arm,hdlcd" },
48 {},
49};
50
51/* Framebuffer size. */
52static unsigned long framebuffer_size;
53
Chris Redpath7195cb12012-06-29 16:07:08 +010054#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
55static unsigned long buffer_underrun_events;
56static DEFINE_SPINLOCK(hdlcd_underrun_lock);
57
58static void hdlcd_underrun_set(unsigned long val)
59{
60 spin_lock(&hdlcd_underrun_lock);
61 buffer_underrun_events = val;
62 spin_unlock(&hdlcd_underrun_lock);
63}
64
65static unsigned long hdlcd_underrun_get(void)
66{
67 unsigned long val;
68 spin_lock(&hdlcd_underrun_lock);
69 val = buffer_underrun_events;
70 spin_unlock(&hdlcd_underrun_lock);
71 return val;
72}
73
74#ifdef CONFIG_PROC_FS
75static int hdlcd_underrun_show(struct seq_file *m, void *v)
76{
77 unsigned char underrun_string[32];
78 snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get());
79 seq_puts(m, underrun_string);
80 return 0;
81}
82
83static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
84{
85 return single_open(file, hdlcd_underrun_show, NULL);
86}
87
88static const struct file_operations proc_hdlcd_underrun_operations = {
89 .open = proc_hdlcd_underrun_open,
90 .read = seq_read,
91 .llseek = seq_lseek,
92 .release = single_release,
93};
94
95static int hdlcd_underrun_init(void)
96{
97 hdlcd_underrun_set(0);
98 proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations);
99 return 0;
100}
101static void hdlcd_underrun_close(void)
102{
103 remove_proc_entry("hdlcd_underrun", NULL);
104}
105#else
106static int hdlcd_underrun_init(void) { return 0; }
107static void hdlcd_underrun_close(void) { }
108#endif
109#endif
110
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000111static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
112
113static struct fb_var_screeninfo cached_var_screeninfo;
114
115static struct fb_videomode hdlcd_default_mode = {
116 .refresh = 60,
117 .xres = 1680,
118 .yres = 1050,
119 .pixclock = 8403,
120 .left_margin = 80,
121 .right_margin = 48,
122 .upper_margin = 21,
123 .lower_margin = 3,
124 .hsync_len = 32,
125 .vsync_len = 6,
126 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
127 .vmode = FB_VMODE_NONINTERLACED
128};
129
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000130static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
131{
132 dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
133 writel(1, hdlcd->base + HDLCD_REG_COMMAND);
134}
135
136static inline void hdlcd_disable(struct hdlcd_device *hdlcd)
137{
138 dev_dbg(hdlcd->dev, "HDLCD: output disabled\n");
139 writel(0, hdlcd->base + HDLCD_REG_COMMAND);
140}
141
142static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd,
143 struct fb_var_screeninfo *var)
144{
145 int ret = 0;
146
147 memset(&var->transp, 0, sizeof(var->transp));
148 var->red.msb_right = 0;
149 var->green.msb_right = 0;
150 var->blue.msb_right = 0;
151 var->blue.offset = 0;
152
153 switch (var->bits_per_pixel) {
154 case 8:
155 /* pseudocolor */
156 var->red.length = 8;
157 var->green.length = 8;
158 var->blue.length = 8;
159 break;
160 case 16:
161 /* 565 format */
162 var->red.length = 5;
163 var->green.length = 6;
164 var->blue.length = 5;
165 break;
166 case 32:
167 var->transp.length = 8;
168 case 24:
169 var->red.length = 8;
170 var->green.length = 8;
171 var->blue.length = 8;
172 break;
173 default:
174 ret = -EINVAL;
175 break;
176 }
177
178 if (!ret) {
Chris Redpath58561732012-06-29 13:10:35 +0100179 if(var->bits_per_pixel != 32)
180 {
181 var->green.offset = var->blue.length;
182 var->red.offset = var->green.offset + var->green.length;
183 }
184 else
185 {
186 /* Previously, the byte ordering for 32-bit color was
187 * (msb)<alpha><red><green><blue>(lsb)
188 * but this does not match what android expects and
189 * the colors are odd. Instead, use
190 * <alpha><blue><green><red>
191 * Since we tell fb what we are doing, console
192 * , X and directfb access should work fine.
193 */
194 var->green.offset = var->red.length;
195 var->blue.offset = var->green.offset + var->green.length;
196 var->transp.offset = var->blue.offset + var->blue.length;
197 }
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000198 }
199
200 return ret;
201}
202
203static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
204{
205 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
206 int bytes_per_pixel = var->bits_per_pixel / 8;
207
208 var->yres_virtual = 2 * var->yres;
209
210 if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len)
211 return -ENOMEM;
212
213 if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
214 return -EINVAL;
215
216 /* make sure the bitfields are set appropriately */
217 return hdlcd_set_bitfields(hdlcd, var);
218}
219
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000220static int hdlcd_set_output_mode(int xres, int yres)
221{
Jon Medhurst51f19112012-07-13 13:32:48 +0100222 /* some firmware uses some stupid protocol: 5 bytes (only byte 1 used)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000223 to send 3 bits of information (value between 0 - 5) */
224 u8 msgbuffer[5];
225
226 memset(msgbuffer, 0, sizeof(msgbuffer));
Jon Medhurst51f19112012-07-13 13:32:48 +0100227
228 if (xres < 800 && yres < 600)
229 msgbuffer[0] = 0; /* default resolution: 640 x 480 */
230 else if (xres < 1024 && yres < 768)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000231 msgbuffer[0] = 1; /* SVGA: 800 * 600 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100232 else if (xres < 1280 && yres < 1024)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000233 msgbuffer[0] = 2; /* XGA: 1024 * 768 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100234 else if (xres < 1600 && yres < 1200)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000235 msgbuffer[0] = 3; /* SXGA: 1280 * 1024 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100236 else if (xres < 1920 && yres <= 1200)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000237 msgbuffer[0] = 4; /* UXGA: 1600 * 1200 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100238 else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000239 msgbuffer[0] = 5; /* WUXGA: 1920 * 1200 */
240
Jon Medhurst51f19112012-07-13 13:32:48 +0100241 return set_dvi_mode(msgbuffer[0]);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000242}
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000243
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100244
245/* prototype */
246static int hdlcd_pan_display(struct fb_var_screeninfo *var,
247 struct fb_info *info);
248
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000249#define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg))
250#define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg))
251
252static int hdlcd_set_par(struct fb_info *info)
253{
254 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
255 int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8;
256 int polarities;
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100257 int old_yoffset;
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000258
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100259 /* check for shortcuts */
260 old_yoffset = cached_var_screeninfo.yoffset;
261 cached_var_screeninfo.yoffset = info->var.yoffset;
262 if (!memcmp(&info->var, &cached_var_screeninfo,
263 sizeof(struct fb_var_screeninfo))) {
264 if(old_yoffset != info->var.yoffset) {
Chris Redpath5705f5f2012-06-29 16:07:46 +0100265 /* we only changed yoffset, and we already
266 * already recorded it a couple lines up
267 */
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100268 hdlcd_pan_display(&info->var, info);
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100269 }
270 /* or no change */
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000271 return 0;
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100272 }
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000273
274 hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel;
275
276 if (hdlcd->fb.var.bits_per_pixel >= 16)
277 hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
278 else
279 hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
280
281 memcpy(&cached_var_screeninfo, &info->var, sizeof(struct fb_var_screeninfo));
282
283 polarities = HDLCD_POLARITY_DATAEN |
284#ifndef CONFIG_ARCH_TUSCAN
285 HDLCD_POLARITY_PIXELCLK |
286#endif
287 HDLCD_POLARITY_DATA;
288 polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
289 polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
290
291 hdlcd_disable(hdlcd);
292
293 WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel);
294 WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel);
295 WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
296 WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
297 WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
298 WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
299 WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
300 WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
301 WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
302 WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
303 WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
304 WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
305 WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
Chris Redpath7195cb12012-06-29 16:07:08 +0100306#ifdef HDLCD_RED_DEFAULT_COLOUR
307 WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \
308 | hdlcd->fb.var.red.offset);
309#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000310 WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset);
Chris Redpath7195cb12012-06-29 16:07:08 +0100311#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000312 WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
313 WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
314
315 hdlcd_set_output_mode(hdlcd->fb.var.xres, hdlcd->fb.var.yres);
316
Jon Medhurst7c7d3b32012-06-13 09:45:43 +0100317 clk_prepare(hdlcd->clk);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000318 clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
319 clk_enable(hdlcd->clk);
320
321 hdlcd_enable(hdlcd);
322
323 return 0;
324}
325
326static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
327 unsigned int blue, unsigned int transp, struct fb_info *info)
328{
329 if (regno < 16) {
330 u32 *pal = info->pseudo_palette;
331
332 pal[regno] = ((red >> 8) << info->var.red.offset) |
333 ((green >> 8) << info->var.green.offset) |
334 ((blue >> 8) << info->var.blue.offset);
335 }
336
337 return 0;
338}
339
340static irqreturn_t hdlcd_irq(int irq, void *data)
341{
342 struct hdlcd_device *hdlcd = data;
343 unsigned long irq_mask, irq_status;
344
345 irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
346 irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
347
348 /* acknowledge interrupt(s) */
349 WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
Chris Redpath7195cb12012-06-29 16:07:08 +0100350#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
351 if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
352 /* increment the count */
353 hdlcd_underrun_set(hdlcd_underrun_get() + 1);
354 }
355#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000356 if (irq_status & HDLCD_INTERRUPT_VSYNC) {
357 /* disable future VSYNC interrupts */
358 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
359
360 complete(&hdlcd->vsync_completion);
361 }
362
363 return IRQ_HANDLED;
364}
365
366static int hdlcd_wait_for_vsync(struct fb_info *info)
367{
368 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
369 unsigned long irq_mask;
370 int err;
371
372 /* enable VSYNC interrupt */
373 irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
374 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
375
376 err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
377 msecs_to_jiffies(100));
378
379 if (!err)
380 return -ETIMEDOUT;
381
382 return 0;
383}
384
385static int hdlcd_blank(int blank_mode, struct fb_info *info)
386{
387 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
388
389 switch (blank_mode) {
390 case FB_BLANK_POWERDOWN:
391 clk_disable(hdlcd->clk);
392 case FB_BLANK_NORMAL:
393 hdlcd_disable(hdlcd);
394 break;
395 case FB_BLANK_UNBLANK:
396 clk_enable(hdlcd->clk);
397 hdlcd_enable(hdlcd);
398 break;
399 case FB_BLANK_VSYNC_SUSPEND:
400 case FB_BLANK_HSYNC_SUSPEND:
401 default:
402 return 1;
403 }
404
405 return 0;
406}
407
408static void hdlcd_mmap_open(struct vm_area_struct *vma)
409{
410}
411
412static void hdlcd_mmap_close(struct vm_area_struct *vma)
413{
414}
415
416static struct vm_operations_struct hdlcd_mmap_ops = {
417 .open = hdlcd_mmap_open,
418 .close = hdlcd_mmap_close,
419};
420
421static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma)
422{
423 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
424 unsigned long off;
425 unsigned long start;
426 unsigned long len = hdlcd->fb.fix.smem_len;
427
428 if (vma->vm_end - vma->vm_start == 0)
429 return 0;
430 if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
431 return -EINVAL;
432
433 off = vma->vm_pgoff << PAGE_SHIFT;
434 if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
435 return -EINVAL;
436
437 start = hdlcd->fb.fix.smem_start;
438 off += start;
439
440 vma->vm_pgoff = off >> PAGE_SHIFT;
441 vma->vm_flags |= VM_IO;
442 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
443 vma->vm_ops = &hdlcd_mmap_ops;
444 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
445 vma->vm_end - vma->vm_start,
446 vma->vm_page_prot))
447 return -EAGAIN;
448
449 return 0;
450}
451
452static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
453{
454 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
455
456 hdlcd->fb.var.yoffset = var->yoffset;
457 WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
458 (var->yoffset * hdlcd->fb.fix.line_length));
459
460 hdlcd_wait_for_vsync(info);
461
462 return 0;
463}
464
465static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
466{
467 int err;
468
469 switch (cmd) {
470 case FBIO_WAITFORVSYNC:
471 err = hdlcd_wait_for_vsync(info);
472 break;
473 default:
474 err = -ENOIOCTLCMD;
475 break;
476 }
477
478 return err;
479}
480
481static struct fb_ops hdlcd_ops = {
482 .owner = THIS_MODULE,
483 .fb_check_var = hdlcd_check_var,
484 .fb_set_par = hdlcd_set_par,
485 .fb_setcolreg = hdlcd_setcolreg,
486 .fb_blank = hdlcd_blank,
487 .fb_fillrect = cfb_fillrect,
488 .fb_copyarea = cfb_copyarea,
489 .fb_imageblit = cfb_imageblit,
490 .fb_mmap = hdlcd_mmap,
491 .fb_pan_display = hdlcd_pan_display,
492 .fb_ioctl = hdlcd_ioctl,
493 .fb_compat_ioctl = hdlcd_ioctl
494};
495
496static int hdlcd_setup(struct hdlcd_device *hdlcd)
497{
498 u32 version;
499 int err = -EFAULT;
500
501 hdlcd->clk = clk_get(hdlcd->dev, NULL);
502 if (IS_ERR(hdlcd->clk)) {
503 dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
504 return PTR_ERR(hdlcd->clk);
505 }
506
507 hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
508 if (!hdlcd->base) {
509 dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
510 goto remap_err;
511 }
512
513 hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
514 if (!hdlcd->fb.pseudo_palette) {
515 dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
516 err = -ENOMEM;
517 goto kmalloc_err;
518 }
519
520 version = readl(hdlcd->base + HDLCD_REG_VERSION);
521 if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
522 dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
523 err = -EINVAL;
524 goto kmalloc_err;
525 }
526 dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n",
527 (version & HDLCD_VERSION_MAJOR_MASK) >> 8,
528 version & HDLCD_VERSION_MINOR_MASK);
529
530 strcpy(hdlcd->fb.fix.id, "hdlcd");
531 hdlcd->fb.fbops = &hdlcd_ops;
532 hdlcd->fb.flags = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
533
534 hdlcd->fb.fix.type = FB_TYPE_PACKED_PIXELS;
535 hdlcd->fb.fix.type_aux = 0;
536 hdlcd->fb.fix.xpanstep = 0;
537 hdlcd->fb.fix.ypanstep = 1;
538 hdlcd->fb.fix.ywrapstep = 0;
539 hdlcd->fb.fix.accel = FB_ACCEL_NONE;
540
541 hdlcd->fb.var.nonstd = 0;
542 hdlcd->fb.var.activate = FB_ACTIVATE_NOW;
543 hdlcd->fb.var.height = -1;
544 hdlcd->fb.var.width = -1;
545 hdlcd->fb.var.accel_flags = 0;
546
547 init_completion(&hdlcd->vsync_completion);
548
549 if (hdlcd->edid) {
550 /* build modedb from EDID */
551 fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
552 fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
553 hdlcd->fb.monspecs.modedb_len,
554 &hdlcd->fb.modelist);
555 fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
556 hdlcd->fb.monspecs.modedb,
557 hdlcd->fb.monspecs.modedb_len,
558 &hdlcd_default_mode, 32);
559 } else {
560 hdlcd->fb.monspecs.hfmin = 0;
561 hdlcd->fb.monspecs.hfmax = 100000;
562 hdlcd->fb.monspecs.vfmin = 0;
563 hdlcd->fb.monspecs.vfmax = 400;
564 hdlcd->fb.monspecs.dclkmin = 1000000;
565 hdlcd->fb.monspecs.dclkmax = 100000000;
566 fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
567 }
568
569 dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres,
570 hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel,
571 hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60);
572 hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres;
573 hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2;
574
575 /* initialise and set the palette */
576 if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
577 dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
578 err = -ENOMEM;
579 goto setup_err;
580 }
581 fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
582
583 /* Allow max number of outstanding requests with the largest beat burst */
584 WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
585 /* Set the framebuffer base to start of allocated memory */
586 WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
Chris Redpath7195cb12012-06-29 16:07:08 +0100587#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
588 /* turn on underrun interrupt for counting */
589 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
590#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000591 /* Ensure interrupts are disabled */
592 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
Chris Redpath7195cb12012-06-29 16:07:08 +0100593#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000594 if (!register_framebuffer(&hdlcd->fb)) {
595 fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
596 clk_enable(hdlcd->clk);
597 return 0;
598 }
599
600 dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
601
602 fb_dealloc_cmap(&hdlcd->fb.cmap);
603setup_err:
604 iounmap(hdlcd->base);
605kmalloc_err:
606 kfree(hdlcd->fb.pseudo_palette);
607remap_err:
608 clk_put(hdlcd->clk);
609 return err;
610}
611
612static inline unsigned char atohex(u8 data)
613{
614 if (!isxdigit(data))
615 return 0;
616 /* truncate the upper nibble and add 9 to non-digit values */
617 return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf);
618}
619
620/* EDID data is passed from devicetree in a literal string that can contain spaces and
621 the hexadecimal dump of the data */
622static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
623{
624 int i, j;
625
626 if (!edid_data)
627 return -EINVAL;
628
629 hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
630 if (!hdlcd->edid)
631 return -ENOMEM;
632
633 for (i = 0, j = 0; i < data_len; i++) {
634 if (isspace(edid_data[i]))
635 continue;
636 hdlcd->edid[j++] = atohex(edid_data[i]);
637 if (j >= EDID_LENGTH)
638 break;
639 }
640
641 if (j < EDID_LENGTH) {
642 kfree(hdlcd->edid);
643 hdlcd->edid = NULL;
644 return -EINVAL;
645 }
646
647 return 0;
648}
649
650static int hdlcd_probe(struct platform_device *pdev)
651{
652 int err = 0, i;
653 struct hdlcd_device *hdlcd;
654 struct resource *mem;
655#ifdef CONFIG_OF
656 struct device_node *of_node;
657#endif
658
659 memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo));
660
661 dev_dbg(&pdev->dev, "HDLCD: probing\n");
662
663 hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL);
664 if (!hdlcd)
665 return -ENOMEM;
666
667#ifdef CONFIG_OF
668 of_node = pdev->dev.of_node;
669 if (of_node) {
670 int len;
671 const u8 *edid;
Jon Medhurst51f19112012-07-13 13:32:48 +0100672 const __be32 *prop = of_get_property(of_node, "mode", &len);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000673 if (prop)
Jon Medhurst51f19112012-07-13 13:32:48 +0100674 strncpy(fb_mode, (char *)prop, len);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000675 prop = of_get_property(of_node, "framebuffer", &len);
676 if (prop) {
Jon Medhurst51f19112012-07-13 13:32:48 +0100677 hdlcd->fb.fix.smem_start = of_read_ulong(prop,
678 of_n_addr_cells(of_node));
679 prop += of_n_addr_cells(of_node);
680 framebuffer_size = of_read_ulong(prop,
681 of_n_size_cells(of_node));
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000682 if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
683 framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE;
684 dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
685 hdlcd->fb.fix.smem_start, framebuffer_size);
686 }
687 edid = of_get_property(of_node, "edid", &len);
688 if (edid) {
689 err = parse_edid_data(hdlcd, edid, len);
690#ifdef CONFIG_SERIAL_AMBA_PCU_UART
691 } else {
692 /* ask the firmware to fetch the EDID */
693 dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n");
694 hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
695 if (!hdlcd->edid)
696 return -ENOMEM;
697 err = get_edid(hdlcd->edid);
698#endif /* CONFIG_SERIAL_AMBA_PCU_UART */
699 }
700 if (err)
701 dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
702 }
703#endif /* CONFIG_OF */
704
705 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
706 if (!mem) {
707 dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
708 err = -EINVAL;
709 goto resource_err;
710 }
711
712 i = platform_get_irq(pdev, 0);
713 if (i < 0) {
714 dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
715 err = -ENOENT;
716 goto resource_err;
717 } else {
718 err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
719 if (err) {
720 dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
721 goto resource_err;
722 }
723 hdlcd->irq = i;
724 }
725
726 if (!request_mem_region(mem->start, resource_size(mem), dev_name(&pdev->dev))) {
727 err = -ENXIO;
728 goto request_err;
729 }
730
731 if (!hdlcd->fb.fix.smem_start) {
732 dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
733 err = -ENOMEM;
734 goto memalloc_err;
735 }
736 hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size);
737 if (!hdlcd->fb.screen_base) {
738 dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
739 err = -ENOMEM;
740 goto probe_err;
741 }
742
743 hdlcd->fb.screen_size = framebuffer_size;
744 hdlcd->fb.fix.smem_len = framebuffer_size;
745 hdlcd->fb.fix.mmio_start = mem->start;
746 hdlcd->fb.fix.mmio_len = resource_size(mem);
747
748 /* Clear the framebuffer */
749 memset(hdlcd->fb.screen_base, 0, framebuffer_size);
750
751 hdlcd->dev = &pdev->dev;
752
753 dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n",
754 hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start);
755
756 err = hdlcd_setup(hdlcd);
757
758 if (err)
759 goto probe_err;
760
761 platform_set_drvdata(pdev, hdlcd);
762 return 0;
763
764probe_err:
765 iounmap(hdlcd->fb.screen_base);
766 memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
767
768memalloc_err:
769 release_mem_region(mem->start, resource_size(mem));
770
771request_err:
772 free_irq(hdlcd->irq, hdlcd);
773
774resource_err:
775 kfree(hdlcd);
776
777 return err;
778}
779
780static int hdlcd_remove(struct platform_device *pdev)
781{
782 struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
783
784 clk_disable(hdlcd->clk);
Jon Medhurst7c7d3b32012-06-13 09:45:43 +0100785 clk_unprepare(hdlcd->clk);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000786 clk_put(hdlcd->clk);
787
788 /* unmap memory */
789 iounmap(hdlcd->fb.screen_base);
790 iounmap(hdlcd->base);
791
792 /* deallocate fb memory */
793 fb_dealloc_cmap(&hdlcd->fb.cmap);
794 kfree(hdlcd->fb.pseudo_palette);
795 memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
796 release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
797
798 free_irq(hdlcd->irq, NULL);
799 kfree(hdlcd);
800
801 return 0;
802}
803
804#ifdef CONFIG_PM
805static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
806{
807 /* not implemented yet */
808 return 0;
809}
810
811static int hdlcd_resume(struct platform_device *pdev)
812{
813 /* not implemented yet */
814 return 0;
815}
816#else
817#define hdlcd_suspend NULL
818#define hdlcd_resume NULL
819#endif
820
821static struct platform_driver hdlcd_driver = {
822 .probe = hdlcd_probe,
823 .remove = hdlcd_remove,
824 .suspend = hdlcd_suspend,
825 .resume = hdlcd_resume,
826 .driver = {
827 .name = "hdlcd",
828 .owner = THIS_MODULE,
829 .of_match_table = hdlcd_of_matches,
830 },
831};
832
833static int __init hdlcd_init(void)
834{
Chris Redpath7195cb12012-06-29 16:07:08 +0100835#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
836 int err = platform_driver_register(&hdlcd_driver);
837 if (!err)
838 hdlcd_underrun_init();
839 return err;
840#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000841 return platform_driver_register(&hdlcd_driver);
Chris Redpath7195cb12012-06-29 16:07:08 +0100842#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000843}
844
845void __exit hdlcd_exit(void)
846{
Chris Redpath7195cb12012-06-29 16:07:08 +0100847#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
848 hdlcd_underrun_close();
849#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000850 platform_driver_unregister(&hdlcd_driver);
851}
852
853module_init(hdlcd_init);
854module_exit(hdlcd_exit);
855
856MODULE_AUTHOR("Liviu Dudau");
857MODULE_DESCRIPTION("ARM HDLCD core driver");
858MODULE_LICENSE("GPL v2");