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