blob: 9600c70209b268d2d84e229bd7c4db70d3c48b23 [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
Chris Redpath9f142662012-07-12 12:33:13 +0100208#ifdef HDLCD_NO_VIRTUAL_SCREEN
209 var->yres_virtual = var->yres;
210#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000211 var->yres_virtual = 2 * var->yres;
Chris Redpath9f142662012-07-12 12:33:13 +0100212#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000213
214 if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len)
215 return -ENOMEM;
216
217 if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
218 return -EINVAL;
219
220 /* make sure the bitfields are set appropriately */
221 return hdlcd_set_bitfields(hdlcd, var);
222}
223
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000224static int hdlcd_set_output_mode(int xres, int yres)
225{
Jon Medhurst51f19112012-07-13 13:32:48 +0100226 /* some firmware uses some stupid protocol: 5 bytes (only byte 1 used)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000227 to send 3 bits of information (value between 0 - 5) */
228 u8 msgbuffer[5];
229
230 memset(msgbuffer, 0, sizeof(msgbuffer));
Jon Medhurst51f19112012-07-13 13:32:48 +0100231
232 if (xres < 800 && yres < 600)
233 msgbuffer[0] = 0; /* default resolution: 640 x 480 */
234 else if (xres < 1024 && yres < 768)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000235 msgbuffer[0] = 1; /* SVGA: 800 * 600 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100236 else if (xres < 1280 && yres < 1024)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000237 msgbuffer[0] = 2; /* XGA: 1024 * 768 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100238 else if (xres < 1600 && yres < 1200)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000239 msgbuffer[0] = 3; /* SXGA: 1280 * 1024 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100240 else if (xres < 1920 && yres <= 1200)
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000241 msgbuffer[0] = 4; /* UXGA: 1600 * 1200 */
Jon Medhurst51f19112012-07-13 13:32:48 +0100242 else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000243 msgbuffer[0] = 5; /* WUXGA: 1920 * 1200 */
244
Jon Medhurst51f19112012-07-13 13:32:48 +0100245 return set_dvi_mode(msgbuffer[0]);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000246}
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000247
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100248
249/* prototype */
250static int hdlcd_pan_display(struct fb_var_screeninfo *var,
251 struct fb_info *info);
252
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000253#define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg))
254#define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg))
255
256static int hdlcd_set_par(struct fb_info *info)
257{
258 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
259 int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8;
260 int polarities;
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100261 int old_yoffset;
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000262
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100263 /* check for shortcuts */
264 old_yoffset = cached_var_screeninfo.yoffset;
265 cached_var_screeninfo.yoffset = info->var.yoffset;
266 if (!memcmp(&info->var, &cached_var_screeninfo,
267 sizeof(struct fb_var_screeninfo))) {
268 if(old_yoffset != info->var.yoffset) {
Chris Redpath5705f5f2012-06-29 16:07:46 +0100269 /* we only changed yoffset, and we already
270 * already recorded it a couple lines up
271 */
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100272 hdlcd_pan_display(&info->var, info);
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100273 }
274 /* or no change */
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000275 return 0;
Dietmar Eggemannf03c6632012-06-21 17:16:33 +0100276 }
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000277
278 hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel;
279
280 if (hdlcd->fb.var.bits_per_pixel >= 16)
281 hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
282 else
283 hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
284
285 memcpy(&cached_var_screeninfo, &info->var, sizeof(struct fb_var_screeninfo));
286
287 polarities = HDLCD_POLARITY_DATAEN |
288#ifndef CONFIG_ARCH_TUSCAN
289 HDLCD_POLARITY_PIXELCLK |
290#endif
291 HDLCD_POLARITY_DATA;
292 polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
293 polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
294
295 hdlcd_disable(hdlcd);
296
297 WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel);
298 WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel);
299 WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
300 WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
301 WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
302 WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
303 WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
304 WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
305 WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
306 WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
307 WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
308 WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
309 WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
Chris Redpath7195cb12012-06-29 16:07:08 +0100310#ifdef HDLCD_RED_DEFAULT_COLOUR
311 WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \
312 | hdlcd->fb.var.red.offset);
313#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000314 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 +0100315#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000316 WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
317 WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
318
319 hdlcd_set_output_mode(hdlcd->fb.var.xres, hdlcd->fb.var.yres);
320
Jon Medhurst7c7d3b32012-06-13 09:45:43 +0100321 clk_prepare(hdlcd->clk);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000322 clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
323 clk_enable(hdlcd->clk);
324
325 hdlcd_enable(hdlcd);
326
327 return 0;
328}
329
330static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
331 unsigned int blue, unsigned int transp, struct fb_info *info)
332{
333 if (regno < 16) {
334 u32 *pal = info->pseudo_palette;
335
336 pal[regno] = ((red >> 8) << info->var.red.offset) |
337 ((green >> 8) << info->var.green.offset) |
338 ((blue >> 8) << info->var.blue.offset);
339 }
340
341 return 0;
342}
343
344static irqreturn_t hdlcd_irq(int irq, void *data)
345{
346 struct hdlcd_device *hdlcd = data;
347 unsigned long irq_mask, irq_status;
348
349 irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
350 irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
351
352 /* acknowledge interrupt(s) */
353 WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
Chris Redpath7195cb12012-06-29 16:07:08 +0100354#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
355 if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
356 /* increment the count */
357 hdlcd_underrun_set(hdlcd_underrun_get() + 1);
358 }
359#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000360 if (irq_status & HDLCD_INTERRUPT_VSYNC) {
361 /* disable future VSYNC interrupts */
362 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
363
364 complete(&hdlcd->vsync_completion);
365 }
366
367 return IRQ_HANDLED;
368}
369
370static int hdlcd_wait_for_vsync(struct fb_info *info)
371{
372 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
373 unsigned long irq_mask;
374 int err;
375
376 /* enable VSYNC interrupt */
377 irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
378 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
379
380 err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
381 msecs_to_jiffies(100));
382
383 if (!err)
384 return -ETIMEDOUT;
385
386 return 0;
387}
388
389static int hdlcd_blank(int blank_mode, struct fb_info *info)
390{
391 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
392
393 switch (blank_mode) {
394 case FB_BLANK_POWERDOWN:
395 clk_disable(hdlcd->clk);
396 case FB_BLANK_NORMAL:
397 hdlcd_disable(hdlcd);
398 break;
399 case FB_BLANK_UNBLANK:
400 clk_enable(hdlcd->clk);
401 hdlcd_enable(hdlcd);
402 break;
403 case FB_BLANK_VSYNC_SUSPEND:
404 case FB_BLANK_HSYNC_SUSPEND:
405 default:
406 return 1;
407 }
408
409 return 0;
410}
411
412static void hdlcd_mmap_open(struct vm_area_struct *vma)
413{
414}
415
416static void hdlcd_mmap_close(struct vm_area_struct *vma)
417{
418}
419
420static struct vm_operations_struct hdlcd_mmap_ops = {
421 .open = hdlcd_mmap_open,
422 .close = hdlcd_mmap_close,
423};
424
425static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma)
426{
427 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
428 unsigned long off;
429 unsigned long start;
430 unsigned long len = hdlcd->fb.fix.smem_len;
431
432 if (vma->vm_end - vma->vm_start == 0)
433 return 0;
434 if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
435 return -EINVAL;
436
437 off = vma->vm_pgoff << PAGE_SHIFT;
438 if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
439 return -EINVAL;
440
441 start = hdlcd->fb.fix.smem_start;
442 off += start;
443
444 vma->vm_pgoff = off >> PAGE_SHIFT;
445 vma->vm_flags |= VM_IO;
446 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
447 vma->vm_ops = &hdlcd_mmap_ops;
448 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
449 vma->vm_end - vma->vm_start,
450 vma->vm_page_prot))
451 return -EAGAIN;
452
453 return 0;
454}
455
456static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
457{
458 struct hdlcd_device *hdlcd = to_hdlcd_device(info);
459
460 hdlcd->fb.var.yoffset = var->yoffset;
461 WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
462 (var->yoffset * hdlcd->fb.fix.line_length));
463
464 hdlcd_wait_for_vsync(info);
465
466 return 0;
467}
468
469static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
470{
471 int err;
472
473 switch (cmd) {
474 case FBIO_WAITFORVSYNC:
475 err = hdlcd_wait_for_vsync(info);
476 break;
477 default:
478 err = -ENOIOCTLCMD;
479 break;
480 }
481
482 return err;
483}
484
485static struct fb_ops hdlcd_ops = {
486 .owner = THIS_MODULE,
487 .fb_check_var = hdlcd_check_var,
488 .fb_set_par = hdlcd_set_par,
489 .fb_setcolreg = hdlcd_setcolreg,
490 .fb_blank = hdlcd_blank,
491 .fb_fillrect = cfb_fillrect,
492 .fb_copyarea = cfb_copyarea,
493 .fb_imageblit = cfb_imageblit,
494 .fb_mmap = hdlcd_mmap,
495 .fb_pan_display = hdlcd_pan_display,
496 .fb_ioctl = hdlcd_ioctl,
497 .fb_compat_ioctl = hdlcd_ioctl
498};
499
500static int hdlcd_setup(struct hdlcd_device *hdlcd)
501{
502 u32 version;
503 int err = -EFAULT;
504
505 hdlcd->clk = clk_get(hdlcd->dev, NULL);
506 if (IS_ERR(hdlcd->clk)) {
507 dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
508 return PTR_ERR(hdlcd->clk);
509 }
510
511 hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
512 if (!hdlcd->base) {
513 dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
514 goto remap_err;
515 }
516
517 hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
518 if (!hdlcd->fb.pseudo_palette) {
519 dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
520 err = -ENOMEM;
521 goto kmalloc_err;
522 }
523
524 version = readl(hdlcd->base + HDLCD_REG_VERSION);
525 if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
526 dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
527 err = -EINVAL;
528 goto kmalloc_err;
529 }
530 dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n",
531 (version & HDLCD_VERSION_MAJOR_MASK) >> 8,
532 version & HDLCD_VERSION_MINOR_MASK);
533
534 strcpy(hdlcd->fb.fix.id, "hdlcd");
535 hdlcd->fb.fbops = &hdlcd_ops;
536 hdlcd->fb.flags = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
537
538 hdlcd->fb.fix.type = FB_TYPE_PACKED_PIXELS;
539 hdlcd->fb.fix.type_aux = 0;
540 hdlcd->fb.fix.xpanstep = 0;
541 hdlcd->fb.fix.ypanstep = 1;
542 hdlcd->fb.fix.ywrapstep = 0;
543 hdlcd->fb.fix.accel = FB_ACCEL_NONE;
544
545 hdlcd->fb.var.nonstd = 0;
546 hdlcd->fb.var.activate = FB_ACTIVATE_NOW;
547 hdlcd->fb.var.height = -1;
548 hdlcd->fb.var.width = -1;
549 hdlcd->fb.var.accel_flags = 0;
550
551 init_completion(&hdlcd->vsync_completion);
552
553 if (hdlcd->edid) {
554 /* build modedb from EDID */
555 fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
556 fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
557 hdlcd->fb.monspecs.modedb_len,
558 &hdlcd->fb.modelist);
559 fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
560 hdlcd->fb.monspecs.modedb,
561 hdlcd->fb.monspecs.modedb_len,
562 &hdlcd_default_mode, 32);
563 } else {
564 hdlcd->fb.monspecs.hfmin = 0;
565 hdlcd->fb.monspecs.hfmax = 100000;
566 hdlcd->fb.monspecs.vfmin = 0;
567 hdlcd->fb.monspecs.vfmax = 400;
568 hdlcd->fb.monspecs.dclkmin = 1000000;
569 hdlcd->fb.monspecs.dclkmax = 100000000;
570 fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
571 }
572
573 dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres,
574 hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel,
575 hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60);
576 hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres;
Chris Redpath9f142662012-07-12 12:33:13 +0100577#ifdef HDLCD_NO_VIRTUAL_SCREEN
578 hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres;
579#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000580 hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2;
Chris Redpath9f142662012-07-12 12:33:13 +0100581#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000582
583 /* initialise and set the palette */
584 if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
585 dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
586 err = -ENOMEM;
587 goto setup_err;
588 }
589 fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
590
591 /* Allow max number of outstanding requests with the largest beat burst */
592 WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
593 /* Set the framebuffer base to start of allocated memory */
594 WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
Chris Redpath7195cb12012-06-29 16:07:08 +0100595#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
596 /* turn on underrun interrupt for counting */
597 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
598#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000599 /* Ensure interrupts are disabled */
600 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
Chris Redpath7195cb12012-06-29 16:07:08 +0100601#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000602 if (!register_framebuffer(&hdlcd->fb)) {
603 fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
604 clk_enable(hdlcd->clk);
605 return 0;
606 }
607
608 dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
609
610 fb_dealloc_cmap(&hdlcd->fb.cmap);
611setup_err:
612 iounmap(hdlcd->base);
613kmalloc_err:
614 kfree(hdlcd->fb.pseudo_palette);
615remap_err:
616 clk_put(hdlcd->clk);
617 return err;
618}
619
620static inline unsigned char atohex(u8 data)
621{
622 if (!isxdigit(data))
623 return 0;
624 /* truncate the upper nibble and add 9 to non-digit values */
625 return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf);
626}
627
628/* EDID data is passed from devicetree in a literal string that can contain spaces and
629 the hexadecimal dump of the data */
630static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
631{
632 int i, j;
633
634 if (!edid_data)
635 return -EINVAL;
636
637 hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
638 if (!hdlcd->edid)
639 return -ENOMEM;
640
641 for (i = 0, j = 0; i < data_len; i++) {
642 if (isspace(edid_data[i]))
643 continue;
644 hdlcd->edid[j++] = atohex(edid_data[i]);
645 if (j >= EDID_LENGTH)
646 break;
647 }
648
649 if (j < EDID_LENGTH) {
650 kfree(hdlcd->edid);
651 hdlcd->edid = NULL;
652 return -EINVAL;
653 }
654
655 return 0;
656}
657
658static int hdlcd_probe(struct platform_device *pdev)
659{
660 int err = 0, i;
661 struct hdlcd_device *hdlcd;
662 struct resource *mem;
663#ifdef CONFIG_OF
664 struct device_node *of_node;
665#endif
666
667 memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo));
668
669 dev_dbg(&pdev->dev, "HDLCD: probing\n");
670
671 hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL);
672 if (!hdlcd)
673 return -ENOMEM;
674
675#ifdef CONFIG_OF
676 of_node = pdev->dev.of_node;
677 if (of_node) {
678 int len;
679 const u8 *edid;
Jon Medhurst51f19112012-07-13 13:32:48 +0100680 const __be32 *prop = of_get_property(of_node, "mode", &len);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000681 if (prop)
Jon Medhurst51f19112012-07-13 13:32:48 +0100682 strncpy(fb_mode, (char *)prop, len);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000683 prop = of_get_property(of_node, "framebuffer", &len);
684 if (prop) {
Jon Medhurst51f19112012-07-13 13:32:48 +0100685 hdlcd->fb.fix.smem_start = of_read_ulong(prop,
686 of_n_addr_cells(of_node));
687 prop += of_n_addr_cells(of_node);
688 framebuffer_size = of_read_ulong(prop,
689 of_n_size_cells(of_node));
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000690 if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
691 framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE;
692 dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
693 hdlcd->fb.fix.smem_start, framebuffer_size);
694 }
695 edid = of_get_property(of_node, "edid", &len);
696 if (edid) {
697 err = parse_edid_data(hdlcd, edid, len);
698#ifdef CONFIG_SERIAL_AMBA_PCU_UART
699 } else {
700 /* ask the firmware to fetch the EDID */
701 dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n");
702 hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
703 if (!hdlcd->edid)
704 return -ENOMEM;
705 err = get_edid(hdlcd->edid);
706#endif /* CONFIG_SERIAL_AMBA_PCU_UART */
707 }
708 if (err)
709 dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
710 }
711#endif /* CONFIG_OF */
712
713 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
714 if (!mem) {
715 dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
716 err = -EINVAL;
717 goto resource_err;
718 }
719
720 i = platform_get_irq(pdev, 0);
721 if (i < 0) {
722 dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
723 err = -ENOENT;
724 goto resource_err;
725 } else {
726 err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
727 if (err) {
728 dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
729 goto resource_err;
730 }
731 hdlcd->irq = i;
732 }
733
734 if (!request_mem_region(mem->start, resource_size(mem), dev_name(&pdev->dev))) {
735 err = -ENXIO;
736 goto request_err;
737 }
738
739 if (!hdlcd->fb.fix.smem_start) {
740 dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
741 err = -ENOMEM;
742 goto memalloc_err;
743 }
744 hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size);
745 if (!hdlcd->fb.screen_base) {
746 dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
747 err = -ENOMEM;
748 goto probe_err;
749 }
750
751 hdlcd->fb.screen_size = framebuffer_size;
752 hdlcd->fb.fix.smem_len = framebuffer_size;
753 hdlcd->fb.fix.mmio_start = mem->start;
754 hdlcd->fb.fix.mmio_len = resource_size(mem);
755
756 /* Clear the framebuffer */
757 memset(hdlcd->fb.screen_base, 0, framebuffer_size);
758
759 hdlcd->dev = &pdev->dev;
760
761 dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n",
762 hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start);
763
764 err = hdlcd_setup(hdlcd);
765
766 if (err)
767 goto probe_err;
768
769 platform_set_drvdata(pdev, hdlcd);
770 return 0;
771
772probe_err:
773 iounmap(hdlcd->fb.screen_base);
774 memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
775
776memalloc_err:
777 release_mem_region(mem->start, resource_size(mem));
778
779request_err:
780 free_irq(hdlcd->irq, hdlcd);
781
782resource_err:
783 kfree(hdlcd);
784
785 return err;
786}
787
788static int hdlcd_remove(struct platform_device *pdev)
789{
790 struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
791
792 clk_disable(hdlcd->clk);
Jon Medhurst7c7d3b32012-06-13 09:45:43 +0100793 clk_unprepare(hdlcd->clk);
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000794 clk_put(hdlcd->clk);
795
796 /* unmap memory */
797 iounmap(hdlcd->fb.screen_base);
798 iounmap(hdlcd->base);
799
800 /* deallocate fb memory */
801 fb_dealloc_cmap(&hdlcd->fb.cmap);
802 kfree(hdlcd->fb.pseudo_palette);
803 memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
804 release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
805
806 free_irq(hdlcd->irq, NULL);
807 kfree(hdlcd);
808
809 return 0;
810}
811
812#ifdef CONFIG_PM
813static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
814{
815 /* not implemented yet */
816 return 0;
817}
818
819static int hdlcd_resume(struct platform_device *pdev)
820{
821 /* not implemented yet */
822 return 0;
823}
824#else
825#define hdlcd_suspend NULL
826#define hdlcd_resume NULL
827#endif
828
829static struct platform_driver hdlcd_driver = {
830 .probe = hdlcd_probe,
831 .remove = hdlcd_remove,
832 .suspend = hdlcd_suspend,
833 .resume = hdlcd_resume,
834 .driver = {
835 .name = "hdlcd",
836 .owner = THIS_MODULE,
837 .of_match_table = hdlcd_of_matches,
838 },
839};
840
841static int __init hdlcd_init(void)
842{
Chris Redpath7195cb12012-06-29 16:07:08 +0100843#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
844 int err = platform_driver_register(&hdlcd_driver);
845 if (!err)
846 hdlcd_underrun_init();
847 return err;
848#else
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000849 return platform_driver_register(&hdlcd_driver);
Chris Redpath7195cb12012-06-29 16:07:08 +0100850#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000851}
852
853void __exit hdlcd_exit(void)
854{
Chris Redpath7195cb12012-06-29 16:07:08 +0100855#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
856 hdlcd_underrun_close();
857#endif
Liviu Dudaucaf003f2012-01-18 16:52:04 +0000858 platform_driver_unregister(&hdlcd_driver);
859}
860
861module_init(hdlcd_init);
862module_exit(hdlcd_exit);
863
864MODULE_AUTHOR("Liviu Dudau");
865MODULE_DESCRIPTION("ARM HDLCD core driver");
866MODULE_LICENSE("GPL v2");