162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/pxafb.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1999 Eric A. Thomas. 562306a36Sopenharmony_ci * Copyright (C) 2004 Jean-Frederic Clere. 662306a36Sopenharmony_ci * Copyright (C) 2004 Ian Campbell. 762306a36Sopenharmony_ci * Copyright (C) 2004 Jeff Lackey. 862306a36Sopenharmony_ci * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas 962306a36Sopenharmony_ci * which in turn is 1062306a36Sopenharmony_ci * Based on acornfb.c Copyright (C) Russell King. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 1362306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 1462306a36Sopenharmony_ci * more details. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Intel PXA250/210 LCD Controller Frame Buffer Driver 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Please direct your questions and comments on this driver to the following 1962306a36Sopenharmony_ci * email address: 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * linux-arm-kernel@lists.arm.linux.org.uk 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Add support for overlay1 and overlay2 based on pxafb_overlay.c: 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Copyright (C) 2004, Intel Corporation 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * 2003/08/27: <yu.tang@intel.com> 2862306a36Sopenharmony_ci * 2004/03/10: <stanley.cai@intel.com> 2962306a36Sopenharmony_ci * 2004/10/28: <yan.yin@intel.com> 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * Copyright (C) 2006-2008 Marvell International Ltd. 3262306a36Sopenharmony_ci * All Rights Reserved 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/moduleparam.h> 3762306a36Sopenharmony_ci#include <linux/kernel.h> 3862306a36Sopenharmony_ci#include <linux/sched.h> 3962306a36Sopenharmony_ci#include <linux/errno.h> 4062306a36Sopenharmony_ci#include <linux/string.h> 4162306a36Sopenharmony_ci#include <linux/interrupt.h> 4262306a36Sopenharmony_ci#include <linux/slab.h> 4362306a36Sopenharmony_ci#include <linux/mm.h> 4462306a36Sopenharmony_ci#include <linux/fb.h> 4562306a36Sopenharmony_ci#include <linux/delay.h> 4662306a36Sopenharmony_ci#include <linux/init.h> 4762306a36Sopenharmony_ci#include <linux/ioport.h> 4862306a36Sopenharmony_ci#include <linux/cpufreq.h> 4962306a36Sopenharmony_ci#include <linux/platform_device.h> 5062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 5162306a36Sopenharmony_ci#include <linux/clk.h> 5262306a36Sopenharmony_ci#include <linux/err.h> 5362306a36Sopenharmony_ci#include <linux/completion.h> 5462306a36Sopenharmony_ci#include <linux/mutex.h> 5562306a36Sopenharmony_ci#include <linux/kthread.h> 5662306a36Sopenharmony_ci#include <linux/freezer.h> 5762306a36Sopenharmony_ci#include <linux/console.h> 5862306a36Sopenharmony_ci#include <linux/of_graph.h> 5962306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 6062306a36Sopenharmony_ci#include <linux/soc/pxa/cpu.h> 6162306a36Sopenharmony_ci#include <video/of_display_timing.h> 6262306a36Sopenharmony_ci#include <video/videomode.h> 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#include <asm/io.h> 6562306a36Sopenharmony_ci#include <asm/irq.h> 6662306a36Sopenharmony_ci#include <asm/div64.h> 6762306a36Sopenharmony_ci#include <linux/platform_data/video-pxafb.h> 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Complain if VAR is out of range. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci#define DEBUG_VAR 1 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#include "pxafb.h" 7562306a36Sopenharmony_ci#include "pxa3xx-regs.h" 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* Bits which should not be set in machine configuration structures */ 7862306a36Sopenharmony_ci#define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ 7962306a36Sopenharmony_ci LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ 8062306a36Sopenharmony_ci LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ 8362306a36Sopenharmony_ci LCCR3_PCD | LCCR3_BPP(0xf)) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int pxafb_activate_var(struct fb_var_screeninfo *var, 8662306a36Sopenharmony_ci struct pxafb_info *); 8762306a36Sopenharmony_cistatic void set_ctrlr_state(struct pxafb_info *fbi, u_int state); 8862306a36Sopenharmony_cistatic void setup_base_frame(struct pxafb_info *fbi, 8962306a36Sopenharmony_ci struct fb_var_screeninfo *var, int branch); 9062306a36Sopenharmony_cistatic int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, 9162306a36Sopenharmony_ci unsigned long offset, size_t size); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic unsigned long video_mem_size = 0; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic inline unsigned long 9662306a36Sopenharmony_cilcd_readl(struct pxafb_info *fbi, unsigned int off) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci return __raw_readl(fbi->mmio_base + off); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic inline void 10262306a36Sopenharmony_cilcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci __raw_writel(val, fbi->mmio_base + off); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned long flags; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci local_irq_save(flags); 11262306a36Sopenharmony_ci /* 11362306a36Sopenharmony_ci * We need to handle two requests being made at the same time. 11462306a36Sopenharmony_ci * There are two important cases: 11562306a36Sopenharmony_ci * 1. When we are changing VT (C_REENABLE) while unblanking 11662306a36Sopenharmony_ci * (C_ENABLE) We must perform the unblanking, which will 11762306a36Sopenharmony_ci * do our REENABLE for us. 11862306a36Sopenharmony_ci * 2. When we are blanking, but immediately unblank before 11962306a36Sopenharmony_ci * we have blanked. We do the "REENABLE" thing here as 12062306a36Sopenharmony_ci * well, just to be sure. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci if (fbi->task_state == C_ENABLE && state == C_REENABLE) 12362306a36Sopenharmony_ci state = (u_int) -1; 12462306a36Sopenharmony_ci if (fbi->task_state == C_DISABLE && state == C_ENABLE) 12562306a36Sopenharmony_ci state = C_REENABLE; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (state != (u_int)-1) { 12862306a36Sopenharmony_ci fbi->task_state = state; 12962306a36Sopenharmony_ci schedule_work(&fbi->task); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci local_irq_restore(flags); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci chan &= 0xffff; 13762306a36Sopenharmony_ci chan >>= 16 - bf->length; 13862306a36Sopenharmony_ci return chan << bf->offset; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int 14262306a36Sopenharmony_cipxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, 14362306a36Sopenharmony_ci u_int trans, struct fb_info *info) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 14662306a36Sopenharmony_ci u_int val; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (regno >= fbi->palette_size) 14962306a36Sopenharmony_ci return 1; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (fbi->fb.var.grayscale) { 15262306a36Sopenharmony_ci fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff); 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) { 15762306a36Sopenharmony_ci case LCCR4_PAL_FOR_0: 15862306a36Sopenharmony_ci val = ((red >> 0) & 0xf800); 15962306a36Sopenharmony_ci val |= ((green >> 5) & 0x07e0); 16062306a36Sopenharmony_ci val |= ((blue >> 11) & 0x001f); 16162306a36Sopenharmony_ci fbi->palette_cpu[regno] = val; 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci case LCCR4_PAL_FOR_1: 16462306a36Sopenharmony_ci val = ((red << 8) & 0x00f80000); 16562306a36Sopenharmony_ci val |= ((green >> 0) & 0x0000fc00); 16662306a36Sopenharmony_ci val |= ((blue >> 8) & 0x000000f8); 16762306a36Sopenharmony_ci ((u32 *)(fbi->palette_cpu))[regno] = val; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case LCCR4_PAL_FOR_2: 17062306a36Sopenharmony_ci val = ((red << 8) & 0x00fc0000); 17162306a36Sopenharmony_ci val |= ((green >> 0) & 0x0000fc00); 17262306a36Sopenharmony_ci val |= ((blue >> 8) & 0x000000fc); 17362306a36Sopenharmony_ci ((u32 *)(fbi->palette_cpu))[regno] = val; 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci case LCCR4_PAL_FOR_3: 17662306a36Sopenharmony_ci val = ((red << 8) & 0x00ff0000); 17762306a36Sopenharmony_ci val |= ((green >> 0) & 0x0000ff00); 17862306a36Sopenharmony_ci val |= ((blue >> 8) & 0x000000ff); 17962306a36Sopenharmony_ci ((u32 *)(fbi->palette_cpu))[regno] = val; 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int 18762306a36Sopenharmony_cipxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 18862306a36Sopenharmony_ci u_int trans, struct fb_info *info) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 19162306a36Sopenharmony_ci unsigned int val; 19262306a36Sopenharmony_ci int ret = 1; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * If inverse mode was selected, invert all the colours 19662306a36Sopenharmony_ci * rather than the register number. The register number 19762306a36Sopenharmony_ci * is what you poke into the framebuffer to produce the 19862306a36Sopenharmony_ci * colour you requested. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if (fbi->cmap_inverse) { 20162306a36Sopenharmony_ci red = 0xffff - red; 20262306a36Sopenharmony_ci green = 0xffff - green; 20362306a36Sopenharmony_ci blue = 0xffff - blue; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * If greyscale is true, then we convert the RGB value 20862306a36Sopenharmony_ci * to greyscale no matter what visual we are using. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci if (fbi->fb.var.grayscale) 21162306a36Sopenharmony_ci red = green = blue = (19595 * red + 38470 * green + 21262306a36Sopenharmony_ci 7471 * blue) >> 16; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (fbi->fb.fix.visual) { 21562306a36Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * 16-bit True Colour. We encode the RGB value 21862306a36Sopenharmony_ci * according to the RGB bitfield information. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_ci if (regno < 16) { 22162306a36Sopenharmony_ci u32 *pal = fbi->fb.pseudo_palette; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci val = chan_to_field(red, &fbi->fb.var.red); 22462306a36Sopenharmony_ci val |= chan_to_field(green, &fbi->fb.var.green); 22562306a36Sopenharmony_ci val |= chan_to_field(blue, &fbi->fb.var.blue); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci pal[regno] = val; 22862306a36Sopenharmony_ci ret = 0; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci case FB_VISUAL_STATIC_PSEUDOCOLOR: 23362306a36Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 23462306a36Sopenharmony_ci ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return ret; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */ 24262306a36Sopenharmony_cistatic inline int var_to_depth(struct fb_var_screeninfo *var) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return var->red.length + var->green.length + 24562306a36Sopenharmony_ci var->blue.length + var->transp.length; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */ 24962306a36Sopenharmony_cistatic int pxafb_var_to_bpp(struct fb_var_screeninfo *var) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int bpp = -EINVAL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci switch (var->bits_per_pixel) { 25462306a36Sopenharmony_ci case 1: bpp = 0; break; 25562306a36Sopenharmony_ci case 2: bpp = 1; break; 25662306a36Sopenharmony_ci case 4: bpp = 2; break; 25762306a36Sopenharmony_ci case 8: bpp = 3; break; 25862306a36Sopenharmony_ci case 16: bpp = 4; break; 25962306a36Sopenharmony_ci case 24: 26062306a36Sopenharmony_ci switch (var_to_depth(var)) { 26162306a36Sopenharmony_ci case 18: bpp = 6; break; /* 18-bits/pixel packed */ 26262306a36Sopenharmony_ci case 19: bpp = 8; break; /* 19-bits/pixel packed */ 26362306a36Sopenharmony_ci case 24: bpp = 9; break; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case 32: 26762306a36Sopenharmony_ci switch (var_to_depth(var)) { 26862306a36Sopenharmony_ci case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ 26962306a36Sopenharmony_ci case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ 27062306a36Sopenharmony_ci case 25: bpp = 10; break; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci return bpp; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * pxafb_var_to_lccr3(): 27962306a36Sopenharmony_ci * Convert a bits per pixel value to the correct bit pattern for LCCR3 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an 28262306a36Sopenharmony_ci * implication of the acutal use of transparency bit, which we handle it 28362306a36Sopenharmony_ci * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel 28462306a36Sopenharmony_ci * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * Transparency for palette pixel formats is not supported at the moment. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_cistatic uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int bpp = pxafb_var_to_bpp(var); 29162306a36Sopenharmony_ci uint32_t lccr3; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (bpp < 0) 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci lccr3 = LCCR3_BPP(bpp); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci switch (var_to_depth(var)) { 29962306a36Sopenharmony_ci case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; 30062306a36Sopenharmony_ci case 18: lccr3 |= LCCR3_PDFOR_3; break; 30162306a36Sopenharmony_ci case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case 19: 30462306a36Sopenharmony_ci case 25: lccr3 |= LCCR3_PDFOR_0; break; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci return lccr3; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci#define SET_PIXFMT(v, r, g, b, t) \ 31062306a36Sopenharmony_ci({ \ 31162306a36Sopenharmony_ci (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ 31262306a36Sopenharmony_ci (v)->transp.length = (t) ? (t) : 0; \ 31362306a36Sopenharmony_ci (v)->blue.length = (b); (v)->blue.offset = 0; \ 31462306a36Sopenharmony_ci (v)->green.length = (g); (v)->green.offset = (b); \ 31562306a36Sopenharmony_ci (v)->red.length = (r); (v)->red.offset = (b) + (g); \ 31662306a36Sopenharmony_ci}) 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* set the RGBT bitfields of fb_var_screeninf according to 31962306a36Sopenharmony_ci * var->bits_per_pixel and given depth 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistatic void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci if (depth == 0) 32462306a36Sopenharmony_ci depth = var->bits_per_pixel; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (var->bits_per_pixel < 16) { 32762306a36Sopenharmony_ci /* indexed pixel formats */ 32862306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 32962306a36Sopenharmony_ci var->green.offset = 0; var->green.length = 8; 33062306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 33162306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 8; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci switch (depth) { 33562306a36Sopenharmony_ci case 16: var->transp.length ? 33662306a36Sopenharmony_ci SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ 33762306a36Sopenharmony_ci SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ 33862306a36Sopenharmony_ci case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ 33962306a36Sopenharmony_ci case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ 34062306a36Sopenharmony_ci case 24: var->transp.length ? 34162306a36Sopenharmony_ci SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ 34262306a36Sopenharmony_ci SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ 34362306a36Sopenharmony_ci case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 34862306a36Sopenharmony_ci/* 34962306a36Sopenharmony_ci * pxafb_display_dma_period() 35062306a36Sopenharmony_ci * Calculate the minimum period (in picoseconds) between two DMA 35162306a36Sopenharmony_ci * requests for the LCD controller. If we hit this, it means we're 35262306a36Sopenharmony_ci * doing nothing but LCD DMA. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * Period = pixclock * bits_per_byte * bytes_per_transfer 35862306a36Sopenharmony_ci * / memory_bits_per_pixel; 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci return var->pixclock * 8 * 16 / var->bits_per_pixel; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci#endif 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/* 36562306a36Sopenharmony_ci * Select the smallest mode that allows the desired resolution to be 36662306a36Sopenharmony_ci * displayed. If desired parameters can be rounded up. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, 36962306a36Sopenharmony_ci struct fb_var_screeninfo *var) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct pxafb_mode_info *mode = NULL; 37262306a36Sopenharmony_ci struct pxafb_mode_info *modelist = mach->modes; 37362306a36Sopenharmony_ci unsigned int best_x = 0xffffffff, best_y = 0xffffffff; 37462306a36Sopenharmony_ci unsigned int i; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci for (i = 0; i < mach->num_modes; i++) { 37762306a36Sopenharmony_ci if (modelist[i].xres >= var->xres && 37862306a36Sopenharmony_ci modelist[i].yres >= var->yres && 37962306a36Sopenharmony_ci modelist[i].xres < best_x && 38062306a36Sopenharmony_ci modelist[i].yres < best_y && 38162306a36Sopenharmony_ci modelist[i].bpp >= var->bits_per_pixel) { 38262306a36Sopenharmony_ci best_x = modelist[i].xres; 38362306a36Sopenharmony_ci best_y = modelist[i].yres; 38462306a36Sopenharmony_ci mode = &modelist[i]; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return mode; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void pxafb_setmode(struct fb_var_screeninfo *var, 39262306a36Sopenharmony_ci struct pxafb_mode_info *mode) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci var->xres = mode->xres; 39562306a36Sopenharmony_ci var->yres = mode->yres; 39662306a36Sopenharmony_ci var->bits_per_pixel = mode->bpp; 39762306a36Sopenharmony_ci var->pixclock = mode->pixclock; 39862306a36Sopenharmony_ci var->hsync_len = mode->hsync_len; 39962306a36Sopenharmony_ci var->left_margin = mode->left_margin; 40062306a36Sopenharmony_ci var->right_margin = mode->right_margin; 40162306a36Sopenharmony_ci var->vsync_len = mode->vsync_len; 40262306a36Sopenharmony_ci var->upper_margin = mode->upper_margin; 40362306a36Sopenharmony_ci var->lower_margin = mode->lower_margin; 40462306a36Sopenharmony_ci var->sync = mode->sync; 40562306a36Sopenharmony_ci var->grayscale = mode->cmap_greyscale; 40662306a36Sopenharmony_ci var->transp.length = mode->transparency; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* set the initial RGBA bitfields */ 40962306a36Sopenharmony_ci pxafb_set_pixfmt(var, mode->depth); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int pxafb_adjust_timing(struct pxafb_info *fbi, 41362306a36Sopenharmony_ci struct fb_var_screeninfo *var) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci int line_length; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci var->xres = max_t(int, var->xres, MIN_XRES); 41862306a36Sopenharmony_ci var->yres = max_t(int, var->yres, MIN_YRES); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!(fbi->lccr0 & LCCR0_LCDT)) { 42162306a36Sopenharmony_ci clamp_val(var->hsync_len, 1, 64); 42262306a36Sopenharmony_ci clamp_val(var->vsync_len, 1, 64); 42362306a36Sopenharmony_ci clamp_val(var->left_margin, 1, 255); 42462306a36Sopenharmony_ci clamp_val(var->right_margin, 1, 255); 42562306a36Sopenharmony_ci clamp_val(var->upper_margin, 1, 255); 42662306a36Sopenharmony_ci clamp_val(var->lower_margin, 1, 255); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* make sure each line is aligned on word boundary */ 43062306a36Sopenharmony_ci line_length = var->xres * var->bits_per_pixel / 8; 43162306a36Sopenharmony_ci line_length = ALIGN(line_length, 4); 43262306a36Sopenharmony_ci var->xres = line_length * 8 / var->bits_per_pixel; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* we don't support xpan, force xres_virtual to be equal to xres */ 43562306a36Sopenharmony_ci var->xres_virtual = var->xres; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (var->accel_flags & FB_ACCELF_TEXT) 43862306a36Sopenharmony_ci var->yres_virtual = fbi->fb.fix.smem_len / line_length; 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci var->yres_virtual = max(var->yres_virtual, var->yres); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* check for limits */ 44362306a36Sopenharmony_ci if (var->xres > MAX_XRES || var->yres > MAX_YRES) 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (var->yres > var->yres_virtual) 44762306a36Sopenharmony_ci return -EINVAL; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/* 45362306a36Sopenharmony_ci * pxafb_check_var(): 45462306a36Sopenharmony_ci * Get the video params out of 'var'. If a value doesn't fit, round it up, 45562306a36Sopenharmony_ci * if it's too big, return -EINVAL. 45662306a36Sopenharmony_ci * 45762306a36Sopenharmony_ci * Round up in the following order: bits_per_pixel, xres, 45862306a36Sopenharmony_ci * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, 45962306a36Sopenharmony_ci * bitfields, horizontal timing, vertical timing. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_cistatic int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 46462306a36Sopenharmony_ci struct pxafb_mach_info *inf = fbi->inf; 46562306a36Sopenharmony_ci int err; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (inf->fixed_modes) { 46862306a36Sopenharmony_ci struct pxafb_mode_info *mode; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci mode = pxafb_getmode(inf, var); 47162306a36Sopenharmony_ci if (!mode) 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci pxafb_setmode(var, mode); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* do a test conversion to BPP fields to check the color formats */ 47762306a36Sopenharmony_ci err = pxafb_var_to_bpp(var); 47862306a36Sopenharmony_ci if (err < 0) 47962306a36Sopenharmony_ci return err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci pxafb_set_pixfmt(var, var_to_depth(var)); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci err = pxafb_adjust_timing(fbi, var); 48462306a36Sopenharmony_ci if (err) 48562306a36Sopenharmony_ci return err; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 48862306a36Sopenharmony_ci pr_debug("pxafb: dma period = %d ps\n", 48962306a36Sopenharmony_ci pxafb_display_dma_period(var)); 49062306a36Sopenharmony_ci#endif 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci/* 49662306a36Sopenharmony_ci * pxafb_set_par(): 49762306a36Sopenharmony_ci * Set the user defined part of the display for the specified console 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic int pxafb_set_par(struct fb_info *info) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 50262306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (var->bits_per_pixel >= 16) 50562306a36Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; 50662306a36Sopenharmony_ci else if (!fbi->cmap_static) 50762306a36Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 50862306a36Sopenharmony_ci else { 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * Some people have weird ideas about wanting static 51162306a36Sopenharmony_ci * pseudocolor maps. I suspect their user space 51262306a36Sopenharmony_ci * applications are broken. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci fbi->fb.fix.line_length = var->xres_virtual * 51862306a36Sopenharmony_ci var->bits_per_pixel / 8; 51962306a36Sopenharmony_ci if (var->bits_per_pixel >= 16) 52062306a36Sopenharmony_ci fbi->palette_size = 0; 52162306a36Sopenharmony_ci else 52262306a36Sopenharmony_ci fbi->palette_size = var->bits_per_pixel == 1 ? 52362306a36Sopenharmony_ci 4 : 1 << var->bits_per_pixel; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0]; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (fbi->fb.var.bits_per_pixel >= 16) 52862306a36Sopenharmony_ci fb_dealloc_cmap(&fbi->fb.cmap); 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci pxafb_activate_var(var, fbi); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int pxafb_pan_display(struct fb_var_screeninfo *var, 53862306a36Sopenharmony_ci struct fb_info *info) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 54162306a36Sopenharmony_ci struct fb_var_screeninfo newvar; 54262306a36Sopenharmony_ci int dma = DMA_MAX + DMA_BASE; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (fbi->state != C_ENABLE) 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Only take .xoffset, .yoffset and .vmode & FB_VMODE_YWRAP from what 54862306a36Sopenharmony_ci * was passed in and copy the rest from the old screeninfo. 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci memcpy(&newvar, &fbi->fb.var, sizeof(newvar)); 55162306a36Sopenharmony_ci newvar.xoffset = var->xoffset; 55262306a36Sopenharmony_ci newvar.yoffset = var->yoffset; 55362306a36Sopenharmony_ci newvar.vmode &= ~FB_VMODE_YWRAP; 55462306a36Sopenharmony_ci newvar.vmode |= var->vmode & FB_VMODE_YWRAP; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci setup_base_frame(fbi, &newvar, 1); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (fbi->lccr0 & LCCR0_SDS) 55962306a36Sopenharmony_ci lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1); 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* 56662306a36Sopenharmony_ci * pxafb_blank(): 56762306a36Sopenharmony_ci * Blank the display by setting all palette values to zero. Note, the 56862306a36Sopenharmony_ci * 16 bpp mode does not really use the palette, so this will not 56962306a36Sopenharmony_ci * blank the display in all modes. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_cistatic int pxafb_blank(int blank, struct fb_info *info) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 57462306a36Sopenharmony_ci int i; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci switch (blank) { 57762306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 57862306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 57962306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 58062306a36Sopenharmony_ci case FB_BLANK_NORMAL: 58162306a36Sopenharmony_ci if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 58262306a36Sopenharmony_ci fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 58362306a36Sopenharmony_ci for (i = 0; i < fbi->palette_size; i++) 58462306a36Sopenharmony_ci pxafb_setpalettereg(i, 0, 0, 0, 0, info); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci pxafb_schedule_work(fbi, C_DISABLE); 58762306a36Sopenharmony_ci /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 59162306a36Sopenharmony_ci /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ 59262306a36Sopenharmony_ci if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 59362306a36Sopenharmony_ci fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 59462306a36Sopenharmony_ci fb_set_cmap(&fbi->fb.cmap, info); 59562306a36Sopenharmony_ci pxafb_schedule_work(fbi, C_ENABLE); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic const struct fb_ops pxafb_ops = { 60162306a36Sopenharmony_ci .owner = THIS_MODULE, 60262306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 60362306a36Sopenharmony_ci .fb_check_var = pxafb_check_var, 60462306a36Sopenharmony_ci .fb_set_par = pxafb_set_par, 60562306a36Sopenharmony_ci .fb_pan_display = pxafb_pan_display, 60662306a36Sopenharmony_ci .fb_setcolreg = pxafb_setcolreg, 60762306a36Sopenharmony_ci .fb_blank = pxafb_blank, 60862306a36Sopenharmony_ci}; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 61162306a36Sopenharmony_cistatic void overlay1fb_setup(struct pxafb_layer *ofb) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; 61462306a36Sopenharmony_ci unsigned long start = ofb->video_mem_phys; 61562306a36Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* Depending on the enable status of overlay1/2, the DMA should be 61962306a36Sopenharmony_ci * updated from FDADRx (when disabled) or FBRx (when enabled). 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic void overlay1fb_enable(struct pxafb_layer *ofb) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN; 62462306a36Sopenharmony_ci uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1); 62762306a36Sopenharmony_ci lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]); 62862306a36Sopenharmony_ci lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN); 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cistatic void overlay1fb_disable(struct pxafb_layer *ofb) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci uint32_t lccr5; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (!(lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN)) 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci lccr5 = lcd_readl(ofb->fbi, LCCR5); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1)); 64362306a36Sopenharmony_ci lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1)); 64462306a36Sopenharmony_ci lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) 64762306a36Sopenharmony_ci pr_warn("%s: timeout disabling overlay1\n", __func__); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci lcd_writel(ofb->fbi, LCCR5, lccr5); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic void overlay2fb_setup(struct pxafb_layer *ofb) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); 65562306a36Sopenharmony_ci unsigned long start[3] = { ofb->video_mem_phys, 0, 0 }; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) { 65862306a36Sopenharmony_ci size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; 65962306a36Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); 66062306a36Sopenharmony_ci } else { 66162306a36Sopenharmony_ci size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual; 66262306a36Sopenharmony_ci switch (pfor) { 66362306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break; 66462306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break; 66562306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci start[1] = start[0] + size; 66862306a36Sopenharmony_ci start[2] = start[1] + size / div; 66962306a36Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); 67062306a36Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div); 67162306a36Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void overlay2fb_enable(struct pxafb_layer *ofb) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); 67862306a36Sopenharmony_ci int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN; 67962306a36Sopenharmony_ci uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0); 68062306a36Sopenharmony_ci uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0); 68162306a36Sopenharmony_ci uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) 68462306a36Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); 68562306a36Sopenharmony_ci else { 68662306a36Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); 68762306a36Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3); 68862306a36Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4); 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]); 69162306a36Sopenharmony_ci lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN); 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic void overlay2fb_disable(struct pxafb_layer *ofb) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci uint32_t lccr5; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (!(lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN)) 69962306a36Sopenharmony_ci return; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci lccr5 = lcd_readl(ofb->fbi, LCCR5); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2)); 70662306a36Sopenharmony_ci lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2)); 70762306a36Sopenharmony_ci lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3); 70862306a36Sopenharmony_ci lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3); 70962306a36Sopenharmony_ci lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) 71262306a36Sopenharmony_ci pr_warn("%s: timeout disabling overlay2\n", __func__); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic struct pxafb_layer_ops ofb_ops[] = { 71662306a36Sopenharmony_ci [0] = { 71762306a36Sopenharmony_ci .enable = overlay1fb_enable, 71862306a36Sopenharmony_ci .disable = overlay1fb_disable, 71962306a36Sopenharmony_ci .setup = overlay1fb_setup, 72062306a36Sopenharmony_ci }, 72162306a36Sopenharmony_ci [1] = { 72262306a36Sopenharmony_ci .enable = overlay2fb_enable, 72362306a36Sopenharmony_ci .disable = overlay2fb_disable, 72462306a36Sopenharmony_ci .setup = overlay2fb_setup, 72562306a36Sopenharmony_ci }, 72662306a36Sopenharmony_ci}; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic int overlayfb_open(struct fb_info *info, int user) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* no support for framebuffer console on overlay */ 73362306a36Sopenharmony_ci if (user == 0) 73462306a36Sopenharmony_ci return -ENODEV; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (ofb->usage++ == 0) { 73762306a36Sopenharmony_ci /* unblank the base framebuffer */ 73862306a36Sopenharmony_ci console_lock(); 73962306a36Sopenharmony_ci fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); 74062306a36Sopenharmony_ci console_unlock(); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return 0; 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int overlayfb_release(struct fb_info *info, int user) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (ofb->usage == 1) { 75162306a36Sopenharmony_ci ofb->ops->disable(ofb); 75262306a36Sopenharmony_ci ofb->fb.var.height = -1; 75362306a36Sopenharmony_ci ofb->fb.var.width = -1; 75462306a36Sopenharmony_ci ofb->fb.var.xres = ofb->fb.var.xres_virtual = 0; 75562306a36Sopenharmony_ci ofb->fb.var.yres = ofb->fb.var.yres_virtual = 0; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ofb->usage--; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int overlayfb_check_var(struct fb_var_screeninfo *var, 76362306a36Sopenharmony_ci struct fb_info *info) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 76662306a36Sopenharmony_ci struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var; 76762306a36Sopenharmony_ci int xpos, ypos, pfor, bpp; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci xpos = NONSTD_TO_XPOS(var->nonstd); 77062306a36Sopenharmony_ci ypos = NONSTD_TO_YPOS(var->nonstd); 77162306a36Sopenharmony_ci pfor = NONSTD_TO_PFOR(var->nonstd); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci bpp = pxafb_var_to_bpp(var); 77462306a36Sopenharmony_ci if (bpp < 0) 77562306a36Sopenharmony_ci return -EINVAL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* no support for YUV format on overlay1 */ 77862306a36Sopenharmony_ci if (ofb->id == OVERLAY1 && pfor != 0) 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */ 78262306a36Sopenharmony_ci switch (pfor) { 78362306a36Sopenharmony_ci case OVERLAY_FORMAT_RGB: 78462306a36Sopenharmony_ci bpp = pxafb_var_to_bpp(var); 78562306a36Sopenharmony_ci if (bpp < 0) 78662306a36Sopenharmony_ci return -EINVAL; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci pxafb_set_pixfmt(var, var_to_depth(var)); 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; 79162306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break; 79262306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break; 79362306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break; 79462306a36Sopenharmony_ci default: 79562306a36Sopenharmony_ci return -EINVAL; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* each line must start at a 32-bit word boundary */ 79962306a36Sopenharmony_ci if ((xpos * bpp) % 32) 80062306a36Sopenharmony_ci return -EINVAL; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* xres must align on 32-bit word boundary */ 80362306a36Sopenharmony_ci var->xres = roundup(var->xres * bpp, 32) / bpp; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci if ((xpos + var->xres > base_var->xres) || 80662306a36Sopenharmony_ci (ypos + var->yres > base_var->yres)) 80762306a36Sopenharmony_ci return -EINVAL; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci var->xres_virtual = var->xres; 81062306a36Sopenharmony_ci var->yres_virtual = max(var->yres, var->yres_virtual); 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic int overlayfb_check_video_memory(struct pxafb_layer *ofb) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct fb_var_screeninfo *var = &ofb->fb.var; 81762306a36Sopenharmony_ci int pfor = NONSTD_TO_PFOR(var->nonstd); 81862306a36Sopenharmony_ci int size, bpp = 0; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci switch (pfor) { 82162306a36Sopenharmony_ci case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break; 82262306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; 82362306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break; 82462306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break; 82562306a36Sopenharmony_ci case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci ofb->fb.fix.line_length = var->xres_virtual * bpp / 8; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (ofb->video_mem) { 83362306a36Sopenharmony_ci if (ofb->video_mem_size >= size) 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci return -EINVAL; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int overlayfb_set_par(struct fb_info *info) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 84262306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 84362306a36Sopenharmony_ci int xpos, ypos, pfor, bpp, ret; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci ret = overlayfb_check_video_memory(ofb); 84662306a36Sopenharmony_ci if (ret) 84762306a36Sopenharmony_ci return ret; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci bpp = pxafb_var_to_bpp(var); 85062306a36Sopenharmony_ci xpos = NONSTD_TO_XPOS(var->nonstd); 85162306a36Sopenharmony_ci ypos = NONSTD_TO_YPOS(var->nonstd); 85262306a36Sopenharmony_ci pfor = NONSTD_TO_PFOR(var->nonstd); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) | 85562306a36Sopenharmony_ci OVLxC1_BPP(bpp); 85662306a36Sopenharmony_ci ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (ofb->id == OVERLAY2) 85962306a36Sopenharmony_ci ofb->control[1] |= OVL2C2_PFOR(pfor); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci ofb->ops->setup(ofb); 86262306a36Sopenharmony_ci ofb->ops->enable(ofb); 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic const struct fb_ops overlay_fb_ops = { 86762306a36Sopenharmony_ci .owner = THIS_MODULE, 86862306a36Sopenharmony_ci .fb_open = overlayfb_open, 86962306a36Sopenharmony_ci .fb_release = overlayfb_release, 87062306a36Sopenharmony_ci .fb_check_var = overlayfb_check_var, 87162306a36Sopenharmony_ci .fb_set_par = overlayfb_set_par, 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic void init_pxafb_overlay(struct pxafb_info *fbi, struct pxafb_layer *ofb, 87562306a36Sopenharmony_ci int id) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci sprintf(ofb->fb.fix.id, "overlay%d", id + 1); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS; 88062306a36Sopenharmony_ci ofb->fb.fix.xpanstep = 0; 88162306a36Sopenharmony_ci ofb->fb.fix.ypanstep = 1; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci ofb->fb.var.activate = FB_ACTIVATE_NOW; 88462306a36Sopenharmony_ci ofb->fb.var.height = -1; 88562306a36Sopenharmony_ci ofb->fb.var.width = -1; 88662306a36Sopenharmony_ci ofb->fb.var.vmode = FB_VMODE_NONINTERLACED; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ofb->fb.fbops = &overlay_fb_ops; 88962306a36Sopenharmony_ci ofb->fb.node = -1; 89062306a36Sopenharmony_ci ofb->fb.pseudo_palette = NULL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ofb->id = id; 89362306a36Sopenharmony_ci ofb->ops = &ofb_ops[id]; 89462306a36Sopenharmony_ci ofb->usage = 0; 89562306a36Sopenharmony_ci ofb->fbi = fbi; 89662306a36Sopenharmony_ci init_completion(&ofb->branch_done); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic inline int pxafb_overlay_supported(void) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci if (cpu_is_pxa27x() || cpu_is_pxa3xx()) 90262306a36Sopenharmony_ci return 1; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci return 0; 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int pxafb_overlay_map_video_memory(struct pxafb_info *pxafb, 90862306a36Sopenharmony_ci struct pxafb_layer *ofb) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci /* We assume that user will use at most video_mem_size for overlay fb, 91162306a36Sopenharmony_ci * anyway, it's useless to use 16bpp main plane and 24bpp overlay 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci ofb->video_mem = alloc_pages_exact(PAGE_ALIGN(pxafb->video_mem_size), 91462306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 91562306a36Sopenharmony_ci if (ofb->video_mem == NULL) 91662306a36Sopenharmony_ci return -ENOMEM; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ofb->video_mem_phys = virt_to_phys(ofb->video_mem); 91962306a36Sopenharmony_ci ofb->video_mem_size = PAGE_ALIGN(pxafb->video_mem_size); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci mutex_lock(&ofb->fb.mm_lock); 92262306a36Sopenharmony_ci ofb->fb.fix.smem_start = ofb->video_mem_phys; 92362306a36Sopenharmony_ci ofb->fb.fix.smem_len = pxafb->video_mem_size; 92462306a36Sopenharmony_ci mutex_unlock(&ofb->fb.mm_lock); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci ofb->fb.screen_base = ofb->video_mem; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic void pxafb_overlay_init(struct pxafb_info *fbi) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci int i, ret; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (!pxafb_overlay_supported()) 93662306a36Sopenharmony_ci return; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 93962306a36Sopenharmony_ci struct pxafb_layer *ofb = &fbi->overlay[i]; 94062306a36Sopenharmony_ci init_pxafb_overlay(fbi, ofb, i); 94162306a36Sopenharmony_ci ret = register_framebuffer(&ofb->fb); 94262306a36Sopenharmony_ci if (ret) { 94362306a36Sopenharmony_ci dev_err(fbi->dev, "failed to register overlay %d\n", i); 94462306a36Sopenharmony_ci continue; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci ret = pxafb_overlay_map_video_memory(fbi, ofb); 94762306a36Sopenharmony_ci if (ret) { 94862306a36Sopenharmony_ci dev_err(fbi->dev, 94962306a36Sopenharmony_ci "failed to map video memory for overlay %d\n", 95062306a36Sopenharmony_ci i); 95162306a36Sopenharmony_ci unregister_framebuffer(&ofb->fb); 95262306a36Sopenharmony_ci continue; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci ofb->registered = 1; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* mask all IU/BS/EOF/SOF interrupts */ 95862306a36Sopenharmony_ci lcd_writel(fbi, LCCR5, ~0); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci pr_info("PXA Overlay driver loaded successfully!\n"); 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic void pxafb_overlay_exit(struct pxafb_info *fbi) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci int i; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (!pxafb_overlay_supported()) 96862306a36Sopenharmony_ci return; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 97162306a36Sopenharmony_ci struct pxafb_layer *ofb = &fbi->overlay[i]; 97262306a36Sopenharmony_ci if (ofb->registered) { 97362306a36Sopenharmony_ci if (ofb->video_mem) 97462306a36Sopenharmony_ci free_pages_exact(ofb->video_mem, 97562306a36Sopenharmony_ci ofb->video_mem_size); 97662306a36Sopenharmony_ci unregister_framebuffer(&ofb->fb); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci#else 98162306a36Sopenharmony_cistatic inline void pxafb_overlay_init(struct pxafb_info *fbi) {} 98262306a36Sopenharmony_cistatic inline void pxafb_overlay_exit(struct pxafb_info *fbi) {} 98362306a36Sopenharmony_ci#endif /* CONFIG_FB_PXA_OVERLAY */ 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci/* 98662306a36Sopenharmony_ci * Calculate the PCD value from the clock rate (in picoseconds). 98762306a36Sopenharmony_ci * We take account of the PPCR clock setting. 98862306a36Sopenharmony_ci * From PXA Developer's Manual: 98962306a36Sopenharmony_ci * 99062306a36Sopenharmony_ci * PixelClock = LCLK 99162306a36Sopenharmony_ci * ------------- 99262306a36Sopenharmony_ci * 2 ( PCD + 1 ) 99362306a36Sopenharmony_ci * 99462306a36Sopenharmony_ci * PCD = LCLK 99562306a36Sopenharmony_ci * ------------- - 1 99662306a36Sopenharmony_ci * 2(PixelClock) 99762306a36Sopenharmony_ci * 99862306a36Sopenharmony_ci * Where: 99962306a36Sopenharmony_ci * LCLK = LCD/Memory Clock 100062306a36Sopenharmony_ci * PCD = LCCR3[7:0] 100162306a36Sopenharmony_ci * 100262306a36Sopenharmony_ci * PixelClock here is in Hz while the pixclock argument given is the 100362306a36Sopenharmony_ci * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 ) 100462306a36Sopenharmony_ci * 100562306a36Sopenharmony_ci * The function get_lclk_frequency_10khz returns LCLK in units of 100662306a36Sopenharmony_ci * 10khz. Calling the result of this function lclk gives us the 100762306a36Sopenharmony_ci * following 100862306a36Sopenharmony_ci * 100962306a36Sopenharmony_ci * PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 ) 101062306a36Sopenharmony_ci * -------------------------------------- - 1 101162306a36Sopenharmony_ci * 2 101262306a36Sopenharmony_ci * 101362306a36Sopenharmony_ci * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below. 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_cistatic inline unsigned int get_pcd(struct pxafb_info *fbi, 101662306a36Sopenharmony_ci unsigned int pixclock) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci unsigned long long pcd; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* FIXME: Need to take into account Double Pixel Clock mode 102162306a36Sopenharmony_ci * (DPC) bit? or perhaps set it based on the various clock 102262306a36Sopenharmony_ci * speeds */ 102362306a36Sopenharmony_ci pcd = (unsigned long long)(clk_get_rate(fbi->clk) / 10000); 102462306a36Sopenharmony_ci pcd *= pixclock; 102562306a36Sopenharmony_ci do_div(pcd, 100000000 * 2); 102662306a36Sopenharmony_ci /* no need for this, since we should subtract 1 anyway. they cancel */ 102762306a36Sopenharmony_ci /* pcd += 1; */ /* make up for integer math truncations */ 102862306a36Sopenharmony_ci return (unsigned int)pcd; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/* 103262306a36Sopenharmony_ci * Some touchscreens need hsync information from the video driver to 103362306a36Sopenharmony_ci * function correctly. We export it here. Note that 'hsync_time' and 103462306a36Sopenharmony_ci * the value returned from pxafb_get_hsync_time() is the *reciprocal* 103562306a36Sopenharmony_ci * of the hsync period in seconds. 103662306a36Sopenharmony_ci */ 103762306a36Sopenharmony_cistatic inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci unsigned long htime; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if ((pcd == 0) || (fbi->fb.var.hsync_len == 0)) { 104262306a36Sopenharmony_ci fbi->hsync_time = 0; 104362306a36Sopenharmony_ci return; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci htime = clk_get_rate(fbi->clk) / (pcd * fbi->fb.var.hsync_len); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci fbi->hsync_time = htime; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ciunsigned long pxafb_get_hsync_time(struct device *dev) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct pxafb_info *fbi = dev_get_drvdata(dev); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* If display is blanked/suspended, hsync isn't active */ 105662306a36Sopenharmony_ci if (!fbi || (fbi->state != C_ENABLE)) 105762306a36Sopenharmony_ci return 0; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return fbi->hsync_time; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ciEXPORT_SYMBOL(pxafb_get_hsync_time); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, 106462306a36Sopenharmony_ci unsigned long start, size_t size) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct pxafb_dma_descriptor *dma_desc, *pal_desc; 106762306a36Sopenharmony_ci unsigned int dma_desc_off, pal_desc_off; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (dma < 0 || dma >= DMA_MAX * 2) 107062306a36Sopenharmony_ci return -EINVAL; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci dma_desc = &fbi->dma_buff->dma_desc[dma]; 107362306a36Sopenharmony_ci dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci dma_desc->fsadr = start; 107662306a36Sopenharmony_ci dma_desc->fidr = 0; 107762306a36Sopenharmony_ci dma_desc->ldcmd = size; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci if (pal < 0 || pal >= PAL_MAX * 2) { 108062306a36Sopenharmony_ci dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 108162306a36Sopenharmony_ci fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; 108262306a36Sopenharmony_ci } else { 108362306a36Sopenharmony_ci pal_desc = &fbi->dma_buff->pal_desc[pal]; 108462306a36Sopenharmony_ci pal_desc_off = offsetof(struct pxafb_dma_buff, pal_desc[pal]); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE; 108762306a36Sopenharmony_ci pal_desc->fidr = 0; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) 109062306a36Sopenharmony_ci pal_desc->ldcmd = fbi->palette_size * sizeof(u16); 109162306a36Sopenharmony_ci else 109262306a36Sopenharmony_ci pal_desc->ldcmd = fbi->palette_size * sizeof(u32); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci pal_desc->ldcmd |= LDCMD_PAL; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* flip back and forth between palette and frame buffer */ 109762306a36Sopenharmony_ci pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 109862306a36Sopenharmony_ci dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off; 109962306a36Sopenharmony_ci fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic void setup_base_frame(struct pxafb_info *fbi, 110662306a36Sopenharmony_ci struct fb_var_screeninfo *var, 110762306a36Sopenharmony_ci int branch) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fb.fix; 111062306a36Sopenharmony_ci int nbytes, dma, pal, bpp = var->bits_per_pixel; 111162306a36Sopenharmony_ci unsigned long offset; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci dma = DMA_BASE + (branch ? DMA_MAX : 0); 111462306a36Sopenharmony_ci pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci nbytes = fix->line_length * var->yres; 111762306a36Sopenharmony_ci offset = fix->line_length * var->yoffset + fbi->video_mem_phys; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci if (fbi->lccr0 & LCCR0_SDS) { 112062306a36Sopenharmony_ci nbytes = nbytes / 2; 112162306a36Sopenharmony_ci setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes); 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci setup_frame_dma(fbi, dma, pal, offset, nbytes); 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 112862306a36Sopenharmony_cistatic int setup_smart_dma(struct pxafb_info *fbi) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci struct pxafb_dma_descriptor *dma_desc; 113162306a36Sopenharmony_ci unsigned long dma_desc_off, cmd_buff_off; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci dma_desc = &fbi->dma_buff->dma_desc[DMA_CMD]; 113462306a36Sopenharmony_ci dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[DMA_CMD]); 113562306a36Sopenharmony_ci cmd_buff_off = offsetof(struct pxafb_dma_buff, cmd_buff); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 113862306a36Sopenharmony_ci dma_desc->fsadr = fbi->dma_buff_phys + cmd_buff_off; 113962306a36Sopenharmony_ci dma_desc->fidr = 0; 114062306a36Sopenharmony_ci dma_desc->ldcmd = fbi->n_smart_cmds * sizeof(uint16_t); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci fbi->fdadr[DMA_CMD] = dma_desc->fdadr; 114362306a36Sopenharmony_ci return 0; 114462306a36Sopenharmony_ci} 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ciint pxafb_smart_flush(struct fb_info *info) 114762306a36Sopenharmony_ci{ 114862306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 114962306a36Sopenharmony_ci uint32_t prsr; 115062306a36Sopenharmony_ci int ret = 0; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* disable controller until all registers are set up */ 115362306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* 1. make it an even number of commands to align on 32-bit boundary 115662306a36Sopenharmony_ci * 2. add the interrupt command to the end of the chain so we can 115762306a36Sopenharmony_ci * keep track of the end of the transfer 115862306a36Sopenharmony_ci */ 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci while (fbi->n_smart_cmds & 1) 116162306a36Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_INTERRUPT; 116462306a36Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC; 116562306a36Sopenharmony_ci setup_smart_dma(fbi); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* continue to execute next command */ 116862306a36Sopenharmony_ci prsr = lcd_readl(fbi, PRSR) | PRSR_ST_OK | PRSR_CON_NT; 116962306a36Sopenharmony_ci lcd_writel(fbi, PRSR, prsr); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci /* stop the processor in case it executed "wait for sync" cmd */ 117262306a36Sopenharmony_ci lcd_writel(fbi, CMDCR, 0x0001); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci /* don't send interrupts for fifo underruns on channel 6 */ 117562306a36Sopenharmony_ci lcd_writel(fbi, LCCR5, LCCR5_IUM(6)); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci lcd_writel(fbi, LCCR1, fbi->reg_lccr1); 117862306a36Sopenharmony_ci lcd_writel(fbi, LCCR2, fbi->reg_lccr2); 117962306a36Sopenharmony_ci lcd_writel(fbi, LCCR3, fbi->reg_lccr3); 118062306a36Sopenharmony_ci lcd_writel(fbi, LCCR4, fbi->reg_lccr4); 118162306a36Sopenharmony_ci lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 118262306a36Sopenharmony_ci lcd_writel(fbi, FDADR6, fbi->fdadr[6]); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* begin sending */ 118562306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) { 118862306a36Sopenharmony_ci pr_warn("%s: timeout waiting for command done\n", __func__); 118962306a36Sopenharmony_ci ret = -ETIMEDOUT; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* quick disable */ 119362306a36Sopenharmony_ci prsr = lcd_readl(fbi, PRSR) & ~(PRSR_ST_OK | PRSR_CON_NT); 119462306a36Sopenharmony_ci lcd_writel(fbi, PRSR, prsr); 119562306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 119662306a36Sopenharmony_ci lcd_writel(fbi, FDADR6, 0); 119762306a36Sopenharmony_ci fbi->n_smart_cmds = 0; 119862306a36Sopenharmony_ci return ret; 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ciint pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci int i; 120462306a36Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci for (i = 0; i < n_cmds; i++, cmds++) { 120762306a36Sopenharmony_ci /* if it is a software delay, flush and delay */ 120862306a36Sopenharmony_ci if ((*cmds & 0xff00) == SMART_CMD_DELAY) { 120962306a36Sopenharmony_ci pxafb_smart_flush(info); 121062306a36Sopenharmony_ci mdelay(*cmds & 0xff); 121162306a36Sopenharmony_ci continue; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */ 121562306a36Sopenharmony_ci if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8) 121662306a36Sopenharmony_ci pxafb_smart_flush(info); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return 0; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic unsigned int __smart_timing(unsigned time_ns, unsigned long lcd_clk) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci unsigned int t = (time_ns * (lcd_clk / 1000000) / 1000); 122762306a36Sopenharmony_ci return (t == 0) ? 1 : t; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void setup_smart_timing(struct pxafb_info *fbi, 123162306a36Sopenharmony_ci struct fb_var_screeninfo *var) 123262306a36Sopenharmony_ci{ 123362306a36Sopenharmony_ci struct pxafb_mach_info *inf = fbi->inf; 123462306a36Sopenharmony_ci struct pxafb_mode_info *mode = &inf->modes[0]; 123562306a36Sopenharmony_ci unsigned long lclk = clk_get_rate(fbi->clk); 123662306a36Sopenharmony_ci unsigned t1, t2, t3, t4; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci t1 = max(mode->a0csrd_set_hld, mode->a0cswr_set_hld); 123962306a36Sopenharmony_ci t2 = max(mode->rd_pulse_width, mode->wr_pulse_width); 124062306a36Sopenharmony_ci t3 = mode->op_hold_time; 124162306a36Sopenharmony_ci t4 = mode->cmd_inh_time; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci fbi->reg_lccr1 = 124462306a36Sopenharmony_ci LCCR1_DisWdth(var->xres) | 124562306a36Sopenharmony_ci LCCR1_BegLnDel(__smart_timing(t1, lclk)) | 124662306a36Sopenharmony_ci LCCR1_EndLnDel(__smart_timing(t2, lclk)) | 124762306a36Sopenharmony_ci LCCR1_HorSnchWdth(__smart_timing(t3, lclk)); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci fbi->reg_lccr2 = LCCR2_DisHght(var->yres); 125062306a36Sopenharmony_ci fbi->reg_lccr3 = fbi->lccr3 | LCCR3_PixClkDiv(__smart_timing(t4, lclk)); 125162306a36Sopenharmony_ci fbi->reg_lccr3 |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HSP : 0; 125262306a36Sopenharmony_ci fbi->reg_lccr3 |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VSP : 0; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci /* FIXME: make this configurable */ 125562306a36Sopenharmony_ci fbi->reg_cmdcr = 1; 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic int pxafb_smart_thread(void *arg) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct pxafb_info *fbi = arg; 126162306a36Sopenharmony_ci struct pxafb_mach_info *inf = fbi->inf; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (!inf->smart_update) { 126462306a36Sopenharmony_ci pr_err("%s: not properly initialized, thread terminated\n", 126562306a36Sopenharmony_ci __func__); 126662306a36Sopenharmony_ci return -EINVAL; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci pr_debug("%s(): task starting\n", __func__); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci set_freezable(); 127262306a36Sopenharmony_ci while (!kthread_should_stop()) { 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci if (try_to_freeze()) 127562306a36Sopenharmony_ci continue; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci mutex_lock(&fbi->ctrlr_lock); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (fbi->state == C_ENABLE) { 128062306a36Sopenharmony_ci inf->smart_update(&fbi->fb); 128162306a36Sopenharmony_ci complete(&fbi->refresh_done); 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci mutex_unlock(&fbi->ctrlr_lock); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 128762306a36Sopenharmony_ci schedule_timeout(msecs_to_jiffies(30)); 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci pr_debug("%s(): task ending\n", __func__); 129162306a36Sopenharmony_ci return 0; 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic int pxafb_smart_init(struct pxafb_info *fbi) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci if (!(fbi->lccr0 & LCCR0_LCDT)) 129762306a36Sopenharmony_ci return 0; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff; 130062306a36Sopenharmony_ci fbi->n_smart_cmds = 0; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci init_completion(&fbi->command_done); 130362306a36Sopenharmony_ci init_completion(&fbi->refresh_done); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi, 130662306a36Sopenharmony_ci "lcd_refresh"); 130762306a36Sopenharmony_ci if (IS_ERR(fbi->smart_thread)) { 130862306a36Sopenharmony_ci pr_err("%s: unable to create kernel thread\n", __func__); 130962306a36Sopenharmony_ci return PTR_ERR(fbi->smart_thread); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci return 0; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci#else 131562306a36Sopenharmony_cistatic inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; } 131662306a36Sopenharmony_ci#endif /* CONFIG_FB_PXA_SMARTPANEL */ 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic void setup_parallel_timing(struct pxafb_info *fbi, 131962306a36Sopenharmony_ci struct fb_var_screeninfo *var) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci fbi->reg_lccr1 = 132462306a36Sopenharmony_ci LCCR1_DisWdth(var->xres) + 132562306a36Sopenharmony_ci LCCR1_HorSnchWdth(var->hsync_len) + 132662306a36Sopenharmony_ci LCCR1_BegLnDel(var->left_margin) + 132762306a36Sopenharmony_ci LCCR1_EndLnDel(var->right_margin); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci /* 133062306a36Sopenharmony_ci * If we have a dual scan LCD, we need to halve 133162306a36Sopenharmony_ci * the YRES parameter. 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ci lines_per_panel = var->yres; 133462306a36Sopenharmony_ci if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) 133562306a36Sopenharmony_ci lines_per_panel /= 2; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci fbi->reg_lccr2 = 133862306a36Sopenharmony_ci LCCR2_DisHght(lines_per_panel) + 133962306a36Sopenharmony_ci LCCR2_VrtSnchWdth(var->vsync_len) + 134062306a36Sopenharmony_ci LCCR2_BegFrmDel(var->upper_margin) + 134162306a36Sopenharmony_ci LCCR2_EndFrmDel(var->lower_margin); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci fbi->reg_lccr3 = fbi->lccr3 | 134462306a36Sopenharmony_ci (var->sync & FB_SYNC_HOR_HIGH_ACT ? 134562306a36Sopenharmony_ci LCCR3_HorSnchH : LCCR3_HorSnchL) | 134662306a36Sopenharmony_ci (var->sync & FB_SYNC_VERT_HIGH_ACT ? 134762306a36Sopenharmony_ci LCCR3_VrtSnchH : LCCR3_VrtSnchL); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (pcd) { 135062306a36Sopenharmony_ci fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd); 135162306a36Sopenharmony_ci set_hsync_time(fbi, pcd); 135262306a36Sopenharmony_ci } 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci/* 135662306a36Sopenharmony_ci * pxafb_activate_var(): 135762306a36Sopenharmony_ci * Configures LCD Controller based on entries in var parameter. 135862306a36Sopenharmony_ci * Settings are only written to the controller if changes were made. 135962306a36Sopenharmony_ci */ 136062306a36Sopenharmony_cistatic int pxafb_activate_var(struct fb_var_screeninfo *var, 136162306a36Sopenharmony_ci struct pxafb_info *fbi) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci u_long flags; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* Update shadow copy atomically */ 136662306a36Sopenharmony_ci local_irq_save(flags); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 136962306a36Sopenharmony_ci if (fbi->lccr0 & LCCR0_LCDT) 137062306a36Sopenharmony_ci setup_smart_timing(fbi, var); 137162306a36Sopenharmony_ci else 137262306a36Sopenharmony_ci#endif 137362306a36Sopenharmony_ci setup_parallel_timing(fbi, var); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci setup_base_frame(fbi, var, 0); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci fbi->reg_lccr0 = fbi->lccr0 | 137862306a36Sopenharmony_ci (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | 137962306a36Sopenharmony_ci LCCR0_QDM | LCCR0_BM | LCCR0_OUM); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci fbi->reg_lccr3 |= pxafb_var_to_lccr3(var); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; 138462306a36Sopenharmony_ci fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); 138562306a36Sopenharmony_ci local_irq_restore(flags); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* 138862306a36Sopenharmony_ci * Only update the registers if the controller is enabled 138962306a36Sopenharmony_ci * and something has changed. 139062306a36Sopenharmony_ci */ 139162306a36Sopenharmony_ci if ((lcd_readl(fbi, LCCR0) != fbi->reg_lccr0) || 139262306a36Sopenharmony_ci (lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) || 139362306a36Sopenharmony_ci (lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) || 139462306a36Sopenharmony_ci (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) || 139562306a36Sopenharmony_ci (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) || 139662306a36Sopenharmony_ci (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) || 139762306a36Sopenharmony_ci ((fbi->lccr0 & LCCR0_SDS) && 139862306a36Sopenharmony_ci (lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))) 139962306a36Sopenharmony_ci pxafb_schedule_work(fbi, C_REENABLE); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci return 0; 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci/* 140562306a36Sopenharmony_ci * NOTE! The following functions are purely helpers for set_ctrlr_state. 140662306a36Sopenharmony_ci * Do not call them directly; set_ctrlr_state does the correct serialisation 140762306a36Sopenharmony_ci * to ensure that things happen in the right way 100% of time time. 140862306a36Sopenharmony_ci * -- rmk 140962306a36Sopenharmony_ci */ 141062306a36Sopenharmony_cistatic inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff"); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (fbi->backlight_power) 141562306a36Sopenharmony_ci fbi->backlight_power(on); 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff"); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (fbi->lcd_power) 142362306a36Sopenharmony_ci fbi->lcd_power(on, &fbi->fb.var); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (fbi->lcd_supply && fbi->lcd_supply_enabled != on) { 142662306a36Sopenharmony_ci int ret; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (on) 142962306a36Sopenharmony_ci ret = regulator_enable(fbi->lcd_supply); 143062306a36Sopenharmony_ci else 143162306a36Sopenharmony_ci ret = regulator_disable(fbi->lcd_supply); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci if (ret < 0) 143462306a36Sopenharmony_ci pr_warn("Unable to %s LCD supply regulator: %d\n", 143562306a36Sopenharmony_ci on ? "enable" : "disable", ret); 143662306a36Sopenharmony_ci else 143762306a36Sopenharmony_ci fbi->lcd_supply_enabled = on; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic void pxafb_enable_controller(struct pxafb_info *fbi) 144262306a36Sopenharmony_ci{ 144362306a36Sopenharmony_ci pr_debug("pxafb: Enabling LCD controller\n"); 144462306a36Sopenharmony_ci pr_debug("fdadr0 0x%08x\n", (unsigned int) fbi->fdadr[0]); 144562306a36Sopenharmony_ci pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr[1]); 144662306a36Sopenharmony_ci pr_debug("reg_lccr0 0x%08x\n", (unsigned int) fbi->reg_lccr0); 144762306a36Sopenharmony_ci pr_debug("reg_lccr1 0x%08x\n", (unsigned int) fbi->reg_lccr1); 144862306a36Sopenharmony_ci pr_debug("reg_lccr2 0x%08x\n", (unsigned int) fbi->reg_lccr2); 144962306a36Sopenharmony_ci pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* enable LCD controller clock */ 145262306a36Sopenharmony_ci if (clk_prepare_enable(fbi->clk)) { 145362306a36Sopenharmony_ci pr_err("%s: Failed to prepare clock\n", __func__); 145462306a36Sopenharmony_ci return; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (fbi->lccr0 & LCCR0_LCDT) 145862306a36Sopenharmony_ci return; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* Sequence from 11.7.10 */ 146162306a36Sopenharmony_ci lcd_writel(fbi, LCCR4, fbi->reg_lccr4); 146262306a36Sopenharmony_ci lcd_writel(fbi, LCCR3, fbi->reg_lccr3); 146362306a36Sopenharmony_ci lcd_writel(fbi, LCCR2, fbi->reg_lccr2); 146462306a36Sopenharmony_ci lcd_writel(fbi, LCCR1, fbi->reg_lccr1); 146562306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 146862306a36Sopenharmony_ci if (fbi->lccr0 & LCCR0_SDS) 146962306a36Sopenharmony_ci lcd_writel(fbi, FDADR1, fbi->fdadr[1]); 147062306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic void pxafb_disable_controller(struct pxafb_info *fbi) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci uint32_t lccr0; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 147862306a36Sopenharmony_ci if (fbi->lccr0 & LCCR0_LCDT) { 147962306a36Sopenharmony_ci wait_for_completion_timeout(&fbi->refresh_done, 148062306a36Sopenharmony_ci msecs_to_jiffies(200)); 148162306a36Sopenharmony_ci return; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci#endif 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci /* Clear LCD Status Register */ 148662306a36Sopenharmony_ci lcd_writel(fbi, LCSR, 0xffffffff); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM; 148962306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, lccr0); 149062306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci wait_for_completion_timeout(&fbi->disable_done, msecs_to_jiffies(200)); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* disable LCD controller clock */ 149562306a36Sopenharmony_ci clk_disable_unprepare(fbi->clk); 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci/* 149962306a36Sopenharmony_ci * pxafb_handle_irq: Handle 'LCD DONE' interrupts. 150062306a36Sopenharmony_ci */ 150162306a36Sopenharmony_cistatic irqreturn_t pxafb_handle_irq(int irq, void *dev_id) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct pxafb_info *fbi = dev_id; 150462306a36Sopenharmony_ci unsigned int lccr0, lcsr; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci lcsr = lcd_readl(fbi, LCSR); 150762306a36Sopenharmony_ci if (lcsr & LCSR_LDD) { 150862306a36Sopenharmony_ci lccr0 = lcd_readl(fbi, LCCR0); 150962306a36Sopenharmony_ci lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); 151062306a36Sopenharmony_ci complete(&fbi->disable_done); 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 151462306a36Sopenharmony_ci if (lcsr & LCSR_CMD_INT) 151562306a36Sopenharmony_ci complete(&fbi->command_done); 151662306a36Sopenharmony_ci#endif 151762306a36Sopenharmony_ci lcd_writel(fbi, LCSR, lcsr); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 152062306a36Sopenharmony_ci { 152162306a36Sopenharmony_ci unsigned int lcsr1 = lcd_readl(fbi, LCSR1); 152262306a36Sopenharmony_ci if (lcsr1 & LCSR1_BS(1)) 152362306a36Sopenharmony_ci complete(&fbi->overlay[0].branch_done); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (lcsr1 & LCSR1_BS(2)) 152662306a36Sopenharmony_ci complete(&fbi->overlay[1].branch_done); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci lcd_writel(fbi, LCSR1, lcsr1); 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci#endif 153162306a36Sopenharmony_ci return IRQ_HANDLED; 153262306a36Sopenharmony_ci} 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci/* 153562306a36Sopenharmony_ci * This function must be called from task context only, since it will 153662306a36Sopenharmony_ci * sleep when disabling the LCD controller, or if we get two contending 153762306a36Sopenharmony_ci * processes trying to alter state. 153862306a36Sopenharmony_ci */ 153962306a36Sopenharmony_cistatic void set_ctrlr_state(struct pxafb_info *fbi, u_int state) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci u_int old_state; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci mutex_lock(&fbi->ctrlr_lock); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci old_state = fbi->state; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* 154862306a36Sopenharmony_ci * Hack around fbcon initialisation. 154962306a36Sopenharmony_ci */ 155062306a36Sopenharmony_ci if (old_state == C_STARTUP && state == C_REENABLE) 155162306a36Sopenharmony_ci state = C_ENABLE; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci switch (state) { 155462306a36Sopenharmony_ci case C_DISABLE_CLKCHANGE: 155562306a36Sopenharmony_ci /* 155662306a36Sopenharmony_ci * Disable controller for clock change. If the 155762306a36Sopenharmony_ci * controller is already disabled, then do nothing. 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { 156062306a36Sopenharmony_ci fbi->state = state; 156162306a36Sopenharmony_ci /* TODO __pxafb_lcd_power(fbi, 0); */ 156262306a36Sopenharmony_ci pxafb_disable_controller(fbi); 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci break; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci case C_DISABLE_PM: 156762306a36Sopenharmony_ci case C_DISABLE: 156862306a36Sopenharmony_ci /* 156962306a36Sopenharmony_ci * Disable controller 157062306a36Sopenharmony_ci */ 157162306a36Sopenharmony_ci if (old_state != C_DISABLE) { 157262306a36Sopenharmony_ci fbi->state = state; 157362306a36Sopenharmony_ci __pxafb_backlight_power(fbi, 0); 157462306a36Sopenharmony_ci __pxafb_lcd_power(fbi, 0); 157562306a36Sopenharmony_ci if (old_state != C_DISABLE_CLKCHANGE) 157662306a36Sopenharmony_ci pxafb_disable_controller(fbi); 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci break; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci case C_ENABLE_CLKCHANGE: 158162306a36Sopenharmony_ci /* 158262306a36Sopenharmony_ci * Enable the controller after clock change. Only 158362306a36Sopenharmony_ci * do this if we were disabled for the clock change. 158462306a36Sopenharmony_ci */ 158562306a36Sopenharmony_ci if (old_state == C_DISABLE_CLKCHANGE) { 158662306a36Sopenharmony_ci fbi->state = C_ENABLE; 158762306a36Sopenharmony_ci pxafb_enable_controller(fbi); 158862306a36Sopenharmony_ci /* TODO __pxafb_lcd_power(fbi, 1); */ 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci case C_REENABLE: 159362306a36Sopenharmony_ci /* 159462306a36Sopenharmony_ci * Re-enable the controller only if it was already 159562306a36Sopenharmony_ci * enabled. This is so we reprogram the control 159662306a36Sopenharmony_ci * registers. 159762306a36Sopenharmony_ci */ 159862306a36Sopenharmony_ci if (old_state == C_ENABLE) { 159962306a36Sopenharmony_ci __pxafb_lcd_power(fbi, 0); 160062306a36Sopenharmony_ci pxafb_disable_controller(fbi); 160162306a36Sopenharmony_ci pxafb_enable_controller(fbi); 160262306a36Sopenharmony_ci __pxafb_lcd_power(fbi, 1); 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci break; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci case C_ENABLE_PM: 160762306a36Sopenharmony_ci /* 160862306a36Sopenharmony_ci * Re-enable the controller after PM. This is not 160962306a36Sopenharmony_ci * perfect - think about the case where we were doing 161062306a36Sopenharmony_ci * a clock change, and we suspended half-way through. 161162306a36Sopenharmony_ci */ 161262306a36Sopenharmony_ci if (old_state != C_DISABLE_PM) 161362306a36Sopenharmony_ci break; 161462306a36Sopenharmony_ci fallthrough; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci case C_ENABLE: 161762306a36Sopenharmony_ci /* 161862306a36Sopenharmony_ci * Power up the LCD screen, enable controller, and 161962306a36Sopenharmony_ci * turn on the backlight. 162062306a36Sopenharmony_ci */ 162162306a36Sopenharmony_ci if (old_state != C_ENABLE) { 162262306a36Sopenharmony_ci fbi->state = C_ENABLE; 162362306a36Sopenharmony_ci pxafb_enable_controller(fbi); 162462306a36Sopenharmony_ci __pxafb_lcd_power(fbi, 1); 162562306a36Sopenharmony_ci __pxafb_backlight_power(fbi, 1); 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci break; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci mutex_unlock(&fbi->ctrlr_lock); 163062306a36Sopenharmony_ci} 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci/* 163362306a36Sopenharmony_ci * Our LCD controller task (which is called when we blank or unblank) 163462306a36Sopenharmony_ci * via keventd. 163562306a36Sopenharmony_ci */ 163662306a36Sopenharmony_cistatic void pxafb_task(struct work_struct *work) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci struct pxafb_info *fbi = 163962306a36Sopenharmony_ci container_of(work, struct pxafb_info, task); 164062306a36Sopenharmony_ci u_int state = xchg(&fbi->task_state, -1); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci set_ctrlr_state(fbi, state); 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 164662306a36Sopenharmony_ci/* 164762306a36Sopenharmony_ci * CPU clock speed change handler. We need to adjust the LCD timing 164862306a36Sopenharmony_ci * parameters when the CPU clock is adjusted by the power management 164962306a36Sopenharmony_ci * subsystem. 165062306a36Sopenharmony_ci * 165162306a36Sopenharmony_ci * TODO: Determine why f->new != 10*get_lclk_frequency_10khz() 165262306a36Sopenharmony_ci */ 165362306a36Sopenharmony_cistatic int 165462306a36Sopenharmony_cipxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci struct pxafb_info *fbi = TO_INF(nb, freq_transition); 165762306a36Sopenharmony_ci /* TODO struct cpufreq_freqs *f = data; */ 165862306a36Sopenharmony_ci u_int pcd; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci switch (val) { 166162306a36Sopenharmony_ci case CPUFREQ_PRECHANGE: 166262306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 166362306a36Sopenharmony_ci if (!(fbi->overlay[0].usage || fbi->overlay[1].usage)) 166462306a36Sopenharmony_ci#endif 166562306a36Sopenharmony_ci set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); 166662306a36Sopenharmony_ci break; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci case CPUFREQ_POSTCHANGE: 166962306a36Sopenharmony_ci pcd = get_pcd(fbi, fbi->fb.var.pixclock); 167062306a36Sopenharmony_ci set_hsync_time(fbi, pcd); 167162306a36Sopenharmony_ci fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | 167262306a36Sopenharmony_ci LCCR3_PixClkDiv(pcd); 167362306a36Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); 167462306a36Sopenharmony_ci break; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci return 0; 167762306a36Sopenharmony_ci} 167862306a36Sopenharmony_ci#endif 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci#ifdef CONFIG_PM 168162306a36Sopenharmony_ci/* 168262306a36Sopenharmony_ci * Power management hooks. Note that we won't be called from IRQ context, 168362306a36Sopenharmony_ci * unlike the blank functions above, so we may sleep. 168462306a36Sopenharmony_ci */ 168562306a36Sopenharmony_cistatic int pxafb_suspend(struct device *dev) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci struct pxafb_info *fbi = dev_get_drvdata(dev); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci set_ctrlr_state(fbi, C_DISABLE_PM); 169062306a36Sopenharmony_ci return 0; 169162306a36Sopenharmony_ci} 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cistatic int pxafb_resume(struct device *dev) 169462306a36Sopenharmony_ci{ 169562306a36Sopenharmony_ci struct pxafb_info *fbi = dev_get_drvdata(dev); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE_PM); 169862306a36Sopenharmony_ci return 0; 169962306a36Sopenharmony_ci} 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_cistatic const struct dev_pm_ops pxafb_pm_ops = { 170262306a36Sopenharmony_ci .suspend = pxafb_suspend, 170362306a36Sopenharmony_ci .resume = pxafb_resume, 170462306a36Sopenharmony_ci}; 170562306a36Sopenharmony_ci#endif 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_cistatic int pxafb_init_video_memory(struct pxafb_info *fbi) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci int size = PAGE_ALIGN(fbi->video_mem_size); 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); 171262306a36Sopenharmony_ci if (fbi->video_mem == NULL) 171362306a36Sopenharmony_ci return -ENOMEM; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci fbi->video_mem_phys = virt_to_phys(fbi->video_mem); 171662306a36Sopenharmony_ci fbi->video_mem_size = size; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci fbi->fb.fix.smem_start = fbi->video_mem_phys; 171962306a36Sopenharmony_ci fbi->fb.fix.smem_len = fbi->video_mem_size; 172062306a36Sopenharmony_ci fbi->fb.screen_base = fbi->video_mem; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci return fbi->video_mem ? 0 : -ENOMEM; 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic void pxafb_decode_mach_info(struct pxafb_info *fbi, 172662306a36Sopenharmony_ci struct pxafb_mach_info *inf) 172762306a36Sopenharmony_ci{ 172862306a36Sopenharmony_ci unsigned int lcd_conn = inf->lcd_conn; 172962306a36Sopenharmony_ci struct pxafb_mode_info *m; 173062306a36Sopenharmony_ci int i; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci fbi->cmap_inverse = inf->cmap_inverse; 173362306a36Sopenharmony_ci fbi->cmap_static = inf->cmap_static; 173462306a36Sopenharmony_ci fbi->lccr4 = inf->lccr4; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci switch (lcd_conn & LCD_TYPE_MASK) { 173762306a36Sopenharmony_ci case LCD_TYPE_MONO_STN: 173862306a36Sopenharmony_ci fbi->lccr0 = LCCR0_CMS; 173962306a36Sopenharmony_ci break; 174062306a36Sopenharmony_ci case LCD_TYPE_MONO_DSTN: 174162306a36Sopenharmony_ci fbi->lccr0 = LCCR0_CMS | LCCR0_SDS; 174262306a36Sopenharmony_ci break; 174362306a36Sopenharmony_ci case LCD_TYPE_COLOR_STN: 174462306a36Sopenharmony_ci fbi->lccr0 = 0; 174562306a36Sopenharmony_ci break; 174662306a36Sopenharmony_ci case LCD_TYPE_COLOR_DSTN: 174762306a36Sopenharmony_ci fbi->lccr0 = LCCR0_SDS; 174862306a36Sopenharmony_ci break; 174962306a36Sopenharmony_ci case LCD_TYPE_COLOR_TFT: 175062306a36Sopenharmony_ci fbi->lccr0 = LCCR0_PAS; 175162306a36Sopenharmony_ci break; 175262306a36Sopenharmony_ci case LCD_TYPE_SMART_PANEL: 175362306a36Sopenharmony_ci fbi->lccr0 = LCCR0_LCDT | LCCR0_PAS; 175462306a36Sopenharmony_ci break; 175562306a36Sopenharmony_ci default: 175662306a36Sopenharmony_ci /* fall back to backward compatibility way */ 175762306a36Sopenharmony_ci fbi->lccr0 = inf->lccr0; 175862306a36Sopenharmony_ci fbi->lccr3 = inf->lccr3; 175962306a36Sopenharmony_ci goto decode_mode; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (lcd_conn == LCD_MONO_STN_8BPP) 176362306a36Sopenharmony_ci fbi->lccr0 |= LCCR0_DPD; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci fbi->lccr0 |= (lcd_conn & LCD_ALTERNATE_MAPPING) ? LCCR0_LDDALT : 0; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci fbi->lccr3 = LCCR3_Acb((inf->lcd_conn >> 10) & 0xff); 176862306a36Sopenharmony_ci fbi->lccr3 |= (lcd_conn & LCD_BIAS_ACTIVE_LOW) ? LCCR3_OEP : 0; 176962306a36Sopenharmony_ci fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_cidecode_mode: 177262306a36Sopenharmony_ci pxafb_setmode(&fbi->fb.var, &inf->modes[0]); 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci /* decide video memory size as follows: 177562306a36Sopenharmony_ci * 1. default to mode of maximum resolution 177662306a36Sopenharmony_ci * 2. allow platform to override 177762306a36Sopenharmony_ci * 3. allow module parameter to override 177862306a36Sopenharmony_ci */ 177962306a36Sopenharmony_ci for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++) 178062306a36Sopenharmony_ci fbi->video_mem_size = max_t(size_t, fbi->video_mem_size, 178162306a36Sopenharmony_ci m->xres * m->yres * m->bpp / 8); 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci if (inf->video_mem_size > fbi->video_mem_size) 178462306a36Sopenharmony_ci fbi->video_mem_size = inf->video_mem_size; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci if (video_mem_size > fbi->video_mem_size) 178762306a36Sopenharmony_ci fbi->video_mem_size = video_mem_size; 178862306a36Sopenharmony_ci} 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_cistatic struct pxafb_info *pxafb_init_fbinfo(struct device *dev, 179162306a36Sopenharmony_ci struct pxafb_mach_info *inf) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci struct pxafb_info *fbi; 179462306a36Sopenharmony_ci void *addr; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci /* Alloc the pxafb_info and pseudo_palette in one step */ 179762306a36Sopenharmony_ci fbi = devm_kzalloc(dev, sizeof(struct pxafb_info) + sizeof(u32) * 16, 179862306a36Sopenharmony_ci GFP_KERNEL); 179962306a36Sopenharmony_ci if (!fbi) 180062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci fbi->dev = dev; 180362306a36Sopenharmony_ci fbi->inf = inf; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci fbi->clk = devm_clk_get(dev, NULL); 180662306a36Sopenharmony_ci if (IS_ERR(fbi->clk)) 180762306a36Sopenharmony_ci return ERR_CAST(fbi->clk); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci strcpy(fbi->fb.fix.id, PXA_NAME); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 181262306a36Sopenharmony_ci fbi->fb.fix.type_aux = 0; 181362306a36Sopenharmony_ci fbi->fb.fix.xpanstep = 0; 181462306a36Sopenharmony_ci fbi->fb.fix.ypanstep = 1; 181562306a36Sopenharmony_ci fbi->fb.fix.ywrapstep = 0; 181662306a36Sopenharmony_ci fbi->fb.fix.accel = FB_ACCEL_NONE; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci fbi->fb.var.nonstd = 0; 181962306a36Sopenharmony_ci fbi->fb.var.activate = FB_ACTIVATE_NOW; 182062306a36Sopenharmony_ci fbi->fb.var.height = -1; 182162306a36Sopenharmony_ci fbi->fb.var.width = -1; 182262306a36Sopenharmony_ci fbi->fb.var.accel_flags = FB_ACCELF_TEXT; 182362306a36Sopenharmony_ci fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci fbi->fb.fbops = &pxafb_ops; 182662306a36Sopenharmony_ci fbi->fb.node = -1; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci addr = fbi; 182962306a36Sopenharmony_ci addr = addr + sizeof(struct pxafb_info); 183062306a36Sopenharmony_ci fbi->fb.pseudo_palette = addr; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci fbi->state = C_STARTUP; 183362306a36Sopenharmony_ci fbi->task_state = (u_char)-1; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci pxafb_decode_mach_info(fbi, inf); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 183862306a36Sopenharmony_ci /* place overlay(s) on top of base */ 183962306a36Sopenharmony_ci if (pxafb_overlay_supported()) 184062306a36Sopenharmony_ci fbi->lccr0 |= LCCR0_OUC; 184162306a36Sopenharmony_ci#endif 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci init_waitqueue_head(&fbi->ctrlr_wait); 184462306a36Sopenharmony_ci INIT_WORK(&fbi->task, pxafb_task); 184562306a36Sopenharmony_ci mutex_init(&fbi->ctrlr_lock); 184662306a36Sopenharmony_ci init_completion(&fbi->disable_done); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci return fbi; 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci#ifdef CONFIG_FB_PXA_PARAMETERS 185262306a36Sopenharmony_cistatic int parse_opt_mode(struct device *dev, const char *this_opt, 185362306a36Sopenharmony_ci struct pxafb_mach_info *inf) 185462306a36Sopenharmony_ci{ 185562306a36Sopenharmony_ci const char *name = this_opt+5; 185662306a36Sopenharmony_ci unsigned int namelen = strlen(name); 185762306a36Sopenharmony_ci int res_specified = 0, bpp_specified = 0; 185862306a36Sopenharmony_ci unsigned int xres = 0, yres = 0, bpp = 0; 185962306a36Sopenharmony_ci int yres_specified = 0; 186062306a36Sopenharmony_ci int i; 186162306a36Sopenharmony_ci for (i = namelen-1; i >= 0; i--) { 186262306a36Sopenharmony_ci switch (name[i]) { 186362306a36Sopenharmony_ci case '-': 186462306a36Sopenharmony_ci namelen = i; 186562306a36Sopenharmony_ci if (!bpp_specified && !yres_specified) { 186662306a36Sopenharmony_ci bpp = simple_strtoul(&name[i+1], NULL, 0); 186762306a36Sopenharmony_ci bpp_specified = 1; 186862306a36Sopenharmony_ci } else 186962306a36Sopenharmony_ci goto done; 187062306a36Sopenharmony_ci break; 187162306a36Sopenharmony_ci case 'x': 187262306a36Sopenharmony_ci if (!yres_specified) { 187362306a36Sopenharmony_ci yres = simple_strtoul(&name[i+1], NULL, 0); 187462306a36Sopenharmony_ci yres_specified = 1; 187562306a36Sopenharmony_ci } else 187662306a36Sopenharmony_ci goto done; 187762306a36Sopenharmony_ci break; 187862306a36Sopenharmony_ci case '0' ... '9': 187962306a36Sopenharmony_ci break; 188062306a36Sopenharmony_ci default: 188162306a36Sopenharmony_ci goto done; 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci if (i < 0 && yres_specified) { 188562306a36Sopenharmony_ci xres = simple_strtoul(name, NULL, 0); 188662306a36Sopenharmony_ci res_specified = 1; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_cidone: 188962306a36Sopenharmony_ci if (res_specified) { 189062306a36Sopenharmony_ci dev_info(dev, "overriding resolution: %dx%d\n", xres, yres); 189162306a36Sopenharmony_ci inf->modes[0].xres = xres; inf->modes[0].yres = yres; 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci if (bpp_specified) 189462306a36Sopenharmony_ci switch (bpp) { 189562306a36Sopenharmony_ci case 1: 189662306a36Sopenharmony_ci case 2: 189762306a36Sopenharmony_ci case 4: 189862306a36Sopenharmony_ci case 8: 189962306a36Sopenharmony_ci case 16: 190062306a36Sopenharmony_ci inf->modes[0].bpp = bpp; 190162306a36Sopenharmony_ci dev_info(dev, "overriding bit depth: %d\n", bpp); 190262306a36Sopenharmony_ci break; 190362306a36Sopenharmony_ci default: 190462306a36Sopenharmony_ci dev_err(dev, "Depth %d is not valid\n", bpp); 190562306a36Sopenharmony_ci return -EINVAL; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci return 0; 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_cistatic int parse_opt(struct device *dev, char *this_opt, 191162306a36Sopenharmony_ci struct pxafb_mach_info *inf) 191262306a36Sopenharmony_ci{ 191362306a36Sopenharmony_ci struct pxafb_mode_info *mode = &inf->modes[0]; 191462306a36Sopenharmony_ci char s[64]; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci s[0] = '\0'; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (!strncmp(this_opt, "vmem:", 5)) { 191962306a36Sopenharmony_ci video_mem_size = memparse(this_opt + 5, NULL); 192062306a36Sopenharmony_ci } else if (!strncmp(this_opt, "mode:", 5)) { 192162306a36Sopenharmony_ci return parse_opt_mode(dev, this_opt, inf); 192262306a36Sopenharmony_ci } else if (!strncmp(this_opt, "pixclock:", 9)) { 192362306a36Sopenharmony_ci mode->pixclock = simple_strtoul(this_opt+9, NULL, 0); 192462306a36Sopenharmony_ci sprintf(s, "pixclock: %ld\n", mode->pixclock); 192562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "left:", 5)) { 192662306a36Sopenharmony_ci mode->left_margin = simple_strtoul(this_opt+5, NULL, 0); 192762306a36Sopenharmony_ci sprintf(s, "left: %u\n", mode->left_margin); 192862306a36Sopenharmony_ci } else if (!strncmp(this_opt, "right:", 6)) { 192962306a36Sopenharmony_ci mode->right_margin = simple_strtoul(this_opt+6, NULL, 0); 193062306a36Sopenharmony_ci sprintf(s, "right: %u\n", mode->right_margin); 193162306a36Sopenharmony_ci } else if (!strncmp(this_opt, "upper:", 6)) { 193262306a36Sopenharmony_ci mode->upper_margin = simple_strtoul(this_opt+6, NULL, 0); 193362306a36Sopenharmony_ci sprintf(s, "upper: %u\n", mode->upper_margin); 193462306a36Sopenharmony_ci } else if (!strncmp(this_opt, "lower:", 6)) { 193562306a36Sopenharmony_ci mode->lower_margin = simple_strtoul(this_opt+6, NULL, 0); 193662306a36Sopenharmony_ci sprintf(s, "lower: %u\n", mode->lower_margin); 193762306a36Sopenharmony_ci } else if (!strncmp(this_opt, "hsynclen:", 9)) { 193862306a36Sopenharmony_ci mode->hsync_len = simple_strtoul(this_opt+9, NULL, 0); 193962306a36Sopenharmony_ci sprintf(s, "hsynclen: %u\n", mode->hsync_len); 194062306a36Sopenharmony_ci } else if (!strncmp(this_opt, "vsynclen:", 9)) { 194162306a36Sopenharmony_ci mode->vsync_len = simple_strtoul(this_opt+9, NULL, 0); 194262306a36Sopenharmony_ci sprintf(s, "vsynclen: %u\n", mode->vsync_len); 194362306a36Sopenharmony_ci } else if (!strncmp(this_opt, "hsync:", 6)) { 194462306a36Sopenharmony_ci if (simple_strtoul(this_opt+6, NULL, 0) == 0) { 194562306a36Sopenharmony_ci sprintf(s, "hsync: Active Low\n"); 194662306a36Sopenharmony_ci mode->sync &= ~FB_SYNC_HOR_HIGH_ACT; 194762306a36Sopenharmony_ci } else { 194862306a36Sopenharmony_ci sprintf(s, "hsync: Active High\n"); 194962306a36Sopenharmony_ci mode->sync |= FB_SYNC_HOR_HIGH_ACT; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci } else if (!strncmp(this_opt, "vsync:", 6)) { 195262306a36Sopenharmony_ci if (simple_strtoul(this_opt+6, NULL, 0) == 0) { 195362306a36Sopenharmony_ci sprintf(s, "vsync: Active Low\n"); 195462306a36Sopenharmony_ci mode->sync &= ~FB_SYNC_VERT_HIGH_ACT; 195562306a36Sopenharmony_ci } else { 195662306a36Sopenharmony_ci sprintf(s, "vsync: Active High\n"); 195762306a36Sopenharmony_ci mode->sync |= FB_SYNC_VERT_HIGH_ACT; 195862306a36Sopenharmony_ci } 195962306a36Sopenharmony_ci } else if (!strncmp(this_opt, "dpc:", 4)) { 196062306a36Sopenharmony_ci if (simple_strtoul(this_opt+4, NULL, 0) == 0) { 196162306a36Sopenharmony_ci sprintf(s, "double pixel clock: false\n"); 196262306a36Sopenharmony_ci inf->lccr3 &= ~LCCR3_DPC; 196362306a36Sopenharmony_ci } else { 196462306a36Sopenharmony_ci sprintf(s, "double pixel clock: true\n"); 196562306a36Sopenharmony_ci inf->lccr3 |= LCCR3_DPC; 196662306a36Sopenharmony_ci } 196762306a36Sopenharmony_ci } else if (!strncmp(this_opt, "outputen:", 9)) { 196862306a36Sopenharmony_ci if (simple_strtoul(this_opt+9, NULL, 0) == 0) { 196962306a36Sopenharmony_ci sprintf(s, "output enable: active low\n"); 197062306a36Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL; 197162306a36Sopenharmony_ci } else { 197262306a36Sopenharmony_ci sprintf(s, "output enable: active high\n"); 197362306a36Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "pixclockpol:", 12)) { 197662306a36Sopenharmony_ci if (simple_strtoul(this_opt+12, NULL, 0) == 0) { 197762306a36Sopenharmony_ci sprintf(s, "pixel clock polarity: falling edge\n"); 197862306a36Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg; 197962306a36Sopenharmony_ci } else { 198062306a36Sopenharmony_ci sprintf(s, "pixel clock polarity: rising edge\n"); 198162306a36Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg; 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci } else if (!strncmp(this_opt, "color", 5)) { 198462306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color; 198562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "mono", 4)) { 198662306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Mono; 198762306a36Sopenharmony_ci } else if (!strncmp(this_opt, "active", 6)) { 198862306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Act; 198962306a36Sopenharmony_ci } else if (!strncmp(this_opt, "passive", 7)) { 199062306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Pas; 199162306a36Sopenharmony_ci } else if (!strncmp(this_opt, "single", 6)) { 199262306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Sngl; 199362306a36Sopenharmony_ci } else if (!strncmp(this_opt, "dual", 4)) { 199462306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Dual; 199562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "4pix", 4)) { 199662306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_4PixMono; 199762306a36Sopenharmony_ci } else if (!strncmp(this_opt, "8pix", 4)) { 199862306a36Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_8PixMono; 199962306a36Sopenharmony_ci } else { 200062306a36Sopenharmony_ci dev_err(dev, "unknown option: %s\n", this_opt); 200162306a36Sopenharmony_ci return -EINVAL; 200262306a36Sopenharmony_ci } 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci if (s[0] != '\0') 200562306a36Sopenharmony_ci dev_info(dev, "override %s", s); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci return 0; 200862306a36Sopenharmony_ci} 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_cistatic int pxafb_parse_options(struct device *dev, char *options, 201162306a36Sopenharmony_ci struct pxafb_mach_info *inf) 201262306a36Sopenharmony_ci{ 201362306a36Sopenharmony_ci char *this_opt; 201462306a36Sopenharmony_ci int ret; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (!options || !*options) 201762306a36Sopenharmony_ci return 0; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci dev_dbg(dev, "options are \"%s\"\n", options ? options : "null"); 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci /* could be made table driven or similar?... */ 202262306a36Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 202362306a36Sopenharmony_ci ret = parse_opt(dev, this_opt, inf); 202462306a36Sopenharmony_ci if (ret) 202562306a36Sopenharmony_ci return ret; 202662306a36Sopenharmony_ci } 202762306a36Sopenharmony_ci return 0; 202862306a36Sopenharmony_ci} 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_cistatic char g_options[256] = ""; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci#ifndef MODULE 203362306a36Sopenharmony_cistatic int __init pxafb_setup_options(void) 203462306a36Sopenharmony_ci{ 203562306a36Sopenharmony_ci char *options = NULL; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci if (fb_get_options("pxafb", &options)) 203862306a36Sopenharmony_ci return -ENODEV; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci if (options) 204162306a36Sopenharmony_ci strscpy(g_options, options, sizeof(g_options)); 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci return 0; 204462306a36Sopenharmony_ci} 204562306a36Sopenharmony_ci#else 204662306a36Sopenharmony_ci#define pxafb_setup_options() (0) 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_cimodule_param_string(options, g_options, sizeof(g_options), 0); 204962306a36Sopenharmony_ciMODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.rst)"); 205062306a36Sopenharmony_ci#endif 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci#else 205362306a36Sopenharmony_ci#define pxafb_parse_options(...) (0) 205462306a36Sopenharmony_ci#define pxafb_setup_options() (0) 205562306a36Sopenharmony_ci#endif 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci#ifdef DEBUG_VAR 205862306a36Sopenharmony_ci/* Check for various illegal bit-combinations. Currently only 205962306a36Sopenharmony_ci * a warning is given. */ 206062306a36Sopenharmony_cistatic void pxafb_check_options(struct device *dev, struct pxafb_mach_info *inf) 206162306a36Sopenharmony_ci{ 206262306a36Sopenharmony_ci if (inf->lcd_conn) 206362306a36Sopenharmony_ci return; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK) 206662306a36Sopenharmony_ci dev_warn(dev, "machine LCCR0 setting contains " 206762306a36Sopenharmony_ci "illegal bits: %08x\n", 206862306a36Sopenharmony_ci inf->lccr0 & LCCR0_INVALID_CONFIG_MASK); 206962306a36Sopenharmony_ci if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK) 207062306a36Sopenharmony_ci dev_warn(dev, "machine LCCR3 setting contains " 207162306a36Sopenharmony_ci "illegal bits: %08x\n", 207262306a36Sopenharmony_ci inf->lccr3 & LCCR3_INVALID_CONFIG_MASK); 207362306a36Sopenharmony_ci if (inf->lccr0 & LCCR0_DPD && 207462306a36Sopenharmony_ci ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas || 207562306a36Sopenharmony_ci (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl || 207662306a36Sopenharmony_ci (inf->lccr0 & LCCR0_CMS) != LCCR0_Mono)) 207762306a36Sopenharmony_ci dev_warn(dev, "Double Pixel Data (DPD) mode is " 207862306a36Sopenharmony_ci "only valid in passive mono" 207962306a36Sopenharmony_ci " single panel mode\n"); 208062306a36Sopenharmony_ci if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act && 208162306a36Sopenharmony_ci (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) 208262306a36Sopenharmony_ci dev_warn(dev, "Dual panel only valid in passive mode\n"); 208362306a36Sopenharmony_ci if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && 208462306a36Sopenharmony_ci (inf->modes->upper_margin || inf->modes->lower_margin)) 208562306a36Sopenharmony_ci dev_warn(dev, "Upper and lower margins must be 0 in " 208662306a36Sopenharmony_ci "passive mode\n"); 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci#else 208962306a36Sopenharmony_ci#define pxafb_check_options(...) do {} while (0) 209062306a36Sopenharmony_ci#endif 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci#if defined(CONFIG_OF) 209362306a36Sopenharmony_cistatic const char * const lcd_types[] = { 209462306a36Sopenharmony_ci "unknown", "mono-stn", "mono-dstn", "color-stn", "color-dstn", 209562306a36Sopenharmony_ci "color-tft", "smart-panel", NULL 209662306a36Sopenharmony_ci}; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_cistatic int of_get_pxafb_display(struct device *dev, struct device_node *disp, 209962306a36Sopenharmony_ci struct pxafb_mach_info *info, u32 bus_width) 210062306a36Sopenharmony_ci{ 210162306a36Sopenharmony_ci struct display_timings *timings; 210262306a36Sopenharmony_ci struct videomode vm; 210362306a36Sopenharmony_ci int i, ret = -EINVAL; 210462306a36Sopenharmony_ci const char *s; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci ret = of_property_read_string(disp, "lcd-type", &s); 210762306a36Sopenharmony_ci if (ret) 210862306a36Sopenharmony_ci s = "color-tft"; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci i = match_string(lcd_types, -1, s); 211162306a36Sopenharmony_ci if (i < 0) { 211262306a36Sopenharmony_ci dev_err(dev, "lcd-type %s is unknown\n", s); 211362306a36Sopenharmony_ci return i; 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci info->lcd_conn |= LCD_CONN_TYPE(i); 211662306a36Sopenharmony_ci info->lcd_conn |= LCD_CONN_WIDTH(bus_width); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci timings = of_get_display_timings(disp); 211962306a36Sopenharmony_ci if (!timings) 212062306a36Sopenharmony_ci return -EINVAL; 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci ret = -ENOMEM; 212362306a36Sopenharmony_ci info->modes = devm_kcalloc(dev, timings->num_timings, 212462306a36Sopenharmony_ci sizeof(info->modes[0]), 212562306a36Sopenharmony_ci GFP_KERNEL); 212662306a36Sopenharmony_ci if (!info->modes) 212762306a36Sopenharmony_ci goto out; 212862306a36Sopenharmony_ci info->num_modes = timings->num_timings; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci for (i = 0; i < timings->num_timings; i++) { 213162306a36Sopenharmony_ci ret = videomode_from_timings(timings, &vm, i); 213262306a36Sopenharmony_ci if (ret) { 213362306a36Sopenharmony_ci dev_err(dev, "videomode_from_timings %d failed: %d\n", 213462306a36Sopenharmony_ci i, ret); 213562306a36Sopenharmony_ci goto out; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) 213862306a36Sopenharmony_ci info->lcd_conn |= LCD_PCLK_EDGE_RISE; 213962306a36Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) 214062306a36Sopenharmony_ci info->lcd_conn |= LCD_PCLK_EDGE_FALL; 214162306a36Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_DE_HIGH) 214262306a36Sopenharmony_ci info->lcd_conn |= LCD_BIAS_ACTIVE_HIGH; 214362306a36Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_DE_LOW) 214462306a36Sopenharmony_ci info->lcd_conn |= LCD_BIAS_ACTIVE_LOW; 214562306a36Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH) 214662306a36Sopenharmony_ci info->modes[i].sync |= FB_SYNC_HOR_HIGH_ACT; 214762306a36Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) 214862306a36Sopenharmony_ci info->modes[i].sync |= FB_SYNC_VERT_HIGH_ACT; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci info->modes[i].pixclock = 1000000000UL / (vm.pixelclock / 1000); 215162306a36Sopenharmony_ci info->modes[i].xres = vm.hactive; 215262306a36Sopenharmony_ci info->modes[i].yres = vm.vactive; 215362306a36Sopenharmony_ci info->modes[i].hsync_len = vm.hsync_len; 215462306a36Sopenharmony_ci info->modes[i].left_margin = vm.hback_porch; 215562306a36Sopenharmony_ci info->modes[i].right_margin = vm.hfront_porch; 215662306a36Sopenharmony_ci info->modes[i].vsync_len = vm.vsync_len; 215762306a36Sopenharmony_ci info->modes[i].upper_margin = vm.vback_porch; 215862306a36Sopenharmony_ci info->modes[i].lower_margin = vm.vfront_porch; 215962306a36Sopenharmony_ci } 216062306a36Sopenharmony_ci ret = 0; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ciout: 216362306a36Sopenharmony_ci display_timings_release(timings); 216462306a36Sopenharmony_ci return ret; 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_cistatic int of_get_pxafb_mode_info(struct device *dev, 216862306a36Sopenharmony_ci struct pxafb_mach_info *info) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct device_node *display, *np; 217162306a36Sopenharmony_ci u32 bus_width; 217262306a36Sopenharmony_ci int ret, i; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci np = of_graph_get_next_endpoint(dev->of_node, NULL); 217562306a36Sopenharmony_ci if (!np) { 217662306a36Sopenharmony_ci dev_err(dev, "could not find endpoint\n"); 217762306a36Sopenharmony_ci return -EINVAL; 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci ret = of_property_read_u32(np, "bus-width", &bus_width); 218062306a36Sopenharmony_ci if (ret) { 218162306a36Sopenharmony_ci dev_err(dev, "no bus-width specified: %d\n", ret); 218262306a36Sopenharmony_ci of_node_put(np); 218362306a36Sopenharmony_ci return ret; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci display = of_graph_get_remote_port_parent(np); 218762306a36Sopenharmony_ci of_node_put(np); 218862306a36Sopenharmony_ci if (!display) { 218962306a36Sopenharmony_ci dev_err(dev, "no display defined\n"); 219062306a36Sopenharmony_ci return -EINVAL; 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci ret = of_get_pxafb_display(dev, display, info, bus_width); 219462306a36Sopenharmony_ci of_node_put(display); 219562306a36Sopenharmony_ci if (ret) 219662306a36Sopenharmony_ci return ret; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci for (i = 0; i < info->num_modes; i++) 219962306a36Sopenharmony_ci info->modes[i].bpp = bus_width; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci return 0; 220262306a36Sopenharmony_ci} 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_cistatic struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) 220562306a36Sopenharmony_ci{ 220662306a36Sopenharmony_ci int ret; 220762306a36Sopenharmony_ci struct pxafb_mach_info *info; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (!dev->of_node) 221062306a36Sopenharmony_ci return NULL; 221162306a36Sopenharmony_ci info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 221262306a36Sopenharmony_ci if (!info) 221362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 221462306a36Sopenharmony_ci ret = of_get_pxafb_mode_info(dev, info); 221562306a36Sopenharmony_ci if (ret) 221662306a36Sopenharmony_ci return ERR_PTR(ret); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci /* 221962306a36Sopenharmony_ci * On purpose, neither lccrX registers nor video memory size can be 222062306a36Sopenharmony_ci * specified through device-tree, they are considered more a debug hack 222162306a36Sopenharmony_ci * available through command line. 222262306a36Sopenharmony_ci */ 222362306a36Sopenharmony_ci return info; 222462306a36Sopenharmony_ci} 222562306a36Sopenharmony_ci#else 222662306a36Sopenharmony_cistatic struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci return NULL; 222962306a36Sopenharmony_ci} 223062306a36Sopenharmony_ci#endif 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_cistatic int pxafb_probe(struct platform_device *dev) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct pxafb_info *fbi; 223562306a36Sopenharmony_ci struct pxafb_mach_info *inf, *pdata; 223662306a36Sopenharmony_ci int i, irq, ret; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci dev_dbg(&dev->dev, "pxafb_probe\n"); 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci ret = -ENOMEM; 224162306a36Sopenharmony_ci pdata = dev_get_platdata(&dev->dev); 224262306a36Sopenharmony_ci inf = devm_kmalloc(&dev->dev, sizeof(*inf), GFP_KERNEL); 224362306a36Sopenharmony_ci if (!inf) 224462306a36Sopenharmony_ci goto failed; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci if (pdata) { 224762306a36Sopenharmony_ci *inf = *pdata; 224862306a36Sopenharmony_ci inf->modes = 224962306a36Sopenharmony_ci devm_kmalloc_array(&dev->dev, pdata->num_modes, 225062306a36Sopenharmony_ci sizeof(inf->modes[0]), GFP_KERNEL); 225162306a36Sopenharmony_ci if (!inf->modes) 225262306a36Sopenharmony_ci goto failed; 225362306a36Sopenharmony_ci for (i = 0; i < inf->num_modes; i++) 225462306a36Sopenharmony_ci inf->modes[i] = pdata->modes[i]; 225562306a36Sopenharmony_ci } else { 225662306a36Sopenharmony_ci inf = of_pxafb_of_mach_info(&dev->dev); 225762306a36Sopenharmony_ci } 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci if (IS_ERR_OR_NULL(inf)) 226062306a36Sopenharmony_ci goto failed; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci ret = pxafb_parse_options(&dev->dev, g_options, inf); 226362306a36Sopenharmony_ci if (ret < 0) 226462306a36Sopenharmony_ci goto failed; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci pxafb_check_options(&dev->dev, inf); 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n", 226962306a36Sopenharmony_ci inf->modes->xres, 227062306a36Sopenharmony_ci inf->modes->yres, 227162306a36Sopenharmony_ci inf->modes->bpp); 227262306a36Sopenharmony_ci if (inf->modes->xres == 0 || 227362306a36Sopenharmony_ci inf->modes->yres == 0 || 227462306a36Sopenharmony_ci inf->modes->bpp == 0) { 227562306a36Sopenharmony_ci dev_err(&dev->dev, "Invalid resolution or bit depth\n"); 227662306a36Sopenharmony_ci ret = -EINVAL; 227762306a36Sopenharmony_ci goto failed; 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci fbi = pxafb_init_fbinfo(&dev->dev, inf); 228162306a36Sopenharmony_ci if (IS_ERR(fbi)) { 228262306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); 228362306a36Sopenharmony_ci ret = PTR_ERR(fbi); 228462306a36Sopenharmony_ci goto failed; 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci if (cpu_is_pxa3xx() && inf->acceleration_enabled) 228862306a36Sopenharmony_ci fbi->fb.fix.accel = FB_ACCEL_PXA3XX; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci fbi->backlight_power = inf->pxafb_backlight_power; 229162306a36Sopenharmony_ci fbi->lcd_power = inf->pxafb_lcd_power; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci fbi->lcd_supply = devm_regulator_get_optional(&dev->dev, "lcd"); 229462306a36Sopenharmony_ci if (IS_ERR(fbi->lcd_supply)) { 229562306a36Sopenharmony_ci if (PTR_ERR(fbi->lcd_supply) == -EPROBE_DEFER) 229662306a36Sopenharmony_ci return -EPROBE_DEFER; 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci fbi->lcd_supply = NULL; 229962306a36Sopenharmony_ci } 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci fbi->mmio_base = devm_platform_ioremap_resource(dev, 0); 230262306a36Sopenharmony_ci if (IS_ERR(fbi->mmio_base)) { 230362306a36Sopenharmony_ci dev_err(&dev->dev, "failed to get I/O memory\n"); 230462306a36Sopenharmony_ci ret = PTR_ERR(fbi->mmio_base); 230562306a36Sopenharmony_ci goto failed; 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); 230962306a36Sopenharmony_ci fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size, 231062306a36Sopenharmony_ci &fbi->dma_buff_phys, GFP_KERNEL); 231162306a36Sopenharmony_ci if (fbi->dma_buff == NULL) { 231262306a36Sopenharmony_ci dev_err(&dev->dev, "failed to allocate memory for DMA\n"); 231362306a36Sopenharmony_ci ret = -ENOMEM; 231462306a36Sopenharmony_ci goto failed; 231562306a36Sopenharmony_ci } 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci ret = pxafb_init_video_memory(fbi); 231862306a36Sopenharmony_ci if (ret) { 231962306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret); 232062306a36Sopenharmony_ci ret = -ENOMEM; 232162306a36Sopenharmony_ci goto failed_free_dma; 232262306a36Sopenharmony_ci } 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci irq = platform_get_irq(dev, 0); 232562306a36Sopenharmony_ci if (irq < 0) { 232662306a36Sopenharmony_ci ret = -ENODEV; 232762306a36Sopenharmony_ci goto failed_free_mem; 232862306a36Sopenharmony_ci } 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci ret = devm_request_irq(&dev->dev, irq, pxafb_handle_irq, 0, "LCD", fbi); 233162306a36Sopenharmony_ci if (ret) { 233262306a36Sopenharmony_ci dev_err(&dev->dev, "request_irq failed: %d\n", ret); 233362306a36Sopenharmony_ci ret = -EBUSY; 233462306a36Sopenharmony_ci goto failed_free_mem; 233562306a36Sopenharmony_ci } 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci ret = pxafb_smart_init(fbi); 233862306a36Sopenharmony_ci if (ret) { 233962306a36Sopenharmony_ci dev_err(&dev->dev, "failed to initialize smartpanel\n"); 234062306a36Sopenharmony_ci goto failed_free_mem; 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci /* 234462306a36Sopenharmony_ci * This makes sure that our colour bitfield 234562306a36Sopenharmony_ci * descriptors are correctly initialised. 234662306a36Sopenharmony_ci */ 234762306a36Sopenharmony_ci ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); 234862306a36Sopenharmony_ci if (ret) { 234962306a36Sopenharmony_ci dev_err(&dev->dev, "failed to get suitable mode\n"); 235062306a36Sopenharmony_ci goto failed_free_mem; 235162306a36Sopenharmony_ci } 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci ret = pxafb_set_par(&fbi->fb); 235462306a36Sopenharmony_ci if (ret) { 235562306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to set parameters\n"); 235662306a36Sopenharmony_ci goto failed_free_mem; 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci platform_set_drvdata(dev, fbi); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci ret = register_framebuffer(&fbi->fb); 236262306a36Sopenharmony_ci if (ret < 0) { 236362306a36Sopenharmony_ci dev_err(&dev->dev, 236462306a36Sopenharmony_ci "Failed to register framebuffer device: %d\n", ret); 236562306a36Sopenharmony_ci goto failed_free_cmap; 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci pxafb_overlay_init(fbi); 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 237162306a36Sopenharmony_ci fbi->freq_transition.notifier_call = pxafb_freq_transition; 237262306a36Sopenharmony_ci cpufreq_register_notifier(&fbi->freq_transition, 237362306a36Sopenharmony_ci CPUFREQ_TRANSITION_NOTIFIER); 237462306a36Sopenharmony_ci#endif 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci /* 237762306a36Sopenharmony_ci * Ok, now enable the LCD controller 237862306a36Sopenharmony_ci */ 237962306a36Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE); 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_ci return 0; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_cifailed_free_cmap: 238462306a36Sopenharmony_ci if (fbi->fb.cmap.len) 238562306a36Sopenharmony_ci fb_dealloc_cmap(&fbi->fb.cmap); 238662306a36Sopenharmony_cifailed_free_mem: 238762306a36Sopenharmony_ci free_pages_exact(fbi->video_mem, fbi->video_mem_size); 238862306a36Sopenharmony_cifailed_free_dma: 238962306a36Sopenharmony_ci dma_free_coherent(&dev->dev, fbi->dma_buff_size, 239062306a36Sopenharmony_ci fbi->dma_buff, fbi->dma_buff_phys); 239162306a36Sopenharmony_cifailed: 239262306a36Sopenharmony_ci return ret; 239362306a36Sopenharmony_ci} 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_cistatic void pxafb_remove(struct platform_device *dev) 239662306a36Sopenharmony_ci{ 239762306a36Sopenharmony_ci struct pxafb_info *fbi = platform_get_drvdata(dev); 239862306a36Sopenharmony_ci struct fb_info *info; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci if (!fbi) 240162306a36Sopenharmony_ci return; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci info = &fbi->fb; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci pxafb_overlay_exit(fbi); 240662306a36Sopenharmony_ci unregister_framebuffer(info); 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci pxafb_disable_controller(fbi); 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci if (fbi->fb.cmap.len) 241162306a36Sopenharmony_ci fb_dealloc_cmap(&fbi->fb.cmap); 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci free_pages_exact(fbi->video_mem, fbi->video_mem_size); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci dma_free_coherent(&dev->dev, fbi->dma_buff_size, fbi->dma_buff, 241662306a36Sopenharmony_ci fbi->dma_buff_phys); 241762306a36Sopenharmony_ci} 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_cistatic const struct of_device_id pxafb_of_dev_id[] = { 242062306a36Sopenharmony_ci { .compatible = "marvell,pxa270-lcdc", }, 242162306a36Sopenharmony_ci { .compatible = "marvell,pxa300-lcdc", }, 242262306a36Sopenharmony_ci { .compatible = "marvell,pxa2xx-lcdc", }, 242362306a36Sopenharmony_ci { /* sentinel */ } 242462306a36Sopenharmony_ci}; 242562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pxafb_of_dev_id); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_cistatic struct platform_driver pxafb_driver = { 242862306a36Sopenharmony_ci .probe = pxafb_probe, 242962306a36Sopenharmony_ci .remove_new = pxafb_remove, 243062306a36Sopenharmony_ci .driver = { 243162306a36Sopenharmony_ci .name = "pxa2xx-fb", 243262306a36Sopenharmony_ci .of_match_table = pxafb_of_dev_id, 243362306a36Sopenharmony_ci#ifdef CONFIG_PM 243462306a36Sopenharmony_ci .pm = &pxafb_pm_ops, 243562306a36Sopenharmony_ci#endif 243662306a36Sopenharmony_ci }, 243762306a36Sopenharmony_ci}; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_cistatic int __init pxafb_init(void) 244062306a36Sopenharmony_ci{ 244162306a36Sopenharmony_ci if (pxafb_setup_options()) 244262306a36Sopenharmony_ci return -EINVAL; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci return platform_driver_register(&pxafb_driver); 244562306a36Sopenharmony_ci} 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_cistatic void __exit pxafb_exit(void) 244862306a36Sopenharmony_ci{ 244962306a36Sopenharmony_ci platform_driver_unregister(&pxafb_driver); 245062306a36Sopenharmony_ci} 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_cimodule_init(pxafb_init); 245362306a36Sopenharmony_cimodule_exit(pxafb_exit); 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ciMODULE_DESCRIPTION("loadable framebuffer driver for PXA"); 245662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2457