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