1f7018c21STomi Valkeinen /* 2f7018c21STomi Valkeinen * linux/drivers/video/pxafb.c 3f7018c21STomi Valkeinen * 4f7018c21STomi Valkeinen * Copyright (C) 1999 Eric A. Thomas. 5f7018c21STomi Valkeinen * Copyright (C) 2004 Jean-Frederic Clere. 6f7018c21STomi Valkeinen * Copyright (C) 2004 Ian Campbell. 7f7018c21STomi Valkeinen * Copyright (C) 2004 Jeff Lackey. 8f7018c21STomi Valkeinen * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas 9f7018c21STomi Valkeinen * which in turn is 10f7018c21STomi Valkeinen * Based on acornfb.c Copyright (C) Russell King. 11f7018c21STomi Valkeinen * 12f7018c21STomi Valkeinen * This file is subject to the terms and conditions of the GNU General Public 13f7018c21STomi Valkeinen * License. See the file COPYING in the main directory of this archive for 14f7018c21STomi Valkeinen * more details. 15f7018c21STomi Valkeinen * 16f7018c21STomi Valkeinen * Intel PXA250/210 LCD Controller Frame Buffer Driver 17f7018c21STomi Valkeinen * 18f7018c21STomi Valkeinen * Please direct your questions and comments on this driver to the following 19f7018c21STomi Valkeinen * email address: 20f7018c21STomi Valkeinen * 21f7018c21STomi Valkeinen * linux-arm-kernel@lists.arm.linux.org.uk 22f7018c21STomi Valkeinen * 23f7018c21STomi Valkeinen * Add support for overlay1 and overlay2 based on pxafb_overlay.c: 24f7018c21STomi Valkeinen * 25f7018c21STomi Valkeinen * Copyright (C) 2004, Intel Corporation 26f7018c21STomi Valkeinen * 27f7018c21STomi Valkeinen * 2003/08/27: <yu.tang@intel.com> 28f7018c21STomi Valkeinen * 2004/03/10: <stanley.cai@intel.com> 29f7018c21STomi Valkeinen * 2004/10/28: <yan.yin@intel.com> 30f7018c21STomi Valkeinen * 31f7018c21STomi Valkeinen * Copyright (C) 2006-2008 Marvell International Ltd. 32f7018c21STomi Valkeinen * All Rights Reserved 33f7018c21STomi Valkeinen */ 34f7018c21STomi Valkeinen 35f7018c21STomi Valkeinen #include <linux/module.h> 36f7018c21STomi Valkeinen #include <linux/moduleparam.h> 37f7018c21STomi Valkeinen #include <linux/kernel.h> 38f7018c21STomi Valkeinen #include <linux/sched.h> 39f7018c21STomi Valkeinen #include <linux/errno.h> 40f7018c21STomi Valkeinen #include <linux/string.h> 41f7018c21STomi Valkeinen #include <linux/interrupt.h> 42f7018c21STomi Valkeinen #include <linux/slab.h> 43f7018c21STomi Valkeinen #include <linux/mm.h> 44f7018c21STomi Valkeinen #include <linux/fb.h> 45f7018c21STomi Valkeinen #include <linux/delay.h> 46f7018c21STomi Valkeinen #include <linux/init.h> 47f7018c21STomi Valkeinen #include <linux/ioport.h> 48f7018c21STomi Valkeinen #include <linux/cpufreq.h> 49f7018c21STomi Valkeinen #include <linux/platform_device.h> 50f7018c21STomi Valkeinen #include <linux/dma-mapping.h> 51f7018c21STomi Valkeinen #include <linux/clk.h> 52f7018c21STomi Valkeinen #include <linux/err.h> 53f7018c21STomi Valkeinen #include <linux/completion.h> 54f7018c21STomi Valkeinen #include <linux/mutex.h> 55f7018c21STomi Valkeinen #include <linux/kthread.h> 56f7018c21STomi Valkeinen #include <linux/freezer.h> 57f7018c21STomi Valkeinen #include <linux/console.h> 58420a4882SRobert Jarzmik #include <linux/of_graph.h> 59420a4882SRobert Jarzmik #include <video/of_display_timing.h> 60420a4882SRobert Jarzmik #include <video/videomode.h> 61f7018c21STomi Valkeinen 62f7018c21STomi Valkeinen #include <mach/hardware.h> 63f7018c21STomi Valkeinen #include <asm/io.h> 64f7018c21STomi Valkeinen #include <asm/irq.h> 65f7018c21STomi Valkeinen #include <asm/div64.h> 66f7018c21STomi Valkeinen #include <mach/bitfield.h> 67f7018c21STomi Valkeinen #include <linux/platform_data/video-pxafb.h> 68f7018c21STomi Valkeinen 69f7018c21STomi Valkeinen /* 70f7018c21STomi Valkeinen * Complain if VAR is out of range. 71f7018c21STomi Valkeinen */ 72f7018c21STomi Valkeinen #define DEBUG_VAR 1 73f7018c21STomi Valkeinen 74f7018c21STomi Valkeinen #include "pxafb.h" 75f7018c21STomi Valkeinen 76f7018c21STomi Valkeinen /* Bits which should not be set in machine configuration structures */ 77f7018c21STomi Valkeinen #define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ 78f7018c21STomi Valkeinen LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ 79f7018c21STomi Valkeinen LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) 80f7018c21STomi Valkeinen 81f7018c21STomi Valkeinen #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ 82f7018c21STomi Valkeinen LCCR3_PCD | LCCR3_BPP(0xf)) 83f7018c21STomi Valkeinen 84f7018c21STomi Valkeinen static int pxafb_activate_var(struct fb_var_screeninfo *var, 85f7018c21STomi Valkeinen struct pxafb_info *); 86f7018c21STomi Valkeinen static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); 87f7018c21STomi Valkeinen static void setup_base_frame(struct pxafb_info *fbi, 88f7018c21STomi Valkeinen struct fb_var_screeninfo *var, int branch); 89f7018c21STomi Valkeinen static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, 90f7018c21STomi Valkeinen unsigned long offset, size_t size); 91f7018c21STomi Valkeinen 92f7018c21STomi Valkeinen static unsigned long video_mem_size = 0; 93f7018c21STomi Valkeinen 94f7018c21STomi Valkeinen static inline unsigned long 95f7018c21STomi Valkeinen lcd_readl(struct pxafb_info *fbi, unsigned int off) 96f7018c21STomi Valkeinen { 97f7018c21STomi Valkeinen return __raw_readl(fbi->mmio_base + off); 98f7018c21STomi Valkeinen } 99f7018c21STomi Valkeinen 100f7018c21STomi Valkeinen static inline void 101f7018c21STomi Valkeinen lcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val) 102f7018c21STomi Valkeinen { 103f7018c21STomi Valkeinen __raw_writel(val, fbi->mmio_base + off); 104f7018c21STomi Valkeinen } 105f7018c21STomi Valkeinen 106f7018c21STomi Valkeinen static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) 107f7018c21STomi Valkeinen { 108f7018c21STomi Valkeinen unsigned long flags; 109f7018c21STomi Valkeinen 110f7018c21STomi Valkeinen local_irq_save(flags); 111f7018c21STomi Valkeinen /* 112f7018c21STomi Valkeinen * We need to handle two requests being made at the same time. 113f7018c21STomi Valkeinen * There are two important cases: 114f7018c21STomi Valkeinen * 1. When we are changing VT (C_REENABLE) while unblanking 115f7018c21STomi Valkeinen * (C_ENABLE) We must perform the unblanking, which will 116f7018c21STomi Valkeinen * do our REENABLE for us. 117f7018c21STomi Valkeinen * 2. When we are blanking, but immediately unblank before 118f7018c21STomi Valkeinen * we have blanked. We do the "REENABLE" thing here as 119f7018c21STomi Valkeinen * well, just to be sure. 120f7018c21STomi Valkeinen */ 121f7018c21STomi Valkeinen if (fbi->task_state == C_ENABLE && state == C_REENABLE) 122f7018c21STomi Valkeinen state = (u_int) -1; 123f7018c21STomi Valkeinen if (fbi->task_state == C_DISABLE && state == C_ENABLE) 124f7018c21STomi Valkeinen state = C_REENABLE; 125f7018c21STomi Valkeinen 126f7018c21STomi Valkeinen if (state != (u_int)-1) { 127f7018c21STomi Valkeinen fbi->task_state = state; 128f7018c21STomi Valkeinen schedule_work(&fbi->task); 129f7018c21STomi Valkeinen } 130f7018c21STomi Valkeinen local_irq_restore(flags); 131f7018c21STomi Valkeinen } 132f7018c21STomi Valkeinen 133f7018c21STomi Valkeinen static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 134f7018c21STomi Valkeinen { 135f7018c21STomi Valkeinen chan &= 0xffff; 136f7018c21STomi Valkeinen chan >>= 16 - bf->length; 137f7018c21STomi Valkeinen return chan << bf->offset; 138f7018c21STomi Valkeinen } 139f7018c21STomi Valkeinen 140f7018c21STomi Valkeinen static int 141f7018c21STomi Valkeinen pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, 142f7018c21STomi Valkeinen u_int trans, struct fb_info *info) 143f7018c21STomi Valkeinen { 14429ebebb4SFabian Frederick struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 145f7018c21STomi Valkeinen u_int val; 146f7018c21STomi Valkeinen 147f7018c21STomi Valkeinen if (regno >= fbi->palette_size) 148f7018c21STomi Valkeinen return 1; 149f7018c21STomi Valkeinen 150f7018c21STomi Valkeinen if (fbi->fb.var.grayscale) { 151f7018c21STomi Valkeinen fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff); 152f7018c21STomi Valkeinen return 0; 153f7018c21STomi Valkeinen } 154f7018c21STomi Valkeinen 155f7018c21STomi Valkeinen switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) { 156f7018c21STomi Valkeinen case LCCR4_PAL_FOR_0: 157f7018c21STomi Valkeinen val = ((red >> 0) & 0xf800); 158f7018c21STomi Valkeinen val |= ((green >> 5) & 0x07e0); 159f7018c21STomi Valkeinen val |= ((blue >> 11) & 0x001f); 160f7018c21STomi Valkeinen fbi->palette_cpu[regno] = val; 161f7018c21STomi Valkeinen break; 162f7018c21STomi Valkeinen case LCCR4_PAL_FOR_1: 163f7018c21STomi Valkeinen val = ((red << 8) & 0x00f80000); 164f7018c21STomi Valkeinen val |= ((green >> 0) & 0x0000fc00); 165f7018c21STomi Valkeinen val |= ((blue >> 8) & 0x000000f8); 166f7018c21STomi Valkeinen ((u32 *)(fbi->palette_cpu))[regno] = val; 167f7018c21STomi Valkeinen break; 168f7018c21STomi Valkeinen case LCCR4_PAL_FOR_2: 169f7018c21STomi Valkeinen val = ((red << 8) & 0x00fc0000); 170f7018c21STomi Valkeinen val |= ((green >> 0) & 0x0000fc00); 171f7018c21STomi Valkeinen val |= ((blue >> 8) & 0x000000fc); 172f7018c21STomi Valkeinen ((u32 *)(fbi->palette_cpu))[regno] = val; 173f7018c21STomi Valkeinen break; 174f7018c21STomi Valkeinen case LCCR4_PAL_FOR_3: 175f7018c21STomi Valkeinen val = ((red << 8) & 0x00ff0000); 176f7018c21STomi Valkeinen val |= ((green >> 0) & 0x0000ff00); 177f7018c21STomi Valkeinen val |= ((blue >> 8) & 0x000000ff); 178f7018c21STomi Valkeinen ((u32 *)(fbi->palette_cpu))[regno] = val; 179f7018c21STomi Valkeinen break; 180f7018c21STomi Valkeinen } 181f7018c21STomi Valkeinen 182f7018c21STomi Valkeinen return 0; 183f7018c21STomi Valkeinen } 184f7018c21STomi Valkeinen 185f7018c21STomi Valkeinen static int 186f7018c21STomi Valkeinen pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 187f7018c21STomi Valkeinen u_int trans, struct fb_info *info) 188f7018c21STomi Valkeinen { 18929ebebb4SFabian Frederick struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 190f7018c21STomi Valkeinen unsigned int val; 191f7018c21STomi Valkeinen int ret = 1; 192f7018c21STomi Valkeinen 193f7018c21STomi Valkeinen /* 194f7018c21STomi Valkeinen * If inverse mode was selected, invert all the colours 195f7018c21STomi Valkeinen * rather than the register number. The register number 196f7018c21STomi Valkeinen * is what you poke into the framebuffer to produce the 197f7018c21STomi Valkeinen * colour you requested. 198f7018c21STomi Valkeinen */ 199f7018c21STomi Valkeinen if (fbi->cmap_inverse) { 200f7018c21STomi Valkeinen red = 0xffff - red; 201f7018c21STomi Valkeinen green = 0xffff - green; 202f7018c21STomi Valkeinen blue = 0xffff - blue; 203f7018c21STomi Valkeinen } 204f7018c21STomi Valkeinen 205f7018c21STomi Valkeinen /* 206f7018c21STomi Valkeinen * If greyscale is true, then we convert the RGB value 207f7018c21STomi Valkeinen * to greyscale no matter what visual we are using. 208f7018c21STomi Valkeinen */ 209f7018c21STomi Valkeinen if (fbi->fb.var.grayscale) 210f7018c21STomi Valkeinen red = green = blue = (19595 * red + 38470 * green + 211f7018c21STomi Valkeinen 7471 * blue) >> 16; 212f7018c21STomi Valkeinen 213f7018c21STomi Valkeinen switch (fbi->fb.fix.visual) { 214f7018c21STomi Valkeinen case FB_VISUAL_TRUECOLOR: 215f7018c21STomi Valkeinen /* 216f7018c21STomi Valkeinen * 16-bit True Colour. We encode the RGB value 217f7018c21STomi Valkeinen * according to the RGB bitfield information. 218f7018c21STomi Valkeinen */ 219f7018c21STomi Valkeinen if (regno < 16) { 220f7018c21STomi Valkeinen u32 *pal = fbi->fb.pseudo_palette; 221f7018c21STomi Valkeinen 222f7018c21STomi Valkeinen val = chan_to_field(red, &fbi->fb.var.red); 223f7018c21STomi Valkeinen val |= chan_to_field(green, &fbi->fb.var.green); 224f7018c21STomi Valkeinen val |= chan_to_field(blue, &fbi->fb.var.blue); 225f7018c21STomi Valkeinen 226f7018c21STomi Valkeinen pal[regno] = val; 227f7018c21STomi Valkeinen ret = 0; 228f7018c21STomi Valkeinen } 229f7018c21STomi Valkeinen break; 230f7018c21STomi Valkeinen 231f7018c21STomi Valkeinen case FB_VISUAL_STATIC_PSEUDOCOLOR: 232f7018c21STomi Valkeinen case FB_VISUAL_PSEUDOCOLOR: 233f7018c21STomi Valkeinen ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); 234f7018c21STomi Valkeinen break; 235f7018c21STomi Valkeinen } 236f7018c21STomi Valkeinen 237f7018c21STomi Valkeinen return ret; 238f7018c21STomi Valkeinen } 239f7018c21STomi Valkeinen 240f7018c21STomi Valkeinen /* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */ 241f7018c21STomi Valkeinen static inline int var_to_depth(struct fb_var_screeninfo *var) 242f7018c21STomi Valkeinen { 243f7018c21STomi Valkeinen return var->red.length + var->green.length + 244f7018c21STomi Valkeinen var->blue.length + var->transp.length; 245f7018c21STomi Valkeinen } 246f7018c21STomi Valkeinen 247f7018c21STomi Valkeinen /* calculate 4-bit BPP value for LCCR3 and OVLxC1 */ 248f7018c21STomi Valkeinen static int pxafb_var_to_bpp(struct fb_var_screeninfo *var) 249f7018c21STomi Valkeinen { 250f7018c21STomi Valkeinen int bpp = -EINVAL; 251f7018c21STomi Valkeinen 252f7018c21STomi Valkeinen switch (var->bits_per_pixel) { 253f7018c21STomi Valkeinen case 1: bpp = 0; break; 254f7018c21STomi Valkeinen case 2: bpp = 1; break; 255f7018c21STomi Valkeinen case 4: bpp = 2; break; 256f7018c21STomi Valkeinen case 8: bpp = 3; break; 257f7018c21STomi Valkeinen case 16: bpp = 4; break; 258f7018c21STomi Valkeinen case 24: 259f7018c21STomi Valkeinen switch (var_to_depth(var)) { 260f7018c21STomi Valkeinen case 18: bpp = 6; break; /* 18-bits/pixel packed */ 261f7018c21STomi Valkeinen case 19: bpp = 8; break; /* 19-bits/pixel packed */ 262f7018c21STomi Valkeinen case 24: bpp = 9; break; 263f7018c21STomi Valkeinen } 264f7018c21STomi Valkeinen break; 265f7018c21STomi Valkeinen case 32: 266f7018c21STomi Valkeinen switch (var_to_depth(var)) { 267f7018c21STomi Valkeinen case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ 268f7018c21STomi Valkeinen case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ 269f7018c21STomi Valkeinen case 25: bpp = 10; break; 270f7018c21STomi Valkeinen } 271f7018c21STomi Valkeinen break; 272f7018c21STomi Valkeinen } 273f7018c21STomi Valkeinen return bpp; 274f7018c21STomi Valkeinen } 275f7018c21STomi Valkeinen 276f7018c21STomi Valkeinen /* 277f7018c21STomi Valkeinen * pxafb_var_to_lccr3(): 278f7018c21STomi Valkeinen * Convert a bits per pixel value to the correct bit pattern for LCCR3 279f7018c21STomi Valkeinen * 280f7018c21STomi Valkeinen * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an 281f7018c21STomi Valkeinen * implication of the acutal use of transparency bit, which we handle it 282f7018c21STomi Valkeinen * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel 283f7018c21STomi Valkeinen * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. 284f7018c21STomi Valkeinen * 285f7018c21STomi Valkeinen * Transparency for palette pixel formats is not supported at the moment. 286f7018c21STomi Valkeinen */ 287f7018c21STomi Valkeinen static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var) 288f7018c21STomi Valkeinen { 289f7018c21STomi Valkeinen int bpp = pxafb_var_to_bpp(var); 290f7018c21STomi Valkeinen uint32_t lccr3; 291f7018c21STomi Valkeinen 292f7018c21STomi Valkeinen if (bpp < 0) 293f7018c21STomi Valkeinen return 0; 294f7018c21STomi Valkeinen 295f7018c21STomi Valkeinen lccr3 = LCCR3_BPP(bpp); 296f7018c21STomi Valkeinen 297f7018c21STomi Valkeinen switch (var_to_depth(var)) { 298f7018c21STomi Valkeinen case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; 299f7018c21STomi Valkeinen case 18: lccr3 |= LCCR3_PDFOR_3; break; 300f7018c21STomi Valkeinen case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; 301f7018c21STomi Valkeinen break; 302f7018c21STomi Valkeinen case 19: 303f7018c21STomi Valkeinen case 25: lccr3 |= LCCR3_PDFOR_0; break; 304f7018c21STomi Valkeinen } 305f7018c21STomi Valkeinen return lccr3; 306f7018c21STomi Valkeinen } 307f7018c21STomi Valkeinen 308f7018c21STomi Valkeinen #define SET_PIXFMT(v, r, g, b, t) \ 309f7018c21STomi Valkeinen ({ \ 310f7018c21STomi Valkeinen (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ 311f7018c21STomi Valkeinen (v)->transp.length = (t) ? (t) : 0; \ 312f7018c21STomi Valkeinen (v)->blue.length = (b); (v)->blue.offset = 0; \ 313f7018c21STomi Valkeinen (v)->green.length = (g); (v)->green.offset = (b); \ 314f7018c21STomi Valkeinen (v)->red.length = (r); (v)->red.offset = (b) + (g); \ 315f7018c21STomi Valkeinen }) 316f7018c21STomi Valkeinen 317f7018c21STomi Valkeinen /* set the RGBT bitfields of fb_var_screeninf according to 318f7018c21STomi Valkeinen * var->bits_per_pixel and given depth 319f7018c21STomi Valkeinen */ 320f7018c21STomi Valkeinen static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth) 321f7018c21STomi Valkeinen { 322f7018c21STomi Valkeinen if (depth == 0) 323f7018c21STomi Valkeinen depth = var->bits_per_pixel; 324f7018c21STomi Valkeinen 325f7018c21STomi Valkeinen if (var->bits_per_pixel < 16) { 326f7018c21STomi Valkeinen /* indexed pixel formats */ 327f7018c21STomi Valkeinen var->red.offset = 0; var->red.length = 8; 328f7018c21STomi Valkeinen var->green.offset = 0; var->green.length = 8; 329f7018c21STomi Valkeinen var->blue.offset = 0; var->blue.length = 8; 330f7018c21STomi Valkeinen var->transp.offset = 0; var->transp.length = 8; 331f7018c21STomi Valkeinen } 332f7018c21STomi Valkeinen 333f7018c21STomi Valkeinen switch (depth) { 334f7018c21STomi Valkeinen case 16: var->transp.length ? 335f7018c21STomi Valkeinen SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ 336f7018c21STomi Valkeinen SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ 337f7018c21STomi Valkeinen case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ 338f7018c21STomi Valkeinen case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ 339f7018c21STomi Valkeinen case 24: var->transp.length ? 340f7018c21STomi Valkeinen SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ 341f7018c21STomi Valkeinen SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ 342f7018c21STomi Valkeinen case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ 343f7018c21STomi Valkeinen } 344f7018c21STomi Valkeinen } 345f7018c21STomi Valkeinen 346f7018c21STomi Valkeinen #ifdef CONFIG_CPU_FREQ 347f7018c21STomi Valkeinen /* 348f7018c21STomi Valkeinen * pxafb_display_dma_period() 349f7018c21STomi Valkeinen * Calculate the minimum period (in picoseconds) between two DMA 350f7018c21STomi Valkeinen * requests for the LCD controller. If we hit this, it means we're 351f7018c21STomi Valkeinen * doing nothing but LCD DMA. 352f7018c21STomi Valkeinen */ 353f7018c21STomi Valkeinen static unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var) 354f7018c21STomi Valkeinen { 355f7018c21STomi Valkeinen /* 356f7018c21STomi Valkeinen * Period = pixclock * bits_per_byte * bytes_per_transfer 357f7018c21STomi Valkeinen * / memory_bits_per_pixel; 358f7018c21STomi Valkeinen */ 359f7018c21STomi Valkeinen return var->pixclock * 8 * 16 / var->bits_per_pixel; 360f7018c21STomi Valkeinen } 361f7018c21STomi Valkeinen #endif 362f7018c21STomi Valkeinen 363f7018c21STomi Valkeinen /* 364f7018c21STomi Valkeinen * Select the smallest mode that allows the desired resolution to be 365f7018c21STomi Valkeinen * displayed. If desired parameters can be rounded up. 366f7018c21STomi Valkeinen */ 367f7018c21STomi Valkeinen static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, 368f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 369f7018c21STomi Valkeinen { 370f7018c21STomi Valkeinen struct pxafb_mode_info *mode = NULL; 371f7018c21STomi Valkeinen struct pxafb_mode_info *modelist = mach->modes; 372f7018c21STomi Valkeinen unsigned int best_x = 0xffffffff, best_y = 0xffffffff; 373f7018c21STomi Valkeinen unsigned int i; 374f7018c21STomi Valkeinen 375f7018c21STomi Valkeinen for (i = 0; i < mach->num_modes; i++) { 376f7018c21STomi Valkeinen if (modelist[i].xres >= var->xres && 377f7018c21STomi Valkeinen modelist[i].yres >= var->yres && 378f7018c21STomi Valkeinen modelist[i].xres < best_x && 379f7018c21STomi Valkeinen modelist[i].yres < best_y && 380f7018c21STomi Valkeinen modelist[i].bpp >= var->bits_per_pixel) { 381f7018c21STomi Valkeinen best_x = modelist[i].xres; 382f7018c21STomi Valkeinen best_y = modelist[i].yres; 383f7018c21STomi Valkeinen mode = &modelist[i]; 384f7018c21STomi Valkeinen } 385f7018c21STomi Valkeinen } 386f7018c21STomi Valkeinen 387f7018c21STomi Valkeinen return mode; 388f7018c21STomi Valkeinen } 389f7018c21STomi Valkeinen 390f7018c21STomi Valkeinen static void pxafb_setmode(struct fb_var_screeninfo *var, 391f7018c21STomi Valkeinen struct pxafb_mode_info *mode) 392f7018c21STomi Valkeinen { 393f7018c21STomi Valkeinen var->xres = mode->xres; 394f7018c21STomi Valkeinen var->yres = mode->yres; 395f7018c21STomi Valkeinen var->bits_per_pixel = mode->bpp; 396f7018c21STomi Valkeinen var->pixclock = mode->pixclock; 397f7018c21STomi Valkeinen var->hsync_len = mode->hsync_len; 398f7018c21STomi Valkeinen var->left_margin = mode->left_margin; 399f7018c21STomi Valkeinen var->right_margin = mode->right_margin; 400f7018c21STomi Valkeinen var->vsync_len = mode->vsync_len; 401f7018c21STomi Valkeinen var->upper_margin = mode->upper_margin; 402f7018c21STomi Valkeinen var->lower_margin = mode->lower_margin; 403f7018c21STomi Valkeinen var->sync = mode->sync; 404f7018c21STomi Valkeinen var->grayscale = mode->cmap_greyscale; 405f7018c21STomi Valkeinen var->transp.length = mode->transparency; 406f7018c21STomi Valkeinen 407f7018c21STomi Valkeinen /* set the initial RGBA bitfields */ 408f7018c21STomi Valkeinen pxafb_set_pixfmt(var, mode->depth); 409f7018c21STomi Valkeinen } 410f7018c21STomi Valkeinen 411f7018c21STomi Valkeinen static int pxafb_adjust_timing(struct pxafb_info *fbi, 412f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 413f7018c21STomi Valkeinen { 414f7018c21STomi Valkeinen int line_length; 415f7018c21STomi Valkeinen 416f7018c21STomi Valkeinen var->xres = max_t(int, var->xres, MIN_XRES); 417f7018c21STomi Valkeinen var->yres = max_t(int, var->yres, MIN_YRES); 418f7018c21STomi Valkeinen 419f7018c21STomi Valkeinen if (!(fbi->lccr0 & LCCR0_LCDT)) { 420f7018c21STomi Valkeinen clamp_val(var->hsync_len, 1, 64); 421f7018c21STomi Valkeinen clamp_val(var->vsync_len, 1, 64); 422f7018c21STomi Valkeinen clamp_val(var->left_margin, 1, 255); 423f7018c21STomi Valkeinen clamp_val(var->right_margin, 1, 255); 424f7018c21STomi Valkeinen clamp_val(var->upper_margin, 1, 255); 425f7018c21STomi Valkeinen clamp_val(var->lower_margin, 1, 255); 426f7018c21STomi Valkeinen } 427f7018c21STomi Valkeinen 428f7018c21STomi Valkeinen /* make sure each line is aligned on word boundary */ 429f7018c21STomi Valkeinen line_length = var->xres * var->bits_per_pixel / 8; 430f7018c21STomi Valkeinen line_length = ALIGN(line_length, 4); 431f7018c21STomi Valkeinen var->xres = line_length * 8 / var->bits_per_pixel; 432f7018c21STomi Valkeinen 433f7018c21STomi Valkeinen /* we don't support xpan, force xres_virtual to be equal to xres */ 434f7018c21STomi Valkeinen var->xres_virtual = var->xres; 435f7018c21STomi Valkeinen 436f7018c21STomi Valkeinen if (var->accel_flags & FB_ACCELF_TEXT) 437f7018c21STomi Valkeinen var->yres_virtual = fbi->fb.fix.smem_len / line_length; 438f7018c21STomi Valkeinen else 439f7018c21STomi Valkeinen var->yres_virtual = max(var->yres_virtual, var->yres); 440f7018c21STomi Valkeinen 441f7018c21STomi Valkeinen /* check for limits */ 442f7018c21STomi Valkeinen if (var->xres > MAX_XRES || var->yres > MAX_YRES) 443f7018c21STomi Valkeinen return -EINVAL; 444f7018c21STomi Valkeinen 445f7018c21STomi Valkeinen if (var->yres > var->yres_virtual) 446f7018c21STomi Valkeinen return -EINVAL; 447f7018c21STomi Valkeinen 448f7018c21STomi Valkeinen return 0; 449f7018c21STomi Valkeinen } 450f7018c21STomi Valkeinen 451f7018c21STomi Valkeinen /* 452f7018c21STomi Valkeinen * pxafb_check_var(): 453f7018c21STomi Valkeinen * Get the video params out of 'var'. If a value doesn't fit, round it up, 454f7018c21STomi Valkeinen * if it's too big, return -EINVAL. 455f7018c21STomi Valkeinen * 456f7018c21STomi Valkeinen * Round up in the following order: bits_per_pixel, xres, 457f7018c21STomi Valkeinen * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, 458f7018c21STomi Valkeinen * bitfields, horizontal timing, vertical timing. 459f7018c21STomi Valkeinen */ 460f7018c21STomi Valkeinen static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 461f7018c21STomi Valkeinen { 46229ebebb4SFabian Frederick struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 463f3621a60SRobert Jarzmik struct pxafb_mach_info *inf = fbi->inf; 464f7018c21STomi Valkeinen int err; 465f7018c21STomi Valkeinen 466f7018c21STomi Valkeinen if (inf->fixed_modes) { 467f7018c21STomi Valkeinen struct pxafb_mode_info *mode; 468f7018c21STomi Valkeinen 469f7018c21STomi Valkeinen mode = pxafb_getmode(inf, var); 470f7018c21STomi Valkeinen if (!mode) 471f7018c21STomi Valkeinen return -EINVAL; 472f7018c21STomi Valkeinen pxafb_setmode(var, mode); 473f7018c21STomi Valkeinen } 474f7018c21STomi Valkeinen 475f7018c21STomi Valkeinen /* do a test conversion to BPP fields to check the color formats */ 476f7018c21STomi Valkeinen err = pxafb_var_to_bpp(var); 477f7018c21STomi Valkeinen if (err < 0) 478f7018c21STomi Valkeinen return err; 479f7018c21STomi Valkeinen 480f7018c21STomi Valkeinen pxafb_set_pixfmt(var, var_to_depth(var)); 481f7018c21STomi Valkeinen 482f7018c21STomi Valkeinen err = pxafb_adjust_timing(fbi, var); 483f7018c21STomi Valkeinen if (err) 484f7018c21STomi Valkeinen return err; 485f7018c21STomi Valkeinen 486f7018c21STomi Valkeinen #ifdef CONFIG_CPU_FREQ 487f7018c21STomi Valkeinen pr_debug("pxafb: dma period = %d ps\n", 488f7018c21STomi Valkeinen pxafb_display_dma_period(var)); 489f7018c21STomi Valkeinen #endif 490f7018c21STomi Valkeinen 491f7018c21STomi Valkeinen return 0; 492f7018c21STomi Valkeinen } 493f7018c21STomi Valkeinen 494f7018c21STomi Valkeinen /* 495f7018c21STomi Valkeinen * pxafb_set_par(): 496f7018c21STomi Valkeinen * Set the user defined part of the display for the specified console 497f7018c21STomi Valkeinen */ 498f7018c21STomi Valkeinen static int pxafb_set_par(struct fb_info *info) 499f7018c21STomi Valkeinen { 50029ebebb4SFabian Frederick struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 501f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var; 502f7018c21STomi Valkeinen 503f7018c21STomi Valkeinen if (var->bits_per_pixel >= 16) 504f7018c21STomi Valkeinen fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; 505f7018c21STomi Valkeinen else if (!fbi->cmap_static) 506f7018c21STomi Valkeinen fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 507f7018c21STomi Valkeinen else { 508f7018c21STomi Valkeinen /* 509f7018c21STomi Valkeinen * Some people have weird ideas about wanting static 510f7018c21STomi Valkeinen * pseudocolor maps. I suspect their user space 511f7018c21STomi Valkeinen * applications are broken. 512f7018c21STomi Valkeinen */ 513f7018c21STomi Valkeinen fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 514f7018c21STomi Valkeinen } 515f7018c21STomi Valkeinen 516f7018c21STomi Valkeinen fbi->fb.fix.line_length = var->xres_virtual * 517f7018c21STomi Valkeinen var->bits_per_pixel / 8; 518f7018c21STomi Valkeinen if (var->bits_per_pixel >= 16) 519f7018c21STomi Valkeinen fbi->palette_size = 0; 520f7018c21STomi Valkeinen else 521f7018c21STomi Valkeinen fbi->palette_size = var->bits_per_pixel == 1 ? 522f7018c21STomi Valkeinen 4 : 1 << var->bits_per_pixel; 523f7018c21STomi Valkeinen 524f7018c21STomi Valkeinen fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0]; 525f7018c21STomi Valkeinen 526f7018c21STomi Valkeinen if (fbi->fb.var.bits_per_pixel >= 16) 527f7018c21STomi Valkeinen fb_dealloc_cmap(&fbi->fb.cmap); 528f7018c21STomi Valkeinen else 529f7018c21STomi Valkeinen fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); 530f7018c21STomi Valkeinen 531f7018c21STomi Valkeinen pxafb_activate_var(var, fbi); 532f7018c21STomi Valkeinen 533f7018c21STomi Valkeinen return 0; 534f7018c21STomi Valkeinen } 535f7018c21STomi Valkeinen 536f7018c21STomi Valkeinen static int pxafb_pan_display(struct fb_var_screeninfo *var, 537f7018c21STomi Valkeinen struct fb_info *info) 538f7018c21STomi Valkeinen { 53929ebebb4SFabian Frederick struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 540f7018c21STomi Valkeinen struct fb_var_screeninfo newvar; 541f7018c21STomi Valkeinen int dma = DMA_MAX + DMA_BASE; 542f7018c21STomi Valkeinen 543f7018c21STomi Valkeinen if (fbi->state != C_ENABLE) 544f7018c21STomi Valkeinen return 0; 545f7018c21STomi Valkeinen 546f7018c21STomi Valkeinen /* Only take .xoffset, .yoffset and .vmode & FB_VMODE_YWRAP from what 547f7018c21STomi Valkeinen * was passed in and copy the rest from the old screeninfo. 548f7018c21STomi Valkeinen */ 549f7018c21STomi Valkeinen memcpy(&newvar, &fbi->fb.var, sizeof(newvar)); 550f7018c21STomi Valkeinen newvar.xoffset = var->xoffset; 551f7018c21STomi Valkeinen newvar.yoffset = var->yoffset; 552f7018c21STomi Valkeinen newvar.vmode &= ~FB_VMODE_YWRAP; 553f7018c21STomi Valkeinen newvar.vmode |= var->vmode & FB_VMODE_YWRAP; 554f7018c21STomi Valkeinen 555f7018c21STomi Valkeinen setup_base_frame(fbi, &newvar, 1); 556f7018c21STomi Valkeinen 557f7018c21STomi Valkeinen if (fbi->lccr0 & LCCR0_SDS) 558f7018c21STomi Valkeinen lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1); 559f7018c21STomi Valkeinen 560f7018c21STomi Valkeinen lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1); 561f7018c21STomi Valkeinen return 0; 562f7018c21STomi Valkeinen } 563f7018c21STomi Valkeinen 564f7018c21STomi Valkeinen /* 565f7018c21STomi Valkeinen * pxafb_blank(): 566f7018c21STomi Valkeinen * Blank the display by setting all palette values to zero. Note, the 567f7018c21STomi Valkeinen * 16 bpp mode does not really use the palette, so this will not 568f7018c21STomi Valkeinen * blank the display in all modes. 569f7018c21STomi Valkeinen */ 570f7018c21STomi Valkeinen static int pxafb_blank(int blank, struct fb_info *info) 571f7018c21STomi Valkeinen { 57229ebebb4SFabian Frederick struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 573f7018c21STomi Valkeinen int i; 574f7018c21STomi Valkeinen 575f7018c21STomi Valkeinen switch (blank) { 576f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 577f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 578f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 579f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 580f7018c21STomi Valkeinen if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 581f7018c21STomi Valkeinen fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 582f7018c21STomi Valkeinen for (i = 0; i < fbi->palette_size; i++) 583f7018c21STomi Valkeinen pxafb_setpalettereg(i, 0, 0, 0, 0, info); 584f7018c21STomi Valkeinen 585f7018c21STomi Valkeinen pxafb_schedule_work(fbi, C_DISABLE); 586f7018c21STomi Valkeinen /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ 587f7018c21STomi Valkeinen break; 588f7018c21STomi Valkeinen 589f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 590f7018c21STomi Valkeinen /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ 591f7018c21STomi Valkeinen if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 592f7018c21STomi Valkeinen fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 593f7018c21STomi Valkeinen fb_set_cmap(&fbi->fb.cmap, info); 594f7018c21STomi Valkeinen pxafb_schedule_work(fbi, C_ENABLE); 595f7018c21STomi Valkeinen } 596f7018c21STomi Valkeinen return 0; 597f7018c21STomi Valkeinen } 598f7018c21STomi Valkeinen 599f7018c21STomi Valkeinen static struct fb_ops pxafb_ops = { 600f7018c21STomi Valkeinen .owner = THIS_MODULE, 601f7018c21STomi Valkeinen .fb_check_var = pxafb_check_var, 602f7018c21STomi Valkeinen .fb_set_par = pxafb_set_par, 603f7018c21STomi Valkeinen .fb_pan_display = pxafb_pan_display, 604f7018c21STomi Valkeinen .fb_setcolreg = pxafb_setcolreg, 605f7018c21STomi Valkeinen .fb_fillrect = cfb_fillrect, 606f7018c21STomi Valkeinen .fb_copyarea = cfb_copyarea, 607f7018c21STomi Valkeinen .fb_imageblit = cfb_imageblit, 608f7018c21STomi Valkeinen .fb_blank = pxafb_blank, 609f7018c21STomi Valkeinen }; 610f7018c21STomi Valkeinen 611f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_OVERLAY 612f7018c21STomi Valkeinen static void overlay1fb_setup(struct pxafb_layer *ofb) 613f7018c21STomi Valkeinen { 614f7018c21STomi Valkeinen int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; 615f7018c21STomi Valkeinen unsigned long start = ofb->video_mem_phys; 616f7018c21STomi Valkeinen setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size); 617f7018c21STomi Valkeinen } 618f7018c21STomi Valkeinen 619f7018c21STomi Valkeinen /* Depending on the enable status of overlay1/2, the DMA should be 620f7018c21STomi Valkeinen * updated from FDADRx (when disabled) or FBRx (when enabled). 621f7018c21STomi Valkeinen */ 622f7018c21STomi Valkeinen static void overlay1fb_enable(struct pxafb_layer *ofb) 623f7018c21STomi Valkeinen { 624f7018c21STomi Valkeinen int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN; 625f7018c21STomi Valkeinen uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0); 626f7018c21STomi Valkeinen 627f7018c21STomi Valkeinen lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1); 628f7018c21STomi Valkeinen lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]); 629f7018c21STomi Valkeinen lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN); 630f7018c21STomi Valkeinen } 631f7018c21STomi Valkeinen 632f7018c21STomi Valkeinen static void overlay1fb_disable(struct pxafb_layer *ofb) 633f7018c21STomi Valkeinen { 634f7018c21STomi Valkeinen uint32_t lccr5; 635f7018c21STomi Valkeinen 636f7018c21STomi Valkeinen if (!(lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN)) 637f7018c21STomi Valkeinen return; 638f7018c21STomi Valkeinen 639f7018c21STomi Valkeinen lccr5 = lcd_readl(ofb->fbi, LCCR5); 640f7018c21STomi Valkeinen 641f7018c21STomi Valkeinen lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN); 642f7018c21STomi Valkeinen 643f7018c21STomi Valkeinen lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1)); 644f7018c21STomi Valkeinen lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1)); 645f7018c21STomi Valkeinen lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3); 646f7018c21STomi Valkeinen 647f7018c21STomi Valkeinen if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) 6486f9655b1SJoe Perches pr_warn("%s: timeout disabling overlay1\n", __func__); 649f7018c21STomi Valkeinen 650f7018c21STomi Valkeinen lcd_writel(ofb->fbi, LCCR5, lccr5); 651f7018c21STomi Valkeinen } 652f7018c21STomi Valkeinen 653f7018c21STomi Valkeinen static void overlay2fb_setup(struct pxafb_layer *ofb) 654f7018c21STomi Valkeinen { 655f7018c21STomi Valkeinen int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); 656f7018c21STomi Valkeinen unsigned long start[3] = { ofb->video_mem_phys, 0, 0 }; 657f7018c21STomi Valkeinen 658f7018c21STomi Valkeinen if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) { 659f7018c21STomi Valkeinen size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; 660f7018c21STomi Valkeinen setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); 661f7018c21STomi Valkeinen } else { 662f7018c21STomi Valkeinen size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual; 663f7018c21STomi Valkeinen switch (pfor) { 664f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break; 665f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break; 666f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break; 667f7018c21STomi Valkeinen } 668f7018c21STomi Valkeinen start[1] = start[0] + size; 669f7018c21STomi Valkeinen start[2] = start[1] + size / div; 670f7018c21STomi Valkeinen setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); 671f7018c21STomi Valkeinen setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div); 672f7018c21STomi Valkeinen setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div); 673f7018c21STomi Valkeinen } 674f7018c21STomi Valkeinen } 675f7018c21STomi Valkeinen 676f7018c21STomi Valkeinen static void overlay2fb_enable(struct pxafb_layer *ofb) 677f7018c21STomi Valkeinen { 678f7018c21STomi Valkeinen int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); 679f7018c21STomi Valkeinen int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN; 680f7018c21STomi Valkeinen uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0); 681f7018c21STomi Valkeinen uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0); 682f7018c21STomi Valkeinen uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0); 683f7018c21STomi Valkeinen 684f7018c21STomi Valkeinen if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) 685f7018c21STomi Valkeinen lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); 686f7018c21STomi Valkeinen else { 687f7018c21STomi Valkeinen lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); 688f7018c21STomi Valkeinen lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3); 689f7018c21STomi Valkeinen lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4); 690f7018c21STomi Valkeinen } 691f7018c21STomi Valkeinen lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]); 692f7018c21STomi Valkeinen lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN); 693f7018c21STomi Valkeinen } 694f7018c21STomi Valkeinen 695f7018c21STomi Valkeinen static void overlay2fb_disable(struct pxafb_layer *ofb) 696f7018c21STomi Valkeinen { 697f7018c21STomi Valkeinen uint32_t lccr5; 698f7018c21STomi Valkeinen 699f7018c21STomi Valkeinen if (!(lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN)) 700f7018c21STomi Valkeinen return; 701f7018c21STomi Valkeinen 702f7018c21STomi Valkeinen lccr5 = lcd_readl(ofb->fbi, LCCR5); 703f7018c21STomi Valkeinen 704f7018c21STomi Valkeinen lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN); 705f7018c21STomi Valkeinen 706f7018c21STomi Valkeinen lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2)); 707f7018c21STomi Valkeinen lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2)); 708f7018c21STomi Valkeinen lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3); 709f7018c21STomi Valkeinen lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3); 710f7018c21STomi Valkeinen lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3); 711f7018c21STomi Valkeinen 712f7018c21STomi Valkeinen if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) 7136f9655b1SJoe Perches pr_warn("%s: timeout disabling overlay2\n", __func__); 714f7018c21STomi Valkeinen } 715f7018c21STomi Valkeinen 716f7018c21STomi Valkeinen static struct pxafb_layer_ops ofb_ops[] = { 717f7018c21STomi Valkeinen [0] = { 718f7018c21STomi Valkeinen .enable = overlay1fb_enable, 719f7018c21STomi Valkeinen .disable = overlay1fb_disable, 720f7018c21STomi Valkeinen .setup = overlay1fb_setup, 721f7018c21STomi Valkeinen }, 722f7018c21STomi Valkeinen [1] = { 723f7018c21STomi Valkeinen .enable = overlay2fb_enable, 724f7018c21STomi Valkeinen .disable = overlay2fb_disable, 725f7018c21STomi Valkeinen .setup = overlay2fb_setup, 726f7018c21STomi Valkeinen }, 727f7018c21STomi Valkeinen }; 728f7018c21STomi Valkeinen 729f7018c21STomi Valkeinen static int overlayfb_open(struct fb_info *info, int user) 730f7018c21STomi Valkeinen { 73129ebebb4SFabian Frederick struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 732f7018c21STomi Valkeinen 733f7018c21STomi Valkeinen /* no support for framebuffer console on overlay */ 734f7018c21STomi Valkeinen if (user == 0) 735f7018c21STomi Valkeinen return -ENODEV; 736f7018c21STomi Valkeinen 737f7018c21STomi Valkeinen if (ofb->usage++ == 0) { 738f7018c21STomi Valkeinen /* unblank the base framebuffer */ 739f7018c21STomi Valkeinen console_lock(); 740f7018c21STomi Valkeinen fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); 741f7018c21STomi Valkeinen console_unlock(); 742f7018c21STomi Valkeinen } 743f7018c21STomi Valkeinen 744f7018c21STomi Valkeinen return 0; 745f7018c21STomi Valkeinen } 746f7018c21STomi Valkeinen 747f7018c21STomi Valkeinen static int overlayfb_release(struct fb_info *info, int user) 748f7018c21STomi Valkeinen { 74929ebebb4SFabian Frederick struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 750f7018c21STomi Valkeinen 751f7018c21STomi Valkeinen if (ofb->usage == 1) { 752f7018c21STomi Valkeinen ofb->ops->disable(ofb); 753f7018c21STomi Valkeinen ofb->fb.var.height = -1; 754f7018c21STomi Valkeinen ofb->fb.var.width = -1; 755f7018c21STomi Valkeinen ofb->fb.var.xres = ofb->fb.var.xres_virtual = 0; 756f7018c21STomi Valkeinen ofb->fb.var.yres = ofb->fb.var.yres_virtual = 0; 757f7018c21STomi Valkeinen 758f7018c21STomi Valkeinen ofb->usage--; 759f7018c21STomi Valkeinen } 760f7018c21STomi Valkeinen return 0; 761f7018c21STomi Valkeinen } 762f7018c21STomi Valkeinen 763f7018c21STomi Valkeinen static int overlayfb_check_var(struct fb_var_screeninfo *var, 764f7018c21STomi Valkeinen struct fb_info *info) 765f7018c21STomi Valkeinen { 76629ebebb4SFabian Frederick struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 767f7018c21STomi Valkeinen struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var; 768f7018c21STomi Valkeinen int xpos, ypos, pfor, bpp; 769f7018c21STomi Valkeinen 770f7018c21STomi Valkeinen xpos = NONSTD_TO_XPOS(var->nonstd); 771f7018c21STomi Valkeinen ypos = NONSTD_TO_YPOS(var->nonstd); 772f7018c21STomi Valkeinen pfor = NONSTD_TO_PFOR(var->nonstd); 773f7018c21STomi Valkeinen 774f7018c21STomi Valkeinen bpp = pxafb_var_to_bpp(var); 775f7018c21STomi Valkeinen if (bpp < 0) 776f7018c21STomi Valkeinen return -EINVAL; 777f7018c21STomi Valkeinen 778f7018c21STomi Valkeinen /* no support for YUV format on overlay1 */ 779f7018c21STomi Valkeinen if (ofb->id == OVERLAY1 && pfor != 0) 780f7018c21STomi Valkeinen return -EINVAL; 781f7018c21STomi Valkeinen 782f7018c21STomi Valkeinen /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */ 783f7018c21STomi Valkeinen switch (pfor) { 784f7018c21STomi Valkeinen case OVERLAY_FORMAT_RGB: 785f7018c21STomi Valkeinen bpp = pxafb_var_to_bpp(var); 786f7018c21STomi Valkeinen if (bpp < 0) 787f7018c21STomi Valkeinen return -EINVAL; 788f7018c21STomi Valkeinen 789f7018c21STomi Valkeinen pxafb_set_pixfmt(var, var_to_depth(var)); 790f7018c21STomi Valkeinen break; 791f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; 792f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break; 793f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break; 794f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break; 795f7018c21STomi Valkeinen default: 796f7018c21STomi Valkeinen return -EINVAL; 797f7018c21STomi Valkeinen } 798f7018c21STomi Valkeinen 799f7018c21STomi Valkeinen /* each line must start at a 32-bit word boundary */ 800f7018c21STomi Valkeinen if ((xpos * bpp) % 32) 801f7018c21STomi Valkeinen return -EINVAL; 802f7018c21STomi Valkeinen 803f7018c21STomi Valkeinen /* xres must align on 32-bit word boundary */ 804f7018c21STomi Valkeinen var->xres = roundup(var->xres * bpp, 32) / bpp; 805f7018c21STomi Valkeinen 806f7018c21STomi Valkeinen if ((xpos + var->xres > base_var->xres) || 807f7018c21STomi Valkeinen (ypos + var->yres > base_var->yres)) 808f7018c21STomi Valkeinen return -EINVAL; 809f7018c21STomi Valkeinen 810f7018c21STomi Valkeinen var->xres_virtual = var->xres; 811f7018c21STomi Valkeinen var->yres_virtual = max(var->yres, var->yres_virtual); 812f7018c21STomi Valkeinen return 0; 813f7018c21STomi Valkeinen } 814f7018c21STomi Valkeinen 815f7018c21STomi Valkeinen static int overlayfb_check_video_memory(struct pxafb_layer *ofb) 816f7018c21STomi Valkeinen { 817f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &ofb->fb.var; 818f7018c21STomi Valkeinen int pfor = NONSTD_TO_PFOR(var->nonstd); 819f7018c21STomi Valkeinen int size, bpp = 0; 820f7018c21STomi Valkeinen 821f7018c21STomi Valkeinen switch (pfor) { 822f7018c21STomi Valkeinen case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break; 823f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; 824f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break; 825f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break; 826f7018c21STomi Valkeinen case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break; 827f7018c21STomi Valkeinen } 828f7018c21STomi Valkeinen 829f7018c21STomi Valkeinen ofb->fb.fix.line_length = var->xres_virtual * bpp / 8; 830f7018c21STomi Valkeinen 831f7018c21STomi Valkeinen size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual); 832f7018c21STomi Valkeinen 833f7018c21STomi Valkeinen if (ofb->video_mem) { 834f7018c21STomi Valkeinen if (ofb->video_mem_size >= size) 835f7018c21STomi Valkeinen return 0; 836f7018c21STomi Valkeinen } 837f7018c21STomi Valkeinen return -EINVAL; 838f7018c21STomi Valkeinen } 839f7018c21STomi Valkeinen 840f7018c21STomi Valkeinen static int overlayfb_set_par(struct fb_info *info) 841f7018c21STomi Valkeinen { 84229ebebb4SFabian Frederick struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 843f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var; 844f7018c21STomi Valkeinen int xpos, ypos, pfor, bpp, ret; 845f7018c21STomi Valkeinen 846f7018c21STomi Valkeinen ret = overlayfb_check_video_memory(ofb); 847f7018c21STomi Valkeinen if (ret) 848f7018c21STomi Valkeinen return ret; 849f7018c21STomi Valkeinen 850f7018c21STomi Valkeinen bpp = pxafb_var_to_bpp(var); 851f7018c21STomi Valkeinen xpos = NONSTD_TO_XPOS(var->nonstd); 852f7018c21STomi Valkeinen ypos = NONSTD_TO_YPOS(var->nonstd); 853f7018c21STomi Valkeinen pfor = NONSTD_TO_PFOR(var->nonstd); 854f7018c21STomi Valkeinen 855f7018c21STomi Valkeinen ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) | 856f7018c21STomi Valkeinen OVLxC1_BPP(bpp); 857f7018c21STomi Valkeinen ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos); 858f7018c21STomi Valkeinen 859f7018c21STomi Valkeinen if (ofb->id == OVERLAY2) 860f7018c21STomi Valkeinen ofb->control[1] |= OVL2C2_PFOR(pfor); 861f7018c21STomi Valkeinen 862f7018c21STomi Valkeinen ofb->ops->setup(ofb); 863f7018c21STomi Valkeinen ofb->ops->enable(ofb); 864f7018c21STomi Valkeinen return 0; 865f7018c21STomi Valkeinen } 866f7018c21STomi Valkeinen 867f7018c21STomi Valkeinen static struct fb_ops overlay_fb_ops = { 868f7018c21STomi Valkeinen .owner = THIS_MODULE, 869f7018c21STomi Valkeinen .fb_open = overlayfb_open, 870f7018c21STomi Valkeinen .fb_release = overlayfb_release, 871f7018c21STomi Valkeinen .fb_check_var = overlayfb_check_var, 872f7018c21STomi Valkeinen .fb_set_par = overlayfb_set_par, 873f7018c21STomi Valkeinen }; 874f7018c21STomi Valkeinen 875f7018c21STomi Valkeinen static void init_pxafb_overlay(struct pxafb_info *fbi, struct pxafb_layer *ofb, 876f7018c21STomi Valkeinen int id) 877f7018c21STomi Valkeinen { 878f7018c21STomi Valkeinen sprintf(ofb->fb.fix.id, "overlay%d", id + 1); 879f7018c21STomi Valkeinen 880f7018c21STomi Valkeinen ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS; 881f7018c21STomi Valkeinen ofb->fb.fix.xpanstep = 0; 882f7018c21STomi Valkeinen ofb->fb.fix.ypanstep = 1; 883f7018c21STomi Valkeinen 884f7018c21STomi Valkeinen ofb->fb.var.activate = FB_ACTIVATE_NOW; 885f7018c21STomi Valkeinen ofb->fb.var.height = -1; 886f7018c21STomi Valkeinen ofb->fb.var.width = -1; 887f7018c21STomi Valkeinen ofb->fb.var.vmode = FB_VMODE_NONINTERLACED; 888f7018c21STomi Valkeinen 889f7018c21STomi Valkeinen ofb->fb.fbops = &overlay_fb_ops; 890f7018c21STomi Valkeinen ofb->fb.flags = FBINFO_FLAG_DEFAULT; 891f7018c21STomi Valkeinen ofb->fb.node = -1; 892f7018c21STomi Valkeinen ofb->fb.pseudo_palette = NULL; 893f7018c21STomi Valkeinen 894f7018c21STomi Valkeinen ofb->id = id; 895f7018c21STomi Valkeinen ofb->ops = &ofb_ops[id]; 896f7018c21STomi Valkeinen ofb->usage = 0; 897f7018c21STomi Valkeinen ofb->fbi = fbi; 898f7018c21STomi Valkeinen init_completion(&ofb->branch_done); 899f7018c21STomi Valkeinen } 900f7018c21STomi Valkeinen 901f7018c21STomi Valkeinen static inline int pxafb_overlay_supported(void) 902f7018c21STomi Valkeinen { 903f7018c21STomi Valkeinen if (cpu_is_pxa27x() || cpu_is_pxa3xx()) 904f7018c21STomi Valkeinen return 1; 905f7018c21STomi Valkeinen 906f7018c21STomi Valkeinen return 0; 907f7018c21STomi Valkeinen } 908f7018c21STomi Valkeinen 909f7018c21STomi Valkeinen static int pxafb_overlay_map_video_memory(struct pxafb_info *pxafb, 910f7018c21STomi Valkeinen struct pxafb_layer *ofb) 911f7018c21STomi Valkeinen { 912f7018c21STomi Valkeinen /* We assume that user will use at most video_mem_size for overlay fb, 913f7018c21STomi Valkeinen * anyway, it's useless to use 16bpp main plane and 24bpp overlay 914f7018c21STomi Valkeinen */ 915f7018c21STomi Valkeinen ofb->video_mem = alloc_pages_exact(PAGE_ALIGN(pxafb->video_mem_size), 916f7018c21STomi Valkeinen GFP_KERNEL | __GFP_ZERO); 917f7018c21STomi Valkeinen if (ofb->video_mem == NULL) 918f7018c21STomi Valkeinen return -ENOMEM; 919f7018c21STomi Valkeinen 920f7018c21STomi Valkeinen ofb->video_mem_phys = virt_to_phys(ofb->video_mem); 921f7018c21STomi Valkeinen ofb->video_mem_size = PAGE_ALIGN(pxafb->video_mem_size); 922f7018c21STomi Valkeinen 923f7018c21STomi Valkeinen mutex_lock(&ofb->fb.mm_lock); 924f7018c21STomi Valkeinen ofb->fb.fix.smem_start = ofb->video_mem_phys; 925f7018c21STomi Valkeinen ofb->fb.fix.smem_len = pxafb->video_mem_size; 926f7018c21STomi Valkeinen mutex_unlock(&ofb->fb.mm_lock); 927f7018c21STomi Valkeinen 928f7018c21STomi Valkeinen ofb->fb.screen_base = ofb->video_mem; 929f7018c21STomi Valkeinen 930f7018c21STomi Valkeinen return 0; 931f7018c21STomi Valkeinen } 932f7018c21STomi Valkeinen 933f7018c21STomi Valkeinen static void pxafb_overlay_init(struct pxafb_info *fbi) 934f7018c21STomi Valkeinen { 935f7018c21STomi Valkeinen int i, ret; 936f7018c21STomi Valkeinen 937f7018c21STomi Valkeinen if (!pxafb_overlay_supported()) 938f7018c21STomi Valkeinen return; 939f7018c21STomi Valkeinen 940f7018c21STomi Valkeinen for (i = 0; i < 2; i++) { 941f7018c21STomi Valkeinen struct pxafb_layer *ofb = &fbi->overlay[i]; 942f7018c21STomi Valkeinen init_pxafb_overlay(fbi, ofb, i); 943f7018c21STomi Valkeinen ret = register_framebuffer(&ofb->fb); 944f7018c21STomi Valkeinen if (ret) { 945f7018c21STomi Valkeinen dev_err(fbi->dev, "failed to register overlay %d\n", i); 946f7018c21STomi Valkeinen continue; 947f7018c21STomi Valkeinen } 948f7018c21STomi Valkeinen ret = pxafb_overlay_map_video_memory(fbi, ofb); 949f7018c21STomi Valkeinen if (ret) { 950f7018c21STomi Valkeinen dev_err(fbi->dev, 951f7018c21STomi Valkeinen "failed to map video memory for overlay %d\n", 952f7018c21STomi Valkeinen i); 953f7018c21STomi Valkeinen unregister_framebuffer(&ofb->fb); 954f7018c21STomi Valkeinen continue; 955f7018c21STomi Valkeinen } 956f7018c21STomi Valkeinen ofb->registered = 1; 957f7018c21STomi Valkeinen } 958f7018c21STomi Valkeinen 959f7018c21STomi Valkeinen /* mask all IU/BS/EOF/SOF interrupts */ 960f7018c21STomi Valkeinen lcd_writel(fbi, LCCR5, ~0); 961f7018c21STomi Valkeinen 962f7018c21STomi Valkeinen pr_info("PXA Overlay driver loaded successfully!\n"); 963f7018c21STomi Valkeinen } 964f7018c21STomi Valkeinen 965f7018c21STomi Valkeinen static void pxafb_overlay_exit(struct pxafb_info *fbi) 966f7018c21STomi Valkeinen { 967f7018c21STomi Valkeinen int i; 968f7018c21STomi Valkeinen 969f7018c21STomi Valkeinen if (!pxafb_overlay_supported()) 970f7018c21STomi Valkeinen return; 971f7018c21STomi Valkeinen 972f7018c21STomi Valkeinen for (i = 0; i < 2; i++) { 973f7018c21STomi Valkeinen struct pxafb_layer *ofb = &fbi->overlay[i]; 974f7018c21STomi Valkeinen if (ofb->registered) { 975f7018c21STomi Valkeinen if (ofb->video_mem) 976f7018c21STomi Valkeinen free_pages_exact(ofb->video_mem, 977f7018c21STomi Valkeinen ofb->video_mem_size); 978f7018c21STomi Valkeinen unregister_framebuffer(&ofb->fb); 979f7018c21STomi Valkeinen } 980f7018c21STomi Valkeinen } 981f7018c21STomi Valkeinen } 982f7018c21STomi Valkeinen #else 983f7018c21STomi Valkeinen static inline void pxafb_overlay_init(struct pxafb_info *fbi) {} 984f7018c21STomi Valkeinen static inline void pxafb_overlay_exit(struct pxafb_info *fbi) {} 985f7018c21STomi Valkeinen #endif /* CONFIG_FB_PXA_OVERLAY */ 986f7018c21STomi Valkeinen 987f7018c21STomi Valkeinen /* 988f7018c21STomi Valkeinen * Calculate the PCD value from the clock rate (in picoseconds). 989f7018c21STomi Valkeinen * We take account of the PPCR clock setting. 990f7018c21STomi Valkeinen * From PXA Developer's Manual: 991f7018c21STomi Valkeinen * 992f7018c21STomi Valkeinen * PixelClock = LCLK 993f7018c21STomi Valkeinen * ------------- 994f7018c21STomi Valkeinen * 2 ( PCD + 1 ) 995f7018c21STomi Valkeinen * 996f7018c21STomi Valkeinen * PCD = LCLK 997f7018c21STomi Valkeinen * ------------- - 1 998f7018c21STomi Valkeinen * 2(PixelClock) 999f7018c21STomi Valkeinen * 1000f7018c21STomi Valkeinen * Where: 1001f7018c21STomi Valkeinen * LCLK = LCD/Memory Clock 1002f7018c21STomi Valkeinen * PCD = LCCR3[7:0] 1003f7018c21STomi Valkeinen * 1004f7018c21STomi Valkeinen * PixelClock here is in Hz while the pixclock argument given is the 1005f7018c21STomi Valkeinen * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 ) 1006f7018c21STomi Valkeinen * 1007f7018c21STomi Valkeinen * The function get_lclk_frequency_10khz returns LCLK in units of 1008f7018c21STomi Valkeinen * 10khz. Calling the result of this function lclk gives us the 1009f7018c21STomi Valkeinen * following 1010f7018c21STomi Valkeinen * 1011f7018c21STomi Valkeinen * PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 ) 1012f7018c21STomi Valkeinen * -------------------------------------- - 1 1013f7018c21STomi Valkeinen * 2 1014f7018c21STomi Valkeinen * 1015f7018c21STomi Valkeinen * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below. 1016f7018c21STomi Valkeinen */ 1017f7018c21STomi Valkeinen static inline unsigned int get_pcd(struct pxafb_info *fbi, 1018f7018c21STomi Valkeinen unsigned int pixclock) 1019f7018c21STomi Valkeinen { 1020f7018c21STomi Valkeinen unsigned long long pcd; 1021f7018c21STomi Valkeinen 1022f7018c21STomi Valkeinen /* FIXME: Need to take into account Double Pixel Clock mode 1023f7018c21STomi Valkeinen * (DPC) bit? or perhaps set it based on the various clock 1024f7018c21STomi Valkeinen * speeds */ 1025f7018c21STomi Valkeinen pcd = (unsigned long long)(clk_get_rate(fbi->clk) / 10000); 1026f7018c21STomi Valkeinen pcd *= pixclock; 1027f7018c21STomi Valkeinen do_div(pcd, 100000000 * 2); 1028f7018c21STomi Valkeinen /* no need for this, since we should subtract 1 anyway. they cancel */ 1029f7018c21STomi Valkeinen /* pcd += 1; */ /* make up for integer math truncations */ 1030f7018c21STomi Valkeinen return (unsigned int)pcd; 1031f7018c21STomi Valkeinen } 1032f7018c21STomi Valkeinen 1033f7018c21STomi Valkeinen /* 1034f7018c21STomi Valkeinen * Some touchscreens need hsync information from the video driver to 1035f7018c21STomi Valkeinen * function correctly. We export it here. Note that 'hsync_time' and 1036f7018c21STomi Valkeinen * the value returned from pxafb_get_hsync_time() is the *reciprocal* 1037f7018c21STomi Valkeinen * of the hsync period in seconds. 1038f7018c21STomi Valkeinen */ 1039f7018c21STomi Valkeinen static inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd) 1040f7018c21STomi Valkeinen { 1041f7018c21STomi Valkeinen unsigned long htime; 1042f7018c21STomi Valkeinen 1043f7018c21STomi Valkeinen if ((pcd == 0) || (fbi->fb.var.hsync_len == 0)) { 1044f7018c21STomi Valkeinen fbi->hsync_time = 0; 1045f7018c21STomi Valkeinen return; 1046f7018c21STomi Valkeinen } 1047f7018c21STomi Valkeinen 1048f7018c21STomi Valkeinen htime = clk_get_rate(fbi->clk) / (pcd * fbi->fb.var.hsync_len); 1049f7018c21STomi Valkeinen 1050f7018c21STomi Valkeinen fbi->hsync_time = htime; 1051f7018c21STomi Valkeinen } 1052f7018c21STomi Valkeinen 1053f7018c21STomi Valkeinen unsigned long pxafb_get_hsync_time(struct device *dev) 1054f7018c21STomi Valkeinen { 1055f7018c21STomi Valkeinen struct pxafb_info *fbi = dev_get_drvdata(dev); 1056f7018c21STomi Valkeinen 1057f7018c21STomi Valkeinen /* If display is blanked/suspended, hsync isn't active */ 1058f7018c21STomi Valkeinen if (!fbi || (fbi->state != C_ENABLE)) 1059f7018c21STomi Valkeinen return 0; 1060f7018c21STomi Valkeinen 1061f7018c21STomi Valkeinen return fbi->hsync_time; 1062f7018c21STomi Valkeinen } 1063f7018c21STomi Valkeinen EXPORT_SYMBOL(pxafb_get_hsync_time); 1064f7018c21STomi Valkeinen 1065f7018c21STomi Valkeinen static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, 1066f7018c21STomi Valkeinen unsigned long start, size_t size) 1067f7018c21STomi Valkeinen { 1068f7018c21STomi Valkeinen struct pxafb_dma_descriptor *dma_desc, *pal_desc; 1069f7018c21STomi Valkeinen unsigned int dma_desc_off, pal_desc_off; 1070f7018c21STomi Valkeinen 1071f7018c21STomi Valkeinen if (dma < 0 || dma >= DMA_MAX * 2) 1072f7018c21STomi Valkeinen return -EINVAL; 1073f7018c21STomi Valkeinen 1074f7018c21STomi Valkeinen dma_desc = &fbi->dma_buff->dma_desc[dma]; 1075f7018c21STomi Valkeinen dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); 1076f7018c21STomi Valkeinen 1077f7018c21STomi Valkeinen dma_desc->fsadr = start; 1078f7018c21STomi Valkeinen dma_desc->fidr = 0; 1079f7018c21STomi Valkeinen dma_desc->ldcmd = size; 1080f7018c21STomi Valkeinen 1081f7018c21STomi Valkeinen if (pal < 0 || pal >= PAL_MAX * 2) { 1082f7018c21STomi Valkeinen dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 1083f7018c21STomi Valkeinen fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; 1084f7018c21STomi Valkeinen } else { 1085f7018c21STomi Valkeinen pal_desc = &fbi->dma_buff->pal_desc[pal]; 1086f7018c21STomi Valkeinen pal_desc_off = offsetof(struct pxafb_dma_buff, pal_desc[pal]); 1087f7018c21STomi Valkeinen 1088f7018c21STomi Valkeinen pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE; 1089f7018c21STomi Valkeinen pal_desc->fidr = 0; 1090f7018c21STomi Valkeinen 1091f7018c21STomi Valkeinen if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) 1092f7018c21STomi Valkeinen pal_desc->ldcmd = fbi->palette_size * sizeof(u16); 1093f7018c21STomi Valkeinen else 1094f7018c21STomi Valkeinen pal_desc->ldcmd = fbi->palette_size * sizeof(u32); 1095f7018c21STomi Valkeinen 1096f7018c21STomi Valkeinen pal_desc->ldcmd |= LDCMD_PAL; 1097f7018c21STomi Valkeinen 1098f7018c21STomi Valkeinen /* flip back and forth between palette and frame buffer */ 1099f7018c21STomi Valkeinen pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 1100f7018c21STomi Valkeinen dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off; 1101f7018c21STomi Valkeinen fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; 1102f7018c21STomi Valkeinen } 1103f7018c21STomi Valkeinen 1104f7018c21STomi Valkeinen return 0; 1105f7018c21STomi Valkeinen } 1106f7018c21STomi Valkeinen 1107f7018c21STomi Valkeinen static void setup_base_frame(struct pxafb_info *fbi, 1108f7018c21STomi Valkeinen struct fb_var_screeninfo *var, 1109f7018c21STomi Valkeinen int branch) 1110f7018c21STomi Valkeinen { 1111f7018c21STomi Valkeinen struct fb_fix_screeninfo *fix = &fbi->fb.fix; 1112f7018c21STomi Valkeinen int nbytes, dma, pal, bpp = var->bits_per_pixel; 1113f7018c21STomi Valkeinen unsigned long offset; 1114f7018c21STomi Valkeinen 1115f7018c21STomi Valkeinen dma = DMA_BASE + (branch ? DMA_MAX : 0); 1116f7018c21STomi Valkeinen pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); 1117f7018c21STomi Valkeinen 1118f7018c21STomi Valkeinen nbytes = fix->line_length * var->yres; 1119f7018c21STomi Valkeinen offset = fix->line_length * var->yoffset + fbi->video_mem_phys; 1120f7018c21STomi Valkeinen 1121f7018c21STomi Valkeinen if (fbi->lccr0 & LCCR0_SDS) { 1122f7018c21STomi Valkeinen nbytes = nbytes / 2; 1123f7018c21STomi Valkeinen setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes); 1124f7018c21STomi Valkeinen } 1125f7018c21STomi Valkeinen 1126f7018c21STomi Valkeinen setup_frame_dma(fbi, dma, pal, offset, nbytes); 1127f7018c21STomi Valkeinen } 1128f7018c21STomi Valkeinen 1129f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_SMARTPANEL 1130f7018c21STomi Valkeinen static int setup_smart_dma(struct pxafb_info *fbi) 1131f7018c21STomi Valkeinen { 1132f7018c21STomi Valkeinen struct pxafb_dma_descriptor *dma_desc; 1133f7018c21STomi Valkeinen unsigned long dma_desc_off, cmd_buff_off; 1134f7018c21STomi Valkeinen 1135f7018c21STomi Valkeinen dma_desc = &fbi->dma_buff->dma_desc[DMA_CMD]; 1136f7018c21STomi Valkeinen dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[DMA_CMD]); 1137f7018c21STomi Valkeinen cmd_buff_off = offsetof(struct pxafb_dma_buff, cmd_buff); 1138f7018c21STomi Valkeinen 1139f7018c21STomi Valkeinen dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 1140f7018c21STomi Valkeinen dma_desc->fsadr = fbi->dma_buff_phys + cmd_buff_off; 1141f7018c21STomi Valkeinen dma_desc->fidr = 0; 1142f7018c21STomi Valkeinen dma_desc->ldcmd = fbi->n_smart_cmds * sizeof(uint16_t); 1143f7018c21STomi Valkeinen 1144f7018c21STomi Valkeinen fbi->fdadr[DMA_CMD] = dma_desc->fdadr; 1145f7018c21STomi Valkeinen return 0; 1146f7018c21STomi Valkeinen } 1147f7018c21STomi Valkeinen 1148f7018c21STomi Valkeinen int pxafb_smart_flush(struct fb_info *info) 1149f7018c21STomi Valkeinen { 1150f7018c21STomi Valkeinen struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 1151f7018c21STomi Valkeinen uint32_t prsr; 1152f7018c21STomi Valkeinen int ret = 0; 1153f7018c21STomi Valkeinen 1154f7018c21STomi Valkeinen /* disable controller until all registers are set up */ 1155f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 1156f7018c21STomi Valkeinen 1157f7018c21STomi Valkeinen /* 1. make it an even number of commands to align on 32-bit boundary 1158f7018c21STomi Valkeinen * 2. add the interrupt command to the end of the chain so we can 1159f7018c21STomi Valkeinen * keep track of the end of the transfer 1160f7018c21STomi Valkeinen */ 1161f7018c21STomi Valkeinen 1162f7018c21STomi Valkeinen while (fbi->n_smart_cmds & 1) 1163f7018c21STomi Valkeinen fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP; 1164f7018c21STomi Valkeinen 1165f7018c21STomi Valkeinen fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_INTERRUPT; 1166f7018c21STomi Valkeinen fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC; 1167f7018c21STomi Valkeinen setup_smart_dma(fbi); 1168f7018c21STomi Valkeinen 1169f7018c21STomi Valkeinen /* continue to execute next command */ 1170f7018c21STomi Valkeinen prsr = lcd_readl(fbi, PRSR) | PRSR_ST_OK | PRSR_CON_NT; 1171f7018c21STomi Valkeinen lcd_writel(fbi, PRSR, prsr); 1172f7018c21STomi Valkeinen 1173f7018c21STomi Valkeinen /* stop the processor in case it executed "wait for sync" cmd */ 1174f7018c21STomi Valkeinen lcd_writel(fbi, CMDCR, 0x0001); 1175f7018c21STomi Valkeinen 1176f7018c21STomi Valkeinen /* don't send interrupts for fifo underruns on channel 6 */ 1177f7018c21STomi Valkeinen lcd_writel(fbi, LCCR5, LCCR5_IUM(6)); 1178f7018c21STomi Valkeinen 1179f7018c21STomi Valkeinen lcd_writel(fbi, LCCR1, fbi->reg_lccr1); 1180f7018c21STomi Valkeinen lcd_writel(fbi, LCCR2, fbi->reg_lccr2); 1181f7018c21STomi Valkeinen lcd_writel(fbi, LCCR3, fbi->reg_lccr3); 1182f7018c21STomi Valkeinen lcd_writel(fbi, LCCR4, fbi->reg_lccr4); 1183f7018c21STomi Valkeinen lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 1184f7018c21STomi Valkeinen lcd_writel(fbi, FDADR6, fbi->fdadr[6]); 1185f7018c21STomi Valkeinen 1186f7018c21STomi Valkeinen /* begin sending */ 1187f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 1188f7018c21STomi Valkeinen 1189f7018c21STomi Valkeinen if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) { 11906f9655b1SJoe Perches pr_warn("%s: timeout waiting for command done\n", __func__); 1191f7018c21STomi Valkeinen ret = -ETIMEDOUT; 1192f7018c21STomi Valkeinen } 1193f7018c21STomi Valkeinen 1194f7018c21STomi Valkeinen /* quick disable */ 1195f7018c21STomi Valkeinen prsr = lcd_readl(fbi, PRSR) & ~(PRSR_ST_OK | PRSR_CON_NT); 1196f7018c21STomi Valkeinen lcd_writel(fbi, PRSR, prsr); 1197f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 1198f7018c21STomi Valkeinen lcd_writel(fbi, FDADR6, 0); 1199f7018c21STomi Valkeinen fbi->n_smart_cmds = 0; 1200f7018c21STomi Valkeinen return ret; 1201f7018c21STomi Valkeinen } 1202f7018c21STomi Valkeinen 1203f7018c21STomi Valkeinen int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds) 1204f7018c21STomi Valkeinen { 1205f7018c21STomi Valkeinen int i; 1206f7018c21STomi Valkeinen struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 1207f7018c21STomi Valkeinen 1208f7018c21STomi Valkeinen for (i = 0; i < n_cmds; i++, cmds++) { 1209f7018c21STomi Valkeinen /* if it is a software delay, flush and delay */ 1210f7018c21STomi Valkeinen if ((*cmds & 0xff00) == SMART_CMD_DELAY) { 1211f7018c21STomi Valkeinen pxafb_smart_flush(info); 1212f7018c21STomi Valkeinen mdelay(*cmds & 0xff); 1213f7018c21STomi Valkeinen continue; 1214f7018c21STomi Valkeinen } 1215f7018c21STomi Valkeinen 1216f7018c21STomi Valkeinen /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */ 1217f7018c21STomi Valkeinen if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8) 1218f7018c21STomi Valkeinen pxafb_smart_flush(info); 1219f7018c21STomi Valkeinen 1220f7018c21STomi Valkeinen fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds; 1221f7018c21STomi Valkeinen } 1222f7018c21STomi Valkeinen 1223f7018c21STomi Valkeinen return 0; 1224f7018c21STomi Valkeinen } 1225f7018c21STomi Valkeinen 1226f7018c21STomi Valkeinen static unsigned int __smart_timing(unsigned time_ns, unsigned long lcd_clk) 1227f7018c21STomi Valkeinen { 1228f7018c21STomi Valkeinen unsigned int t = (time_ns * (lcd_clk / 1000000) / 1000); 1229f7018c21STomi Valkeinen return (t == 0) ? 1 : t; 1230f7018c21STomi Valkeinen } 1231f7018c21STomi Valkeinen 1232f7018c21STomi Valkeinen static void setup_smart_timing(struct pxafb_info *fbi, 1233f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 1234f7018c21STomi Valkeinen { 1235f3621a60SRobert Jarzmik struct pxafb_mach_info *inf = fbi->inf; 1236f7018c21STomi Valkeinen struct pxafb_mode_info *mode = &inf->modes[0]; 1237f7018c21STomi Valkeinen unsigned long lclk = clk_get_rate(fbi->clk); 1238f7018c21STomi Valkeinen unsigned t1, t2, t3, t4; 1239f7018c21STomi Valkeinen 1240f7018c21STomi Valkeinen t1 = max(mode->a0csrd_set_hld, mode->a0cswr_set_hld); 1241f7018c21STomi Valkeinen t2 = max(mode->rd_pulse_width, mode->wr_pulse_width); 1242f7018c21STomi Valkeinen t3 = mode->op_hold_time; 1243f7018c21STomi Valkeinen t4 = mode->cmd_inh_time; 1244f7018c21STomi Valkeinen 1245f7018c21STomi Valkeinen fbi->reg_lccr1 = 1246f7018c21STomi Valkeinen LCCR1_DisWdth(var->xres) | 1247f7018c21STomi Valkeinen LCCR1_BegLnDel(__smart_timing(t1, lclk)) | 1248f7018c21STomi Valkeinen LCCR1_EndLnDel(__smart_timing(t2, lclk)) | 1249f7018c21STomi Valkeinen LCCR1_HorSnchWdth(__smart_timing(t3, lclk)); 1250f7018c21STomi Valkeinen 1251f7018c21STomi Valkeinen fbi->reg_lccr2 = LCCR2_DisHght(var->yres); 1252f7018c21STomi Valkeinen fbi->reg_lccr3 = fbi->lccr3 | LCCR3_PixClkDiv(__smart_timing(t4, lclk)); 1253f7018c21STomi Valkeinen fbi->reg_lccr3 |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HSP : 0; 1254f7018c21STomi Valkeinen fbi->reg_lccr3 |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VSP : 0; 1255f7018c21STomi Valkeinen 1256f7018c21STomi Valkeinen /* FIXME: make this configurable */ 1257f7018c21STomi Valkeinen fbi->reg_cmdcr = 1; 1258f7018c21STomi Valkeinen } 1259f7018c21STomi Valkeinen 1260f7018c21STomi Valkeinen static int pxafb_smart_thread(void *arg) 1261f7018c21STomi Valkeinen { 1262f7018c21STomi Valkeinen struct pxafb_info *fbi = arg; 1263f3621a60SRobert Jarzmik struct pxafb_mach_info *inf = fbi->inf; 1264f7018c21STomi Valkeinen 1265f7018c21STomi Valkeinen if (!inf->smart_update) { 1266f7018c21STomi Valkeinen pr_err("%s: not properly initialized, thread terminated\n", 1267f7018c21STomi Valkeinen __func__); 1268f7018c21STomi Valkeinen return -EINVAL; 1269f7018c21STomi Valkeinen } 1270f7018c21STomi Valkeinen 1271f7018c21STomi Valkeinen pr_debug("%s(): task starting\n", __func__); 1272f7018c21STomi Valkeinen 1273f7018c21STomi Valkeinen set_freezable(); 1274f7018c21STomi Valkeinen while (!kthread_should_stop()) { 1275f7018c21STomi Valkeinen 1276f7018c21STomi Valkeinen if (try_to_freeze()) 1277f7018c21STomi Valkeinen continue; 1278f7018c21STomi Valkeinen 1279f7018c21STomi Valkeinen mutex_lock(&fbi->ctrlr_lock); 1280f7018c21STomi Valkeinen 1281f7018c21STomi Valkeinen if (fbi->state == C_ENABLE) { 1282f7018c21STomi Valkeinen inf->smart_update(&fbi->fb); 1283f7018c21STomi Valkeinen complete(&fbi->refresh_done); 1284f7018c21STomi Valkeinen } 1285f7018c21STomi Valkeinen 1286f7018c21STomi Valkeinen mutex_unlock(&fbi->ctrlr_lock); 1287f7018c21STomi Valkeinen 1288f7018c21STomi Valkeinen set_current_state(TASK_INTERRUPTIBLE); 128917713ce0SNicholas Mc Guire schedule_timeout(msecs_to_jiffies(30)); 1290f7018c21STomi Valkeinen } 1291f7018c21STomi Valkeinen 1292f7018c21STomi Valkeinen pr_debug("%s(): task ending\n", __func__); 1293f7018c21STomi Valkeinen return 0; 1294f7018c21STomi Valkeinen } 1295f7018c21STomi Valkeinen 1296f7018c21STomi Valkeinen static int pxafb_smart_init(struct pxafb_info *fbi) 1297f7018c21STomi Valkeinen { 1298f7018c21STomi Valkeinen if (!(fbi->lccr0 & LCCR0_LCDT)) 1299f7018c21STomi Valkeinen return 0; 1300f7018c21STomi Valkeinen 1301f7018c21STomi Valkeinen fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff; 1302f7018c21STomi Valkeinen fbi->n_smart_cmds = 0; 1303f7018c21STomi Valkeinen 1304f7018c21STomi Valkeinen init_completion(&fbi->command_done); 1305f7018c21STomi Valkeinen init_completion(&fbi->refresh_done); 1306f7018c21STomi Valkeinen 1307f7018c21STomi Valkeinen fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi, 1308f7018c21STomi Valkeinen "lcd_refresh"); 1309f7018c21STomi Valkeinen if (IS_ERR(fbi->smart_thread)) { 1310f7018c21STomi Valkeinen pr_err("%s: unable to create kernel thread\n", __func__); 1311f7018c21STomi Valkeinen return PTR_ERR(fbi->smart_thread); 1312f7018c21STomi Valkeinen } 1313f7018c21STomi Valkeinen 1314f7018c21STomi Valkeinen return 0; 1315f7018c21STomi Valkeinen } 1316f7018c21STomi Valkeinen #else 1317f7018c21STomi Valkeinen static inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; } 1318f7018c21STomi Valkeinen #endif /* CONFIG_FB_PXA_SMARTPANEL */ 1319f7018c21STomi Valkeinen 1320f7018c21STomi Valkeinen static void setup_parallel_timing(struct pxafb_info *fbi, 1321f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 1322f7018c21STomi Valkeinen { 1323f7018c21STomi Valkeinen unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock); 1324f7018c21STomi Valkeinen 1325f7018c21STomi Valkeinen fbi->reg_lccr1 = 1326f7018c21STomi Valkeinen LCCR1_DisWdth(var->xres) + 1327f7018c21STomi Valkeinen LCCR1_HorSnchWdth(var->hsync_len) + 1328f7018c21STomi Valkeinen LCCR1_BegLnDel(var->left_margin) + 1329f7018c21STomi Valkeinen LCCR1_EndLnDel(var->right_margin); 1330f7018c21STomi Valkeinen 1331f7018c21STomi Valkeinen /* 1332f7018c21STomi Valkeinen * If we have a dual scan LCD, we need to halve 1333f7018c21STomi Valkeinen * the YRES parameter. 1334f7018c21STomi Valkeinen */ 1335f7018c21STomi Valkeinen lines_per_panel = var->yres; 1336f7018c21STomi Valkeinen if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) 1337f7018c21STomi Valkeinen lines_per_panel /= 2; 1338f7018c21STomi Valkeinen 1339f7018c21STomi Valkeinen fbi->reg_lccr2 = 1340f7018c21STomi Valkeinen LCCR2_DisHght(lines_per_panel) + 1341f7018c21STomi Valkeinen LCCR2_VrtSnchWdth(var->vsync_len) + 1342f7018c21STomi Valkeinen LCCR2_BegFrmDel(var->upper_margin) + 1343f7018c21STomi Valkeinen LCCR2_EndFrmDel(var->lower_margin); 1344f7018c21STomi Valkeinen 1345f7018c21STomi Valkeinen fbi->reg_lccr3 = fbi->lccr3 | 1346f7018c21STomi Valkeinen (var->sync & FB_SYNC_HOR_HIGH_ACT ? 1347f7018c21STomi Valkeinen LCCR3_HorSnchH : LCCR3_HorSnchL) | 1348f7018c21STomi Valkeinen (var->sync & FB_SYNC_VERT_HIGH_ACT ? 1349f7018c21STomi Valkeinen LCCR3_VrtSnchH : LCCR3_VrtSnchL); 1350f7018c21STomi Valkeinen 1351f7018c21STomi Valkeinen if (pcd) { 1352f7018c21STomi Valkeinen fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd); 1353f7018c21STomi Valkeinen set_hsync_time(fbi, pcd); 1354f7018c21STomi Valkeinen } 1355f7018c21STomi Valkeinen } 1356f7018c21STomi Valkeinen 1357f7018c21STomi Valkeinen /* 1358f7018c21STomi Valkeinen * pxafb_activate_var(): 1359f7018c21STomi Valkeinen * Configures LCD Controller based on entries in var parameter. 1360f7018c21STomi Valkeinen * Settings are only written to the controller if changes were made. 1361f7018c21STomi Valkeinen */ 1362f7018c21STomi Valkeinen static int pxafb_activate_var(struct fb_var_screeninfo *var, 1363f7018c21STomi Valkeinen struct pxafb_info *fbi) 1364f7018c21STomi Valkeinen { 1365f7018c21STomi Valkeinen u_long flags; 1366f7018c21STomi Valkeinen 1367f7018c21STomi Valkeinen /* Update shadow copy atomically */ 1368f7018c21STomi Valkeinen local_irq_save(flags); 1369f7018c21STomi Valkeinen 1370f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_SMARTPANEL 1371f7018c21STomi Valkeinen if (fbi->lccr0 & LCCR0_LCDT) 1372f7018c21STomi Valkeinen setup_smart_timing(fbi, var); 1373f7018c21STomi Valkeinen else 1374f7018c21STomi Valkeinen #endif 1375f7018c21STomi Valkeinen setup_parallel_timing(fbi, var); 1376f7018c21STomi Valkeinen 1377f7018c21STomi Valkeinen setup_base_frame(fbi, var, 0); 1378f7018c21STomi Valkeinen 1379f7018c21STomi Valkeinen fbi->reg_lccr0 = fbi->lccr0 | 1380f7018c21STomi Valkeinen (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | 1381f7018c21STomi Valkeinen LCCR0_QDM | LCCR0_BM | LCCR0_OUM); 1382f7018c21STomi Valkeinen 1383f7018c21STomi Valkeinen fbi->reg_lccr3 |= pxafb_var_to_lccr3(var); 1384f7018c21STomi Valkeinen 1385f7018c21STomi Valkeinen fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; 1386f7018c21STomi Valkeinen fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); 1387f7018c21STomi Valkeinen local_irq_restore(flags); 1388f7018c21STomi Valkeinen 1389f7018c21STomi Valkeinen /* 1390f7018c21STomi Valkeinen * Only update the registers if the controller is enabled 1391f7018c21STomi Valkeinen * and something has changed. 1392f7018c21STomi Valkeinen */ 1393f7018c21STomi Valkeinen if ((lcd_readl(fbi, LCCR0) != fbi->reg_lccr0) || 1394f7018c21STomi Valkeinen (lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) || 1395f7018c21STomi Valkeinen (lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) || 1396f7018c21STomi Valkeinen (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) || 1397f7018c21STomi Valkeinen (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) || 1398f7018c21STomi Valkeinen (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) || 1399f7018c21STomi Valkeinen ((fbi->lccr0 & LCCR0_SDS) && 1400f7018c21STomi Valkeinen (lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))) 1401f7018c21STomi Valkeinen pxafb_schedule_work(fbi, C_REENABLE); 1402f7018c21STomi Valkeinen 1403f7018c21STomi Valkeinen return 0; 1404f7018c21STomi Valkeinen } 1405f7018c21STomi Valkeinen 1406f7018c21STomi Valkeinen /* 1407f7018c21STomi Valkeinen * NOTE! The following functions are purely helpers for set_ctrlr_state. 1408f7018c21STomi Valkeinen * Do not call them directly; set_ctrlr_state does the correct serialisation 1409f7018c21STomi Valkeinen * to ensure that things happen in the right way 100% of time time. 1410f7018c21STomi Valkeinen * -- rmk 1411f7018c21STomi Valkeinen */ 1412f7018c21STomi Valkeinen static inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on) 1413f7018c21STomi Valkeinen { 1414f7018c21STomi Valkeinen pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff"); 1415f7018c21STomi Valkeinen 1416f7018c21STomi Valkeinen if (fbi->backlight_power) 1417f7018c21STomi Valkeinen fbi->backlight_power(on); 1418f7018c21STomi Valkeinen } 1419f7018c21STomi Valkeinen 1420f7018c21STomi Valkeinen static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) 1421f7018c21STomi Valkeinen { 1422f7018c21STomi Valkeinen pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff"); 1423f7018c21STomi Valkeinen 1424f7018c21STomi Valkeinen if (fbi->lcd_power) 1425f7018c21STomi Valkeinen fbi->lcd_power(on, &fbi->fb.var); 1426f7018c21STomi Valkeinen } 1427f7018c21STomi Valkeinen 1428f7018c21STomi Valkeinen static void pxafb_enable_controller(struct pxafb_info *fbi) 1429f7018c21STomi Valkeinen { 1430f7018c21STomi Valkeinen pr_debug("pxafb: Enabling LCD controller\n"); 1431f7018c21STomi Valkeinen pr_debug("fdadr0 0x%08x\n", (unsigned int) fbi->fdadr[0]); 1432f7018c21STomi Valkeinen pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr[1]); 1433f7018c21STomi Valkeinen pr_debug("reg_lccr0 0x%08x\n", (unsigned int) fbi->reg_lccr0); 1434f7018c21STomi Valkeinen pr_debug("reg_lccr1 0x%08x\n", (unsigned int) fbi->reg_lccr1); 1435f7018c21STomi Valkeinen pr_debug("reg_lccr2 0x%08x\n", (unsigned int) fbi->reg_lccr2); 1436f7018c21STomi Valkeinen pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3); 1437f7018c21STomi Valkeinen 1438f7018c21STomi Valkeinen /* enable LCD controller clock */ 1439*86388d83SArvind Yadav if (clk_prepare_enable(fbi->clk)) { 1440*86388d83SArvind Yadav pr_err("%s: Failed to prepare clock\n", __func__); 1441*86388d83SArvind Yadav return; 1442*86388d83SArvind Yadav } 1443f7018c21STomi Valkeinen 1444f7018c21STomi Valkeinen if (fbi->lccr0 & LCCR0_LCDT) 1445f7018c21STomi Valkeinen return; 1446f7018c21STomi Valkeinen 1447f7018c21STomi Valkeinen /* Sequence from 11.7.10 */ 1448f7018c21STomi Valkeinen lcd_writel(fbi, LCCR4, fbi->reg_lccr4); 1449f7018c21STomi Valkeinen lcd_writel(fbi, LCCR3, fbi->reg_lccr3); 1450f7018c21STomi Valkeinen lcd_writel(fbi, LCCR2, fbi->reg_lccr2); 1451f7018c21STomi Valkeinen lcd_writel(fbi, LCCR1, fbi->reg_lccr1); 1452f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 1453f7018c21STomi Valkeinen 1454f7018c21STomi Valkeinen lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 1455f7018c21STomi Valkeinen if (fbi->lccr0 & LCCR0_SDS) 1456f7018c21STomi Valkeinen lcd_writel(fbi, FDADR1, fbi->fdadr[1]); 1457f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 1458f7018c21STomi Valkeinen } 1459f7018c21STomi Valkeinen 1460f7018c21STomi Valkeinen static void pxafb_disable_controller(struct pxafb_info *fbi) 1461f7018c21STomi Valkeinen { 1462f7018c21STomi Valkeinen uint32_t lccr0; 1463f7018c21STomi Valkeinen 1464f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_SMARTPANEL 1465f7018c21STomi Valkeinen if (fbi->lccr0 & LCCR0_LCDT) { 1466f7018c21STomi Valkeinen wait_for_completion_timeout(&fbi->refresh_done, 146717713ce0SNicholas Mc Guire msecs_to_jiffies(200)); 1468f7018c21STomi Valkeinen return; 1469f7018c21STomi Valkeinen } 1470f7018c21STomi Valkeinen #endif 1471f7018c21STomi Valkeinen 1472f7018c21STomi Valkeinen /* Clear LCD Status Register */ 1473f7018c21STomi Valkeinen lcd_writel(fbi, LCSR, 0xffffffff); 1474f7018c21STomi Valkeinen 1475f7018c21STomi Valkeinen lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM; 1476f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, lccr0); 1477f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS); 1478f7018c21STomi Valkeinen 147917713ce0SNicholas Mc Guire wait_for_completion_timeout(&fbi->disable_done, msecs_to_jiffies(200)); 1480f7018c21STomi Valkeinen 1481f7018c21STomi Valkeinen /* disable LCD controller clock */ 1482f7018c21STomi Valkeinen clk_disable_unprepare(fbi->clk); 1483f7018c21STomi Valkeinen } 1484f7018c21STomi Valkeinen 1485f7018c21STomi Valkeinen /* 1486f7018c21STomi Valkeinen * pxafb_handle_irq: Handle 'LCD DONE' interrupts. 1487f7018c21STomi Valkeinen */ 1488f7018c21STomi Valkeinen static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) 1489f7018c21STomi Valkeinen { 1490f7018c21STomi Valkeinen struct pxafb_info *fbi = dev_id; 1491f7018c21STomi Valkeinen unsigned int lccr0, lcsr; 1492f7018c21STomi Valkeinen 1493f7018c21STomi Valkeinen lcsr = lcd_readl(fbi, LCSR); 1494f7018c21STomi Valkeinen if (lcsr & LCSR_LDD) { 1495f7018c21STomi Valkeinen lccr0 = lcd_readl(fbi, LCCR0); 1496f7018c21STomi Valkeinen lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); 1497f7018c21STomi Valkeinen complete(&fbi->disable_done); 1498f7018c21STomi Valkeinen } 1499f7018c21STomi Valkeinen 1500f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_SMARTPANEL 1501f7018c21STomi Valkeinen if (lcsr & LCSR_CMD_INT) 1502f7018c21STomi Valkeinen complete(&fbi->command_done); 1503f7018c21STomi Valkeinen #endif 1504f7018c21STomi Valkeinen lcd_writel(fbi, LCSR, lcsr); 1505f7018c21STomi Valkeinen 1506f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_OVERLAY 1507f7018c21STomi Valkeinen { 1508f7018c21STomi Valkeinen unsigned int lcsr1 = lcd_readl(fbi, LCSR1); 1509f7018c21STomi Valkeinen if (lcsr1 & LCSR1_BS(1)) 1510f7018c21STomi Valkeinen complete(&fbi->overlay[0].branch_done); 1511f7018c21STomi Valkeinen 1512f7018c21STomi Valkeinen if (lcsr1 & LCSR1_BS(2)) 1513f7018c21STomi Valkeinen complete(&fbi->overlay[1].branch_done); 1514f7018c21STomi Valkeinen 1515f7018c21STomi Valkeinen lcd_writel(fbi, LCSR1, lcsr1); 1516f7018c21STomi Valkeinen } 1517f7018c21STomi Valkeinen #endif 1518f7018c21STomi Valkeinen return IRQ_HANDLED; 1519f7018c21STomi Valkeinen } 1520f7018c21STomi Valkeinen 1521f7018c21STomi Valkeinen /* 1522f7018c21STomi Valkeinen * This function must be called from task context only, since it will 1523f7018c21STomi Valkeinen * sleep when disabling the LCD controller, or if we get two contending 1524f7018c21STomi Valkeinen * processes trying to alter state. 1525f7018c21STomi Valkeinen */ 1526f7018c21STomi Valkeinen static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) 1527f7018c21STomi Valkeinen { 1528f7018c21STomi Valkeinen u_int old_state; 1529f7018c21STomi Valkeinen 1530f7018c21STomi Valkeinen mutex_lock(&fbi->ctrlr_lock); 1531f7018c21STomi Valkeinen 1532f7018c21STomi Valkeinen old_state = fbi->state; 1533f7018c21STomi Valkeinen 1534f7018c21STomi Valkeinen /* 1535f7018c21STomi Valkeinen * Hack around fbcon initialisation. 1536f7018c21STomi Valkeinen */ 1537f7018c21STomi Valkeinen if (old_state == C_STARTUP && state == C_REENABLE) 1538f7018c21STomi Valkeinen state = C_ENABLE; 1539f7018c21STomi Valkeinen 1540f7018c21STomi Valkeinen switch (state) { 1541f7018c21STomi Valkeinen case C_DISABLE_CLKCHANGE: 1542f7018c21STomi Valkeinen /* 1543f7018c21STomi Valkeinen * Disable controller for clock change. If the 1544f7018c21STomi Valkeinen * controller is already disabled, then do nothing. 1545f7018c21STomi Valkeinen */ 1546f7018c21STomi Valkeinen if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { 1547f7018c21STomi Valkeinen fbi->state = state; 1548f7018c21STomi Valkeinen /* TODO __pxafb_lcd_power(fbi, 0); */ 1549f7018c21STomi Valkeinen pxafb_disable_controller(fbi); 1550f7018c21STomi Valkeinen } 1551f7018c21STomi Valkeinen break; 1552f7018c21STomi Valkeinen 1553f7018c21STomi Valkeinen case C_DISABLE_PM: 1554f7018c21STomi Valkeinen case C_DISABLE: 1555f7018c21STomi Valkeinen /* 1556f7018c21STomi Valkeinen * Disable controller 1557f7018c21STomi Valkeinen */ 1558f7018c21STomi Valkeinen if (old_state != C_DISABLE) { 1559f7018c21STomi Valkeinen fbi->state = state; 1560f7018c21STomi Valkeinen __pxafb_backlight_power(fbi, 0); 1561f7018c21STomi Valkeinen __pxafb_lcd_power(fbi, 0); 1562f7018c21STomi Valkeinen if (old_state != C_DISABLE_CLKCHANGE) 1563f7018c21STomi Valkeinen pxafb_disable_controller(fbi); 1564f7018c21STomi Valkeinen } 1565f7018c21STomi Valkeinen break; 1566f7018c21STomi Valkeinen 1567f7018c21STomi Valkeinen case C_ENABLE_CLKCHANGE: 1568f7018c21STomi Valkeinen /* 1569f7018c21STomi Valkeinen * Enable the controller after clock change. Only 1570f7018c21STomi Valkeinen * do this if we were disabled for the clock change. 1571f7018c21STomi Valkeinen */ 1572f7018c21STomi Valkeinen if (old_state == C_DISABLE_CLKCHANGE) { 1573f7018c21STomi Valkeinen fbi->state = C_ENABLE; 1574f7018c21STomi Valkeinen pxafb_enable_controller(fbi); 1575f7018c21STomi Valkeinen /* TODO __pxafb_lcd_power(fbi, 1); */ 1576f7018c21STomi Valkeinen } 1577f7018c21STomi Valkeinen break; 1578f7018c21STomi Valkeinen 1579f7018c21STomi Valkeinen case C_REENABLE: 1580f7018c21STomi Valkeinen /* 1581f7018c21STomi Valkeinen * Re-enable the controller only if it was already 1582f7018c21STomi Valkeinen * enabled. This is so we reprogram the control 1583f7018c21STomi Valkeinen * registers. 1584f7018c21STomi Valkeinen */ 1585f7018c21STomi Valkeinen if (old_state == C_ENABLE) { 1586f7018c21STomi Valkeinen __pxafb_lcd_power(fbi, 0); 1587f7018c21STomi Valkeinen pxafb_disable_controller(fbi); 1588f7018c21STomi Valkeinen pxafb_enable_controller(fbi); 1589f7018c21STomi Valkeinen __pxafb_lcd_power(fbi, 1); 1590f7018c21STomi Valkeinen } 1591f7018c21STomi Valkeinen break; 1592f7018c21STomi Valkeinen 1593f7018c21STomi Valkeinen case C_ENABLE_PM: 1594f7018c21STomi Valkeinen /* 1595f7018c21STomi Valkeinen * Re-enable the controller after PM. This is not 1596f7018c21STomi Valkeinen * perfect - think about the case where we were doing 1597f7018c21STomi Valkeinen * a clock change, and we suspended half-way through. 1598f7018c21STomi Valkeinen */ 1599f7018c21STomi Valkeinen if (old_state != C_DISABLE_PM) 1600f7018c21STomi Valkeinen break; 1601f7018c21STomi Valkeinen /* fall through */ 1602f7018c21STomi Valkeinen 1603f7018c21STomi Valkeinen case C_ENABLE: 1604f7018c21STomi Valkeinen /* 1605f7018c21STomi Valkeinen * Power up the LCD screen, enable controller, and 1606f7018c21STomi Valkeinen * turn on the backlight. 1607f7018c21STomi Valkeinen */ 1608f7018c21STomi Valkeinen if (old_state != C_ENABLE) { 1609f7018c21STomi Valkeinen fbi->state = C_ENABLE; 1610f7018c21STomi Valkeinen pxafb_enable_controller(fbi); 1611f7018c21STomi Valkeinen __pxafb_lcd_power(fbi, 1); 1612f7018c21STomi Valkeinen __pxafb_backlight_power(fbi, 1); 1613f7018c21STomi Valkeinen } 1614f7018c21STomi Valkeinen break; 1615f7018c21STomi Valkeinen } 1616f7018c21STomi Valkeinen mutex_unlock(&fbi->ctrlr_lock); 1617f7018c21STomi Valkeinen } 1618f7018c21STomi Valkeinen 1619f7018c21STomi Valkeinen /* 1620f7018c21STomi Valkeinen * Our LCD controller task (which is called when we blank or unblank) 1621f7018c21STomi Valkeinen * via keventd. 1622f7018c21STomi Valkeinen */ 1623f7018c21STomi Valkeinen static void pxafb_task(struct work_struct *work) 1624f7018c21STomi Valkeinen { 1625f7018c21STomi Valkeinen struct pxafb_info *fbi = 1626f7018c21STomi Valkeinen container_of(work, struct pxafb_info, task); 1627f7018c21STomi Valkeinen u_int state = xchg(&fbi->task_state, -1); 1628f7018c21STomi Valkeinen 1629f7018c21STomi Valkeinen set_ctrlr_state(fbi, state); 1630f7018c21STomi Valkeinen } 1631f7018c21STomi Valkeinen 1632f7018c21STomi Valkeinen #ifdef CONFIG_CPU_FREQ 1633f7018c21STomi Valkeinen /* 1634f7018c21STomi Valkeinen * CPU clock speed change handler. We need to adjust the LCD timing 1635f7018c21STomi Valkeinen * parameters when the CPU clock is adjusted by the power management 1636f7018c21STomi Valkeinen * subsystem. 1637f7018c21STomi Valkeinen * 1638f7018c21STomi Valkeinen * TODO: Determine why f->new != 10*get_lclk_frequency_10khz() 1639f7018c21STomi Valkeinen */ 1640f7018c21STomi Valkeinen static int 1641f7018c21STomi Valkeinen pxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) 1642f7018c21STomi Valkeinen { 1643f7018c21STomi Valkeinen struct pxafb_info *fbi = TO_INF(nb, freq_transition); 1644f7018c21STomi Valkeinen /* TODO struct cpufreq_freqs *f = data; */ 1645f7018c21STomi Valkeinen u_int pcd; 1646f7018c21STomi Valkeinen 1647f7018c21STomi Valkeinen switch (val) { 1648f7018c21STomi Valkeinen case CPUFREQ_PRECHANGE: 1649f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_OVERLAY 1650f7018c21STomi Valkeinen if (!(fbi->overlay[0].usage || fbi->overlay[1].usage)) 1651f7018c21STomi Valkeinen #endif 1652f7018c21STomi Valkeinen set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); 1653f7018c21STomi Valkeinen break; 1654f7018c21STomi Valkeinen 1655f7018c21STomi Valkeinen case CPUFREQ_POSTCHANGE: 1656f7018c21STomi Valkeinen pcd = get_pcd(fbi, fbi->fb.var.pixclock); 1657f7018c21STomi Valkeinen set_hsync_time(fbi, pcd); 1658f7018c21STomi Valkeinen fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | 1659f7018c21STomi Valkeinen LCCR3_PixClkDiv(pcd); 1660f7018c21STomi Valkeinen set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); 1661f7018c21STomi Valkeinen break; 1662f7018c21STomi Valkeinen } 1663f7018c21STomi Valkeinen return 0; 1664f7018c21STomi Valkeinen } 1665f7018c21STomi Valkeinen 1666f7018c21STomi Valkeinen static int 1667f7018c21STomi Valkeinen pxafb_freq_policy(struct notifier_block *nb, unsigned long val, void *data) 1668f7018c21STomi Valkeinen { 1669f7018c21STomi Valkeinen struct pxafb_info *fbi = TO_INF(nb, freq_policy); 1670f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &fbi->fb.var; 1671f7018c21STomi Valkeinen struct cpufreq_policy *policy = data; 1672f7018c21STomi Valkeinen 1673f7018c21STomi Valkeinen switch (val) { 1674f7018c21STomi Valkeinen case CPUFREQ_ADJUST: 1675f7018c21STomi Valkeinen pr_debug("min dma period: %d ps, " 1676f7018c21STomi Valkeinen "new clock %d kHz\n", pxafb_display_dma_period(var), 1677f7018c21STomi Valkeinen policy->max); 1678f7018c21STomi Valkeinen /* TODO: fill in min/max values */ 1679f7018c21STomi Valkeinen break; 1680f7018c21STomi Valkeinen } 1681f7018c21STomi Valkeinen return 0; 1682f7018c21STomi Valkeinen } 1683f7018c21STomi Valkeinen #endif 1684f7018c21STomi Valkeinen 1685f7018c21STomi Valkeinen #ifdef CONFIG_PM 1686f7018c21STomi Valkeinen /* 1687f7018c21STomi Valkeinen * Power management hooks. Note that we won't be called from IRQ context, 1688f7018c21STomi Valkeinen * unlike the blank functions above, so we may sleep. 1689f7018c21STomi Valkeinen */ 1690f7018c21STomi Valkeinen static int pxafb_suspend(struct device *dev) 1691f7018c21STomi Valkeinen { 1692f7018c21STomi Valkeinen struct pxafb_info *fbi = dev_get_drvdata(dev); 1693f7018c21STomi Valkeinen 1694f7018c21STomi Valkeinen set_ctrlr_state(fbi, C_DISABLE_PM); 1695f7018c21STomi Valkeinen return 0; 1696f7018c21STomi Valkeinen } 1697f7018c21STomi Valkeinen 1698f7018c21STomi Valkeinen static int pxafb_resume(struct device *dev) 1699f7018c21STomi Valkeinen { 1700f7018c21STomi Valkeinen struct pxafb_info *fbi = dev_get_drvdata(dev); 1701f7018c21STomi Valkeinen 1702f7018c21STomi Valkeinen set_ctrlr_state(fbi, C_ENABLE_PM); 1703f7018c21STomi Valkeinen return 0; 1704f7018c21STomi Valkeinen } 1705f7018c21STomi Valkeinen 1706f7018c21STomi Valkeinen static const struct dev_pm_ops pxafb_pm_ops = { 1707f7018c21STomi Valkeinen .suspend = pxafb_suspend, 1708f7018c21STomi Valkeinen .resume = pxafb_resume, 1709f7018c21STomi Valkeinen }; 1710f7018c21STomi Valkeinen #endif 1711f7018c21STomi Valkeinen 1712f7018c21STomi Valkeinen static int pxafb_init_video_memory(struct pxafb_info *fbi) 1713f7018c21STomi Valkeinen { 1714f7018c21STomi Valkeinen int size = PAGE_ALIGN(fbi->video_mem_size); 1715f7018c21STomi Valkeinen 1716f7018c21STomi Valkeinen fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); 1717f7018c21STomi Valkeinen if (fbi->video_mem == NULL) 1718f7018c21STomi Valkeinen return -ENOMEM; 1719f7018c21STomi Valkeinen 1720f7018c21STomi Valkeinen fbi->video_mem_phys = virt_to_phys(fbi->video_mem); 1721f7018c21STomi Valkeinen fbi->video_mem_size = size; 1722f7018c21STomi Valkeinen 1723f7018c21STomi Valkeinen fbi->fb.fix.smem_start = fbi->video_mem_phys; 1724f7018c21STomi Valkeinen fbi->fb.fix.smem_len = fbi->video_mem_size; 1725f7018c21STomi Valkeinen fbi->fb.screen_base = fbi->video_mem; 1726f7018c21STomi Valkeinen 1727f7018c21STomi Valkeinen return fbi->video_mem ? 0 : -ENOMEM; 1728f7018c21STomi Valkeinen } 1729f7018c21STomi Valkeinen 1730f7018c21STomi Valkeinen static void pxafb_decode_mach_info(struct pxafb_info *fbi, 1731f7018c21STomi Valkeinen struct pxafb_mach_info *inf) 1732f7018c21STomi Valkeinen { 1733f7018c21STomi Valkeinen unsigned int lcd_conn = inf->lcd_conn; 1734f7018c21STomi Valkeinen struct pxafb_mode_info *m; 1735f7018c21STomi Valkeinen int i; 1736f7018c21STomi Valkeinen 1737f7018c21STomi Valkeinen fbi->cmap_inverse = inf->cmap_inverse; 1738f7018c21STomi Valkeinen fbi->cmap_static = inf->cmap_static; 1739f7018c21STomi Valkeinen fbi->lccr4 = inf->lccr4; 1740f7018c21STomi Valkeinen 1741f7018c21STomi Valkeinen switch (lcd_conn & LCD_TYPE_MASK) { 1742f7018c21STomi Valkeinen case LCD_TYPE_MONO_STN: 1743f7018c21STomi Valkeinen fbi->lccr0 = LCCR0_CMS; 1744f7018c21STomi Valkeinen break; 1745f7018c21STomi Valkeinen case LCD_TYPE_MONO_DSTN: 1746f7018c21STomi Valkeinen fbi->lccr0 = LCCR0_CMS | LCCR0_SDS; 1747f7018c21STomi Valkeinen break; 1748f7018c21STomi Valkeinen case LCD_TYPE_COLOR_STN: 1749f7018c21STomi Valkeinen fbi->lccr0 = 0; 1750f7018c21STomi Valkeinen break; 1751f7018c21STomi Valkeinen case LCD_TYPE_COLOR_DSTN: 1752f7018c21STomi Valkeinen fbi->lccr0 = LCCR0_SDS; 1753f7018c21STomi Valkeinen break; 1754f7018c21STomi Valkeinen case LCD_TYPE_COLOR_TFT: 1755f7018c21STomi Valkeinen fbi->lccr0 = LCCR0_PAS; 1756f7018c21STomi Valkeinen break; 1757f7018c21STomi Valkeinen case LCD_TYPE_SMART_PANEL: 1758f7018c21STomi Valkeinen fbi->lccr0 = LCCR0_LCDT | LCCR0_PAS; 1759f7018c21STomi Valkeinen break; 1760f7018c21STomi Valkeinen default: 1761f7018c21STomi Valkeinen /* fall back to backward compatibility way */ 1762f7018c21STomi Valkeinen fbi->lccr0 = inf->lccr0; 1763f7018c21STomi Valkeinen fbi->lccr3 = inf->lccr3; 1764f7018c21STomi Valkeinen goto decode_mode; 1765f7018c21STomi Valkeinen } 1766f7018c21STomi Valkeinen 1767f7018c21STomi Valkeinen if (lcd_conn == LCD_MONO_STN_8BPP) 1768f7018c21STomi Valkeinen fbi->lccr0 |= LCCR0_DPD; 1769f7018c21STomi Valkeinen 1770f7018c21STomi Valkeinen fbi->lccr0 |= (lcd_conn & LCD_ALTERNATE_MAPPING) ? LCCR0_LDDALT : 0; 1771f7018c21STomi Valkeinen 1772f7018c21STomi Valkeinen fbi->lccr3 = LCCR3_Acb((inf->lcd_conn >> 10) & 0xff); 1773f7018c21STomi Valkeinen fbi->lccr3 |= (lcd_conn & LCD_BIAS_ACTIVE_LOW) ? LCCR3_OEP : 0; 1774f7018c21STomi Valkeinen fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0; 1775f7018c21STomi Valkeinen 1776f7018c21STomi Valkeinen decode_mode: 1777f7018c21STomi Valkeinen pxafb_setmode(&fbi->fb.var, &inf->modes[0]); 1778f7018c21STomi Valkeinen 1779f7018c21STomi Valkeinen /* decide video memory size as follows: 1780f7018c21STomi Valkeinen * 1. default to mode of maximum resolution 1781f7018c21STomi Valkeinen * 2. allow platform to override 1782f7018c21STomi Valkeinen * 3. allow module parameter to override 1783f7018c21STomi Valkeinen */ 1784f7018c21STomi Valkeinen for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++) 1785f7018c21STomi Valkeinen fbi->video_mem_size = max_t(size_t, fbi->video_mem_size, 1786f7018c21STomi Valkeinen m->xres * m->yres * m->bpp / 8); 1787f7018c21STomi Valkeinen 1788f7018c21STomi Valkeinen if (inf->video_mem_size > fbi->video_mem_size) 1789f7018c21STomi Valkeinen fbi->video_mem_size = inf->video_mem_size; 1790f7018c21STomi Valkeinen 1791f7018c21STomi Valkeinen if (video_mem_size > fbi->video_mem_size) 1792f7018c21STomi Valkeinen fbi->video_mem_size = video_mem_size; 1793f7018c21STomi Valkeinen } 1794f7018c21STomi Valkeinen 1795f3621a60SRobert Jarzmik static struct pxafb_info *pxafb_init_fbinfo(struct device *dev, 1796f3621a60SRobert Jarzmik struct pxafb_mach_info *inf) 1797f7018c21STomi Valkeinen { 1798f7018c21STomi Valkeinen struct pxafb_info *fbi; 1799f7018c21STomi Valkeinen void *addr; 1800f7018c21STomi Valkeinen 1801f7018c21STomi Valkeinen /* Alloc the pxafb_info and pseudo_palette in one step */ 1802f7018c21STomi Valkeinen fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL); 1803f7018c21STomi Valkeinen if (!fbi) 1804f7018c21STomi Valkeinen return NULL; 1805f7018c21STomi Valkeinen 1806f7018c21STomi Valkeinen memset(fbi, 0, sizeof(struct pxafb_info)); 1807f7018c21STomi Valkeinen fbi->dev = dev; 1808f3621a60SRobert Jarzmik fbi->inf = inf; 1809f7018c21STomi Valkeinen 1810f7018c21STomi Valkeinen fbi->clk = clk_get(dev, NULL); 1811f7018c21STomi Valkeinen if (IS_ERR(fbi->clk)) { 1812f7018c21STomi Valkeinen kfree(fbi); 1813f7018c21STomi Valkeinen return NULL; 1814f7018c21STomi Valkeinen } 1815f7018c21STomi Valkeinen 1816f7018c21STomi Valkeinen strcpy(fbi->fb.fix.id, PXA_NAME); 1817f7018c21STomi Valkeinen 1818f7018c21STomi Valkeinen fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 1819f7018c21STomi Valkeinen fbi->fb.fix.type_aux = 0; 1820f7018c21STomi Valkeinen fbi->fb.fix.xpanstep = 0; 1821f7018c21STomi Valkeinen fbi->fb.fix.ypanstep = 1; 1822f7018c21STomi Valkeinen fbi->fb.fix.ywrapstep = 0; 1823f7018c21STomi Valkeinen fbi->fb.fix.accel = FB_ACCEL_NONE; 1824f7018c21STomi Valkeinen 1825f7018c21STomi Valkeinen fbi->fb.var.nonstd = 0; 1826f7018c21STomi Valkeinen fbi->fb.var.activate = FB_ACTIVATE_NOW; 1827f7018c21STomi Valkeinen fbi->fb.var.height = -1; 1828f7018c21STomi Valkeinen fbi->fb.var.width = -1; 1829f7018c21STomi Valkeinen fbi->fb.var.accel_flags = FB_ACCELF_TEXT; 1830f7018c21STomi Valkeinen fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; 1831f7018c21STomi Valkeinen 1832f7018c21STomi Valkeinen fbi->fb.fbops = &pxafb_ops; 1833f7018c21STomi Valkeinen fbi->fb.flags = FBINFO_DEFAULT; 1834f7018c21STomi Valkeinen fbi->fb.node = -1; 1835f7018c21STomi Valkeinen 1836f7018c21STomi Valkeinen addr = fbi; 1837f7018c21STomi Valkeinen addr = addr + sizeof(struct pxafb_info); 1838f7018c21STomi Valkeinen fbi->fb.pseudo_palette = addr; 1839f7018c21STomi Valkeinen 1840f7018c21STomi Valkeinen fbi->state = C_STARTUP; 1841f7018c21STomi Valkeinen fbi->task_state = (u_char)-1; 1842f7018c21STomi Valkeinen 1843f7018c21STomi Valkeinen pxafb_decode_mach_info(fbi, inf); 1844f7018c21STomi Valkeinen 1845f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_OVERLAY 1846f7018c21STomi Valkeinen /* place overlay(s) on top of base */ 1847f7018c21STomi Valkeinen if (pxafb_overlay_supported()) 1848f7018c21STomi Valkeinen fbi->lccr0 |= LCCR0_OUC; 1849f7018c21STomi Valkeinen #endif 1850f7018c21STomi Valkeinen 1851f7018c21STomi Valkeinen init_waitqueue_head(&fbi->ctrlr_wait); 1852f7018c21STomi Valkeinen INIT_WORK(&fbi->task, pxafb_task); 1853f7018c21STomi Valkeinen mutex_init(&fbi->ctrlr_lock); 1854f7018c21STomi Valkeinen init_completion(&fbi->disable_done); 1855f7018c21STomi Valkeinen 1856f7018c21STomi Valkeinen return fbi; 1857f7018c21STomi Valkeinen } 1858f7018c21STomi Valkeinen 1859f7018c21STomi Valkeinen #ifdef CONFIG_FB_PXA_PARAMETERS 1860f3621a60SRobert Jarzmik static int parse_opt_mode(struct device *dev, const char *this_opt, 1861f3621a60SRobert Jarzmik struct pxafb_mach_info *inf) 1862f7018c21STomi Valkeinen { 1863f7018c21STomi Valkeinen const char *name = this_opt+5; 1864f7018c21STomi Valkeinen unsigned int namelen = strlen(name); 1865f7018c21STomi Valkeinen int res_specified = 0, bpp_specified = 0; 1866f7018c21STomi Valkeinen unsigned int xres = 0, yres = 0, bpp = 0; 1867f7018c21STomi Valkeinen int yres_specified = 0; 1868f7018c21STomi Valkeinen int i; 1869f7018c21STomi Valkeinen for (i = namelen-1; i >= 0; i--) { 1870f7018c21STomi Valkeinen switch (name[i]) { 1871f7018c21STomi Valkeinen case '-': 1872f7018c21STomi Valkeinen namelen = i; 1873f7018c21STomi Valkeinen if (!bpp_specified && !yres_specified) { 1874f7018c21STomi Valkeinen bpp = simple_strtoul(&name[i+1], NULL, 0); 1875f7018c21STomi Valkeinen bpp_specified = 1; 1876f7018c21STomi Valkeinen } else 1877f7018c21STomi Valkeinen goto done; 1878f7018c21STomi Valkeinen break; 1879f7018c21STomi Valkeinen case 'x': 1880f7018c21STomi Valkeinen if (!yres_specified) { 1881f7018c21STomi Valkeinen yres = simple_strtoul(&name[i+1], NULL, 0); 1882f7018c21STomi Valkeinen yres_specified = 1; 1883f7018c21STomi Valkeinen } else 1884f7018c21STomi Valkeinen goto done; 1885f7018c21STomi Valkeinen break; 1886f7018c21STomi Valkeinen case '0' ... '9': 1887f7018c21STomi Valkeinen break; 1888f7018c21STomi Valkeinen default: 1889f7018c21STomi Valkeinen goto done; 1890f7018c21STomi Valkeinen } 1891f7018c21STomi Valkeinen } 1892f7018c21STomi Valkeinen if (i < 0 && yres_specified) { 1893f7018c21STomi Valkeinen xres = simple_strtoul(name, NULL, 0); 1894f7018c21STomi Valkeinen res_specified = 1; 1895f7018c21STomi Valkeinen } 1896f7018c21STomi Valkeinen done: 1897f7018c21STomi Valkeinen if (res_specified) { 1898f7018c21STomi Valkeinen dev_info(dev, "overriding resolution: %dx%d\n", xres, yres); 1899f7018c21STomi Valkeinen inf->modes[0].xres = xres; inf->modes[0].yres = yres; 1900f7018c21STomi Valkeinen } 1901f7018c21STomi Valkeinen if (bpp_specified) 1902f7018c21STomi Valkeinen switch (bpp) { 1903f7018c21STomi Valkeinen case 1: 1904f7018c21STomi Valkeinen case 2: 1905f7018c21STomi Valkeinen case 4: 1906f7018c21STomi Valkeinen case 8: 1907f7018c21STomi Valkeinen case 16: 1908f7018c21STomi Valkeinen inf->modes[0].bpp = bpp; 1909f7018c21STomi Valkeinen dev_info(dev, "overriding bit depth: %d\n", bpp); 1910f7018c21STomi Valkeinen break; 1911f7018c21STomi Valkeinen default: 1912f7018c21STomi Valkeinen dev_err(dev, "Depth %d is not valid\n", bpp); 1913f7018c21STomi Valkeinen return -EINVAL; 1914f7018c21STomi Valkeinen } 1915f7018c21STomi Valkeinen return 0; 1916f7018c21STomi Valkeinen } 1917f7018c21STomi Valkeinen 1918f3621a60SRobert Jarzmik static int parse_opt(struct device *dev, char *this_opt, 1919f3621a60SRobert Jarzmik struct pxafb_mach_info *inf) 1920f7018c21STomi Valkeinen { 1921f7018c21STomi Valkeinen struct pxafb_mode_info *mode = &inf->modes[0]; 1922f7018c21STomi Valkeinen char s[64]; 1923f7018c21STomi Valkeinen 1924f7018c21STomi Valkeinen s[0] = '\0'; 1925f7018c21STomi Valkeinen 1926f7018c21STomi Valkeinen if (!strncmp(this_opt, "vmem:", 5)) { 1927f7018c21STomi Valkeinen video_mem_size = memparse(this_opt + 5, NULL); 1928f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "mode:", 5)) { 1929f3621a60SRobert Jarzmik return parse_opt_mode(dev, this_opt, inf); 1930f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "pixclock:", 9)) { 1931f7018c21STomi Valkeinen mode->pixclock = simple_strtoul(this_opt+9, NULL, 0); 1932f7018c21STomi Valkeinen sprintf(s, "pixclock: %ld\n", mode->pixclock); 1933f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "left:", 5)) { 1934f7018c21STomi Valkeinen mode->left_margin = simple_strtoul(this_opt+5, NULL, 0); 1935f7018c21STomi Valkeinen sprintf(s, "left: %u\n", mode->left_margin); 1936f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "right:", 6)) { 1937f7018c21STomi Valkeinen mode->right_margin = simple_strtoul(this_opt+6, NULL, 0); 1938f7018c21STomi Valkeinen sprintf(s, "right: %u\n", mode->right_margin); 1939f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "upper:", 6)) { 1940f7018c21STomi Valkeinen mode->upper_margin = simple_strtoul(this_opt+6, NULL, 0); 1941f7018c21STomi Valkeinen sprintf(s, "upper: %u\n", mode->upper_margin); 1942f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "lower:", 6)) { 1943f7018c21STomi Valkeinen mode->lower_margin = simple_strtoul(this_opt+6, NULL, 0); 1944f7018c21STomi Valkeinen sprintf(s, "lower: %u\n", mode->lower_margin); 1945f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "hsynclen:", 9)) { 1946f7018c21STomi Valkeinen mode->hsync_len = simple_strtoul(this_opt+9, NULL, 0); 1947f7018c21STomi Valkeinen sprintf(s, "hsynclen: %u\n", mode->hsync_len); 1948f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "vsynclen:", 9)) { 1949f7018c21STomi Valkeinen mode->vsync_len = simple_strtoul(this_opt+9, NULL, 0); 1950f7018c21STomi Valkeinen sprintf(s, "vsynclen: %u\n", mode->vsync_len); 1951f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "hsync:", 6)) { 1952f7018c21STomi Valkeinen if (simple_strtoul(this_opt+6, NULL, 0) == 0) { 1953f7018c21STomi Valkeinen sprintf(s, "hsync: Active Low\n"); 1954f7018c21STomi Valkeinen mode->sync &= ~FB_SYNC_HOR_HIGH_ACT; 1955f7018c21STomi Valkeinen } else { 1956f7018c21STomi Valkeinen sprintf(s, "hsync: Active High\n"); 1957f7018c21STomi Valkeinen mode->sync |= FB_SYNC_HOR_HIGH_ACT; 1958f7018c21STomi Valkeinen } 1959f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "vsync:", 6)) { 1960f7018c21STomi Valkeinen if (simple_strtoul(this_opt+6, NULL, 0) == 0) { 1961f7018c21STomi Valkeinen sprintf(s, "vsync: Active Low\n"); 1962f7018c21STomi Valkeinen mode->sync &= ~FB_SYNC_VERT_HIGH_ACT; 1963f7018c21STomi Valkeinen } else { 1964f7018c21STomi Valkeinen sprintf(s, "vsync: Active High\n"); 1965f7018c21STomi Valkeinen mode->sync |= FB_SYNC_VERT_HIGH_ACT; 1966f7018c21STomi Valkeinen } 1967f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "dpc:", 4)) { 1968f7018c21STomi Valkeinen if (simple_strtoul(this_opt+4, NULL, 0) == 0) { 1969f7018c21STomi Valkeinen sprintf(s, "double pixel clock: false\n"); 1970f7018c21STomi Valkeinen inf->lccr3 &= ~LCCR3_DPC; 1971f7018c21STomi Valkeinen } else { 1972f7018c21STomi Valkeinen sprintf(s, "double pixel clock: true\n"); 1973f7018c21STomi Valkeinen inf->lccr3 |= LCCR3_DPC; 1974f7018c21STomi Valkeinen } 1975f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "outputen:", 9)) { 1976f7018c21STomi Valkeinen if (simple_strtoul(this_opt+9, NULL, 0) == 0) { 1977f7018c21STomi Valkeinen sprintf(s, "output enable: active low\n"); 1978f7018c21STomi Valkeinen inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL; 1979f7018c21STomi Valkeinen } else { 1980f7018c21STomi Valkeinen sprintf(s, "output enable: active high\n"); 1981f7018c21STomi Valkeinen inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH; 1982f7018c21STomi Valkeinen } 1983f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "pixclockpol:", 12)) { 1984f7018c21STomi Valkeinen if (simple_strtoul(this_opt+12, NULL, 0) == 0) { 1985f7018c21STomi Valkeinen sprintf(s, "pixel clock polarity: falling edge\n"); 1986f7018c21STomi Valkeinen inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg; 1987f7018c21STomi Valkeinen } else { 1988f7018c21STomi Valkeinen sprintf(s, "pixel clock polarity: rising edge\n"); 1989f7018c21STomi Valkeinen inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg; 1990f7018c21STomi Valkeinen } 1991f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "color", 5)) { 1992f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color; 1993f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "mono", 4)) { 1994f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Mono; 1995f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "active", 6)) { 1996f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Act; 1997f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "passive", 7)) { 1998f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Pas; 1999f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "single", 6)) { 2000f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Sngl; 2001f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "dual", 4)) { 2002f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Dual; 2003f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "4pix", 4)) { 2004f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_4PixMono; 2005f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "8pix", 4)) { 2006f7018c21STomi Valkeinen inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_8PixMono; 2007f7018c21STomi Valkeinen } else { 2008f7018c21STomi Valkeinen dev_err(dev, "unknown option: %s\n", this_opt); 2009f7018c21STomi Valkeinen return -EINVAL; 2010f7018c21STomi Valkeinen } 2011f7018c21STomi Valkeinen 2012f7018c21STomi Valkeinen if (s[0] != '\0') 2013f7018c21STomi Valkeinen dev_info(dev, "override %s", s); 2014f7018c21STomi Valkeinen 2015f7018c21STomi Valkeinen return 0; 2016f7018c21STomi Valkeinen } 2017f7018c21STomi Valkeinen 2018f3621a60SRobert Jarzmik static int pxafb_parse_options(struct device *dev, char *options, 2019f3621a60SRobert Jarzmik struct pxafb_mach_info *inf) 2020f7018c21STomi Valkeinen { 2021f7018c21STomi Valkeinen char *this_opt; 2022f7018c21STomi Valkeinen int ret; 2023f7018c21STomi Valkeinen 2024f7018c21STomi Valkeinen if (!options || !*options) 2025f7018c21STomi Valkeinen return 0; 2026f7018c21STomi Valkeinen 2027f7018c21STomi Valkeinen dev_dbg(dev, "options are \"%s\"\n", options ? options : "null"); 2028f7018c21STomi Valkeinen 2029f7018c21STomi Valkeinen /* could be made table driven or similar?... */ 2030f7018c21STomi Valkeinen while ((this_opt = strsep(&options, ",")) != NULL) { 2031f3621a60SRobert Jarzmik ret = parse_opt(dev, this_opt, inf); 2032f7018c21STomi Valkeinen if (ret) 2033f7018c21STomi Valkeinen return ret; 2034f7018c21STomi Valkeinen } 2035f7018c21STomi Valkeinen return 0; 2036f7018c21STomi Valkeinen } 2037f7018c21STomi Valkeinen 2038f7018c21STomi Valkeinen static char g_options[256] = ""; 2039f7018c21STomi Valkeinen 2040f7018c21STomi Valkeinen #ifndef MODULE 2041f7018c21STomi Valkeinen static int __init pxafb_setup_options(void) 2042f7018c21STomi Valkeinen { 2043f7018c21STomi Valkeinen char *options = NULL; 2044f7018c21STomi Valkeinen 2045f7018c21STomi Valkeinen if (fb_get_options("pxafb", &options)) 2046f7018c21STomi Valkeinen return -ENODEV; 2047f7018c21STomi Valkeinen 2048f7018c21STomi Valkeinen if (options) 2049f7018c21STomi Valkeinen strlcpy(g_options, options, sizeof(g_options)); 2050f7018c21STomi Valkeinen 2051f7018c21STomi Valkeinen return 0; 2052f7018c21STomi Valkeinen } 2053f7018c21STomi Valkeinen #else 2054f7018c21STomi Valkeinen #define pxafb_setup_options() (0) 2055f7018c21STomi Valkeinen 2056f7018c21STomi Valkeinen module_param_string(options, g_options, sizeof(g_options), 0); 2057f7018c21STomi Valkeinen MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)"); 2058f7018c21STomi Valkeinen #endif 2059f7018c21STomi Valkeinen 2060f7018c21STomi Valkeinen #else 2061f7018c21STomi Valkeinen #define pxafb_parse_options(...) (0) 2062f7018c21STomi Valkeinen #define pxafb_setup_options() (0) 2063f7018c21STomi Valkeinen #endif 2064f7018c21STomi Valkeinen 2065f7018c21STomi Valkeinen #ifdef DEBUG_VAR 2066f7018c21STomi Valkeinen /* Check for various illegal bit-combinations. Currently only 2067f7018c21STomi Valkeinen * a warning is given. */ 2068f7018c21STomi Valkeinen static void pxafb_check_options(struct device *dev, struct pxafb_mach_info *inf) 2069f7018c21STomi Valkeinen { 2070f7018c21STomi Valkeinen if (inf->lcd_conn) 2071f7018c21STomi Valkeinen return; 2072f7018c21STomi Valkeinen 2073f7018c21STomi Valkeinen if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK) 2074f7018c21STomi Valkeinen dev_warn(dev, "machine LCCR0 setting contains " 2075f7018c21STomi Valkeinen "illegal bits: %08x\n", 2076f7018c21STomi Valkeinen inf->lccr0 & LCCR0_INVALID_CONFIG_MASK); 2077f7018c21STomi Valkeinen if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK) 2078f7018c21STomi Valkeinen dev_warn(dev, "machine LCCR3 setting contains " 2079f7018c21STomi Valkeinen "illegal bits: %08x\n", 2080f7018c21STomi Valkeinen inf->lccr3 & LCCR3_INVALID_CONFIG_MASK); 2081f7018c21STomi Valkeinen if (inf->lccr0 & LCCR0_DPD && 2082f7018c21STomi Valkeinen ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas || 2083f7018c21STomi Valkeinen (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl || 2084f7018c21STomi Valkeinen (inf->lccr0 & LCCR0_CMS) != LCCR0_Mono)) 2085f7018c21STomi Valkeinen dev_warn(dev, "Double Pixel Data (DPD) mode is " 2086f7018c21STomi Valkeinen "only valid in passive mono" 2087f7018c21STomi Valkeinen " single panel mode\n"); 2088f7018c21STomi Valkeinen if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act && 2089f7018c21STomi Valkeinen (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) 2090f7018c21STomi Valkeinen dev_warn(dev, "Dual panel only valid in passive mode\n"); 2091f7018c21STomi Valkeinen if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && 2092f7018c21STomi Valkeinen (inf->modes->upper_margin || inf->modes->lower_margin)) 2093f7018c21STomi Valkeinen dev_warn(dev, "Upper and lower margins must be 0 in " 2094f7018c21STomi Valkeinen "passive mode\n"); 2095f7018c21STomi Valkeinen } 2096f7018c21STomi Valkeinen #else 2097f7018c21STomi Valkeinen #define pxafb_check_options(...) do {} while (0) 2098f7018c21STomi Valkeinen #endif 2099f7018c21STomi Valkeinen 2100420a4882SRobert Jarzmik #if defined(CONFIG_OF) 2101420a4882SRobert Jarzmik static const char * const lcd_types[] = { 2102420a4882SRobert Jarzmik "unknown", "mono-stn", "mono-dstn", "color-stn", "color-dstn", 2103420a4882SRobert Jarzmik "color-tft", "smart-panel", NULL 2104420a4882SRobert Jarzmik }; 2105420a4882SRobert Jarzmik 2106420a4882SRobert Jarzmik static int of_get_pxafb_display(struct device *dev, struct device_node *disp, 2107420a4882SRobert Jarzmik struct pxafb_mach_info *info, u32 bus_width) 2108420a4882SRobert Jarzmik { 2109420a4882SRobert Jarzmik struct display_timings *timings; 2110420a4882SRobert Jarzmik struct videomode vm; 2111420a4882SRobert Jarzmik int i, ret = -EINVAL; 2112420a4882SRobert Jarzmik const char *s; 2113420a4882SRobert Jarzmik 2114420a4882SRobert Jarzmik ret = of_property_read_string(disp, "lcd-type", &s); 2115420a4882SRobert Jarzmik if (ret) 2116420a4882SRobert Jarzmik s = "color-tft"; 2117420a4882SRobert Jarzmik 2118420a4882SRobert Jarzmik for (i = 0; lcd_types[i]; i++) 2119420a4882SRobert Jarzmik if (!strcmp(s, lcd_types[i])) 2120420a4882SRobert Jarzmik break; 2121420a4882SRobert Jarzmik if (!i || !lcd_types[i]) { 2122420a4882SRobert Jarzmik dev_err(dev, "lcd-type %s is unknown\n", s); 2123420a4882SRobert Jarzmik return -EINVAL; 2124420a4882SRobert Jarzmik } 2125420a4882SRobert Jarzmik info->lcd_conn |= LCD_CONN_TYPE(i); 2126420a4882SRobert Jarzmik info->lcd_conn |= LCD_CONN_WIDTH(bus_width); 2127420a4882SRobert Jarzmik 2128420a4882SRobert Jarzmik timings = of_get_display_timings(disp); 2129420a4882SRobert Jarzmik if (!timings) 2130e0299908SDan Carpenter return -EINVAL; 2131420a4882SRobert Jarzmik 2132420a4882SRobert Jarzmik ret = -ENOMEM; 2133420a4882SRobert Jarzmik info->modes = kmalloc_array(timings->num_timings, 2134420a4882SRobert Jarzmik sizeof(info->modes[0]), GFP_KERNEL); 2135420a4882SRobert Jarzmik if (!info->modes) 2136420a4882SRobert Jarzmik goto out; 2137420a4882SRobert Jarzmik info->num_modes = timings->num_timings; 2138420a4882SRobert Jarzmik 2139420a4882SRobert Jarzmik for (i = 0; i < timings->num_timings; i++) { 2140420a4882SRobert Jarzmik ret = videomode_from_timings(timings, &vm, i); 2141420a4882SRobert Jarzmik if (ret) { 2142420a4882SRobert Jarzmik dev_err(dev, "videomode_from_timings %d failed: %d\n", 2143420a4882SRobert Jarzmik i, ret); 2144420a4882SRobert Jarzmik goto out; 2145420a4882SRobert Jarzmik } 2146420a4882SRobert Jarzmik if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) 2147420a4882SRobert Jarzmik info->lcd_conn |= LCD_PCLK_EDGE_RISE; 2148420a4882SRobert Jarzmik if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) 2149420a4882SRobert Jarzmik info->lcd_conn |= LCD_PCLK_EDGE_FALL; 2150420a4882SRobert Jarzmik if (vm.flags & DISPLAY_FLAGS_DE_HIGH) 2151420a4882SRobert Jarzmik info->lcd_conn |= LCD_BIAS_ACTIVE_HIGH; 2152420a4882SRobert Jarzmik if (vm.flags & DISPLAY_FLAGS_DE_LOW) 2153420a4882SRobert Jarzmik info->lcd_conn |= LCD_BIAS_ACTIVE_LOW; 2154420a4882SRobert Jarzmik if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH) 2155420a4882SRobert Jarzmik info->modes[i].sync |= FB_SYNC_HOR_HIGH_ACT; 2156420a4882SRobert Jarzmik if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) 2157420a4882SRobert Jarzmik info->modes[i].sync |= FB_SYNC_VERT_HIGH_ACT; 2158420a4882SRobert Jarzmik 2159420a4882SRobert Jarzmik info->modes[i].pixclock = 1000000000UL / (vm.pixelclock / 1000); 2160420a4882SRobert Jarzmik info->modes[i].xres = vm.hactive; 2161420a4882SRobert Jarzmik info->modes[i].yres = vm.vactive; 2162420a4882SRobert Jarzmik info->modes[i].hsync_len = vm.hsync_len; 2163420a4882SRobert Jarzmik info->modes[i].left_margin = vm.hback_porch; 2164420a4882SRobert Jarzmik info->modes[i].right_margin = vm.hfront_porch; 2165420a4882SRobert Jarzmik info->modes[i].vsync_len = vm.vsync_len; 2166420a4882SRobert Jarzmik info->modes[i].upper_margin = vm.vback_porch; 2167420a4882SRobert Jarzmik info->modes[i].lower_margin = vm.vfront_porch; 2168420a4882SRobert Jarzmik } 2169420a4882SRobert Jarzmik ret = 0; 2170420a4882SRobert Jarzmik 2171420a4882SRobert Jarzmik out: 2172420a4882SRobert Jarzmik display_timings_release(timings); 2173420a4882SRobert Jarzmik return ret; 2174420a4882SRobert Jarzmik } 2175420a4882SRobert Jarzmik 2176420a4882SRobert Jarzmik static int of_get_pxafb_mode_info(struct device *dev, 2177420a4882SRobert Jarzmik struct pxafb_mach_info *info) 2178420a4882SRobert Jarzmik { 2179420a4882SRobert Jarzmik struct device_node *display, *np; 2180420a4882SRobert Jarzmik u32 bus_width; 2181420a4882SRobert Jarzmik int ret, i; 2182420a4882SRobert Jarzmik 2183420a4882SRobert Jarzmik np = of_graph_get_next_endpoint(dev->of_node, NULL); 2184420a4882SRobert Jarzmik if (!np) { 2185420a4882SRobert Jarzmik dev_err(dev, "could not find endpoint\n"); 2186420a4882SRobert Jarzmik return -EINVAL; 2187420a4882SRobert Jarzmik } 2188420a4882SRobert Jarzmik ret = of_property_read_u32(np, "bus-width", &bus_width); 2189420a4882SRobert Jarzmik if (ret) { 2190420a4882SRobert Jarzmik dev_err(dev, "no bus-width specified: %d\n", ret); 2191d4b9efa3SWei Yongjun of_node_put(np); 2192420a4882SRobert Jarzmik return ret; 2193420a4882SRobert Jarzmik } 2194420a4882SRobert Jarzmik 2195420a4882SRobert Jarzmik display = of_graph_get_remote_port_parent(np); 2196420a4882SRobert Jarzmik of_node_put(np); 2197420a4882SRobert Jarzmik if (!display) { 2198420a4882SRobert Jarzmik dev_err(dev, "no display defined\n"); 2199420a4882SRobert Jarzmik return -EINVAL; 2200420a4882SRobert Jarzmik } 2201420a4882SRobert Jarzmik 2202420a4882SRobert Jarzmik ret = of_get_pxafb_display(dev, display, info, bus_width); 2203420a4882SRobert Jarzmik of_node_put(display); 2204420a4882SRobert Jarzmik if (ret) 2205420a4882SRobert Jarzmik return ret; 2206420a4882SRobert Jarzmik 2207420a4882SRobert Jarzmik for (i = 0; i < info->num_modes; i++) 2208420a4882SRobert Jarzmik info->modes[i].bpp = bus_width; 2209420a4882SRobert Jarzmik 2210420a4882SRobert Jarzmik return 0; 2211420a4882SRobert Jarzmik } 2212420a4882SRobert Jarzmik 2213420a4882SRobert Jarzmik static struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) 2214420a4882SRobert Jarzmik { 2215420a4882SRobert Jarzmik int ret; 2216420a4882SRobert Jarzmik struct pxafb_mach_info *info; 2217420a4882SRobert Jarzmik 2218420a4882SRobert Jarzmik if (!dev->of_node) 2219420a4882SRobert Jarzmik return NULL; 2220420a4882SRobert Jarzmik info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 2221420a4882SRobert Jarzmik if (!info) 2222420a4882SRobert Jarzmik return ERR_PTR(-ENOMEM); 2223420a4882SRobert Jarzmik ret = of_get_pxafb_mode_info(dev, info); 2224420a4882SRobert Jarzmik if (ret) { 2225420a4882SRobert Jarzmik kfree(info->modes); 2226420a4882SRobert Jarzmik return ERR_PTR(ret); 2227420a4882SRobert Jarzmik } 2228420a4882SRobert Jarzmik 2229420a4882SRobert Jarzmik /* 2230420a4882SRobert Jarzmik * On purpose, neither lccrX registers nor video memory size can be 2231420a4882SRobert Jarzmik * specified through device-tree, they are considered more a debug hack 2232420a4882SRobert Jarzmik * available through command line. 2233420a4882SRobert Jarzmik */ 2234420a4882SRobert Jarzmik return info; 2235420a4882SRobert Jarzmik } 2236420a4882SRobert Jarzmik #else 2237420a4882SRobert Jarzmik static struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) 2238420a4882SRobert Jarzmik { 2239420a4882SRobert Jarzmik return NULL; 2240420a4882SRobert Jarzmik } 2241420a4882SRobert Jarzmik #endif 2242420a4882SRobert Jarzmik 2243f7018c21STomi Valkeinen static int pxafb_probe(struct platform_device *dev) 2244f7018c21STomi Valkeinen { 2245f7018c21STomi Valkeinen struct pxafb_info *fbi; 2246f3621a60SRobert Jarzmik struct pxafb_mach_info *inf, *pdata; 2247f7018c21STomi Valkeinen struct resource *r; 2248f3621a60SRobert Jarzmik int i, irq, ret; 2249f7018c21STomi Valkeinen 2250f7018c21STomi Valkeinen dev_dbg(&dev->dev, "pxafb_probe\n"); 2251f7018c21STomi Valkeinen 2252f7018c21STomi Valkeinen ret = -ENOMEM; 2253f3621a60SRobert Jarzmik pdata = dev_get_platdata(&dev->dev); 2254f3621a60SRobert Jarzmik inf = devm_kmalloc(&dev->dev, sizeof(*inf), GFP_KERNEL); 22556f6abd36SRobert Jarzmik if (!inf) 22566f6abd36SRobert Jarzmik goto failed; 2257420a4882SRobert Jarzmik 2258f3621a60SRobert Jarzmik if (pdata) { 2259f3621a60SRobert Jarzmik *inf = *pdata; 2260f3621a60SRobert Jarzmik inf->modes = 2261f3621a60SRobert Jarzmik devm_kmalloc_array(&dev->dev, pdata->num_modes, 2262f3621a60SRobert Jarzmik sizeof(inf->modes[0]), GFP_KERNEL); 2263f3621a60SRobert Jarzmik if (!inf->modes) 2264f3621a60SRobert Jarzmik goto failed; 2265f3621a60SRobert Jarzmik for (i = 0; i < inf->num_modes; i++) 2266f3621a60SRobert Jarzmik inf->modes[i] = pdata->modes[i]; 2267f3621a60SRobert Jarzmik } 2268f7018c21STomi Valkeinen 2269f3621a60SRobert Jarzmik if (!pdata) 2270420a4882SRobert Jarzmik inf = of_pxafb_of_mach_info(&dev->dev); 2271420a4882SRobert Jarzmik if (IS_ERR_OR_NULL(inf)) 2272f3621a60SRobert Jarzmik goto failed; 2273f3621a60SRobert Jarzmik 2274f3621a60SRobert Jarzmik ret = pxafb_parse_options(&dev->dev, g_options, inf); 2275f7018c21STomi Valkeinen if (ret < 0) 2276f7018c21STomi Valkeinen goto failed; 2277f7018c21STomi Valkeinen 2278f7018c21STomi Valkeinen pxafb_check_options(&dev->dev, inf); 2279f7018c21STomi Valkeinen 2280f7018c21STomi Valkeinen dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n", 2281f7018c21STomi Valkeinen inf->modes->xres, 2282f7018c21STomi Valkeinen inf->modes->yres, 2283f7018c21STomi Valkeinen inf->modes->bpp); 2284f7018c21STomi Valkeinen if (inf->modes->xres == 0 || 2285f7018c21STomi Valkeinen inf->modes->yres == 0 || 2286f7018c21STomi Valkeinen inf->modes->bpp == 0) { 2287f7018c21STomi Valkeinen dev_err(&dev->dev, "Invalid resolution or bit depth\n"); 2288f7018c21STomi Valkeinen ret = -EINVAL; 2289f7018c21STomi Valkeinen goto failed; 2290f7018c21STomi Valkeinen } 2291f7018c21STomi Valkeinen 2292f3621a60SRobert Jarzmik fbi = pxafb_init_fbinfo(&dev->dev, inf); 2293f7018c21STomi Valkeinen if (!fbi) { 2294f7018c21STomi Valkeinen /* only reason for pxafb_init_fbinfo to fail is kmalloc */ 2295f7018c21STomi Valkeinen dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); 2296f7018c21STomi Valkeinen ret = -ENOMEM; 2297f7018c21STomi Valkeinen goto failed; 2298f7018c21STomi Valkeinen } 2299f7018c21STomi Valkeinen 2300f7018c21STomi Valkeinen if (cpu_is_pxa3xx() && inf->acceleration_enabled) 2301f7018c21STomi Valkeinen fbi->fb.fix.accel = FB_ACCEL_PXA3XX; 2302f7018c21STomi Valkeinen 2303f7018c21STomi Valkeinen fbi->backlight_power = inf->pxafb_backlight_power; 2304f7018c21STomi Valkeinen fbi->lcd_power = inf->pxafb_lcd_power; 2305f7018c21STomi Valkeinen 2306f7018c21STomi Valkeinen r = platform_get_resource(dev, IORESOURCE_MEM, 0); 2307f7018c21STomi Valkeinen if (r == NULL) { 2308f7018c21STomi Valkeinen dev_err(&dev->dev, "no I/O memory resource defined\n"); 2309f7018c21STomi Valkeinen ret = -ENODEV; 2310f7018c21STomi Valkeinen goto failed_fbi; 2311f7018c21STomi Valkeinen } 2312f7018c21STomi Valkeinen 2313f7018c21STomi Valkeinen r = request_mem_region(r->start, resource_size(r), dev->name); 2314f7018c21STomi Valkeinen if (r == NULL) { 2315f7018c21STomi Valkeinen dev_err(&dev->dev, "failed to request I/O memory\n"); 2316f7018c21STomi Valkeinen ret = -EBUSY; 2317f7018c21STomi Valkeinen goto failed_fbi; 2318f7018c21STomi Valkeinen } 2319f7018c21STomi Valkeinen 2320f7018c21STomi Valkeinen fbi->mmio_base = ioremap(r->start, resource_size(r)); 2321f7018c21STomi Valkeinen if (fbi->mmio_base == NULL) { 2322f7018c21STomi Valkeinen dev_err(&dev->dev, "failed to map I/O memory\n"); 2323f7018c21STomi Valkeinen ret = -EBUSY; 2324f7018c21STomi Valkeinen goto failed_free_res; 2325f7018c21STomi Valkeinen } 2326f7018c21STomi Valkeinen 2327f7018c21STomi Valkeinen fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); 2328f7018c21STomi Valkeinen fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size, 2329f7018c21STomi Valkeinen &fbi->dma_buff_phys, GFP_KERNEL); 2330f7018c21STomi Valkeinen if (fbi->dma_buff == NULL) { 2331f7018c21STomi Valkeinen dev_err(&dev->dev, "failed to allocate memory for DMA\n"); 2332f7018c21STomi Valkeinen ret = -ENOMEM; 2333f7018c21STomi Valkeinen goto failed_free_io; 2334f7018c21STomi Valkeinen } 2335f7018c21STomi Valkeinen 2336f7018c21STomi Valkeinen ret = pxafb_init_video_memory(fbi); 2337f7018c21STomi Valkeinen if (ret) { 2338f7018c21STomi Valkeinen dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret); 2339f7018c21STomi Valkeinen ret = -ENOMEM; 2340f7018c21STomi Valkeinen goto failed_free_dma; 2341f7018c21STomi Valkeinen } 2342f7018c21STomi Valkeinen 2343f7018c21STomi Valkeinen irq = platform_get_irq(dev, 0); 2344f7018c21STomi Valkeinen if (irq < 0) { 2345f7018c21STomi Valkeinen dev_err(&dev->dev, "no IRQ defined\n"); 2346f7018c21STomi Valkeinen ret = -ENODEV; 2347f7018c21STomi Valkeinen goto failed_free_mem; 2348f7018c21STomi Valkeinen } 2349f7018c21STomi Valkeinen 2350f7018c21STomi Valkeinen ret = request_irq(irq, pxafb_handle_irq, 0, "LCD", fbi); 2351f7018c21STomi Valkeinen if (ret) { 2352f7018c21STomi Valkeinen dev_err(&dev->dev, "request_irq failed: %d\n", ret); 2353f7018c21STomi Valkeinen ret = -EBUSY; 2354f7018c21STomi Valkeinen goto failed_free_mem; 2355f7018c21STomi Valkeinen } 2356f7018c21STomi Valkeinen 2357f7018c21STomi Valkeinen ret = pxafb_smart_init(fbi); 2358f7018c21STomi Valkeinen if (ret) { 2359f7018c21STomi Valkeinen dev_err(&dev->dev, "failed to initialize smartpanel\n"); 2360f7018c21STomi Valkeinen goto failed_free_irq; 2361f7018c21STomi Valkeinen } 2362f7018c21STomi Valkeinen 2363f7018c21STomi Valkeinen /* 2364f7018c21STomi Valkeinen * This makes sure that our colour bitfield 2365f7018c21STomi Valkeinen * descriptors are correctly initialised. 2366f7018c21STomi Valkeinen */ 2367f7018c21STomi Valkeinen ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); 2368f7018c21STomi Valkeinen if (ret) { 2369f7018c21STomi Valkeinen dev_err(&dev->dev, "failed to get suitable mode\n"); 2370f7018c21STomi Valkeinen goto failed_free_irq; 2371f7018c21STomi Valkeinen } 2372f7018c21STomi Valkeinen 2373f7018c21STomi Valkeinen ret = pxafb_set_par(&fbi->fb); 2374f7018c21STomi Valkeinen if (ret) { 2375f7018c21STomi Valkeinen dev_err(&dev->dev, "Failed to set parameters\n"); 2376f7018c21STomi Valkeinen goto failed_free_irq; 2377f7018c21STomi Valkeinen } 2378f7018c21STomi Valkeinen 2379f7018c21STomi Valkeinen platform_set_drvdata(dev, fbi); 2380f7018c21STomi Valkeinen 2381f7018c21STomi Valkeinen ret = register_framebuffer(&fbi->fb); 2382f7018c21STomi Valkeinen if (ret < 0) { 2383f7018c21STomi Valkeinen dev_err(&dev->dev, 2384f7018c21STomi Valkeinen "Failed to register framebuffer device: %d\n", ret); 2385f7018c21STomi Valkeinen goto failed_free_cmap; 2386f7018c21STomi Valkeinen } 2387f7018c21STomi Valkeinen 2388f7018c21STomi Valkeinen pxafb_overlay_init(fbi); 2389f7018c21STomi Valkeinen 2390f7018c21STomi Valkeinen #ifdef CONFIG_CPU_FREQ 2391f7018c21STomi Valkeinen fbi->freq_transition.notifier_call = pxafb_freq_transition; 2392f7018c21STomi Valkeinen fbi->freq_policy.notifier_call = pxafb_freq_policy; 2393f7018c21STomi Valkeinen cpufreq_register_notifier(&fbi->freq_transition, 2394f7018c21STomi Valkeinen CPUFREQ_TRANSITION_NOTIFIER); 2395f7018c21STomi Valkeinen cpufreq_register_notifier(&fbi->freq_policy, 2396f7018c21STomi Valkeinen CPUFREQ_POLICY_NOTIFIER); 2397f7018c21STomi Valkeinen #endif 2398f7018c21STomi Valkeinen 2399f7018c21STomi Valkeinen /* 2400f7018c21STomi Valkeinen * Ok, now enable the LCD controller 2401f7018c21STomi Valkeinen */ 2402f7018c21STomi Valkeinen set_ctrlr_state(fbi, C_ENABLE); 2403f7018c21STomi Valkeinen 2404f7018c21STomi Valkeinen return 0; 2405f7018c21STomi Valkeinen 2406f7018c21STomi Valkeinen failed_free_cmap: 2407f7018c21STomi Valkeinen if (fbi->fb.cmap.len) 2408f7018c21STomi Valkeinen fb_dealloc_cmap(&fbi->fb.cmap); 2409f7018c21STomi Valkeinen failed_free_irq: 2410f7018c21STomi Valkeinen free_irq(irq, fbi); 2411f7018c21STomi Valkeinen failed_free_mem: 2412f7018c21STomi Valkeinen free_pages_exact(fbi->video_mem, fbi->video_mem_size); 2413f7018c21STomi Valkeinen failed_free_dma: 2414f7018c21STomi Valkeinen dma_free_coherent(&dev->dev, fbi->dma_buff_size, 2415f7018c21STomi Valkeinen fbi->dma_buff, fbi->dma_buff_phys); 2416f7018c21STomi Valkeinen failed_free_io: 2417f7018c21STomi Valkeinen iounmap(fbi->mmio_base); 2418f7018c21STomi Valkeinen failed_free_res: 2419f7018c21STomi Valkeinen release_mem_region(r->start, resource_size(r)); 2420f7018c21STomi Valkeinen failed_fbi: 2421f7018c21STomi Valkeinen clk_put(fbi->clk); 2422f7018c21STomi Valkeinen kfree(fbi); 2423f7018c21STomi Valkeinen failed: 2424f7018c21STomi Valkeinen return ret; 2425f7018c21STomi Valkeinen } 2426f7018c21STomi Valkeinen 2427f7018c21STomi Valkeinen static int pxafb_remove(struct platform_device *dev) 2428f7018c21STomi Valkeinen { 2429f7018c21STomi Valkeinen struct pxafb_info *fbi = platform_get_drvdata(dev); 2430f7018c21STomi Valkeinen struct resource *r; 2431f7018c21STomi Valkeinen int irq; 2432f7018c21STomi Valkeinen struct fb_info *info; 2433f7018c21STomi Valkeinen 2434f7018c21STomi Valkeinen if (!fbi) 2435f7018c21STomi Valkeinen return 0; 2436f7018c21STomi Valkeinen 2437f7018c21STomi Valkeinen info = &fbi->fb; 2438f7018c21STomi Valkeinen 2439f7018c21STomi Valkeinen pxafb_overlay_exit(fbi); 2440f7018c21STomi Valkeinen unregister_framebuffer(info); 2441f7018c21STomi Valkeinen 2442f7018c21STomi Valkeinen pxafb_disable_controller(fbi); 2443f7018c21STomi Valkeinen 2444f7018c21STomi Valkeinen if (fbi->fb.cmap.len) 2445f7018c21STomi Valkeinen fb_dealloc_cmap(&fbi->fb.cmap); 2446f7018c21STomi Valkeinen 2447f7018c21STomi Valkeinen irq = platform_get_irq(dev, 0); 2448f7018c21STomi Valkeinen free_irq(irq, fbi); 2449f7018c21STomi Valkeinen 2450f7018c21STomi Valkeinen free_pages_exact(fbi->video_mem, fbi->video_mem_size); 2451f7018c21STomi Valkeinen 2452f6e45661SLuis R. Rodriguez dma_free_wc(&dev->dev, fbi->dma_buff_size, fbi->dma_buff, 2453f6e45661SLuis R. Rodriguez fbi->dma_buff_phys); 2454f7018c21STomi Valkeinen 2455f7018c21STomi Valkeinen iounmap(fbi->mmio_base); 2456f7018c21STomi Valkeinen 2457f7018c21STomi Valkeinen r = platform_get_resource(dev, IORESOURCE_MEM, 0); 2458f7018c21STomi Valkeinen release_mem_region(r->start, resource_size(r)); 2459f7018c21STomi Valkeinen 2460f7018c21STomi Valkeinen clk_put(fbi->clk); 2461f7018c21STomi Valkeinen kfree(fbi); 2462f7018c21STomi Valkeinen 2463f7018c21STomi Valkeinen return 0; 2464f7018c21STomi Valkeinen } 2465f7018c21STomi Valkeinen 2466420a4882SRobert Jarzmik static const struct of_device_id pxafb_of_dev_id[] = { 2467420a4882SRobert Jarzmik { .compatible = "marvell,pxa270-lcdc", }, 2468420a4882SRobert Jarzmik { .compatible = "marvell,pxa300-lcdc", }, 2469420a4882SRobert Jarzmik { .compatible = "marvell,pxa2xx-lcdc", }, 2470420a4882SRobert Jarzmik { /* sentinel */ } 2471420a4882SRobert Jarzmik }; 2472420a4882SRobert Jarzmik MODULE_DEVICE_TABLE(of, pxafb_of_dev_id); 2473420a4882SRobert Jarzmik 2474f7018c21STomi Valkeinen static struct platform_driver pxafb_driver = { 2475f7018c21STomi Valkeinen .probe = pxafb_probe, 2476f7018c21STomi Valkeinen .remove = pxafb_remove, 2477f7018c21STomi Valkeinen .driver = { 2478f7018c21STomi Valkeinen .name = "pxa2xx-fb", 2479420a4882SRobert Jarzmik .of_match_table = pxafb_of_dev_id, 2480f7018c21STomi Valkeinen #ifdef CONFIG_PM 2481f7018c21STomi Valkeinen .pm = &pxafb_pm_ops, 2482f7018c21STomi Valkeinen #endif 2483f7018c21STomi Valkeinen }, 2484f7018c21STomi Valkeinen }; 2485f7018c21STomi Valkeinen 2486f7018c21STomi Valkeinen static int __init pxafb_init(void) 2487f7018c21STomi Valkeinen { 2488f7018c21STomi Valkeinen if (pxafb_setup_options()) 2489f7018c21STomi Valkeinen return -EINVAL; 2490f7018c21STomi Valkeinen 2491f7018c21STomi Valkeinen return platform_driver_register(&pxafb_driver); 2492f7018c21STomi Valkeinen } 2493f7018c21STomi Valkeinen 2494f7018c21STomi Valkeinen static void __exit pxafb_exit(void) 2495f7018c21STomi Valkeinen { 2496f7018c21STomi Valkeinen platform_driver_unregister(&pxafb_driver); 2497f7018c21STomi Valkeinen } 2498f7018c21STomi Valkeinen 2499f7018c21STomi Valkeinen module_init(pxafb_init); 2500f7018c21STomi Valkeinen module_exit(pxafb_exit); 2501f7018c21STomi Valkeinen 2502f7018c21STomi Valkeinen MODULE_DESCRIPTION("loadable framebuffer driver for PXA"); 2503f7018c21STomi Valkeinen MODULE_LICENSE("GPL"); 2504