Branch data Line data Source code
1 : : /*
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/interrupt.h>
25 : : #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>
30 : : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
31 : : #include <linux/proc_fs.h>
32 : : #include <linux/seq_file.h>
33 : : #endif
34 : :
35 : : #include "edid.h"
36 : :
37 : : #ifdef CONFIG_SERIAL_AMBA_PCU_UART
38 : : int get_edid(u8 *msgbuf);
39 : : #else
40 : : #endif
41 : :
42 : : #define to_hdlcd_device(info) container_of(info, struct hdlcd_device, fb)
43 : :
44 : : static struct of_device_id hdlcd_of_matches[] = {
45 : : { .compatible = "arm,hdlcd" },
46 : : {},
47 : : };
48 : :
49 : : /* Framebuffer size. */
50 : : static unsigned long framebuffer_size;
51 : :
52 : : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
53 : : static unsigned long buffer_underrun_events;
54 : : static DEFINE_SPINLOCK(hdlcd_underrun_lock);
55 : :
56 : : static 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 : :
63 : : static 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
73 : : static 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 : :
81 : : static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
82 : : {
83 : : return single_open(file, hdlcd_underrun_show, NULL);
84 : : }
85 : :
86 : : static 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 : :
93 : : static 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 : : }
99 : : static void hdlcd_underrun_close(void)
100 : : {
101 : : remove_proc_entry("hdlcd_underrun", NULL);
102 : : }
103 : : #else
104 : : static int hdlcd_underrun_init(void) { return 0; }
105 : : static void hdlcd_underrun_close(void) { }
106 : : #endif
107 : : #endif
108 : :
109 : : static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
110 : :
111 : : static struct fb_var_screeninfo cached_var_screeninfo;
112 : :
113 : : static 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 : :
128 : : static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
129 : : {
130 : : dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
131 : 0 : writel(1, hdlcd->base + HDLCD_REG_COMMAND);
132 : : }
133 : :
134 : : static inline void hdlcd_disable(struct hdlcd_device *hdlcd)
135 : : {
136 : : dev_dbg(hdlcd->dev, "HDLCD: output disabled\n");
137 : 0 : writel(0, hdlcd->base + HDLCD_REG_COMMAND);
138 : : }
139 : :
140 : 0 : static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd,
141 : : struct fb_var_screeninfo *var)
142 : : {
143 : : int ret = 0;
144 : :
145 : 0 : memset(&var->transp, 0, sizeof(var->transp));
146 : 0 : var->red.msb_right = 0;
147 : 0 : var->green.msb_right = 0;
148 : 0 : var->blue.msb_right = 0;
149 : 0 : var->blue.offset = 0;
150 : :
151 [ # # # # : 0 : switch (var->bits_per_pixel) {
# ]
152 : : case 8:
153 : : /* pseudocolor */
154 : 0 : var->red.length = 8;
155 : 0 : var->green.length = 8;
156 : 0 : var->blue.length = 8;
157 : : break;
158 : : case 16:
159 : : /* 565 format */
160 : 0 : var->red.length = 5;
161 : 0 : var->green.length = 6;
162 : 0 : var->blue.length = 5;
163 : : break;
164 : : case 32:
165 : 0 : var->transp.length = 8;
166 : : case 24:
167 : 0 : var->red.length = 8;
168 : 0 : var->green.length = 8;
169 : 0 : var->blue.length = 8;
170 : : break;
171 : : default:
172 : : ret = -EINVAL;
173 : : break;
174 : : }
175 : :
176 [ # # ]: 0 : if (!ret) {
177 [ # # ]: 0 : if(var->bits_per_pixel != 32)
178 : : {
179 : 0 : var->green.offset = var->blue.length;
180 : 0 : 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 : 0 : var->green.offset = var->red.length;
193 : 0 : var->blue.offset = var->green.offset + var->green.length;
194 : 0 : var->transp.offset = var->blue.offset + var->blue.length;
195 : : }
196 : : }
197 : :
198 : 0 : return ret;
199 : : }
200 : :
201 : 0 : static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
202 : : {
203 : : struct hdlcd_device *hdlcd = to_hdlcd_device(info);
204 : 0 : int bytes_per_pixel = var->bits_per_pixel / 8;
205 : :
206 : : #ifdef HDLCD_NO_VIRTUAL_SCREEN
207 : : var->yres_virtual = var->yres;
208 : : #else
209 : 0 : var->yres_virtual = 2 * var->yres;
210 : : #endif
211 : :
212 [ # # ]: 0 : if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len)
213 : : return -ENOMEM;
214 : :
215 [ # # ][ # # ]: 0 : if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
216 : : return -EINVAL;
217 : :
218 : : /* make sure the bitfields are set appropriately */
219 : 0 : return hdlcd_set_bitfields(hdlcd, var);
220 : : }
221 : :
222 : : /* prototype */
223 : : static int hdlcd_pan_display(struct fb_var_screeninfo *var,
224 : : struct fb_info *info);
225 : :
226 : : #define WRITE_HDLCD_REG(reg, value) writel((value), hdlcd->base + (reg))
227 : : #define READ_HDLCD_REG(reg) readl(hdlcd->base + (reg))
228 : :
229 : 0 : static int hdlcd_set_par(struct fb_info *info)
230 : : {
231 : : struct hdlcd_device *hdlcd = to_hdlcd_device(info);
232 : 0 : int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8;
233 : : int polarities;
234 : : int old_yoffset;
235 : :
236 : : /* check for shortcuts */
237 : 0 : old_yoffset = cached_var_screeninfo.yoffset;
238 : 0 : cached_var_screeninfo.yoffset = info->var.yoffset;
239 [ # # ]: 0 : if (!memcmp(&info->var, &cached_var_screeninfo,
240 : : sizeof(struct fb_var_screeninfo))) {
241 [ # # ]: 0 : if(old_yoffset != info->var.yoffset) {
242 : : /* we only changed yoffset, and we already
243 : : * already recorded it a couple lines up
244 : : */
245 : 0 : hdlcd_pan_display(&info->var, info);
246 : : }
247 : : /* or no change */
248 : : return 0;
249 : : }
250 : :
251 : 0 : hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel;
252 : :
253 [ # # ]: 0 : if (hdlcd->fb.var.bits_per_pixel >= 16)
254 : 0 : hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
255 : : else
256 : 0 : hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
257 : :
258 : 0 : 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 [ # # ]: 0 : polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
266 : 0 : polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
267 : :
268 : : hdlcd_disable(hdlcd);
269 : :
270 : 0 : WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel);
271 : 0 : WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel);
272 : 0 : WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
273 : 0 : WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
274 : 0 : WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
275 : 0 : WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
276 : 0 : WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
277 : 0 : WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
278 : 0 : WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
279 : 0 : WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
280 : 0 : WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
281 : 0 : WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
282 : 0 : WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
283 : : #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
287 : 0 : WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset);
288 : : #endif
289 : 0 : WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
290 : 0 : WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
291 : :
292 : 0 : clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
293 : 0 : clk_enable(hdlcd->clk);
294 : :
295 : : hdlcd_enable(hdlcd);
296 : :
297 : 0 : return 0;
298 : : }
299 : :
300 : 0 : static 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 [ # # ]: 0 : if (regno < 16) {
304 : 0 : u32 *pal = info->pseudo_palette;
305 : :
306 : 0 : pal[regno] = ((red >> 8) << info->var.red.offset) |
307 : 0 : ((green >> 8) << info->var.green.offset) |
308 : 0 : ((blue >> 8) << info->var.blue.offset);
309 : : }
310 : :
311 : 0 : return 0;
312 : : }
313 : :
314 : 0 : static irqreturn_t hdlcd_irq(int irq, void *data)
315 : : {
316 : : struct hdlcd_device *hdlcd = data;
317 : : unsigned long irq_mask, irq_status;
318 : :
319 : 0 : irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
320 : 0 : irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
321 : :
322 : : /* acknowledge interrupt(s) */
323 : 0 : WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
324 : : #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
330 [ # # ]: 0 : if (irq_status & HDLCD_INTERRUPT_VSYNC) {
331 : : /* disable future VSYNC interrupts */
332 : 0 : WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
333 : :
334 : 0 : complete(&hdlcd->vsync_completion);
335 : : }
336 : :
337 : 0 : return IRQ_HANDLED;
338 : : }
339 : :
340 : 0 : static 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 : 0 : irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
348 : 0 : WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
349 : :
350 : 0 : err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
351 : : msecs_to_jiffies(100));
352 : :
353 [ # # ]: 0 : if (!err)
354 : : return -ETIMEDOUT;
355 : :
356 : 0 : return 0;
357 : : }
358 : :
359 : 0 : static int hdlcd_blank(int blank_mode, struct fb_info *info)
360 : : {
361 : : struct hdlcd_device *hdlcd = to_hdlcd_device(info);
362 : :
363 [ # # # # ]: 0 : switch (blank_mode) {
364 : : case FB_BLANK_POWERDOWN:
365 : 0 : clk_disable(hdlcd->clk);
366 : : case FB_BLANK_NORMAL:
367 : : hdlcd_disable(hdlcd);
368 : : break;
369 : : case FB_BLANK_UNBLANK:
370 : 0 : 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 : :
382 : 0 : static void hdlcd_mmap_open(struct vm_area_struct *vma)
383 : : {
384 : 0 : }
385 : :
386 : 0 : static void hdlcd_mmap_close(struct vm_area_struct *vma)
387 : : {
388 : 0 : }
389 : :
390 : : static struct vm_operations_struct hdlcd_mmap_ops = {
391 : : .open = hdlcd_mmap_open,
392 : : .close = hdlcd_mmap_close,
393 : : };
394 : :
395 : 0 : static 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 : 0 : unsigned long len = hdlcd->fb.fix.smem_len;
401 : :
402 [ # # ]: 0 : if (vma->vm_end - vma->vm_start == 0)
403 : : return 0;
404 [ # # ]: 0 : if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
405 : : return -EINVAL;
406 : :
407 : 0 : off = vma->vm_pgoff << PAGE_SHIFT;
408 [ # # ][ # # ]: 0 : if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
409 : : return -EINVAL;
410 : :
411 : 0 : start = hdlcd->fb.fix.smem_start;
412 : 0 : off += start;
413 : :
414 : 0 : vma->vm_pgoff = off >> PAGE_SHIFT;
415 : 0 : vma->vm_flags |= VM_IO;
416 : 0 : vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
417 : 0 : vma->vm_ops = &hdlcd_mmap_ops;
418 [ # # ]: 0 : 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 : 0 : return 0;
424 : : }
425 : :
426 : 0 : static 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 : 0 : hdlcd->fb.var.yoffset = var->yoffset;
431 : 0 : WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
432 : : (var->yoffset * hdlcd->fb.fix.line_length));
433 : :
434 : 0 : hdlcd_wait_for_vsync(info);
435 : :
436 : 0 : return 0;
437 : : }
438 : :
439 : 0 : static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
440 : : {
441 : : int err;
442 : :
443 [ # # ]: 0 : switch (cmd) {
444 : : case FBIO_WAITFORVSYNC:
445 : 0 : err = hdlcd_wait_for_vsync(info);
446 : 0 : break;
447 : : default:
448 : : err = -ENOIOCTLCMD;
449 : : break;
450 : : }
451 : :
452 : 0 : return err;
453 : : }
454 : :
455 : : static 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 : :
470 : 0 : static int hdlcd_setup(struct hdlcd_device *hdlcd)
471 : : {
472 : : u32 version;
473 : : int err = -EFAULT;
474 : :
475 : 0 : hdlcd->fb.device = hdlcd->dev;
476 : :
477 : 0 : hdlcd->clk = clk_get(hdlcd->dev, NULL);
478 [ # # ]: 0 : if (IS_ERR(hdlcd->clk)) {
479 : 0 : dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
480 : 0 : return PTR_ERR(hdlcd->clk);
481 : : }
482 : :
483 : 0 : err = clk_prepare(hdlcd->clk);
484 [ # # ]: 0 : if (err)
485 : : goto clk_prepare_err;
486 : :
487 : 0 : hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
488 [ # # ]: 0 : if (!hdlcd->base) {
489 : 0 : dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
490 : 0 : goto remap_err;
491 : : }
492 : :
493 : 0 : hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
494 [ # # ]: 0 : if (!hdlcd->fb.pseudo_palette) {
495 : 0 : dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
496 : : err = -ENOMEM;
497 : 0 : goto kmalloc_err;
498 : : }
499 : :
500 : 0 : version = readl(hdlcd->base + HDLCD_REG_VERSION);
501 [ # # ]: 0 : if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
502 : 0 : dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
503 : : err = -EINVAL;
504 : 0 : goto kmalloc_err;
505 : : }
506 : 0 : 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 : 0 : strcpy(hdlcd->fb.fix.id, "hdlcd");
511 : 0 : hdlcd->fb.fbops = &hdlcd_ops;
512 : 0 : hdlcd->fb.flags = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
513 : :
514 : 0 : hdlcd->fb.fix.type = FB_TYPE_PACKED_PIXELS;
515 : 0 : hdlcd->fb.fix.type_aux = 0;
516 : 0 : hdlcd->fb.fix.xpanstep = 0;
517 : 0 : hdlcd->fb.fix.ypanstep = 1;
518 : 0 : hdlcd->fb.fix.ywrapstep = 0;
519 : 0 : hdlcd->fb.fix.accel = FB_ACCEL_NONE;
520 : :
521 : 0 : hdlcd->fb.var.nonstd = 0;
522 : 0 : hdlcd->fb.var.activate = FB_ACTIVATE_NOW;
523 : 0 : hdlcd->fb.var.height = -1;
524 : 0 : hdlcd->fb.var.width = -1;
525 : 0 : hdlcd->fb.var.accel_flags = 0;
526 : :
527 : : init_completion(&hdlcd->vsync_completion);
528 : :
529 [ # # ]: 0 : if (hdlcd->edid) {
530 : : /* build modedb from EDID */
531 : 0 : fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
532 : 0 : fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
533 : 0 : hdlcd->fb.monspecs.modedb_len,
534 : : &hdlcd->fb.modelist);
535 : 0 : fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
536 : 0 : hdlcd->fb.monspecs.modedb,
537 : : hdlcd->fb.monspecs.modedb_len,
538 : : &hdlcd_default_mode, 32);
539 : : } else {
540 : 0 : hdlcd->fb.monspecs.hfmin = 0;
541 : 0 : hdlcd->fb.monspecs.hfmax = 100000;
542 : 0 : hdlcd->fb.monspecs.vfmin = 0;
543 : 0 : hdlcd->fb.monspecs.vfmax = 400;
544 : 0 : hdlcd->fb.monspecs.dclkmin = 1000000;
545 : 0 : hdlcd->fb.monspecs.dclkmax = 100000000;
546 : 0 : fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
547 : : }
548 : :
549 [ # # ]: 0 : 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 : 0 : hdlcd->fb.var.xres_virtual = hdlcd->fb.var.xres;
553 : : #ifdef HDLCD_NO_VIRTUAL_SCREEN
554 : : hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres;
555 : : #else
556 : 0 : hdlcd->fb.var.yres_virtual = hdlcd->fb.var.yres * 2;
557 : : #endif
558 : :
559 : : /* initialise and set the palette */
560 [ # # ]: 0 : if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
561 : 0 : dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
562 : : err = -ENOMEM;
563 : 0 : goto setup_err;
564 : : }
565 : 0 : fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
566 : :
567 : : /* Allow max number of outstanding requests with the largest beat burst */
568 : 0 : 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 : 0 : WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
571 : : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
572 : : /* turn on underrun interrupt for counting */
573 : : WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
574 : : #else
575 : : /* Ensure interrupts are disabled */
576 : 0 : WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
577 : : #endif
578 : 0 : fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
579 : :
580 [ # # ]: 0 : if (!register_framebuffer(&hdlcd->fb)) {
581 : : return 0;
582 : : }
583 : :
584 : 0 : dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
585 : :
586 : 0 : fb_dealloc_cmap(&hdlcd->fb.cmap);
587 : : setup_err:
588 : 0 : iounmap(hdlcd->base);
589 : : kmalloc_err:
590 : 0 : kfree(hdlcd->fb.pseudo_palette);
591 : : remap_err:
592 : 0 : clk_unprepare(hdlcd->clk);
593 : : clk_prepare_err:
594 : 0 : clk_put(hdlcd->clk);
595 : 0 : return err;
596 : : }
597 : :
598 : : static inline unsigned char atohex(u8 data)
599 : : {
600 [ # # ]: 0 : if (!isxdigit(data))
601 : : return 0;
602 : : /* truncate the upper nibble and add 9 to non-digit values */
603 [ # # ]: 0 : 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 */
608 : 0 : static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
609 : : {
610 : : int i, j;
611 : :
612 [ # # ]: 0 : if (!edid_data)
613 : : return -EINVAL;
614 : :
615 : 0 : hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
616 [ # # ]: 0 : if (!hdlcd->edid)
617 : : return -ENOMEM;
618 : :
619 [ # # ]: 0 : for (i = 0, j = 0; i < data_len; i++) {
620 [ # # ]: 0 : if (isspace(edid_data[i]))
621 : 0 : continue;
622 : 0 : hdlcd->edid[j++] = atohex(edid_data[i]);
623 [ # # ]: 0 : if (j >= EDID_LENGTH)
624 : : break;
625 : : }
626 : :
627 [ # # ]: 0 : if (j < EDID_LENGTH) {
628 : 0 : kfree(hdlcd->edid);
629 : 0 : hdlcd->edid = NULL;
630 : : return -EINVAL;
631 : : }
632 : :
633 : : return 0;
634 : : }
635 : :
636 : 0 : static int hdlcd_probe(struct platform_device *pdev)
637 : : {
638 : : int err = 0, i;
639 : : struct hdlcd_device *hdlcd;
640 : 0 : struct resource *mem;
641 : : #ifdef CONFIG_OF
642 : : struct device_node *of_node;
643 : : #endif
644 : :
645 : 0 : 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 [ # # ]: 0 : if (!hdlcd)
651 : : return -ENOMEM;
652 : :
653 : : #ifdef CONFIG_OF
654 : 0 : of_node = pdev->dev.of_node;
655 [ # # ]: 0 : if (of_node) {
656 : : int len;
657 : : const u8 *edid;
658 : 0 : const __be32 *prop = of_get_property(of_node, "mode", &len);
659 [ # # ]: 0 : if (prop)
660 : 0 : strncpy(fb_mode, (char *)prop, len);
661 : 0 : prop = of_get_property(of_node, "framebuffer", &len);
662 [ # # ]: 0 : if (prop) {
663 : 0 : hdlcd->fb.fix.smem_start = of_read_ulong(prop,
664 : : of_n_addr_cells(of_node));
665 : 0 : prop += of_n_addr_cells(of_node);
666 : 0 : framebuffer_size = of_read_ulong(prop,
667 : : of_n_size_cells(of_node));
668 [ # # ]: 0 : if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
669 : 0 : 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 : 0 : edid = of_get_property(of_node, "edid", &len);
674 [ # # ]: 0 : if (edid) {
675 : 0 : 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 [ # # ]: 0 : if (err)
687 : 0 : dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
688 : : }
689 : : #endif /* CONFIG_OF */
690 : :
691 : 0 : mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
692 [ # # ]: 0 : if (!mem) {
693 : 0 : dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
694 : : err = -EINVAL;
695 : 0 : goto resource_err;
696 : : }
697 : :
698 : 0 : i = platform_get_irq(pdev, 0);
699 [ # # ]: 0 : if (i < 0) {
700 : 0 : dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
701 : : err = -ENOENT;
702 : 0 : goto resource_err;
703 : : } else {
704 : 0 : err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
705 [ # # ]: 0 : if (err) {
706 : 0 : dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
707 : 0 : goto resource_err;
708 : : }
709 : 0 : hdlcd->irq = i;
710 : : }
711 : :
712 [ # # ]: 0 : if (!request_mem_region(mem->start, resource_size(mem), dev_name(&pdev->dev))) {
713 : : err = -ENXIO;
714 : : goto request_err;
715 : : }
716 : :
717 [ # # ]: 0 : if (!hdlcd->fb.fix.smem_start) {
718 : 0 : dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
719 : : err = -ENOMEM;
720 : 0 : goto memalloc_err;
721 : : }
722 : 0 : hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size);
723 [ # # ]: 0 : if (!hdlcd->fb.screen_base) {
724 : 0 : dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
725 : : err = -ENOMEM;
726 : 0 : goto probe_err;
727 : : }
728 : :
729 : 0 : hdlcd->fb.screen_size = framebuffer_size;
730 : 0 : hdlcd->fb.fix.smem_len = framebuffer_size;
731 : 0 : hdlcd->fb.fix.mmio_start = mem->start;
732 : 0 : hdlcd->fb.fix.mmio_len = resource_size(mem);
733 : :
734 : : /* Clear the framebuffer */
735 [ # # ]: 0 : memset(hdlcd->fb.screen_base, 0, framebuffer_size);
736 : :
737 : 0 : 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 : 0 : err = hdlcd_setup(hdlcd);
743 : :
744 [ # # ]: 0 : if (err)
745 : : goto probe_err;
746 : :
747 : : platform_set_drvdata(pdev, hdlcd);
748 : 0 : return 0;
749 : :
750 : : probe_err:
751 : 0 : iounmap(hdlcd->fb.screen_base);
752 : 0 : memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
753 : :
754 : : memalloc_err:
755 : 0 : release_mem_region(mem->start, resource_size(mem));
756 : :
757 : : request_err:
758 : 0 : free_irq(hdlcd->irq, hdlcd);
759 : :
760 : : resource_err:
761 : 0 : kfree(hdlcd);
762 : :
763 : 0 : return err;
764 : : }
765 : :
766 : 0 : static int hdlcd_remove(struct platform_device *pdev)
767 : : {
768 : : struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
769 : :
770 : 0 : clk_disable(hdlcd->clk);
771 : 0 : clk_unprepare(hdlcd->clk);
772 : 0 : clk_put(hdlcd->clk);
773 : :
774 : : /* unmap memory */
775 : 0 : iounmap(hdlcd->fb.screen_base);
776 : 0 : iounmap(hdlcd->base);
777 : :
778 : : /* deallocate fb memory */
779 : 0 : fb_dealloc_cmap(&hdlcd->fb.cmap);
780 : 0 : kfree(hdlcd->fb.pseudo_palette);
781 : 0 : memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
782 : 0 : release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
783 : :
784 : 0 : free_irq(hdlcd->irq, NULL);
785 : 0 : kfree(hdlcd);
786 : :
787 : 0 : return 0;
788 : : }
789 : :
790 : : #ifdef CONFIG_PM
791 : 0 : static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
792 : : {
793 : : /* not implemented yet */
794 : 0 : return 0;
795 : : }
796 : :
797 : 0 : static int hdlcd_resume(struct platform_device *pdev)
798 : : {
799 : : /* not implemented yet */
800 : 0 : return 0;
801 : : }
802 : : #else
803 : : #define hdlcd_suspend NULL
804 : : #define hdlcd_resume NULL
805 : : #endif
806 : :
807 : : static 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 : :
819 : 0 : static int __init hdlcd_init(void)
820 : : {
821 : : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
822 : : int err = platform_driver_register(&hdlcd_driver);
823 : : if (!err)
824 : : hdlcd_underrun_init();
825 : : return err;
826 : : #else
827 : 0 : return platform_driver_register(&hdlcd_driver);
828 : : #endif
829 : : }
830 : :
831 : 0 : void __exit hdlcd_exit(void)
832 : : {
833 : : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
834 : : hdlcd_underrun_close();
835 : : #endif
836 : 0 : platform_driver_unregister(&hdlcd_driver);
837 : 0 : }
838 : :
839 : : module_init(hdlcd_init);
840 : : module_exit(hdlcd_exit);
841 : :
842 : : MODULE_AUTHOR("Liviu Dudau");
843 : : MODULE_DESCRIPTION("ARM HDLCD core driver");
844 : : MODULE_LICENSE("GPL v2");
|