/* * linux/drivers/video/pxafb.c * * Copyright (C) 1999 Eric A. Thomas. * Copyright (C) 2004 Jean-Frederic Clere. * Copyright (C) 2004 Ian Campbell. * Copyright (C) 2004 Jeff Lackey. * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas * which in turn is * Based on acornfb.c Copyright (C) Russell King. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * Intel PXA250/210 LCD Controller Frame Buffer Driver * * Please direct your questions and comments on this driver to the following * email address: * * linux-arm-kernel@lists.arm.linux.org.uk * * Add support for overlay1 and overlay2 based on pxafb_overlay.c: * * Copyright (C) 2004, Intel Corporation * * 2003/08/27: <yu.tang@intel.com> * 2004/03/10: <stanley.cai@intel.com> * 2004/10/28: <yan.yin@intel.com> * * Copyright (C) 2006-2008 Marvell International Ltd. * All Rights Reserved */ #include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/cpufreq.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <linux/err.h>#include <linux/completion.h>#include <linux/mutex.h>#include <linux/kthread.h>#include <linux/freezer.h>#include <linux/console.h>#include <linux/of_graph.h>#include <linux/regulator/consumer.h>#include <linux/soc/pxa/cpu.h>#include <video/of_display_timing.h>#include <video/videomode.h>#include <linux/string_choices.h> #include <asm/io.h>#include <asm/irq.h>#include <asm/div64.h>#include <linux/platform_data/video-pxafb.h> /* * Complain if VAR is out of range. */#define DEBUG_VAR 1 #include "pxafb.h"#include "pxa3xx-regs.h" /* Bits which should not be set in machine configuration structures */#define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ LCCR3_PCD | LCCR3_BPP(0xf)) static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *);static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);static void setup_base_frame(struct pxafb_info *fbi, struct fb_var_screeninfo *var, int branch);static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, unsigned long offset, size_t size); static unsigned long video_mem_size = 0; static inline unsigned longlcd_readl(struct pxafb_info *fbi, unsigned int off){ return __raw_readl(fbi->mmio_base + off);} static inline voidlcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val){ __raw_writel(val, fbi->mmio_base + off);} static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state){ unsigned long flags; local_irq_save(flags); /* * We need to handle two requests being made at the same time. * There are two important cases: * 1. When we are changing VT (C_REENABLE) while unblanking * (C_ENABLE) We must perform the unblanking, which will * do our REENABLE for us. * 2. When we are blanking, but immediately unblank before * we have blanked. We do the "REENABLE" thing here as * well, just to be sure. */ if (fbi->task_state == C_ENABLE && state == C_REENABLE) state = (u_int) -1; if (fbi->task_state == C_DISABLE && state == C_ENABLE) state = C_REENABLE; if (state != (u_int)-1) { fbi->task_state = state; schedule_work(&fbi->task); } local_irq_restore(flags);} static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf){ chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset;} static intpxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info){ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); u_int val; if (regno >= fbi->palette_size) return 1; if (fbi->fb.var.grayscale) { fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff); return 0; } switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) { case LCCR4_PAL_FOR_0: val = ((red >> 0) & 0xf800); val |= ((green >> 5) & 0x07e0); val |= ((blue >> 11) & 0x001f); fbi->palette_cpu[regno] = val; break; case LCCR4_PAL_FOR_1: val = ((red << 8) & 0x00f80000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000f8); ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_2: val = ((red << 8) & 0x00fc0000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000fc); ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_3: val = ((red << 8) & 0x00ff0000); val |= ((green >> 0) & 0x0000ff00); val |= ((blue >> 8) & 0x000000ff); ((u32 *)(fbi->palette_cpu))[regno] = val; break; } return 0;} static intpxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info){ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); unsigned int val; int ret = 1; /* * If inverse mode was selected, invert all the colours * rather than the register number. The register number * is what you poke into the framebuffer to produce the * colour you requested. */ if (fbi->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; } /* * If greyscale is true, then we convert the RGB value * to greyscale no matter what visual we are using. */ if (fbi->fb.var.grayscale) red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; switch (fbi->fb.fix.visual) { case FB_VISUAL_TRUECOLOR: /* * 16-bit True Colour. We encode the RGB value * according to the RGB bitfield information. */ if (regno < 16) { u32 *pal = fbi->fb.pseudo_palette; val = chan_to_field(red, &fbi->fb.var.red); val |= chan_to_field(green, &fbi->fb.var.green); val |= chan_to_field(blue, &fbi->fb.var.blue); pal[regno] = val; ret = 0; } break; case FB_VISUAL_STATIC_PSEUDOCOLOR: case FB_VISUAL_PSEUDOCOLOR: ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); break; } return ret;} /* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */static inline int var_to_depth(struct fb_var_screeninfo *var){ return var->red.length + var->green.length + var->blue.length + var->transp.length;} /* calculate 4-bit BPP value for LCCR3 and OVLxC1 */static int pxafb_var_to_bpp(struct fb_var_screeninfo *var){ int bpp = -EINVAL; switch (var->bits_per_pixel) { case 1: bpp = 0; break; case 2: bpp = 1; break; case 4: bpp = 2; break; case 8: bpp = 3; break; case 16: bpp = 4; break; case 24: switch (var_to_depth(var)) { case 18: bpp = 6; break; /* 18-bits/pixel packed */ case 19: bpp = 8; break; /* 19-bits/pixel packed */ case 24: bpp = 9; break; } break; case 32: switch (var_to_depth(var)) { case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ case 25: bpp = 10; break; } break; } return bpp;} /* * pxafb_var_to_lccr3(): * Convert a bits per pixel value to the correct bit pattern for LCCR3 * * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an * implication of the acutal use of transparency bit, which we handle it * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. * * Transparency for palette pixel formats is not supported at the moment. */static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var){ int bpp = pxafb_var_to_bpp(var); uint32_t lccr3; if (bpp < 0) return 0; lccr3 = LCCR3_BPP(bpp); switch (var_to_depth(var)) { case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; case 18: lccr3 |= LCCR3_PDFOR_3; break; case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; break; case 19: case 25: lccr3 |= LCCR3_PDFOR_0; break; } return lccr3;} #define SET_PIXFMT(v, r, g, b, t) \({ \ (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ (v)->transp.length = (t) ? (t) : 0; \ (v)->blue.length = (b); (v)->blue.offset = 0; \ (v)->green.length = (g); (v)->green.offset = (b); \ (v)->red.length = (r); (v)->red.offset = (b) + (g); \}) /* set the RGBT bitfields of fb_var_screeninf according to * var->bits_per_pixel and given depth */static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth){ if (depth == 0) depth = var->bits_per_pixel; if (var->bits_per_pixel < 16) { /* indexed pixel formats */ var->red.offset = 0; var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 8; } switch (depth) { case 16: var->transp.length ? SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ case 24: var->transp.length ? SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ }} #ifdef CONFIG_CPU_FREQ/* * pxafb_display_dma_period() * Calculate the minimum period (in picoseconds) between two DMA * requests for the LCD controller. If we hit this, it means we're * doing nothing but LCD DMA. */static unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var){ /* * Period = pixclock * bits_per_byte * bytes_per_transfer * / memory_bits_per_pixel; */ return var->pixclock * 8 * 16 / var->bits_per_pixel;}#endif /* * Select the smallest mode that allows the desired resolution to be * displayed. If desired parameters can be rounded up. */static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var){ struct pxafb_mode_info *mode = NULL; struct pxafb_mode_info *modelist = mach->modes; unsigned int best_x = 0xffffffff, best_y = 0xffffffff; unsigned int i; for (i = 0; i < mach->num_modes; i++) { if (modelist[i].xres >= var->xres && modelist[i].yres >= var->yres && modelist[i].xres < best_x && modelist[i].yres < best_y && modelist[i].bpp >= var->bits_per_pixel) { best_x = modelist[i].xres; best_y = modelist[i].yres; mode = &modelist[i]; } } return mode;} static void pxafb_setmode(struct fb_var_screeninfo *var, struct pxafb_mode_info *mode){ var->xres = mode->xres; var->yres = mode->yres; var->bits_per_pixel = mode->bpp; var->pixclock = mode->pixclock; var->hsync_len = mode->hsync_len; var->left_margin = mode->left_margin; var->right_margin = mode->right_margin; var->vsync_len = mode->vsync_len; var->upper_margin = mode->upper_margin; var->lower_margin = mode->lower_margin; var->sync = mode->sync; var->grayscale = mode->cmap_greyscale; var->transp.length = mode->transparency; /* set the initial RGBA bitfields */ pxafb_set_pixfmt(var, mode->depth);} static int pxafb_adjust_timing(struct pxafb_info *fbi, struct fb_var_screeninfo *var){ int line_length; var->xres = max_t(int, var->xres, MIN_XRES); var->yres = max_t(int, var->yres, MIN_YRES); if (!(fbi->lccr0 & LCCR0_LCDT)) { var->hsync_len = clamp(var->hsync_len, 1, 64); var->vsync_len = clamp(var->vsync_len, 1, 64); var->left_margin = clamp(var->left_margin, 1, 255); var->right_margin = clamp(var->right_margin, 1, 255); var->upper_margin = clamp(var->upper_margin, 1, 255); var->lower_margin = clamp(var->lower_margin, 1, 255); } /* make sure each line is aligned on word boundary */ line_length = var->xres * var->bits_per_pixel / 8; line_length = ALIGN(line_length, 4); var->xres = line_length * 8 / var->bits_per_pixel; /* we don't support xpan, force xres_virtual to be equal to xres */ var->xres_virtual = var->xres; if (var->accel_flags & FB_ACCELF_TEXT) var->yres_virtual = fbi->fb.fix.smem_len / line_length; else var->yres_virtual = max(var->yres_virtual, var->yres); /* check for limits */ if (var->xres > MAX_XRES || var->yres > MAX_YRES) return -EINVAL; if (var->yres > var->yres_virtual) return -EINVAL; return 0;} /* * pxafb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, * if it's too big, return -EINVAL. * * Round up in the following order: bits_per_pixel, xres, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing. */static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); struct pxafb_mach_info *inf = fbi->inf; int err; if (inf->fixed_modes) { struct pxafb_mode_info *mode; mode = pxafb_getmode(inf, var); if (!mode) return -EINVAL; pxafb_setmode(var, mode); } /* do a test conversion to BPP fields to check the color formats */ err = pxafb_var_to_bpp(var); if (err < 0) return err; pxafb_set_pixfmt(var, var_to_depth(var)); err = pxafb_adjust_timing(fbi, var); if (err) return err; #ifdef CONFIG_CPU_FREQ pr_debug("pxafb: dma period = %d ps\n", pxafb_display_dma_period(var));#endif return 0;} /* * pxafb_set_par(): * Set the user defined part of the display for the specified console */static int pxafb_set_par(struct fb_info *info)Lines 1–500 of 2439. Load more
/* * linux/drivers/video/pxafb.c * * Copyright (C) 1999 Eric A. Thomas. * Copyright (C) 2004 Jean-Frederic Clere. * Copyright (C) 2004 Ian Campbell. * Copyright (C) 2004 Jeff Lackey. * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas * which in turn is * Based on acornfb.c Copyright (C) Russell King. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive for * more details. * * Intel PXA250/210 LCD Controller Frame Buffer Driver * * Please direct your questions and comments on this driver to the following * email address: * * linux-arm-kernel@lists.arm.linux.org.uk * * Add support for overlay1 and overlay2 based on pxafb_overlay.c: * * Copyright (C) 2004, Intel Corporation * * 2003/08/27: <yu.tang@intel.com> * 2004/03/10: <stanley.cai@intel.com> * 2004/10/28: <yan.yin@intel.com> * * Copyright (C) 2006-2008 Marvell International Ltd. * All Rights Reserved */ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/fb.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/ioport.h> #include <linux/cpufreq.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/completion.h> #include <linux/mutex.h> #include <linux/kthread.h> #include <linux/freezer.h> #include <linux/console.h> #include <linux/of_graph.h> #include <linux/regulator/consumer.h> #include <linux/soc/pxa/cpu.h> #include <video/of_display_timing.h> #include <video/videomode.h> #include <linux/string_choices.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/div64.h> #include <linux/platform_data/video-pxafb.h> /* * Complain if VAR is out of range. */ #define DEBUG_VAR 1 #include "pxafb.h" #include "pxa3xx-regs.h" /* Bits which should not be set in machine configuration structures */ #define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ LCCR3_PCD | LCCR3_BPP(0xf)) static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); static void setup_base_frame(struct pxafb_info *fbi, struct fb_var_screeninfo *var, int branch); static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, unsigned long offset, size_t size); static unsigned long video_mem_size = 0; static inline unsigned long lcd_readl(struct pxafb_info *fbi, unsigned int off) { return __raw_readl(fbi->mmio_base + off); } static inline void lcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val) { __raw_writel(val, fbi->mmio_base + off); } static inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) { unsigned long flags; local_irq_save(flags); /* * We need to handle two requests being made at the same time. * There are two important cases: * 1. When we are changing VT (C_REENABLE) while unblanking * (C_ENABLE) We must perform the unblanking, which will * do our REENABLE for us. * 2. When we are blanking, but immediately unblank before * we have blanked. We do the "REENABLE" thing here as * well, just to be sure. */ if (fbi->task_state == C_ENABLE && state == C_REENABLE) state = (u_int) -1; if (fbi->task_state == C_DISABLE && state == C_ENABLE) state = C_REENABLE; if (state != (u_int)-1) { fbi->task_state = state; schedule_work(&fbi->task); } local_irq_restore(flags); } static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) { chan &= 0xffff; chan >>= 16 - bf->length; return chan << bf->offset; } static int pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info) { struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); u_int val; if (regno >= fbi->palette_size) return 1; if (fbi->fb.var.grayscale) { fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff); return 0; } switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) { case LCCR4_PAL_FOR_0: val = ((red >> 0) & 0xf800); val |= ((green >> 5) & 0x07e0); val |= ((blue >> 11) & 0x001f); fbi->palette_cpu[regno] = val; break; case LCCR4_PAL_FOR_1: val = ((red << 8) & 0x00f80000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000f8); ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_2: val = ((red << 8) & 0x00fc0000); val |= ((green >> 0) & 0x0000fc00); val |= ((blue >> 8) & 0x000000fc); ((u32 *)(fbi->palette_cpu))[regno] = val; break; case LCCR4_PAL_FOR_3: val = ((red << 8) & 0x00ff0000); val |= ((green >> 0) & 0x0000ff00); val |= ((blue >> 8) & 0x000000ff); ((u32 *)(fbi->palette_cpu))[regno] = val; break; } return 0; } static int pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info) { struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); unsigned int val; int ret = 1; /* * If inverse mode was selected, invert all the colours * rather than the register number. The register number * is what you poke into the framebuffer to produce the * colour you requested. */ if (fbi->cmap_inverse) { red = 0xffff - red; green = 0xffff - green; blue = 0xffff - blue; } /* * If greyscale is true, then we convert the RGB value * to greyscale no matter what visual we are using. */ if (fbi->fb.var.grayscale) red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; switch (fbi->fb.fix.visual) { case FB_VISUAL_TRUECOLOR: /* * 16-bit True Colour. We encode the RGB value * according to the RGB bitfield information. */ if (regno < 16) { u32 *pal = fbi->fb.pseudo_palette; val = chan_to_field(red, &fbi->fb.var.red); val |= chan_to_field(green, &fbi->fb.var.green); val |= chan_to_field(blue, &fbi->fb.var.blue); pal[regno] = val; ret = 0; } break; case FB_VISUAL_STATIC_PSEUDOCOLOR: case FB_VISUAL_PSEUDOCOLOR: ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); break; } return ret; } /* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */ static inline int var_to_depth(struct fb_var_screeninfo *var) { return var->red.length + var->green.length + var->blue.length + var->transp.length; } /* calculate 4-bit BPP value for LCCR3 and OVLxC1 */ static int pxafb_var_to_bpp(struct fb_var_screeninfo *var) { int bpp = -EINVAL; switch (var->bits_per_pixel) { case 1: bpp = 0; break; case 2: bpp = 1; break; case 4: bpp = 2; break; case 8: bpp = 3; break; case 16: bpp = 4; break; case 24: switch (var_to_depth(var)) { case 18: bpp = 6; break; /* 18-bits/pixel packed */ case 19: bpp = 8; break; /* 19-bits/pixel packed */ case 24: bpp = 9; break; } break; case 32: switch (var_to_depth(var)) { case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ case 25: bpp = 10; break; } break; } return bpp; } /* * pxafb_var_to_lccr3(): * Convert a bits per pixel value to the correct bit pattern for LCCR3 * * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an * implication of the acutal use of transparency bit, which we handle it * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. * * Transparency for palette pixel formats is not supported at the moment. */ static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var) { int bpp = pxafb_var_to_bpp(var); uint32_t lccr3; if (bpp < 0) return 0; lccr3 = LCCR3_BPP(bpp); switch (var_to_depth(var)) { case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; case 18: lccr3 |= LCCR3_PDFOR_3; break; case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; break; case 19: case 25: lccr3 |= LCCR3_PDFOR_0; break; } return lccr3; } #define SET_PIXFMT(v, r, g, b, t) \ ({ \ (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ (v)->transp.length = (t) ? (t) : 0; \ (v)->blue.length = (b); (v)->blue.offset = 0; \ (v)->green.length = (g); (v)->green.offset = (b); \ (v)->red.length = (r); (v)->red.offset = (b) + (g); \ }) /* set the RGBT bitfields of fb_var_screeninf according to * var->bits_per_pixel and given depth */ static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth) { if (depth == 0) depth = var->bits_per_pixel; if (var->bits_per_pixel < 16) { /* indexed pixel formats */ var->red.offset = 0; var->red.length = 8; var->green.offset = 0; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 8; } switch (depth) { case 16: var->transp.length ? SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ case 24: var->transp.length ? SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ } } #ifdef CONFIG_CPU_FREQ /* * pxafb_display_dma_period() * Calculate the minimum period (in picoseconds) between two DMA * requests for the LCD controller. If we hit this, it means we're * doing nothing but LCD DMA. */ static unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var) { /* * Period = pixclock * bits_per_byte * bytes_per_transfer * / memory_bits_per_pixel; */ return var->pixclock * 8 * 16 / var->bits_per_pixel; } #endif /* * Select the smallest mode that allows the desired resolution to be * displayed. If desired parameters can be rounded up. */ static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var) { struct pxafb_mode_info *mode = NULL; struct pxafb_mode_info *modelist = mach->modes; unsigned int best_x = 0xffffffff, best_y = 0xffffffff; unsigned int i; for (i = 0; i < mach->num_modes; i++) { if (modelist[i].xres >= var->xres && modelist[i].yres >= var->yres && modelist[i].xres < best_x && modelist[i].yres < best_y && modelist[i].bpp >= var->bits_per_pixel) { best_x = modelist[i].xres; best_y = modelist[i].yres; mode = &modelist[i]; } } return mode; } static void pxafb_setmode(struct fb_var_screeninfo *var, struct pxafb_mode_info *mode) { var->xres = mode->xres; var->yres = mode->yres; var->bits_per_pixel = mode->bpp; var->pixclock = mode->pixclock; var->hsync_len = mode->hsync_len; var->left_margin = mode->left_margin; var->right_margin = mode->right_margin; var->vsync_len = mode->vsync_len; var->upper_margin = mode->upper_margin; var->lower_margin = mode->lower_margin; var->sync = mode->sync; var->grayscale = mode->cmap_greyscale; var->transp.length = mode->transparency; /* set the initial RGBA bitfields */ pxafb_set_pixfmt(var, mode->depth); } static int pxafb_adjust_timing(struct pxafb_info *fbi, struct fb_var_screeninfo *var) { int line_length; var->xres = max_t(int, var->xres, MIN_XRES); var->yres = max_t(int, var->yres, MIN_YRES); if (!(fbi->lccr0 & LCCR0_LCDT)) { var->hsync_len = clamp(var->hsync_len, 1, 64); var->vsync_len = clamp(var->vsync_len, 1, 64); var->left_margin = clamp(var->left_margin, 1, 255); var->right_margin = clamp(var->right_margin, 1, 255); var->upper_margin = clamp(var->upper_margin, 1, 255); var->lower_margin = clamp(var->lower_margin, 1, 255); } /* make sure each line is aligned on word boundary */ line_length = var->xres * var->bits_per_pixel / 8; line_length = ALIGN(line_length, 4); var->xres = line_length * 8 / var->bits_per_pixel; /* we don't support xpan, force xres_virtual to be equal to xres */ var->xres_virtual = var->xres; if (var->accel_flags & FB_ACCELF_TEXT) var->yres_virtual = fbi->fb.fix.smem_len / line_length; else var->yres_virtual = max(var->yres_virtual, var->yres); /* check for limits */ if (var->xres > MAX_XRES || var->yres > MAX_YRES) return -EINVAL; if (var->yres > var->yres_virtual) return -EINVAL; return 0; } /* * pxafb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, * if it's too big, return -EINVAL. * * Round up in the following order: bits_per_pixel, xres, * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, * bitfields, horizontal timing, vertical timing. */ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); struct pxafb_mach_info *inf = fbi->inf; int err; if (inf->fixed_modes) { struct pxafb_mode_info *mode; mode = pxafb_getmode(inf, var); if (!mode) return -EINVAL; pxafb_setmode(var, mode); } /* do a test conversion to BPP fields to check the color formats */ err = pxafb_var_to_bpp(var); if (err < 0) return err; pxafb_set_pixfmt(var, var_to_depth(var)); err = pxafb_adjust_timing(fbi, var); if (err) return err; #ifdef CONFIG_CPU_FREQ pr_debug("pxafb: dma period = %d ps\n", pxafb_display_dma_period(var)); #endif return 0; } /* * pxafb_set_par(): * Set the user defined part of the display for the specified console */ static int pxafb_set_par(struct fb_info *info)