18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/pxafb.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1999 Eric A. Thomas. 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Jean-Frederic Clere. 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Ian Campbell. 78c2ecf20Sopenharmony_ci * Copyright (C) 2004 Jeff Lackey. 88c2ecf20Sopenharmony_ci * Based on sa1100fb.c Copyright (C) 1999 Eric A. Thomas 98c2ecf20Sopenharmony_ci * which in turn is 108c2ecf20Sopenharmony_ci * Based on acornfb.c Copyright (C) Russell King. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 138c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 148c2ecf20Sopenharmony_ci * more details. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Intel PXA250/210 LCD Controller Frame Buffer Driver 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Please direct your questions and comments on this driver to the following 198c2ecf20Sopenharmony_ci * email address: 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * linux-arm-kernel@lists.arm.linux.org.uk 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Add support for overlay1 and overlay2 based on pxafb_overlay.c: 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Copyright (C) 2004, Intel Corporation 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * 2003/08/27: <yu.tang@intel.com> 288c2ecf20Sopenharmony_ci * 2004/03/10: <stanley.cai@intel.com> 298c2ecf20Sopenharmony_ci * 2004/10/28: <yan.yin@intel.com> 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Marvell International Ltd. 328c2ecf20Sopenharmony_ci * All Rights Reserved 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/sched.h> 398c2ecf20Sopenharmony_ci#include <linux/errno.h> 408c2ecf20Sopenharmony_ci#include <linux/string.h> 418c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 428c2ecf20Sopenharmony_ci#include <linux/slab.h> 438c2ecf20Sopenharmony_ci#include <linux/mm.h> 448c2ecf20Sopenharmony_ci#include <linux/fb.h> 458c2ecf20Sopenharmony_ci#include <linux/delay.h> 468c2ecf20Sopenharmony_ci#include <linux/init.h> 478c2ecf20Sopenharmony_ci#include <linux/ioport.h> 488c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 498c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 508c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 518c2ecf20Sopenharmony_ci#include <linux/clk.h> 528c2ecf20Sopenharmony_ci#include <linux/err.h> 538c2ecf20Sopenharmony_ci#include <linux/completion.h> 548c2ecf20Sopenharmony_ci#include <linux/mutex.h> 558c2ecf20Sopenharmony_ci#include <linux/kthread.h> 568c2ecf20Sopenharmony_ci#include <linux/freezer.h> 578c2ecf20Sopenharmony_ci#include <linux/console.h> 588c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 598c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 608c2ecf20Sopenharmony_ci#include <video/of_display_timing.h> 618c2ecf20Sopenharmony_ci#include <video/videomode.h> 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#include <mach/hardware.h> 648c2ecf20Sopenharmony_ci#include <asm/io.h> 658c2ecf20Sopenharmony_ci#include <asm/irq.h> 668c2ecf20Sopenharmony_ci#include <asm/div64.h> 678c2ecf20Sopenharmony_ci#include <mach/bitfield.h> 688c2ecf20Sopenharmony_ci#include <linux/platform_data/video-pxafb.h> 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * Complain if VAR is out of range. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci#define DEBUG_VAR 1 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#include "pxafb.h" 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Bits which should not be set in machine configuration structures */ 788c2ecf20Sopenharmony_ci#define LCCR0_INVALID_CONFIG_MASK (LCCR0_OUM | LCCR0_BM | LCCR0_QDM |\ 798c2ecf20Sopenharmony_ci LCCR0_DIS | LCCR0_EFM | LCCR0_IUM |\ 808c2ecf20Sopenharmony_ci LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ 838c2ecf20Sopenharmony_ci LCCR3_PCD | LCCR3_BPP(0xf)) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int pxafb_activate_var(struct fb_var_screeninfo *var, 868c2ecf20Sopenharmony_ci struct pxafb_info *); 878c2ecf20Sopenharmony_cistatic void set_ctrlr_state(struct pxafb_info *fbi, u_int state); 888c2ecf20Sopenharmony_cistatic void setup_base_frame(struct pxafb_info *fbi, 898c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var, int branch); 908c2ecf20Sopenharmony_cistatic int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, 918c2ecf20Sopenharmony_ci unsigned long offset, size_t size); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic unsigned long video_mem_size = 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline unsigned long 968c2ecf20Sopenharmony_cilcd_readl(struct pxafb_info *fbi, unsigned int off) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return __raw_readl(fbi->mmio_base + off); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic inline void 1028c2ecf20Sopenharmony_cilcd_writel(struct pxafb_info *fbi, unsigned int off, unsigned long val) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci __raw_writel(val, fbi->mmio_base + off); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline void pxafb_schedule_work(struct pxafb_info *fbi, u_int state) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned long flags; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci local_irq_save(flags); 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * We need to handle two requests being made at the same time. 1148c2ecf20Sopenharmony_ci * There are two important cases: 1158c2ecf20Sopenharmony_ci * 1. When we are changing VT (C_REENABLE) while unblanking 1168c2ecf20Sopenharmony_ci * (C_ENABLE) We must perform the unblanking, which will 1178c2ecf20Sopenharmony_ci * do our REENABLE for us. 1188c2ecf20Sopenharmony_ci * 2. When we are blanking, but immediately unblank before 1198c2ecf20Sopenharmony_ci * we have blanked. We do the "REENABLE" thing here as 1208c2ecf20Sopenharmony_ci * well, just to be sure. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci if (fbi->task_state == C_ENABLE && state == C_REENABLE) 1238c2ecf20Sopenharmony_ci state = (u_int) -1; 1248c2ecf20Sopenharmony_ci if (fbi->task_state == C_DISABLE && state == C_ENABLE) 1258c2ecf20Sopenharmony_ci state = C_REENABLE; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (state != (u_int)-1) { 1288c2ecf20Sopenharmony_ci fbi->task_state = state; 1298c2ecf20Sopenharmony_ci schedule_work(&fbi->task); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci local_irq_restore(flags); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci chan &= 0xffff; 1378c2ecf20Sopenharmony_ci chan >>= 16 - bf->length; 1388c2ecf20Sopenharmony_ci return chan << bf->offset; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int 1428c2ecf20Sopenharmony_cipxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, 1438c2ecf20Sopenharmony_ci u_int trans, struct fb_info *info) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 1468c2ecf20Sopenharmony_ci u_int val; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (regno >= fbi->palette_size) 1498c2ecf20Sopenharmony_ci return 1; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (fbi->fb.var.grayscale) { 1528c2ecf20Sopenharmony_ci fbi->palette_cpu[regno] = ((blue >> 8) & 0x00ff); 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (fbi->lccr4 & LCCR4_PAL_FOR_MASK) { 1578c2ecf20Sopenharmony_ci case LCCR4_PAL_FOR_0: 1588c2ecf20Sopenharmony_ci val = ((red >> 0) & 0xf800); 1598c2ecf20Sopenharmony_ci val |= ((green >> 5) & 0x07e0); 1608c2ecf20Sopenharmony_ci val |= ((blue >> 11) & 0x001f); 1618c2ecf20Sopenharmony_ci fbi->palette_cpu[regno] = val; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case LCCR4_PAL_FOR_1: 1648c2ecf20Sopenharmony_ci val = ((red << 8) & 0x00f80000); 1658c2ecf20Sopenharmony_ci val |= ((green >> 0) & 0x0000fc00); 1668c2ecf20Sopenharmony_ci val |= ((blue >> 8) & 0x000000f8); 1678c2ecf20Sopenharmony_ci ((u32 *)(fbi->palette_cpu))[regno] = val; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case LCCR4_PAL_FOR_2: 1708c2ecf20Sopenharmony_ci val = ((red << 8) & 0x00fc0000); 1718c2ecf20Sopenharmony_ci val |= ((green >> 0) & 0x0000fc00); 1728c2ecf20Sopenharmony_ci val |= ((blue >> 8) & 0x000000fc); 1738c2ecf20Sopenharmony_ci ((u32 *)(fbi->palette_cpu))[regno] = val; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case LCCR4_PAL_FOR_3: 1768c2ecf20Sopenharmony_ci val = ((red << 8) & 0x00ff0000); 1778c2ecf20Sopenharmony_ci val |= ((green >> 0) & 0x0000ff00); 1788c2ecf20Sopenharmony_ci val |= ((blue >> 8) & 0x000000ff); 1798c2ecf20Sopenharmony_ci ((u32 *)(fbi->palette_cpu))[regno] = val; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int 1878c2ecf20Sopenharmony_cipxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 1888c2ecf20Sopenharmony_ci u_int trans, struct fb_info *info) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 1918c2ecf20Sopenharmony_ci unsigned int val; 1928c2ecf20Sopenharmony_ci int ret = 1; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* 1958c2ecf20Sopenharmony_ci * If inverse mode was selected, invert all the colours 1968c2ecf20Sopenharmony_ci * rather than the register number. The register number 1978c2ecf20Sopenharmony_ci * is what you poke into the framebuffer to produce the 1988c2ecf20Sopenharmony_ci * colour you requested. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (fbi->cmap_inverse) { 2018c2ecf20Sopenharmony_ci red = 0xffff - red; 2028c2ecf20Sopenharmony_ci green = 0xffff - green; 2038c2ecf20Sopenharmony_ci blue = 0xffff - blue; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * If greyscale is true, then we convert the RGB value 2088c2ecf20Sopenharmony_ci * to greyscale no matter what visual we are using. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci if (fbi->fb.var.grayscale) 2118c2ecf20Sopenharmony_ci red = green = blue = (19595 * red + 38470 * green + 2128c2ecf20Sopenharmony_ci 7471 * blue) >> 16; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (fbi->fb.fix.visual) { 2158c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * 16-bit True Colour. We encode the RGB value 2188c2ecf20Sopenharmony_ci * according to the RGB bitfield information. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci if (regno < 16) { 2218c2ecf20Sopenharmony_ci u32 *pal = fbi->fb.pseudo_palette; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci val = chan_to_field(red, &fbi->fb.var.red); 2248c2ecf20Sopenharmony_ci val |= chan_to_field(green, &fbi->fb.var.green); 2258c2ecf20Sopenharmony_ci val |= chan_to_field(blue, &fbi->fb.var.blue); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci pal[regno] = val; 2288c2ecf20Sopenharmony_ci ret = 0; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci case FB_VISUAL_STATIC_PSEUDOCOLOR: 2338c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 2348c2ecf20Sopenharmony_ci ret = pxafb_setpalettereg(regno, red, green, blue, trans, info); 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */ 2428c2ecf20Sopenharmony_cistatic inline int var_to_depth(struct fb_var_screeninfo *var) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci return var->red.length + var->green.length + 2458c2ecf20Sopenharmony_ci var->blue.length + var->transp.length; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */ 2498c2ecf20Sopenharmony_cistatic int pxafb_var_to_bpp(struct fb_var_screeninfo *var) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci int bpp = -EINVAL; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 2548c2ecf20Sopenharmony_ci case 1: bpp = 0; break; 2558c2ecf20Sopenharmony_ci case 2: bpp = 1; break; 2568c2ecf20Sopenharmony_ci case 4: bpp = 2; break; 2578c2ecf20Sopenharmony_ci case 8: bpp = 3; break; 2588c2ecf20Sopenharmony_ci case 16: bpp = 4; break; 2598c2ecf20Sopenharmony_ci case 24: 2608c2ecf20Sopenharmony_ci switch (var_to_depth(var)) { 2618c2ecf20Sopenharmony_ci case 18: bpp = 6; break; /* 18-bits/pixel packed */ 2628c2ecf20Sopenharmony_ci case 19: bpp = 8; break; /* 19-bits/pixel packed */ 2638c2ecf20Sopenharmony_ci case 24: bpp = 9; break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci case 32: 2678c2ecf20Sopenharmony_ci switch (var_to_depth(var)) { 2688c2ecf20Sopenharmony_ci case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ 2698c2ecf20Sopenharmony_ci case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ 2708c2ecf20Sopenharmony_ci case 25: bpp = 10; break; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci return bpp; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * pxafb_var_to_lccr3(): 2798c2ecf20Sopenharmony_ci * Convert a bits per pixel value to the correct bit pattern for LCCR3 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an 2828c2ecf20Sopenharmony_ci * implication of the acutal use of transparency bit, which we handle it 2838c2ecf20Sopenharmony_ci * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel 2848c2ecf20Sopenharmony_ci * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * Transparency for palette pixel formats is not supported at the moment. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_cistatic uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int bpp = pxafb_var_to_bpp(var); 2918c2ecf20Sopenharmony_ci uint32_t lccr3; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (bpp < 0) 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci lccr3 = LCCR3_BPP(bpp); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci switch (var_to_depth(var)) { 2998c2ecf20Sopenharmony_ci case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; 3008c2ecf20Sopenharmony_ci case 18: lccr3 |= LCCR3_PDFOR_3; break; 3018c2ecf20Sopenharmony_ci case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case 19: 3048c2ecf20Sopenharmony_ci case 25: lccr3 |= LCCR3_PDFOR_0; break; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci return lccr3; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci#define SET_PIXFMT(v, r, g, b, t) \ 3108c2ecf20Sopenharmony_ci({ \ 3118c2ecf20Sopenharmony_ci (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ 3128c2ecf20Sopenharmony_ci (v)->transp.length = (t) ? (t) : 0; \ 3138c2ecf20Sopenharmony_ci (v)->blue.length = (b); (v)->blue.offset = 0; \ 3148c2ecf20Sopenharmony_ci (v)->green.length = (g); (v)->green.offset = (b); \ 3158c2ecf20Sopenharmony_ci (v)->red.length = (r); (v)->red.offset = (b) + (g); \ 3168c2ecf20Sopenharmony_ci}) 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* set the RGBT bitfields of fb_var_screeninf according to 3198c2ecf20Sopenharmony_ci * var->bits_per_pixel and given depth 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_cistatic void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci if (depth == 0) 3248c2ecf20Sopenharmony_ci depth = var->bits_per_pixel; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (var->bits_per_pixel < 16) { 3278c2ecf20Sopenharmony_ci /* indexed pixel formats */ 3288c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 3298c2ecf20Sopenharmony_ci var->green.offset = 0; var->green.length = 8; 3308c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 3318c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 8; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci switch (depth) { 3358c2ecf20Sopenharmony_ci case 16: var->transp.length ? 3368c2ecf20Sopenharmony_ci SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ 3378c2ecf20Sopenharmony_ci SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ 3388c2ecf20Sopenharmony_ci case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ 3398c2ecf20Sopenharmony_ci case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ 3408c2ecf20Sopenharmony_ci case 24: var->transp.length ? 3418c2ecf20Sopenharmony_ci SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ 3428c2ecf20Sopenharmony_ci SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ 3438c2ecf20Sopenharmony_ci case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * pxafb_display_dma_period() 3508c2ecf20Sopenharmony_ci * Calculate the minimum period (in picoseconds) between two DMA 3518c2ecf20Sopenharmony_ci * requests for the LCD controller. If we hit this, it means we're 3528c2ecf20Sopenharmony_ci * doing nothing but LCD DMA. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_cistatic unsigned int pxafb_display_dma_period(struct fb_var_screeninfo *var) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * Period = pixclock * bits_per_byte * bytes_per_transfer 3588c2ecf20Sopenharmony_ci * / memory_bits_per_pixel; 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci return var->pixclock * 8 * 16 / var->bits_per_pixel; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci#endif 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* 3658c2ecf20Sopenharmony_ci * Select the smallest mode that allows the desired resolution to be 3668c2ecf20Sopenharmony_ci * displayed. If desired parameters can be rounded up. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_cistatic struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, 3698c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct pxafb_mode_info *mode = NULL; 3728c2ecf20Sopenharmony_ci struct pxafb_mode_info *modelist = mach->modes; 3738c2ecf20Sopenharmony_ci unsigned int best_x = 0xffffffff, best_y = 0xffffffff; 3748c2ecf20Sopenharmony_ci unsigned int i; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci for (i = 0; i < mach->num_modes; i++) { 3778c2ecf20Sopenharmony_ci if (modelist[i].xres >= var->xres && 3788c2ecf20Sopenharmony_ci modelist[i].yres >= var->yres && 3798c2ecf20Sopenharmony_ci modelist[i].xres < best_x && 3808c2ecf20Sopenharmony_ci modelist[i].yres < best_y && 3818c2ecf20Sopenharmony_ci modelist[i].bpp >= var->bits_per_pixel) { 3828c2ecf20Sopenharmony_ci best_x = modelist[i].xres; 3838c2ecf20Sopenharmony_ci best_y = modelist[i].yres; 3848c2ecf20Sopenharmony_ci mode = &modelist[i]; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return mode; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void pxafb_setmode(struct fb_var_screeninfo *var, 3928c2ecf20Sopenharmony_ci struct pxafb_mode_info *mode) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci var->xres = mode->xres; 3958c2ecf20Sopenharmony_ci var->yres = mode->yres; 3968c2ecf20Sopenharmony_ci var->bits_per_pixel = mode->bpp; 3978c2ecf20Sopenharmony_ci var->pixclock = mode->pixclock; 3988c2ecf20Sopenharmony_ci var->hsync_len = mode->hsync_len; 3998c2ecf20Sopenharmony_ci var->left_margin = mode->left_margin; 4008c2ecf20Sopenharmony_ci var->right_margin = mode->right_margin; 4018c2ecf20Sopenharmony_ci var->vsync_len = mode->vsync_len; 4028c2ecf20Sopenharmony_ci var->upper_margin = mode->upper_margin; 4038c2ecf20Sopenharmony_ci var->lower_margin = mode->lower_margin; 4048c2ecf20Sopenharmony_ci var->sync = mode->sync; 4058c2ecf20Sopenharmony_ci var->grayscale = mode->cmap_greyscale; 4068c2ecf20Sopenharmony_ci var->transp.length = mode->transparency; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* set the initial RGBA bitfields */ 4098c2ecf20Sopenharmony_ci pxafb_set_pixfmt(var, mode->depth); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int pxafb_adjust_timing(struct pxafb_info *fbi, 4138c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci int line_length; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci var->xres = max_t(int, var->xres, MIN_XRES); 4188c2ecf20Sopenharmony_ci var->yres = max_t(int, var->yres, MIN_YRES); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (!(fbi->lccr0 & LCCR0_LCDT)) { 4218c2ecf20Sopenharmony_ci clamp_val(var->hsync_len, 1, 64); 4228c2ecf20Sopenharmony_ci clamp_val(var->vsync_len, 1, 64); 4238c2ecf20Sopenharmony_ci clamp_val(var->left_margin, 1, 255); 4248c2ecf20Sopenharmony_ci clamp_val(var->right_margin, 1, 255); 4258c2ecf20Sopenharmony_ci clamp_val(var->upper_margin, 1, 255); 4268c2ecf20Sopenharmony_ci clamp_val(var->lower_margin, 1, 255); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* make sure each line is aligned on word boundary */ 4308c2ecf20Sopenharmony_ci line_length = var->xres * var->bits_per_pixel / 8; 4318c2ecf20Sopenharmony_ci line_length = ALIGN(line_length, 4); 4328c2ecf20Sopenharmony_ci var->xres = line_length * 8 / var->bits_per_pixel; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* we don't support xpan, force xres_virtual to be equal to xres */ 4358c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (var->accel_flags & FB_ACCELF_TEXT) 4388c2ecf20Sopenharmony_ci var->yres_virtual = fbi->fb.fix.smem_len / line_length; 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci var->yres_virtual = max(var->yres_virtual, var->yres); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* check for limits */ 4438c2ecf20Sopenharmony_ci if (var->xres > MAX_XRES || var->yres > MAX_YRES) 4448c2ecf20Sopenharmony_ci return -EINVAL; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if (var->yres > var->yres_virtual) 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* 4538c2ecf20Sopenharmony_ci * pxafb_check_var(): 4548c2ecf20Sopenharmony_ci * Get the video params out of 'var'. If a value doesn't fit, round it up, 4558c2ecf20Sopenharmony_ci * if it's too big, return -EINVAL. 4568c2ecf20Sopenharmony_ci * 4578c2ecf20Sopenharmony_ci * Round up in the following order: bits_per_pixel, xres, 4588c2ecf20Sopenharmony_ci * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, 4598c2ecf20Sopenharmony_ci * bitfields, horizontal timing, vertical timing. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_cistatic int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 4648c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf = fbi->inf; 4658c2ecf20Sopenharmony_ci int err; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (inf->fixed_modes) { 4688c2ecf20Sopenharmony_ci struct pxafb_mode_info *mode; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci mode = pxafb_getmode(inf, var); 4718c2ecf20Sopenharmony_ci if (!mode) 4728c2ecf20Sopenharmony_ci return -EINVAL; 4738c2ecf20Sopenharmony_ci pxafb_setmode(var, mode); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* do a test conversion to BPP fields to check the color formats */ 4778c2ecf20Sopenharmony_ci err = pxafb_var_to_bpp(var); 4788c2ecf20Sopenharmony_ci if (err < 0) 4798c2ecf20Sopenharmony_ci return err; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci pxafb_set_pixfmt(var, var_to_depth(var)); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci err = pxafb_adjust_timing(fbi, var); 4848c2ecf20Sopenharmony_ci if (err) 4858c2ecf20Sopenharmony_ci return err; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 4888c2ecf20Sopenharmony_ci pr_debug("pxafb: dma period = %d ps\n", 4898c2ecf20Sopenharmony_ci pxafb_display_dma_period(var)); 4908c2ecf20Sopenharmony_ci#endif 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci/* 4968c2ecf20Sopenharmony_ci * pxafb_set_par(): 4978c2ecf20Sopenharmony_ci * Set the user defined part of the display for the specified console 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic int pxafb_set_par(struct fb_info *info) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 5028c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (var->bits_per_pixel >= 16) 5058c2ecf20Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; 5068c2ecf20Sopenharmony_ci else if (!fbi->cmap_static) 5078c2ecf20Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 5088c2ecf20Sopenharmony_ci else { 5098c2ecf20Sopenharmony_ci /* 5108c2ecf20Sopenharmony_ci * Some people have weird ideas about wanting static 5118c2ecf20Sopenharmony_ci * pseudocolor maps. I suspect their user space 5128c2ecf20Sopenharmony_ci * applications are broken. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci fbi->fb.fix.line_length = var->xres_virtual * 5188c2ecf20Sopenharmony_ci var->bits_per_pixel / 8; 5198c2ecf20Sopenharmony_ci if (var->bits_per_pixel >= 16) 5208c2ecf20Sopenharmony_ci fbi->palette_size = 0; 5218c2ecf20Sopenharmony_ci else 5228c2ecf20Sopenharmony_ci fbi->palette_size = var->bits_per_pixel == 1 ? 5238c2ecf20Sopenharmony_ci 4 : 1 << var->bits_per_pixel; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0]; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (fbi->fb.var.bits_per_pixel >= 16) 5288c2ecf20Sopenharmony_ci fb_dealloc_cmap(&fbi->fb.cmap); 5298c2ecf20Sopenharmony_ci else 5308c2ecf20Sopenharmony_ci fb_alloc_cmap(&fbi->fb.cmap, 1<<fbi->fb.var.bits_per_pixel, 0); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci pxafb_activate_var(var, fbi); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int pxafb_pan_display(struct fb_var_screeninfo *var, 5388c2ecf20Sopenharmony_ci struct fb_info *info) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 5418c2ecf20Sopenharmony_ci struct fb_var_screeninfo newvar; 5428c2ecf20Sopenharmony_ci int dma = DMA_MAX + DMA_BASE; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if (fbi->state != C_ENABLE) 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Only take .xoffset, .yoffset and .vmode & FB_VMODE_YWRAP from what 5488c2ecf20Sopenharmony_ci * was passed in and copy the rest from the old screeninfo. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ci memcpy(&newvar, &fbi->fb.var, sizeof(newvar)); 5518c2ecf20Sopenharmony_ci newvar.xoffset = var->xoffset; 5528c2ecf20Sopenharmony_ci newvar.yoffset = var->yoffset; 5538c2ecf20Sopenharmony_ci newvar.vmode &= ~FB_VMODE_YWRAP; 5548c2ecf20Sopenharmony_ci newvar.vmode |= var->vmode & FB_VMODE_YWRAP; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci setup_base_frame(fbi, &newvar, 1); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (fbi->lccr0 & LCCR0_SDS) 5598c2ecf20Sopenharmony_ci lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1); 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci/* 5668c2ecf20Sopenharmony_ci * pxafb_blank(): 5678c2ecf20Sopenharmony_ci * Blank the display by setting all palette values to zero. Note, the 5688c2ecf20Sopenharmony_ci * 16 bpp mode does not really use the palette, so this will not 5698c2ecf20Sopenharmony_ci * blank the display in all modes. 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_cistatic int pxafb_blank(int blank, struct fb_info *info) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 5748c2ecf20Sopenharmony_ci int i; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci switch (blank) { 5778c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 5788c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 5798c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 5808c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 5818c2ecf20Sopenharmony_ci if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 5828c2ecf20Sopenharmony_ci fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 5838c2ecf20Sopenharmony_ci for (i = 0; i < fbi->palette_size; i++) 5848c2ecf20Sopenharmony_ci pxafb_setpalettereg(i, 0, 0, 0, 0, info); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci pxafb_schedule_work(fbi, C_DISABLE); 5878c2ecf20Sopenharmony_ci /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ 5888c2ecf20Sopenharmony_ci break; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 5918c2ecf20Sopenharmony_ci /* TODO if (pxafb_blank_helper) pxafb_blank_helper(blank); */ 5928c2ecf20Sopenharmony_ci if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 5938c2ecf20Sopenharmony_ci fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 5948c2ecf20Sopenharmony_ci fb_set_cmap(&fbi->fb.cmap, info); 5958c2ecf20Sopenharmony_ci pxafb_schedule_work(fbi, C_ENABLE); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic const struct fb_ops pxafb_ops = { 6018c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6028c2ecf20Sopenharmony_ci .fb_check_var = pxafb_check_var, 6038c2ecf20Sopenharmony_ci .fb_set_par = pxafb_set_par, 6048c2ecf20Sopenharmony_ci .fb_pan_display = pxafb_pan_display, 6058c2ecf20Sopenharmony_ci .fb_setcolreg = pxafb_setcolreg, 6068c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 6078c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 6088c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 6098c2ecf20Sopenharmony_ci .fb_blank = pxafb_blank, 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 6138c2ecf20Sopenharmony_cistatic void overlay1fb_setup(struct pxafb_layer *ofb) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; 6168c2ecf20Sopenharmony_ci unsigned long start = ofb->video_mem_phys; 6178c2ecf20Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/* Depending on the enable status of overlay1/2, the DMA should be 6218c2ecf20Sopenharmony_ci * updated from FDADRx (when disabled) or FBRx (when enabled). 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_cistatic void overlay1fb_enable(struct pxafb_layer *ofb) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN; 6268c2ecf20Sopenharmony_ci uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1); 6298c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]); 6308c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void overlay1fb_disable(struct pxafb_layer *ofb) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci uint32_t lccr5; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (!(lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN)) 6388c2ecf20Sopenharmony_ci return; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci lccr5 = lcd_readl(ofb->fbi, LCCR5); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1)); 6458c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1)); 6468c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) 6498c2ecf20Sopenharmony_ci pr_warn("%s: timeout disabling overlay1\n", __func__); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, LCCR5, lccr5); 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void overlay2fb_setup(struct pxafb_layer *ofb) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); 6578c2ecf20Sopenharmony_ci unsigned long start[3] = { ofb->video_mem_phys, 0, 0 }; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) { 6608c2ecf20Sopenharmony_ci size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; 6618c2ecf20Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); 6628c2ecf20Sopenharmony_ci } else { 6638c2ecf20Sopenharmony_ci size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual; 6648c2ecf20Sopenharmony_ci switch (pfor) { 6658c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break; 6668c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break; 6678c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci start[1] = start[0] + size; 6708c2ecf20Sopenharmony_ci start[2] = start[1] + size / div; 6718c2ecf20Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); 6728c2ecf20Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div); 6738c2ecf20Sopenharmony_ci setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div); 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic void overlay2fb_enable(struct pxafb_layer *ofb) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); 6808c2ecf20Sopenharmony_ci int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN; 6818c2ecf20Sopenharmony_ci uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0); 6828c2ecf20Sopenharmony_ci uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0); 6838c2ecf20Sopenharmony_ci uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) 6868c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); 6878c2ecf20Sopenharmony_ci else { 6888c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); 6898c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3); 6908c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4); 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]); 6938c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic void overlay2fb_disable(struct pxafb_layer *ofb) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci uint32_t lccr5; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (!(lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN)) 7018c2ecf20Sopenharmony_ci return; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci lccr5 = lcd_readl(ofb->fbi, LCCR5); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2)); 7088c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2)); 7098c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3); 7108c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3); 7118c2ecf20Sopenharmony_ci lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) 7148c2ecf20Sopenharmony_ci pr_warn("%s: timeout disabling overlay2\n", __func__); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic struct pxafb_layer_ops ofb_ops[] = { 7188c2ecf20Sopenharmony_ci [0] = { 7198c2ecf20Sopenharmony_ci .enable = overlay1fb_enable, 7208c2ecf20Sopenharmony_ci .disable = overlay1fb_disable, 7218c2ecf20Sopenharmony_ci .setup = overlay1fb_setup, 7228c2ecf20Sopenharmony_ci }, 7238c2ecf20Sopenharmony_ci [1] = { 7248c2ecf20Sopenharmony_ci .enable = overlay2fb_enable, 7258c2ecf20Sopenharmony_ci .disable = overlay2fb_disable, 7268c2ecf20Sopenharmony_ci .setup = overlay2fb_setup, 7278c2ecf20Sopenharmony_ci }, 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int overlayfb_open(struct fb_info *info, int user) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* no support for framebuffer console on overlay */ 7358c2ecf20Sopenharmony_ci if (user == 0) 7368c2ecf20Sopenharmony_ci return -ENODEV; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (ofb->usage++ == 0) { 7398c2ecf20Sopenharmony_ci /* unblank the base framebuffer */ 7408c2ecf20Sopenharmony_ci console_lock(); 7418c2ecf20Sopenharmony_ci fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); 7428c2ecf20Sopenharmony_ci console_unlock(); 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int overlayfb_release(struct fb_info *info, int user) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (ofb->usage == 1) { 7538c2ecf20Sopenharmony_ci ofb->ops->disable(ofb); 7548c2ecf20Sopenharmony_ci ofb->fb.var.height = -1; 7558c2ecf20Sopenharmony_ci ofb->fb.var.width = -1; 7568c2ecf20Sopenharmony_ci ofb->fb.var.xres = ofb->fb.var.xres_virtual = 0; 7578c2ecf20Sopenharmony_ci ofb->fb.var.yres = ofb->fb.var.yres_virtual = 0; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci ofb->usage--; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int overlayfb_check_var(struct fb_var_screeninfo *var, 7658c2ecf20Sopenharmony_ci struct fb_info *info) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 7688c2ecf20Sopenharmony_ci struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var; 7698c2ecf20Sopenharmony_ci int xpos, ypos, pfor, bpp; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci xpos = NONSTD_TO_XPOS(var->nonstd); 7728c2ecf20Sopenharmony_ci ypos = NONSTD_TO_YPOS(var->nonstd); 7738c2ecf20Sopenharmony_ci pfor = NONSTD_TO_PFOR(var->nonstd); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci bpp = pxafb_var_to_bpp(var); 7768c2ecf20Sopenharmony_ci if (bpp < 0) 7778c2ecf20Sopenharmony_ci return -EINVAL; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* no support for YUV format on overlay1 */ 7808c2ecf20Sopenharmony_ci if (ofb->id == OVERLAY1 && pfor != 0) 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */ 7848c2ecf20Sopenharmony_ci switch (pfor) { 7858c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_RGB: 7868c2ecf20Sopenharmony_ci bpp = pxafb_var_to_bpp(var); 7878c2ecf20Sopenharmony_ci if (bpp < 0) 7888c2ecf20Sopenharmony_ci return -EINVAL; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci pxafb_set_pixfmt(var, var_to_depth(var)); 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; 7938c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break; 7948c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break; 7958c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break; 7968c2ecf20Sopenharmony_ci default: 7978c2ecf20Sopenharmony_ci return -EINVAL; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* each line must start at a 32-bit word boundary */ 8018c2ecf20Sopenharmony_ci if ((xpos * bpp) % 32) 8028c2ecf20Sopenharmony_ci return -EINVAL; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* xres must align on 32-bit word boundary */ 8058c2ecf20Sopenharmony_ci var->xres = roundup(var->xres * bpp, 32) / bpp; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if ((xpos + var->xres > base_var->xres) || 8088c2ecf20Sopenharmony_ci (ypos + var->yres > base_var->yres)) 8098c2ecf20Sopenharmony_ci return -EINVAL; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 8128c2ecf20Sopenharmony_ci var->yres_virtual = max(var->yres, var->yres_virtual); 8138c2ecf20Sopenharmony_ci return 0; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic int overlayfb_check_video_memory(struct pxafb_layer *ofb) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &ofb->fb.var; 8198c2ecf20Sopenharmony_ci int pfor = NONSTD_TO_PFOR(var->nonstd); 8208c2ecf20Sopenharmony_ci int size, bpp = 0; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci switch (pfor) { 8238c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break; 8248c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; 8258c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break; 8268c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break; 8278c2ecf20Sopenharmony_ci case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci ofb->fb.fix.line_length = var->xres_virtual * bpp / 8; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (ofb->video_mem) { 8358c2ecf20Sopenharmony_ci if (ofb->video_mem_size >= size) 8368c2ecf20Sopenharmony_ci return 0; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci return -EINVAL; 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic int overlayfb_set_par(struct fb_info *info) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci struct pxafb_layer *ofb = container_of(info, struct pxafb_layer, fb); 8448c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 8458c2ecf20Sopenharmony_ci int xpos, ypos, pfor, bpp, ret; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci ret = overlayfb_check_video_memory(ofb); 8488c2ecf20Sopenharmony_ci if (ret) 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci bpp = pxafb_var_to_bpp(var); 8528c2ecf20Sopenharmony_ci xpos = NONSTD_TO_XPOS(var->nonstd); 8538c2ecf20Sopenharmony_ci ypos = NONSTD_TO_YPOS(var->nonstd); 8548c2ecf20Sopenharmony_ci pfor = NONSTD_TO_PFOR(var->nonstd); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) | 8578c2ecf20Sopenharmony_ci OVLxC1_BPP(bpp); 8588c2ecf20Sopenharmony_ci ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (ofb->id == OVERLAY2) 8618c2ecf20Sopenharmony_ci ofb->control[1] |= OVL2C2_PFOR(pfor); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci ofb->ops->setup(ofb); 8648c2ecf20Sopenharmony_ci ofb->ops->enable(ofb); 8658c2ecf20Sopenharmony_ci return 0; 8668c2ecf20Sopenharmony_ci} 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic const struct fb_ops overlay_fb_ops = { 8698c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8708c2ecf20Sopenharmony_ci .fb_open = overlayfb_open, 8718c2ecf20Sopenharmony_ci .fb_release = overlayfb_release, 8728c2ecf20Sopenharmony_ci .fb_check_var = overlayfb_check_var, 8738c2ecf20Sopenharmony_ci .fb_set_par = overlayfb_set_par, 8748c2ecf20Sopenharmony_ci}; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic void init_pxafb_overlay(struct pxafb_info *fbi, struct pxafb_layer *ofb, 8778c2ecf20Sopenharmony_ci int id) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci sprintf(ofb->fb.fix.id, "overlay%d", id + 1); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS; 8828c2ecf20Sopenharmony_ci ofb->fb.fix.xpanstep = 0; 8838c2ecf20Sopenharmony_ci ofb->fb.fix.ypanstep = 1; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci ofb->fb.var.activate = FB_ACTIVATE_NOW; 8868c2ecf20Sopenharmony_ci ofb->fb.var.height = -1; 8878c2ecf20Sopenharmony_ci ofb->fb.var.width = -1; 8888c2ecf20Sopenharmony_ci ofb->fb.var.vmode = FB_VMODE_NONINTERLACED; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci ofb->fb.fbops = &overlay_fb_ops; 8918c2ecf20Sopenharmony_ci ofb->fb.flags = FBINFO_FLAG_DEFAULT; 8928c2ecf20Sopenharmony_ci ofb->fb.node = -1; 8938c2ecf20Sopenharmony_ci ofb->fb.pseudo_palette = NULL; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci ofb->id = id; 8968c2ecf20Sopenharmony_ci ofb->ops = &ofb_ops[id]; 8978c2ecf20Sopenharmony_ci ofb->usage = 0; 8988c2ecf20Sopenharmony_ci ofb->fbi = fbi; 8998c2ecf20Sopenharmony_ci init_completion(&ofb->branch_done); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic inline int pxafb_overlay_supported(void) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci if (cpu_is_pxa27x() || cpu_is_pxa3xx()) 9058c2ecf20Sopenharmony_ci return 1; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci return 0; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic int pxafb_overlay_map_video_memory(struct pxafb_info *pxafb, 9118c2ecf20Sopenharmony_ci struct pxafb_layer *ofb) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci /* We assume that user will use at most video_mem_size for overlay fb, 9148c2ecf20Sopenharmony_ci * anyway, it's useless to use 16bpp main plane and 24bpp overlay 9158c2ecf20Sopenharmony_ci */ 9168c2ecf20Sopenharmony_ci ofb->video_mem = alloc_pages_exact(PAGE_ALIGN(pxafb->video_mem_size), 9178c2ecf20Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 9188c2ecf20Sopenharmony_ci if (ofb->video_mem == NULL) 9198c2ecf20Sopenharmony_ci return -ENOMEM; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci ofb->video_mem_phys = virt_to_phys(ofb->video_mem); 9228c2ecf20Sopenharmony_ci ofb->video_mem_size = PAGE_ALIGN(pxafb->video_mem_size); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci mutex_lock(&ofb->fb.mm_lock); 9258c2ecf20Sopenharmony_ci ofb->fb.fix.smem_start = ofb->video_mem_phys; 9268c2ecf20Sopenharmony_ci ofb->fb.fix.smem_len = pxafb->video_mem_size; 9278c2ecf20Sopenharmony_ci mutex_unlock(&ofb->fb.mm_lock); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci ofb->fb.screen_base = ofb->video_mem; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return 0; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic void pxafb_overlay_init(struct pxafb_info *fbi) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci int i, ret; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci if (!pxafb_overlay_supported()) 9398c2ecf20Sopenharmony_ci return; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 9428c2ecf20Sopenharmony_ci struct pxafb_layer *ofb = &fbi->overlay[i]; 9438c2ecf20Sopenharmony_ci init_pxafb_overlay(fbi, ofb, i); 9448c2ecf20Sopenharmony_ci ret = register_framebuffer(&ofb->fb); 9458c2ecf20Sopenharmony_ci if (ret) { 9468c2ecf20Sopenharmony_ci dev_err(fbi->dev, "failed to register overlay %d\n", i); 9478c2ecf20Sopenharmony_ci continue; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci ret = pxafb_overlay_map_video_memory(fbi, ofb); 9508c2ecf20Sopenharmony_ci if (ret) { 9518c2ecf20Sopenharmony_ci dev_err(fbi->dev, 9528c2ecf20Sopenharmony_ci "failed to map video memory for overlay %d\n", 9538c2ecf20Sopenharmony_ci i); 9548c2ecf20Sopenharmony_ci unregister_framebuffer(&ofb->fb); 9558c2ecf20Sopenharmony_ci continue; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci ofb->registered = 1; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* mask all IU/BS/EOF/SOF interrupts */ 9618c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR5, ~0); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci pr_info("PXA Overlay driver loaded successfully!\n"); 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic void pxafb_overlay_exit(struct pxafb_info *fbi) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci int i; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (!pxafb_overlay_supported()) 9718c2ecf20Sopenharmony_ci return; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 9748c2ecf20Sopenharmony_ci struct pxafb_layer *ofb = &fbi->overlay[i]; 9758c2ecf20Sopenharmony_ci if (ofb->registered) { 9768c2ecf20Sopenharmony_ci if (ofb->video_mem) 9778c2ecf20Sopenharmony_ci free_pages_exact(ofb->video_mem, 9788c2ecf20Sopenharmony_ci ofb->video_mem_size); 9798c2ecf20Sopenharmony_ci unregister_framebuffer(&ofb->fb); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci#else 9848c2ecf20Sopenharmony_cistatic inline void pxafb_overlay_init(struct pxafb_info *fbi) {} 9858c2ecf20Sopenharmony_cistatic inline void pxafb_overlay_exit(struct pxafb_info *fbi) {} 9868c2ecf20Sopenharmony_ci#endif /* CONFIG_FB_PXA_OVERLAY */ 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci/* 9898c2ecf20Sopenharmony_ci * Calculate the PCD value from the clock rate (in picoseconds). 9908c2ecf20Sopenharmony_ci * We take account of the PPCR clock setting. 9918c2ecf20Sopenharmony_ci * From PXA Developer's Manual: 9928c2ecf20Sopenharmony_ci * 9938c2ecf20Sopenharmony_ci * PixelClock = LCLK 9948c2ecf20Sopenharmony_ci * ------------- 9958c2ecf20Sopenharmony_ci * 2 ( PCD + 1 ) 9968c2ecf20Sopenharmony_ci * 9978c2ecf20Sopenharmony_ci * PCD = LCLK 9988c2ecf20Sopenharmony_ci * ------------- - 1 9998c2ecf20Sopenharmony_ci * 2(PixelClock) 10008c2ecf20Sopenharmony_ci * 10018c2ecf20Sopenharmony_ci * Where: 10028c2ecf20Sopenharmony_ci * LCLK = LCD/Memory Clock 10038c2ecf20Sopenharmony_ci * PCD = LCCR3[7:0] 10048c2ecf20Sopenharmony_ci * 10058c2ecf20Sopenharmony_ci * PixelClock here is in Hz while the pixclock argument given is the 10068c2ecf20Sopenharmony_ci * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 ) 10078c2ecf20Sopenharmony_ci * 10088c2ecf20Sopenharmony_ci * The function get_lclk_frequency_10khz returns LCLK in units of 10098c2ecf20Sopenharmony_ci * 10khz. Calling the result of this function lclk gives us the 10108c2ecf20Sopenharmony_ci * following 10118c2ecf20Sopenharmony_ci * 10128c2ecf20Sopenharmony_ci * PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 ) 10138c2ecf20Sopenharmony_ci * -------------------------------------- - 1 10148c2ecf20Sopenharmony_ci * 2 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_cistatic inline unsigned int get_pcd(struct pxafb_info *fbi, 10198c2ecf20Sopenharmony_ci unsigned int pixclock) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci unsigned long long pcd; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* FIXME: Need to take into account Double Pixel Clock mode 10248c2ecf20Sopenharmony_ci * (DPC) bit? or perhaps set it based on the various clock 10258c2ecf20Sopenharmony_ci * speeds */ 10268c2ecf20Sopenharmony_ci pcd = (unsigned long long)(clk_get_rate(fbi->clk) / 10000); 10278c2ecf20Sopenharmony_ci pcd *= pixclock; 10288c2ecf20Sopenharmony_ci do_div(pcd, 100000000 * 2); 10298c2ecf20Sopenharmony_ci /* no need for this, since we should subtract 1 anyway. they cancel */ 10308c2ecf20Sopenharmony_ci /* pcd += 1; */ /* make up for integer math truncations */ 10318c2ecf20Sopenharmony_ci return (unsigned int)pcd; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci/* 10358c2ecf20Sopenharmony_ci * Some touchscreens need hsync information from the video driver to 10368c2ecf20Sopenharmony_ci * function correctly. We export it here. Note that 'hsync_time' and 10378c2ecf20Sopenharmony_ci * the value returned from pxafb_get_hsync_time() is the *reciprocal* 10388c2ecf20Sopenharmony_ci * of the hsync period in seconds. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_cistatic inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci unsigned long htime; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if ((pcd == 0) || (fbi->fb.var.hsync_len == 0)) { 10458c2ecf20Sopenharmony_ci fbi->hsync_time = 0; 10468c2ecf20Sopenharmony_ci return; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci htime = clk_get_rate(fbi->clk) / (pcd * fbi->fb.var.hsync_len); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci fbi->hsync_time = htime; 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ciunsigned long pxafb_get_hsync_time(struct device *dev) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci struct pxafb_info *fbi = dev_get_drvdata(dev); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* If display is blanked/suspended, hsync isn't active */ 10598c2ecf20Sopenharmony_ci if (!fbi || (fbi->state != C_ENABLE)) 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return fbi->hsync_time; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pxafb_get_hsync_time); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cistatic int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, 10678c2ecf20Sopenharmony_ci unsigned long start, size_t size) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct pxafb_dma_descriptor *dma_desc, *pal_desc; 10708c2ecf20Sopenharmony_ci unsigned int dma_desc_off, pal_desc_off; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (dma < 0 || dma >= DMA_MAX * 2) 10738c2ecf20Sopenharmony_ci return -EINVAL; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci dma_desc = &fbi->dma_buff->dma_desc[dma]; 10768c2ecf20Sopenharmony_ci dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci dma_desc->fsadr = start; 10798c2ecf20Sopenharmony_ci dma_desc->fidr = 0; 10808c2ecf20Sopenharmony_ci dma_desc->ldcmd = size; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (pal < 0 || pal >= PAL_MAX * 2) { 10838c2ecf20Sopenharmony_ci dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 10848c2ecf20Sopenharmony_ci fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; 10858c2ecf20Sopenharmony_ci } else { 10868c2ecf20Sopenharmony_ci pal_desc = &fbi->dma_buff->pal_desc[pal]; 10878c2ecf20Sopenharmony_ci pal_desc_off = offsetof(struct pxafb_dma_buff, pal_desc[pal]); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE; 10908c2ecf20Sopenharmony_ci pal_desc->fidr = 0; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0) 10938c2ecf20Sopenharmony_ci pal_desc->ldcmd = fbi->palette_size * sizeof(u16); 10948c2ecf20Sopenharmony_ci else 10958c2ecf20Sopenharmony_ci pal_desc->ldcmd = fbi->palette_size * sizeof(u32); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci pal_desc->ldcmd |= LDCMD_PAL; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci /* flip back and forth between palette and frame buffer */ 11008c2ecf20Sopenharmony_ci pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 11018c2ecf20Sopenharmony_ci dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off; 11028c2ecf20Sopenharmony_ci fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci return 0; 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic void setup_base_frame(struct pxafb_info *fbi, 11098c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var, 11108c2ecf20Sopenharmony_ci int branch) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fb.fix; 11138c2ecf20Sopenharmony_ci int nbytes, dma, pal, bpp = var->bits_per_pixel; 11148c2ecf20Sopenharmony_ci unsigned long offset; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci dma = DMA_BASE + (branch ? DMA_MAX : 0); 11178c2ecf20Sopenharmony_ci pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci nbytes = fix->line_length * var->yres; 11208c2ecf20Sopenharmony_ci offset = fix->line_length * var->yoffset + fbi->video_mem_phys; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (fbi->lccr0 & LCCR0_SDS) { 11238c2ecf20Sopenharmony_ci nbytes = nbytes / 2; 11248c2ecf20Sopenharmony_ci setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes); 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci setup_frame_dma(fbi, dma, pal, offset, nbytes); 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 11318c2ecf20Sopenharmony_cistatic int setup_smart_dma(struct pxafb_info *fbi) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci struct pxafb_dma_descriptor *dma_desc; 11348c2ecf20Sopenharmony_ci unsigned long dma_desc_off, cmd_buff_off; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci dma_desc = &fbi->dma_buff->dma_desc[DMA_CMD]; 11378c2ecf20Sopenharmony_ci dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[DMA_CMD]); 11388c2ecf20Sopenharmony_ci cmd_buff_off = offsetof(struct pxafb_dma_buff, cmd_buff); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; 11418c2ecf20Sopenharmony_ci dma_desc->fsadr = fbi->dma_buff_phys + cmd_buff_off; 11428c2ecf20Sopenharmony_ci dma_desc->fidr = 0; 11438c2ecf20Sopenharmony_ci dma_desc->ldcmd = fbi->n_smart_cmds * sizeof(uint16_t); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci fbi->fdadr[DMA_CMD] = dma_desc->fdadr; 11468c2ecf20Sopenharmony_ci return 0; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ciint pxafb_smart_flush(struct fb_info *info) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 11528c2ecf20Sopenharmony_ci uint32_t prsr; 11538c2ecf20Sopenharmony_ci int ret = 0; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* disable controller until all registers are set up */ 11568c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 1. make it an even number of commands to align on 32-bit boundary 11598c2ecf20Sopenharmony_ci * 2. add the interrupt command to the end of the chain so we can 11608c2ecf20Sopenharmony_ci * keep track of the end of the transfer 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci while (fbi->n_smart_cmds & 1) 11648c2ecf20Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_INTERRUPT; 11678c2ecf20Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC; 11688c2ecf20Sopenharmony_ci setup_smart_dma(fbi); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* continue to execute next command */ 11718c2ecf20Sopenharmony_ci prsr = lcd_readl(fbi, PRSR) | PRSR_ST_OK | PRSR_CON_NT; 11728c2ecf20Sopenharmony_ci lcd_writel(fbi, PRSR, prsr); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* stop the processor in case it executed "wait for sync" cmd */ 11758c2ecf20Sopenharmony_ci lcd_writel(fbi, CMDCR, 0x0001); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* don't send interrupts for fifo underruns on channel 6 */ 11788c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR5, LCCR5_IUM(6)); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR1, fbi->reg_lccr1); 11818c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR2, fbi->reg_lccr2); 11828c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR3, fbi->reg_lccr3); 11838c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR4, fbi->reg_lccr4); 11848c2ecf20Sopenharmony_ci lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 11858c2ecf20Sopenharmony_ci lcd_writel(fbi, FDADR6, fbi->fdadr[6]); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci /* begin sending */ 11888c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) { 11918c2ecf20Sopenharmony_ci pr_warn("%s: timeout waiting for command done\n", __func__); 11928c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* quick disable */ 11968c2ecf20Sopenharmony_ci prsr = lcd_readl(fbi, PRSR) & ~(PRSR_ST_OK | PRSR_CON_NT); 11978c2ecf20Sopenharmony_ci lcd_writel(fbi, PRSR, prsr); 11988c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 11998c2ecf20Sopenharmony_ci lcd_writel(fbi, FDADR6, 0); 12008c2ecf20Sopenharmony_ci fbi->n_smart_cmds = 0; 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ciint pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci int i; 12078c2ecf20Sopenharmony_ci struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci for (i = 0; i < n_cmds; i++, cmds++) { 12108c2ecf20Sopenharmony_ci /* if it is a software delay, flush and delay */ 12118c2ecf20Sopenharmony_ci if ((*cmds & 0xff00) == SMART_CMD_DELAY) { 12128c2ecf20Sopenharmony_ci pxafb_smart_flush(info); 12138c2ecf20Sopenharmony_ci mdelay(*cmds & 0xff); 12148c2ecf20Sopenharmony_ci continue; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci /* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */ 12188c2ecf20Sopenharmony_ci if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8) 12198c2ecf20Sopenharmony_ci pxafb_smart_flush(info); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic unsigned int __smart_timing(unsigned time_ns, unsigned long lcd_clk) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci unsigned int t = (time_ns * (lcd_clk / 1000000) / 1000); 12308c2ecf20Sopenharmony_ci return (t == 0) ? 1 : t; 12318c2ecf20Sopenharmony_ci} 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_cistatic void setup_smart_timing(struct pxafb_info *fbi, 12348c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf = fbi->inf; 12378c2ecf20Sopenharmony_ci struct pxafb_mode_info *mode = &inf->modes[0]; 12388c2ecf20Sopenharmony_ci unsigned long lclk = clk_get_rate(fbi->clk); 12398c2ecf20Sopenharmony_ci unsigned t1, t2, t3, t4; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci t1 = max(mode->a0csrd_set_hld, mode->a0cswr_set_hld); 12428c2ecf20Sopenharmony_ci t2 = max(mode->rd_pulse_width, mode->wr_pulse_width); 12438c2ecf20Sopenharmony_ci t3 = mode->op_hold_time; 12448c2ecf20Sopenharmony_ci t4 = mode->cmd_inh_time; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci fbi->reg_lccr1 = 12478c2ecf20Sopenharmony_ci LCCR1_DisWdth(var->xres) | 12488c2ecf20Sopenharmony_ci LCCR1_BegLnDel(__smart_timing(t1, lclk)) | 12498c2ecf20Sopenharmony_ci LCCR1_EndLnDel(__smart_timing(t2, lclk)) | 12508c2ecf20Sopenharmony_ci LCCR1_HorSnchWdth(__smart_timing(t3, lclk)); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci fbi->reg_lccr2 = LCCR2_DisHght(var->yres); 12538c2ecf20Sopenharmony_ci fbi->reg_lccr3 = fbi->lccr3 | LCCR3_PixClkDiv(__smart_timing(t4, lclk)); 12548c2ecf20Sopenharmony_ci fbi->reg_lccr3 |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HSP : 0; 12558c2ecf20Sopenharmony_ci fbi->reg_lccr3 |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VSP : 0; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci /* FIXME: make this configurable */ 12588c2ecf20Sopenharmony_ci fbi->reg_cmdcr = 1; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic int pxafb_smart_thread(void *arg) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct pxafb_info *fbi = arg; 12648c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf = fbi->inf; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci if (!inf->smart_update) { 12678c2ecf20Sopenharmony_ci pr_err("%s: not properly initialized, thread terminated\n", 12688c2ecf20Sopenharmony_ci __func__); 12698c2ecf20Sopenharmony_ci return -EINVAL; 12708c2ecf20Sopenharmony_ci } 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci pr_debug("%s(): task starting\n", __func__); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci set_freezable(); 12758c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (try_to_freeze()) 12788c2ecf20Sopenharmony_ci continue; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci mutex_lock(&fbi->ctrlr_lock); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (fbi->state == C_ENABLE) { 12838c2ecf20Sopenharmony_ci inf->smart_update(&fbi->fb); 12848c2ecf20Sopenharmony_ci complete(&fbi->refresh_done); 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci mutex_unlock(&fbi->ctrlr_lock); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 12908c2ecf20Sopenharmony_ci schedule_timeout(msecs_to_jiffies(30)); 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci pr_debug("%s(): task ending\n", __func__); 12948c2ecf20Sopenharmony_ci return 0; 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int pxafb_smart_init(struct pxafb_info *fbi) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci if (!(fbi->lccr0 & LCCR0_LCDT)) 13008c2ecf20Sopenharmony_ci return 0; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff; 13038c2ecf20Sopenharmony_ci fbi->n_smart_cmds = 0; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci init_completion(&fbi->command_done); 13068c2ecf20Sopenharmony_ci init_completion(&fbi->refresh_done); 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi, 13098c2ecf20Sopenharmony_ci "lcd_refresh"); 13108c2ecf20Sopenharmony_ci if (IS_ERR(fbi->smart_thread)) { 13118c2ecf20Sopenharmony_ci pr_err("%s: unable to create kernel thread\n", __func__); 13128c2ecf20Sopenharmony_ci return PTR_ERR(fbi->smart_thread); 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci return 0; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci#else 13188c2ecf20Sopenharmony_cistatic inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; } 13198c2ecf20Sopenharmony_ci#endif /* CONFIG_FB_PXA_SMARTPANEL */ 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_cistatic void setup_parallel_timing(struct pxafb_info *fbi, 13228c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci fbi->reg_lccr1 = 13278c2ecf20Sopenharmony_ci LCCR1_DisWdth(var->xres) + 13288c2ecf20Sopenharmony_ci LCCR1_HorSnchWdth(var->hsync_len) + 13298c2ecf20Sopenharmony_ci LCCR1_BegLnDel(var->left_margin) + 13308c2ecf20Sopenharmony_ci LCCR1_EndLnDel(var->right_margin); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci /* 13338c2ecf20Sopenharmony_ci * If we have a dual scan LCD, we need to halve 13348c2ecf20Sopenharmony_ci * the YRES parameter. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_ci lines_per_panel = var->yres; 13378c2ecf20Sopenharmony_ci if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) 13388c2ecf20Sopenharmony_ci lines_per_panel /= 2; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci fbi->reg_lccr2 = 13418c2ecf20Sopenharmony_ci LCCR2_DisHght(lines_per_panel) + 13428c2ecf20Sopenharmony_ci LCCR2_VrtSnchWdth(var->vsync_len) + 13438c2ecf20Sopenharmony_ci LCCR2_BegFrmDel(var->upper_margin) + 13448c2ecf20Sopenharmony_ci LCCR2_EndFrmDel(var->lower_margin); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci fbi->reg_lccr3 = fbi->lccr3 | 13478c2ecf20Sopenharmony_ci (var->sync & FB_SYNC_HOR_HIGH_ACT ? 13488c2ecf20Sopenharmony_ci LCCR3_HorSnchH : LCCR3_HorSnchL) | 13498c2ecf20Sopenharmony_ci (var->sync & FB_SYNC_VERT_HIGH_ACT ? 13508c2ecf20Sopenharmony_ci LCCR3_VrtSnchH : LCCR3_VrtSnchL); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (pcd) { 13538c2ecf20Sopenharmony_ci fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd); 13548c2ecf20Sopenharmony_ci set_hsync_time(fbi, pcd); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/* 13598c2ecf20Sopenharmony_ci * pxafb_activate_var(): 13608c2ecf20Sopenharmony_ci * Configures LCD Controller based on entries in var parameter. 13618c2ecf20Sopenharmony_ci * Settings are only written to the controller if changes were made. 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_cistatic int pxafb_activate_var(struct fb_var_screeninfo *var, 13648c2ecf20Sopenharmony_ci struct pxafb_info *fbi) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci u_long flags; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci /* Update shadow copy atomically */ 13698c2ecf20Sopenharmony_ci local_irq_save(flags); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 13728c2ecf20Sopenharmony_ci if (fbi->lccr0 & LCCR0_LCDT) 13738c2ecf20Sopenharmony_ci setup_smart_timing(fbi, var); 13748c2ecf20Sopenharmony_ci else 13758c2ecf20Sopenharmony_ci#endif 13768c2ecf20Sopenharmony_ci setup_parallel_timing(fbi, var); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci setup_base_frame(fbi, var, 0); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci fbi->reg_lccr0 = fbi->lccr0 | 13818c2ecf20Sopenharmony_ci (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | 13828c2ecf20Sopenharmony_ci LCCR0_QDM | LCCR0_BM | LCCR0_OUM); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci fbi->reg_lccr3 |= pxafb_var_to_lccr3(var); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; 13878c2ecf20Sopenharmony_ci fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); 13888c2ecf20Sopenharmony_ci local_irq_restore(flags); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* 13918c2ecf20Sopenharmony_ci * Only update the registers if the controller is enabled 13928c2ecf20Sopenharmony_ci * and something has changed. 13938c2ecf20Sopenharmony_ci */ 13948c2ecf20Sopenharmony_ci if ((lcd_readl(fbi, LCCR0) != fbi->reg_lccr0) || 13958c2ecf20Sopenharmony_ci (lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) || 13968c2ecf20Sopenharmony_ci (lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) || 13978c2ecf20Sopenharmony_ci (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) || 13988c2ecf20Sopenharmony_ci (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) || 13998c2ecf20Sopenharmony_ci (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) || 14008c2ecf20Sopenharmony_ci ((fbi->lccr0 & LCCR0_SDS) && 14018c2ecf20Sopenharmony_ci (lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))) 14028c2ecf20Sopenharmony_ci pxafb_schedule_work(fbi, C_REENABLE); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci return 0; 14058c2ecf20Sopenharmony_ci} 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci/* 14088c2ecf20Sopenharmony_ci * NOTE! The following functions are purely helpers for set_ctrlr_state. 14098c2ecf20Sopenharmony_ci * Do not call them directly; set_ctrlr_state does the correct serialisation 14108c2ecf20Sopenharmony_ci * to ensure that things happen in the right way 100% of time time. 14118c2ecf20Sopenharmony_ci * -- rmk 14128c2ecf20Sopenharmony_ci */ 14138c2ecf20Sopenharmony_cistatic inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff"); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (fbi->backlight_power) 14188c2ecf20Sopenharmony_ci fbi->backlight_power(on); 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_cistatic inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff"); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (fbi->lcd_power) 14268c2ecf20Sopenharmony_ci fbi->lcd_power(on, &fbi->fb.var); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (fbi->lcd_supply && fbi->lcd_supply_enabled != on) { 14298c2ecf20Sopenharmony_ci int ret; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (on) 14328c2ecf20Sopenharmony_ci ret = regulator_enable(fbi->lcd_supply); 14338c2ecf20Sopenharmony_ci else 14348c2ecf20Sopenharmony_ci ret = regulator_disable(fbi->lcd_supply); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (ret < 0) 14378c2ecf20Sopenharmony_ci pr_warn("Unable to %s LCD supply regulator: %d\n", 14388c2ecf20Sopenharmony_ci on ? "enable" : "disable", ret); 14398c2ecf20Sopenharmony_ci else 14408c2ecf20Sopenharmony_ci fbi->lcd_supply_enabled = on; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic void pxafb_enable_controller(struct pxafb_info *fbi) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci pr_debug("pxafb: Enabling LCD controller\n"); 14478c2ecf20Sopenharmony_ci pr_debug("fdadr0 0x%08x\n", (unsigned int) fbi->fdadr[0]); 14488c2ecf20Sopenharmony_ci pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr[1]); 14498c2ecf20Sopenharmony_ci pr_debug("reg_lccr0 0x%08x\n", (unsigned int) fbi->reg_lccr0); 14508c2ecf20Sopenharmony_ci pr_debug("reg_lccr1 0x%08x\n", (unsigned int) fbi->reg_lccr1); 14518c2ecf20Sopenharmony_ci pr_debug("reg_lccr2 0x%08x\n", (unsigned int) fbi->reg_lccr2); 14528c2ecf20Sopenharmony_ci pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* enable LCD controller clock */ 14558c2ecf20Sopenharmony_ci if (clk_prepare_enable(fbi->clk)) { 14568c2ecf20Sopenharmony_ci pr_err("%s: Failed to prepare clock\n", __func__); 14578c2ecf20Sopenharmony_ci return; 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (fbi->lccr0 & LCCR0_LCDT) 14618c2ecf20Sopenharmony_ci return; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* Sequence from 11.7.10 */ 14648c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR4, fbi->reg_lccr4); 14658c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR3, fbi->reg_lccr3); 14668c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR2, fbi->reg_lccr2); 14678c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR1, fbi->reg_lccr1); 14688c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci lcd_writel(fbi, FDADR0, fbi->fdadr[0]); 14718c2ecf20Sopenharmony_ci if (fbi->lccr0 & LCCR0_SDS) 14728c2ecf20Sopenharmony_ci lcd_writel(fbi, FDADR1, fbi->fdadr[1]); 14738c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB); 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic void pxafb_disable_controller(struct pxafb_info *fbi) 14778c2ecf20Sopenharmony_ci{ 14788c2ecf20Sopenharmony_ci uint32_t lccr0; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 14818c2ecf20Sopenharmony_ci if (fbi->lccr0 & LCCR0_LCDT) { 14828c2ecf20Sopenharmony_ci wait_for_completion_timeout(&fbi->refresh_done, 14838c2ecf20Sopenharmony_ci msecs_to_jiffies(200)); 14848c2ecf20Sopenharmony_ci return; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci#endif 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci /* Clear LCD Status Register */ 14898c2ecf20Sopenharmony_ci lcd_writel(fbi, LCSR, 0xffffffff); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM; 14928c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, lccr0); 14938c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci wait_for_completion_timeout(&fbi->disable_done, msecs_to_jiffies(200)); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci /* disable LCD controller clock */ 14988c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk); 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci/* 15028c2ecf20Sopenharmony_ci * pxafb_handle_irq: Handle 'LCD DONE' interrupts. 15038c2ecf20Sopenharmony_ci */ 15048c2ecf20Sopenharmony_cistatic irqreturn_t pxafb_handle_irq(int irq, void *dev_id) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct pxafb_info *fbi = dev_id; 15078c2ecf20Sopenharmony_ci unsigned int lccr0, lcsr; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci lcsr = lcd_readl(fbi, LCSR); 15108c2ecf20Sopenharmony_ci if (lcsr & LCSR_LDD) { 15118c2ecf20Sopenharmony_ci lccr0 = lcd_readl(fbi, LCCR0); 15128c2ecf20Sopenharmony_ci lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); 15138c2ecf20Sopenharmony_ci complete(&fbi->disable_done); 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_SMARTPANEL 15178c2ecf20Sopenharmony_ci if (lcsr & LCSR_CMD_INT) 15188c2ecf20Sopenharmony_ci complete(&fbi->command_done); 15198c2ecf20Sopenharmony_ci#endif 15208c2ecf20Sopenharmony_ci lcd_writel(fbi, LCSR, lcsr); 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 15238c2ecf20Sopenharmony_ci { 15248c2ecf20Sopenharmony_ci unsigned int lcsr1 = lcd_readl(fbi, LCSR1); 15258c2ecf20Sopenharmony_ci if (lcsr1 & LCSR1_BS(1)) 15268c2ecf20Sopenharmony_ci complete(&fbi->overlay[0].branch_done); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (lcsr1 & LCSR1_BS(2)) 15298c2ecf20Sopenharmony_ci complete(&fbi->overlay[1].branch_done); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci lcd_writel(fbi, LCSR1, lcsr1); 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci#endif 15348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci/* 15388c2ecf20Sopenharmony_ci * This function must be called from task context only, since it will 15398c2ecf20Sopenharmony_ci * sleep when disabling the LCD controller, or if we get two contending 15408c2ecf20Sopenharmony_ci * processes trying to alter state. 15418c2ecf20Sopenharmony_ci */ 15428c2ecf20Sopenharmony_cistatic void set_ctrlr_state(struct pxafb_info *fbi, u_int state) 15438c2ecf20Sopenharmony_ci{ 15448c2ecf20Sopenharmony_ci u_int old_state; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci mutex_lock(&fbi->ctrlr_lock); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci old_state = fbi->state; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* 15518c2ecf20Sopenharmony_ci * Hack around fbcon initialisation. 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_ci if (old_state == C_STARTUP && state == C_REENABLE) 15548c2ecf20Sopenharmony_ci state = C_ENABLE; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci switch (state) { 15578c2ecf20Sopenharmony_ci case C_DISABLE_CLKCHANGE: 15588c2ecf20Sopenharmony_ci /* 15598c2ecf20Sopenharmony_ci * Disable controller for clock change. If the 15608c2ecf20Sopenharmony_ci * controller is already disabled, then do nothing. 15618c2ecf20Sopenharmony_ci */ 15628c2ecf20Sopenharmony_ci if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { 15638c2ecf20Sopenharmony_ci fbi->state = state; 15648c2ecf20Sopenharmony_ci /* TODO __pxafb_lcd_power(fbi, 0); */ 15658c2ecf20Sopenharmony_ci pxafb_disable_controller(fbi); 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci case C_DISABLE_PM: 15708c2ecf20Sopenharmony_ci case C_DISABLE: 15718c2ecf20Sopenharmony_ci /* 15728c2ecf20Sopenharmony_ci * Disable controller 15738c2ecf20Sopenharmony_ci */ 15748c2ecf20Sopenharmony_ci if (old_state != C_DISABLE) { 15758c2ecf20Sopenharmony_ci fbi->state = state; 15768c2ecf20Sopenharmony_ci __pxafb_backlight_power(fbi, 0); 15778c2ecf20Sopenharmony_ci __pxafb_lcd_power(fbi, 0); 15788c2ecf20Sopenharmony_ci if (old_state != C_DISABLE_CLKCHANGE) 15798c2ecf20Sopenharmony_ci pxafb_disable_controller(fbi); 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci break; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci case C_ENABLE_CLKCHANGE: 15848c2ecf20Sopenharmony_ci /* 15858c2ecf20Sopenharmony_ci * Enable the controller after clock change. Only 15868c2ecf20Sopenharmony_ci * do this if we were disabled for the clock change. 15878c2ecf20Sopenharmony_ci */ 15888c2ecf20Sopenharmony_ci if (old_state == C_DISABLE_CLKCHANGE) { 15898c2ecf20Sopenharmony_ci fbi->state = C_ENABLE; 15908c2ecf20Sopenharmony_ci pxafb_enable_controller(fbi); 15918c2ecf20Sopenharmony_ci /* TODO __pxafb_lcd_power(fbi, 1); */ 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci break; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci case C_REENABLE: 15968c2ecf20Sopenharmony_ci /* 15978c2ecf20Sopenharmony_ci * Re-enable the controller only if it was already 15988c2ecf20Sopenharmony_ci * enabled. This is so we reprogram the control 15998c2ecf20Sopenharmony_ci * registers. 16008c2ecf20Sopenharmony_ci */ 16018c2ecf20Sopenharmony_ci if (old_state == C_ENABLE) { 16028c2ecf20Sopenharmony_ci __pxafb_lcd_power(fbi, 0); 16038c2ecf20Sopenharmony_ci pxafb_disable_controller(fbi); 16048c2ecf20Sopenharmony_ci pxafb_enable_controller(fbi); 16058c2ecf20Sopenharmony_ci __pxafb_lcd_power(fbi, 1); 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci break; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci case C_ENABLE_PM: 16108c2ecf20Sopenharmony_ci /* 16118c2ecf20Sopenharmony_ci * Re-enable the controller after PM. This is not 16128c2ecf20Sopenharmony_ci * perfect - think about the case where we were doing 16138c2ecf20Sopenharmony_ci * a clock change, and we suspended half-way through. 16148c2ecf20Sopenharmony_ci */ 16158c2ecf20Sopenharmony_ci if (old_state != C_DISABLE_PM) 16168c2ecf20Sopenharmony_ci break; 16178c2ecf20Sopenharmony_ci fallthrough; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci case C_ENABLE: 16208c2ecf20Sopenharmony_ci /* 16218c2ecf20Sopenharmony_ci * Power up the LCD screen, enable controller, and 16228c2ecf20Sopenharmony_ci * turn on the backlight. 16238c2ecf20Sopenharmony_ci */ 16248c2ecf20Sopenharmony_ci if (old_state != C_ENABLE) { 16258c2ecf20Sopenharmony_ci fbi->state = C_ENABLE; 16268c2ecf20Sopenharmony_ci pxafb_enable_controller(fbi); 16278c2ecf20Sopenharmony_ci __pxafb_lcd_power(fbi, 1); 16288c2ecf20Sopenharmony_ci __pxafb_backlight_power(fbi, 1); 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci mutex_unlock(&fbi->ctrlr_lock); 16338c2ecf20Sopenharmony_ci} 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci/* 16368c2ecf20Sopenharmony_ci * Our LCD controller task (which is called when we blank or unblank) 16378c2ecf20Sopenharmony_ci * via keventd. 16388c2ecf20Sopenharmony_ci */ 16398c2ecf20Sopenharmony_cistatic void pxafb_task(struct work_struct *work) 16408c2ecf20Sopenharmony_ci{ 16418c2ecf20Sopenharmony_ci struct pxafb_info *fbi = 16428c2ecf20Sopenharmony_ci container_of(work, struct pxafb_info, task); 16438c2ecf20Sopenharmony_ci u_int state = xchg(&fbi->task_state, -1); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, state); 16468c2ecf20Sopenharmony_ci} 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 16498c2ecf20Sopenharmony_ci/* 16508c2ecf20Sopenharmony_ci * CPU clock speed change handler. We need to adjust the LCD timing 16518c2ecf20Sopenharmony_ci * parameters when the CPU clock is adjusted by the power management 16528c2ecf20Sopenharmony_ci * subsystem. 16538c2ecf20Sopenharmony_ci * 16548c2ecf20Sopenharmony_ci * TODO: Determine why f->new != 10*get_lclk_frequency_10khz() 16558c2ecf20Sopenharmony_ci */ 16568c2ecf20Sopenharmony_cistatic int 16578c2ecf20Sopenharmony_cipxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data) 16588c2ecf20Sopenharmony_ci{ 16598c2ecf20Sopenharmony_ci struct pxafb_info *fbi = TO_INF(nb, freq_transition); 16608c2ecf20Sopenharmony_ci /* TODO struct cpufreq_freqs *f = data; */ 16618c2ecf20Sopenharmony_ci u_int pcd; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci switch (val) { 16648c2ecf20Sopenharmony_ci case CPUFREQ_PRECHANGE: 16658c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 16668c2ecf20Sopenharmony_ci if (!(fbi->overlay[0].usage || fbi->overlay[1].usage)) 16678c2ecf20Sopenharmony_ci#endif 16688c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); 16698c2ecf20Sopenharmony_ci break; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci case CPUFREQ_POSTCHANGE: 16728c2ecf20Sopenharmony_ci pcd = get_pcd(fbi, fbi->fb.var.pixclock); 16738c2ecf20Sopenharmony_ci set_hsync_time(fbi, pcd); 16748c2ecf20Sopenharmony_ci fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | 16758c2ecf20Sopenharmony_ci LCCR3_PixClkDiv(pcd); 16768c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); 16778c2ecf20Sopenharmony_ci break; 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci return 0; 16808c2ecf20Sopenharmony_ci} 16818c2ecf20Sopenharmony_ci#endif 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 16848c2ecf20Sopenharmony_ci/* 16858c2ecf20Sopenharmony_ci * Power management hooks. Note that we won't be called from IRQ context, 16868c2ecf20Sopenharmony_ci * unlike the blank functions above, so we may sleep. 16878c2ecf20Sopenharmony_ci */ 16888c2ecf20Sopenharmony_cistatic int pxafb_suspend(struct device *dev) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci struct pxafb_info *fbi = dev_get_drvdata(dev); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_DISABLE_PM); 16938c2ecf20Sopenharmony_ci return 0; 16948c2ecf20Sopenharmony_ci} 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_cistatic int pxafb_resume(struct device *dev) 16978c2ecf20Sopenharmony_ci{ 16988c2ecf20Sopenharmony_ci struct pxafb_info *fbi = dev_get_drvdata(dev); 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE_PM); 17018c2ecf20Sopenharmony_ci return 0; 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pxafb_pm_ops = { 17058c2ecf20Sopenharmony_ci .suspend = pxafb_suspend, 17068c2ecf20Sopenharmony_ci .resume = pxafb_resume, 17078c2ecf20Sopenharmony_ci}; 17088c2ecf20Sopenharmony_ci#endif 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic int pxafb_init_video_memory(struct pxafb_info *fbi) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci int size = PAGE_ALIGN(fbi->video_mem_size); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); 17158c2ecf20Sopenharmony_ci if (fbi->video_mem == NULL) 17168c2ecf20Sopenharmony_ci return -ENOMEM; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci fbi->video_mem_phys = virt_to_phys(fbi->video_mem); 17198c2ecf20Sopenharmony_ci fbi->video_mem_size = size; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci fbi->fb.fix.smem_start = fbi->video_mem_phys; 17228c2ecf20Sopenharmony_ci fbi->fb.fix.smem_len = fbi->video_mem_size; 17238c2ecf20Sopenharmony_ci fbi->fb.screen_base = fbi->video_mem; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci return fbi->video_mem ? 0 : -ENOMEM; 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_cistatic void pxafb_decode_mach_info(struct pxafb_info *fbi, 17298c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf) 17308c2ecf20Sopenharmony_ci{ 17318c2ecf20Sopenharmony_ci unsigned int lcd_conn = inf->lcd_conn; 17328c2ecf20Sopenharmony_ci struct pxafb_mode_info *m; 17338c2ecf20Sopenharmony_ci int i; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci fbi->cmap_inverse = inf->cmap_inverse; 17368c2ecf20Sopenharmony_ci fbi->cmap_static = inf->cmap_static; 17378c2ecf20Sopenharmony_ci fbi->lccr4 = inf->lccr4; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci switch (lcd_conn & LCD_TYPE_MASK) { 17408c2ecf20Sopenharmony_ci case LCD_TYPE_MONO_STN: 17418c2ecf20Sopenharmony_ci fbi->lccr0 = LCCR0_CMS; 17428c2ecf20Sopenharmony_ci break; 17438c2ecf20Sopenharmony_ci case LCD_TYPE_MONO_DSTN: 17448c2ecf20Sopenharmony_ci fbi->lccr0 = LCCR0_CMS | LCCR0_SDS; 17458c2ecf20Sopenharmony_ci break; 17468c2ecf20Sopenharmony_ci case LCD_TYPE_COLOR_STN: 17478c2ecf20Sopenharmony_ci fbi->lccr0 = 0; 17488c2ecf20Sopenharmony_ci break; 17498c2ecf20Sopenharmony_ci case LCD_TYPE_COLOR_DSTN: 17508c2ecf20Sopenharmony_ci fbi->lccr0 = LCCR0_SDS; 17518c2ecf20Sopenharmony_ci break; 17528c2ecf20Sopenharmony_ci case LCD_TYPE_COLOR_TFT: 17538c2ecf20Sopenharmony_ci fbi->lccr0 = LCCR0_PAS; 17548c2ecf20Sopenharmony_ci break; 17558c2ecf20Sopenharmony_ci case LCD_TYPE_SMART_PANEL: 17568c2ecf20Sopenharmony_ci fbi->lccr0 = LCCR0_LCDT | LCCR0_PAS; 17578c2ecf20Sopenharmony_ci break; 17588c2ecf20Sopenharmony_ci default: 17598c2ecf20Sopenharmony_ci /* fall back to backward compatibility way */ 17608c2ecf20Sopenharmony_ci fbi->lccr0 = inf->lccr0; 17618c2ecf20Sopenharmony_ci fbi->lccr3 = inf->lccr3; 17628c2ecf20Sopenharmony_ci goto decode_mode; 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (lcd_conn == LCD_MONO_STN_8BPP) 17668c2ecf20Sopenharmony_ci fbi->lccr0 |= LCCR0_DPD; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci fbi->lccr0 |= (lcd_conn & LCD_ALTERNATE_MAPPING) ? LCCR0_LDDALT : 0; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci fbi->lccr3 = LCCR3_Acb((inf->lcd_conn >> 10) & 0xff); 17718c2ecf20Sopenharmony_ci fbi->lccr3 |= (lcd_conn & LCD_BIAS_ACTIVE_LOW) ? LCCR3_OEP : 0; 17728c2ecf20Sopenharmony_ci fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cidecode_mode: 17758c2ecf20Sopenharmony_ci pxafb_setmode(&fbi->fb.var, &inf->modes[0]); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci /* decide video memory size as follows: 17788c2ecf20Sopenharmony_ci * 1. default to mode of maximum resolution 17798c2ecf20Sopenharmony_ci * 2. allow platform to override 17808c2ecf20Sopenharmony_ci * 3. allow module parameter to override 17818c2ecf20Sopenharmony_ci */ 17828c2ecf20Sopenharmony_ci for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++) 17838c2ecf20Sopenharmony_ci fbi->video_mem_size = max_t(size_t, fbi->video_mem_size, 17848c2ecf20Sopenharmony_ci m->xres * m->yres * m->bpp / 8); 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (inf->video_mem_size > fbi->video_mem_size) 17878c2ecf20Sopenharmony_ci fbi->video_mem_size = inf->video_mem_size; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if (video_mem_size > fbi->video_mem_size) 17908c2ecf20Sopenharmony_ci fbi->video_mem_size = video_mem_size; 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_cistatic struct pxafb_info *pxafb_init_fbinfo(struct device *dev, 17948c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf) 17958c2ecf20Sopenharmony_ci{ 17968c2ecf20Sopenharmony_ci struct pxafb_info *fbi; 17978c2ecf20Sopenharmony_ci void *addr; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Alloc the pxafb_info and pseudo_palette in one step */ 18008c2ecf20Sopenharmony_ci fbi = devm_kzalloc(dev, sizeof(struct pxafb_info) + sizeof(u32) * 16, 18018c2ecf20Sopenharmony_ci GFP_KERNEL); 18028c2ecf20Sopenharmony_ci if (!fbi) 18038c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci fbi->dev = dev; 18068c2ecf20Sopenharmony_ci fbi->inf = inf; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci fbi->clk = devm_clk_get(dev, NULL); 18098c2ecf20Sopenharmony_ci if (IS_ERR(fbi->clk)) 18108c2ecf20Sopenharmony_ci return ERR_CAST(fbi->clk); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci strcpy(fbi->fb.fix.id, PXA_NAME); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 18158c2ecf20Sopenharmony_ci fbi->fb.fix.type_aux = 0; 18168c2ecf20Sopenharmony_ci fbi->fb.fix.xpanstep = 0; 18178c2ecf20Sopenharmony_ci fbi->fb.fix.ypanstep = 1; 18188c2ecf20Sopenharmony_ci fbi->fb.fix.ywrapstep = 0; 18198c2ecf20Sopenharmony_ci fbi->fb.fix.accel = FB_ACCEL_NONE; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci fbi->fb.var.nonstd = 0; 18228c2ecf20Sopenharmony_ci fbi->fb.var.activate = FB_ACTIVATE_NOW; 18238c2ecf20Sopenharmony_ci fbi->fb.var.height = -1; 18248c2ecf20Sopenharmony_ci fbi->fb.var.width = -1; 18258c2ecf20Sopenharmony_ci fbi->fb.var.accel_flags = FB_ACCELF_TEXT; 18268c2ecf20Sopenharmony_ci fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci fbi->fb.fbops = &pxafb_ops; 18298c2ecf20Sopenharmony_ci fbi->fb.flags = FBINFO_DEFAULT; 18308c2ecf20Sopenharmony_ci fbi->fb.node = -1; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci addr = fbi; 18338c2ecf20Sopenharmony_ci addr = addr + sizeof(struct pxafb_info); 18348c2ecf20Sopenharmony_ci fbi->fb.pseudo_palette = addr; 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci fbi->state = C_STARTUP; 18378c2ecf20Sopenharmony_ci fbi->task_state = (u_char)-1; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci pxafb_decode_mach_info(fbi, inf); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_OVERLAY 18428c2ecf20Sopenharmony_ci /* place overlay(s) on top of base */ 18438c2ecf20Sopenharmony_ci if (pxafb_overlay_supported()) 18448c2ecf20Sopenharmony_ci fbi->lccr0 |= LCCR0_OUC; 18458c2ecf20Sopenharmony_ci#endif 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci init_waitqueue_head(&fbi->ctrlr_wait); 18488c2ecf20Sopenharmony_ci INIT_WORK(&fbi->task, pxafb_task); 18498c2ecf20Sopenharmony_ci mutex_init(&fbi->ctrlr_lock); 18508c2ecf20Sopenharmony_ci init_completion(&fbi->disable_done); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci return fbi; 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_PXA_PARAMETERS 18568c2ecf20Sopenharmony_cistatic int parse_opt_mode(struct device *dev, const char *this_opt, 18578c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf) 18588c2ecf20Sopenharmony_ci{ 18598c2ecf20Sopenharmony_ci const char *name = this_opt+5; 18608c2ecf20Sopenharmony_ci unsigned int namelen = strlen(name); 18618c2ecf20Sopenharmony_ci int res_specified = 0, bpp_specified = 0; 18628c2ecf20Sopenharmony_ci unsigned int xres = 0, yres = 0, bpp = 0; 18638c2ecf20Sopenharmony_ci int yres_specified = 0; 18648c2ecf20Sopenharmony_ci int i; 18658c2ecf20Sopenharmony_ci for (i = namelen-1; i >= 0; i--) { 18668c2ecf20Sopenharmony_ci switch (name[i]) { 18678c2ecf20Sopenharmony_ci case '-': 18688c2ecf20Sopenharmony_ci namelen = i; 18698c2ecf20Sopenharmony_ci if (!bpp_specified && !yres_specified) { 18708c2ecf20Sopenharmony_ci bpp = simple_strtoul(&name[i+1], NULL, 0); 18718c2ecf20Sopenharmony_ci bpp_specified = 1; 18728c2ecf20Sopenharmony_ci } else 18738c2ecf20Sopenharmony_ci goto done; 18748c2ecf20Sopenharmony_ci break; 18758c2ecf20Sopenharmony_ci case 'x': 18768c2ecf20Sopenharmony_ci if (!yres_specified) { 18778c2ecf20Sopenharmony_ci yres = simple_strtoul(&name[i+1], NULL, 0); 18788c2ecf20Sopenharmony_ci yres_specified = 1; 18798c2ecf20Sopenharmony_ci } else 18808c2ecf20Sopenharmony_ci goto done; 18818c2ecf20Sopenharmony_ci break; 18828c2ecf20Sopenharmony_ci case '0' ... '9': 18838c2ecf20Sopenharmony_ci break; 18848c2ecf20Sopenharmony_ci default: 18858c2ecf20Sopenharmony_ci goto done; 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci if (i < 0 && yres_specified) { 18898c2ecf20Sopenharmony_ci xres = simple_strtoul(name, NULL, 0); 18908c2ecf20Sopenharmony_ci res_specified = 1; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_cidone: 18938c2ecf20Sopenharmony_ci if (res_specified) { 18948c2ecf20Sopenharmony_ci dev_info(dev, "overriding resolution: %dx%d\n", xres, yres); 18958c2ecf20Sopenharmony_ci inf->modes[0].xres = xres; inf->modes[0].yres = yres; 18968c2ecf20Sopenharmony_ci } 18978c2ecf20Sopenharmony_ci if (bpp_specified) 18988c2ecf20Sopenharmony_ci switch (bpp) { 18998c2ecf20Sopenharmony_ci case 1: 19008c2ecf20Sopenharmony_ci case 2: 19018c2ecf20Sopenharmony_ci case 4: 19028c2ecf20Sopenharmony_ci case 8: 19038c2ecf20Sopenharmony_ci case 16: 19048c2ecf20Sopenharmony_ci inf->modes[0].bpp = bpp; 19058c2ecf20Sopenharmony_ci dev_info(dev, "overriding bit depth: %d\n", bpp); 19068c2ecf20Sopenharmony_ci break; 19078c2ecf20Sopenharmony_ci default: 19088c2ecf20Sopenharmony_ci dev_err(dev, "Depth %d is not valid\n", bpp); 19098c2ecf20Sopenharmony_ci return -EINVAL; 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci return 0; 19128c2ecf20Sopenharmony_ci} 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_cistatic int parse_opt(struct device *dev, char *this_opt, 19158c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci struct pxafb_mode_info *mode = &inf->modes[0]; 19188c2ecf20Sopenharmony_ci char s[64]; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci s[0] = '\0'; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "vmem:", 5)) { 19238c2ecf20Sopenharmony_ci video_mem_size = memparse(this_opt + 5, NULL); 19248c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "mode:", 5)) { 19258c2ecf20Sopenharmony_ci return parse_opt_mode(dev, this_opt, inf); 19268c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "pixclock:", 9)) { 19278c2ecf20Sopenharmony_ci mode->pixclock = simple_strtoul(this_opt+9, NULL, 0); 19288c2ecf20Sopenharmony_ci sprintf(s, "pixclock: %ld\n", mode->pixclock); 19298c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "left:", 5)) { 19308c2ecf20Sopenharmony_ci mode->left_margin = simple_strtoul(this_opt+5, NULL, 0); 19318c2ecf20Sopenharmony_ci sprintf(s, "left: %u\n", mode->left_margin); 19328c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "right:", 6)) { 19338c2ecf20Sopenharmony_ci mode->right_margin = simple_strtoul(this_opt+6, NULL, 0); 19348c2ecf20Sopenharmony_ci sprintf(s, "right: %u\n", mode->right_margin); 19358c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "upper:", 6)) { 19368c2ecf20Sopenharmony_ci mode->upper_margin = simple_strtoul(this_opt+6, NULL, 0); 19378c2ecf20Sopenharmony_ci sprintf(s, "upper: %u\n", mode->upper_margin); 19388c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "lower:", 6)) { 19398c2ecf20Sopenharmony_ci mode->lower_margin = simple_strtoul(this_opt+6, NULL, 0); 19408c2ecf20Sopenharmony_ci sprintf(s, "lower: %u\n", mode->lower_margin); 19418c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "hsynclen:", 9)) { 19428c2ecf20Sopenharmony_ci mode->hsync_len = simple_strtoul(this_opt+9, NULL, 0); 19438c2ecf20Sopenharmony_ci sprintf(s, "hsynclen: %u\n", mode->hsync_len); 19448c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "vsynclen:", 9)) { 19458c2ecf20Sopenharmony_ci mode->vsync_len = simple_strtoul(this_opt+9, NULL, 0); 19468c2ecf20Sopenharmony_ci sprintf(s, "vsynclen: %u\n", mode->vsync_len); 19478c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "hsync:", 6)) { 19488c2ecf20Sopenharmony_ci if (simple_strtoul(this_opt+6, NULL, 0) == 0) { 19498c2ecf20Sopenharmony_ci sprintf(s, "hsync: Active Low\n"); 19508c2ecf20Sopenharmony_ci mode->sync &= ~FB_SYNC_HOR_HIGH_ACT; 19518c2ecf20Sopenharmony_ci } else { 19528c2ecf20Sopenharmony_ci sprintf(s, "hsync: Active High\n"); 19538c2ecf20Sopenharmony_ci mode->sync |= FB_SYNC_HOR_HIGH_ACT; 19548c2ecf20Sopenharmony_ci } 19558c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "vsync:", 6)) { 19568c2ecf20Sopenharmony_ci if (simple_strtoul(this_opt+6, NULL, 0) == 0) { 19578c2ecf20Sopenharmony_ci sprintf(s, "vsync: Active Low\n"); 19588c2ecf20Sopenharmony_ci mode->sync &= ~FB_SYNC_VERT_HIGH_ACT; 19598c2ecf20Sopenharmony_ci } else { 19608c2ecf20Sopenharmony_ci sprintf(s, "vsync: Active High\n"); 19618c2ecf20Sopenharmony_ci mode->sync |= FB_SYNC_VERT_HIGH_ACT; 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "dpc:", 4)) { 19648c2ecf20Sopenharmony_ci if (simple_strtoul(this_opt+4, NULL, 0) == 0) { 19658c2ecf20Sopenharmony_ci sprintf(s, "double pixel clock: false\n"); 19668c2ecf20Sopenharmony_ci inf->lccr3 &= ~LCCR3_DPC; 19678c2ecf20Sopenharmony_ci } else { 19688c2ecf20Sopenharmony_ci sprintf(s, "double pixel clock: true\n"); 19698c2ecf20Sopenharmony_ci inf->lccr3 |= LCCR3_DPC; 19708c2ecf20Sopenharmony_ci } 19718c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "outputen:", 9)) { 19728c2ecf20Sopenharmony_ci if (simple_strtoul(this_opt+9, NULL, 0) == 0) { 19738c2ecf20Sopenharmony_ci sprintf(s, "output enable: active low\n"); 19748c2ecf20Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL; 19758c2ecf20Sopenharmony_ci } else { 19768c2ecf20Sopenharmony_ci sprintf(s, "output enable: active high\n"); 19778c2ecf20Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "pixclockpol:", 12)) { 19808c2ecf20Sopenharmony_ci if (simple_strtoul(this_opt+12, NULL, 0) == 0) { 19818c2ecf20Sopenharmony_ci sprintf(s, "pixel clock polarity: falling edge\n"); 19828c2ecf20Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg; 19838c2ecf20Sopenharmony_ci } else { 19848c2ecf20Sopenharmony_ci sprintf(s, "pixel clock polarity: rising edge\n"); 19858c2ecf20Sopenharmony_ci inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg; 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "color", 5)) { 19888c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color; 19898c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "mono", 4)) { 19908c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Mono; 19918c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "active", 6)) { 19928c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Act; 19938c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "passive", 7)) { 19948c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Pas; 19958c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "single", 6)) { 19968c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Sngl; 19978c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "dual", 4)) { 19988c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Dual; 19998c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "4pix", 4)) { 20008c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_4PixMono; 20018c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "8pix", 4)) { 20028c2ecf20Sopenharmony_ci inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_8PixMono; 20038c2ecf20Sopenharmony_ci } else { 20048c2ecf20Sopenharmony_ci dev_err(dev, "unknown option: %s\n", this_opt); 20058c2ecf20Sopenharmony_ci return -EINVAL; 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci if (s[0] != '\0') 20098c2ecf20Sopenharmony_ci dev_info(dev, "override %s", s); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci return 0; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_cistatic int pxafb_parse_options(struct device *dev, char *options, 20158c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf) 20168c2ecf20Sopenharmony_ci{ 20178c2ecf20Sopenharmony_ci char *this_opt; 20188c2ecf20Sopenharmony_ci int ret; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (!options || !*options) 20218c2ecf20Sopenharmony_ci return 0; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci dev_dbg(dev, "options are \"%s\"\n", options ? options : "null"); 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci /* could be made table driven or similar?... */ 20268c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 20278c2ecf20Sopenharmony_ci ret = parse_opt(dev, this_opt, inf); 20288c2ecf20Sopenharmony_ci if (ret) 20298c2ecf20Sopenharmony_ci return ret; 20308c2ecf20Sopenharmony_ci } 20318c2ecf20Sopenharmony_ci return 0; 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_cistatic char g_options[256] = ""; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci#ifndef MODULE 20378c2ecf20Sopenharmony_cistatic int __init pxafb_setup_options(void) 20388c2ecf20Sopenharmony_ci{ 20398c2ecf20Sopenharmony_ci char *options = NULL; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci if (fb_get_options("pxafb", &options)) 20428c2ecf20Sopenharmony_ci return -ENODEV; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci if (options) 20458c2ecf20Sopenharmony_ci strlcpy(g_options, options, sizeof(g_options)); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci return 0; 20488c2ecf20Sopenharmony_ci} 20498c2ecf20Sopenharmony_ci#else 20508c2ecf20Sopenharmony_ci#define pxafb_setup_options() (0) 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cimodule_param_string(options, g_options, sizeof(g_options), 0); 20538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.rst)"); 20548c2ecf20Sopenharmony_ci#endif 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci#else 20578c2ecf20Sopenharmony_ci#define pxafb_parse_options(...) (0) 20588c2ecf20Sopenharmony_ci#define pxafb_setup_options() (0) 20598c2ecf20Sopenharmony_ci#endif 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci#ifdef DEBUG_VAR 20628c2ecf20Sopenharmony_ci/* Check for various illegal bit-combinations. Currently only 20638c2ecf20Sopenharmony_ci * a warning is given. */ 20648c2ecf20Sopenharmony_cistatic void pxafb_check_options(struct device *dev, struct pxafb_mach_info *inf) 20658c2ecf20Sopenharmony_ci{ 20668c2ecf20Sopenharmony_ci if (inf->lcd_conn) 20678c2ecf20Sopenharmony_ci return; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK) 20708c2ecf20Sopenharmony_ci dev_warn(dev, "machine LCCR0 setting contains " 20718c2ecf20Sopenharmony_ci "illegal bits: %08x\n", 20728c2ecf20Sopenharmony_ci inf->lccr0 & LCCR0_INVALID_CONFIG_MASK); 20738c2ecf20Sopenharmony_ci if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK) 20748c2ecf20Sopenharmony_ci dev_warn(dev, "machine LCCR3 setting contains " 20758c2ecf20Sopenharmony_ci "illegal bits: %08x\n", 20768c2ecf20Sopenharmony_ci inf->lccr3 & LCCR3_INVALID_CONFIG_MASK); 20778c2ecf20Sopenharmony_ci if (inf->lccr0 & LCCR0_DPD && 20788c2ecf20Sopenharmony_ci ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas || 20798c2ecf20Sopenharmony_ci (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl || 20808c2ecf20Sopenharmony_ci (inf->lccr0 & LCCR0_CMS) != LCCR0_Mono)) 20818c2ecf20Sopenharmony_ci dev_warn(dev, "Double Pixel Data (DPD) mode is " 20828c2ecf20Sopenharmony_ci "only valid in passive mono" 20838c2ecf20Sopenharmony_ci " single panel mode\n"); 20848c2ecf20Sopenharmony_ci if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act && 20858c2ecf20Sopenharmony_ci (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) 20868c2ecf20Sopenharmony_ci dev_warn(dev, "Dual panel only valid in passive mode\n"); 20878c2ecf20Sopenharmony_ci if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && 20888c2ecf20Sopenharmony_ci (inf->modes->upper_margin || inf->modes->lower_margin)) 20898c2ecf20Sopenharmony_ci dev_warn(dev, "Upper and lower margins must be 0 in " 20908c2ecf20Sopenharmony_ci "passive mode\n"); 20918c2ecf20Sopenharmony_ci} 20928c2ecf20Sopenharmony_ci#else 20938c2ecf20Sopenharmony_ci#define pxafb_check_options(...) do {} while (0) 20948c2ecf20Sopenharmony_ci#endif 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci#if defined(CONFIG_OF) 20978c2ecf20Sopenharmony_cistatic const char * const lcd_types[] = { 20988c2ecf20Sopenharmony_ci "unknown", "mono-stn", "mono-dstn", "color-stn", "color-dstn", 20998c2ecf20Sopenharmony_ci "color-tft", "smart-panel", NULL 21008c2ecf20Sopenharmony_ci}; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_cistatic int of_get_pxafb_display(struct device *dev, struct device_node *disp, 21038c2ecf20Sopenharmony_ci struct pxafb_mach_info *info, u32 bus_width) 21048c2ecf20Sopenharmony_ci{ 21058c2ecf20Sopenharmony_ci struct display_timings *timings; 21068c2ecf20Sopenharmony_ci struct videomode vm; 21078c2ecf20Sopenharmony_ci int i, ret = -EINVAL; 21088c2ecf20Sopenharmony_ci const char *s; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci ret = of_property_read_string(disp, "lcd-type", &s); 21118c2ecf20Sopenharmony_ci if (ret) 21128c2ecf20Sopenharmony_ci s = "color-tft"; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci i = match_string(lcd_types, -1, s); 21158c2ecf20Sopenharmony_ci if (i < 0) { 21168c2ecf20Sopenharmony_ci dev_err(dev, "lcd-type %s is unknown\n", s); 21178c2ecf20Sopenharmony_ci return i; 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci info->lcd_conn |= LCD_CONN_TYPE(i); 21208c2ecf20Sopenharmony_ci info->lcd_conn |= LCD_CONN_WIDTH(bus_width); 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci timings = of_get_display_timings(disp); 21238c2ecf20Sopenharmony_ci if (!timings) 21248c2ecf20Sopenharmony_ci return -EINVAL; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci ret = -ENOMEM; 21278c2ecf20Sopenharmony_ci info->modes = devm_kcalloc(dev, timings->num_timings, 21288c2ecf20Sopenharmony_ci sizeof(info->modes[0]), 21298c2ecf20Sopenharmony_ci GFP_KERNEL); 21308c2ecf20Sopenharmony_ci if (!info->modes) 21318c2ecf20Sopenharmony_ci goto out; 21328c2ecf20Sopenharmony_ci info->num_modes = timings->num_timings; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci for (i = 0; i < timings->num_timings; i++) { 21358c2ecf20Sopenharmony_ci ret = videomode_from_timings(timings, &vm, i); 21368c2ecf20Sopenharmony_ci if (ret) { 21378c2ecf20Sopenharmony_ci dev_err(dev, "videomode_from_timings %d failed: %d\n", 21388c2ecf20Sopenharmony_ci i, ret); 21398c2ecf20Sopenharmony_ci goto out; 21408c2ecf20Sopenharmony_ci } 21418c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) 21428c2ecf20Sopenharmony_ci info->lcd_conn |= LCD_PCLK_EDGE_RISE; 21438c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) 21448c2ecf20Sopenharmony_ci info->lcd_conn |= LCD_PCLK_EDGE_FALL; 21458c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_DE_HIGH) 21468c2ecf20Sopenharmony_ci info->lcd_conn |= LCD_BIAS_ACTIVE_HIGH; 21478c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_DE_LOW) 21488c2ecf20Sopenharmony_ci info->lcd_conn |= LCD_BIAS_ACTIVE_LOW; 21498c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH) 21508c2ecf20Sopenharmony_ci info->modes[i].sync |= FB_SYNC_HOR_HIGH_ACT; 21518c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) 21528c2ecf20Sopenharmony_ci info->modes[i].sync |= FB_SYNC_VERT_HIGH_ACT; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci info->modes[i].pixclock = 1000000000UL / (vm.pixelclock / 1000); 21558c2ecf20Sopenharmony_ci info->modes[i].xres = vm.hactive; 21568c2ecf20Sopenharmony_ci info->modes[i].yres = vm.vactive; 21578c2ecf20Sopenharmony_ci info->modes[i].hsync_len = vm.hsync_len; 21588c2ecf20Sopenharmony_ci info->modes[i].left_margin = vm.hback_porch; 21598c2ecf20Sopenharmony_ci info->modes[i].right_margin = vm.hfront_porch; 21608c2ecf20Sopenharmony_ci info->modes[i].vsync_len = vm.vsync_len; 21618c2ecf20Sopenharmony_ci info->modes[i].upper_margin = vm.vback_porch; 21628c2ecf20Sopenharmony_ci info->modes[i].lower_margin = vm.vfront_porch; 21638c2ecf20Sopenharmony_ci } 21648c2ecf20Sopenharmony_ci ret = 0; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ciout: 21678c2ecf20Sopenharmony_ci display_timings_release(timings); 21688c2ecf20Sopenharmony_ci return ret; 21698c2ecf20Sopenharmony_ci} 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic int of_get_pxafb_mode_info(struct device *dev, 21728c2ecf20Sopenharmony_ci struct pxafb_mach_info *info) 21738c2ecf20Sopenharmony_ci{ 21748c2ecf20Sopenharmony_ci struct device_node *display, *np; 21758c2ecf20Sopenharmony_ci u32 bus_width; 21768c2ecf20Sopenharmony_ci int ret, i; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci np = of_graph_get_next_endpoint(dev->of_node, NULL); 21798c2ecf20Sopenharmony_ci if (!np) { 21808c2ecf20Sopenharmony_ci dev_err(dev, "could not find endpoint\n"); 21818c2ecf20Sopenharmony_ci return -EINVAL; 21828c2ecf20Sopenharmony_ci } 21838c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "bus-width", &bus_width); 21848c2ecf20Sopenharmony_ci if (ret) { 21858c2ecf20Sopenharmony_ci dev_err(dev, "no bus-width specified: %d\n", ret); 21868c2ecf20Sopenharmony_ci of_node_put(np); 21878c2ecf20Sopenharmony_ci return ret; 21888c2ecf20Sopenharmony_ci } 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci display = of_graph_get_remote_port_parent(np); 21918c2ecf20Sopenharmony_ci of_node_put(np); 21928c2ecf20Sopenharmony_ci if (!display) { 21938c2ecf20Sopenharmony_ci dev_err(dev, "no display defined\n"); 21948c2ecf20Sopenharmony_ci return -EINVAL; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci ret = of_get_pxafb_display(dev, display, info, bus_width); 21988c2ecf20Sopenharmony_ci of_node_put(display); 21998c2ecf20Sopenharmony_ci if (ret) 22008c2ecf20Sopenharmony_ci return ret; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci for (i = 0; i < info->num_modes; i++) 22038c2ecf20Sopenharmony_ci info->modes[i].bpp = bus_width; 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci return 0; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistatic struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) 22098c2ecf20Sopenharmony_ci{ 22108c2ecf20Sopenharmony_ci int ret; 22118c2ecf20Sopenharmony_ci struct pxafb_mach_info *info; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (!dev->of_node) 22148c2ecf20Sopenharmony_ci return NULL; 22158c2ecf20Sopenharmony_ci info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); 22168c2ecf20Sopenharmony_ci if (!info) 22178c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 22188c2ecf20Sopenharmony_ci ret = of_get_pxafb_mode_info(dev, info); 22198c2ecf20Sopenharmony_ci if (ret) 22208c2ecf20Sopenharmony_ci return ERR_PTR(ret); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci /* 22238c2ecf20Sopenharmony_ci * On purpose, neither lccrX registers nor video memory size can be 22248c2ecf20Sopenharmony_ci * specified through device-tree, they are considered more a debug hack 22258c2ecf20Sopenharmony_ci * available through command line. 22268c2ecf20Sopenharmony_ci */ 22278c2ecf20Sopenharmony_ci return info; 22288c2ecf20Sopenharmony_ci} 22298c2ecf20Sopenharmony_ci#else 22308c2ecf20Sopenharmony_cistatic struct pxafb_mach_info *of_pxafb_of_mach_info(struct device *dev) 22318c2ecf20Sopenharmony_ci{ 22328c2ecf20Sopenharmony_ci return NULL; 22338c2ecf20Sopenharmony_ci} 22348c2ecf20Sopenharmony_ci#endif 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_cistatic int pxafb_probe(struct platform_device *dev) 22378c2ecf20Sopenharmony_ci{ 22388c2ecf20Sopenharmony_ci struct pxafb_info *fbi; 22398c2ecf20Sopenharmony_ci struct pxafb_mach_info *inf, *pdata; 22408c2ecf20Sopenharmony_ci int i, irq, ret; 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "pxafb_probe\n"); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci ret = -ENOMEM; 22458c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&dev->dev); 22468c2ecf20Sopenharmony_ci inf = devm_kmalloc(&dev->dev, sizeof(*inf), GFP_KERNEL); 22478c2ecf20Sopenharmony_ci if (!inf) 22488c2ecf20Sopenharmony_ci goto failed; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci if (pdata) { 22518c2ecf20Sopenharmony_ci *inf = *pdata; 22528c2ecf20Sopenharmony_ci inf->modes = 22538c2ecf20Sopenharmony_ci devm_kmalloc_array(&dev->dev, pdata->num_modes, 22548c2ecf20Sopenharmony_ci sizeof(inf->modes[0]), GFP_KERNEL); 22558c2ecf20Sopenharmony_ci if (!inf->modes) 22568c2ecf20Sopenharmony_ci goto failed; 22578c2ecf20Sopenharmony_ci for (i = 0; i < inf->num_modes; i++) 22588c2ecf20Sopenharmony_ci inf->modes[i] = pdata->modes[i]; 22598c2ecf20Sopenharmony_ci } 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci if (!pdata) 22628c2ecf20Sopenharmony_ci inf = of_pxafb_of_mach_info(&dev->dev); 22638c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(inf)) 22648c2ecf20Sopenharmony_ci goto failed; 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci ret = pxafb_parse_options(&dev->dev, g_options, inf); 22678c2ecf20Sopenharmony_ci if (ret < 0) 22688c2ecf20Sopenharmony_ci goto failed; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci pxafb_check_options(&dev->dev, inf); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n", 22738c2ecf20Sopenharmony_ci inf->modes->xres, 22748c2ecf20Sopenharmony_ci inf->modes->yres, 22758c2ecf20Sopenharmony_ci inf->modes->bpp); 22768c2ecf20Sopenharmony_ci if (inf->modes->xres == 0 || 22778c2ecf20Sopenharmony_ci inf->modes->yres == 0 || 22788c2ecf20Sopenharmony_ci inf->modes->bpp == 0) { 22798c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Invalid resolution or bit depth\n"); 22808c2ecf20Sopenharmony_ci ret = -EINVAL; 22818c2ecf20Sopenharmony_ci goto failed; 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci fbi = pxafb_init_fbinfo(&dev->dev, inf); 22858c2ecf20Sopenharmony_ci if (IS_ERR(fbi)) { 22868c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); 22878c2ecf20Sopenharmony_ci ret = PTR_ERR(fbi); 22888c2ecf20Sopenharmony_ci goto failed; 22898c2ecf20Sopenharmony_ci } 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci if (cpu_is_pxa3xx() && inf->acceleration_enabled) 22928c2ecf20Sopenharmony_ci fbi->fb.fix.accel = FB_ACCEL_PXA3XX; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci fbi->backlight_power = inf->pxafb_backlight_power; 22958c2ecf20Sopenharmony_ci fbi->lcd_power = inf->pxafb_lcd_power; 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci fbi->lcd_supply = devm_regulator_get_optional(&dev->dev, "lcd"); 22988c2ecf20Sopenharmony_ci if (IS_ERR(fbi->lcd_supply)) { 22998c2ecf20Sopenharmony_ci if (PTR_ERR(fbi->lcd_supply) == -EPROBE_DEFER) 23008c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci fbi->lcd_supply = NULL; 23038c2ecf20Sopenharmony_ci } 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci fbi->mmio_base = devm_platform_ioremap_resource(dev, 0); 23068c2ecf20Sopenharmony_ci if (IS_ERR(fbi->mmio_base)) { 23078c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to get I/O memory\n"); 23088c2ecf20Sopenharmony_ci ret = PTR_ERR(fbi->mmio_base); 23098c2ecf20Sopenharmony_ci goto failed; 23108c2ecf20Sopenharmony_ci } 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); 23138c2ecf20Sopenharmony_ci fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size, 23148c2ecf20Sopenharmony_ci &fbi->dma_buff_phys, GFP_KERNEL); 23158c2ecf20Sopenharmony_ci if (fbi->dma_buff == NULL) { 23168c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to allocate memory for DMA\n"); 23178c2ecf20Sopenharmony_ci ret = -ENOMEM; 23188c2ecf20Sopenharmony_ci goto failed; 23198c2ecf20Sopenharmony_ci } 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci ret = pxafb_init_video_memory(fbi); 23228c2ecf20Sopenharmony_ci if (ret) { 23238c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret); 23248c2ecf20Sopenharmony_ci ret = -ENOMEM; 23258c2ecf20Sopenharmony_ci goto failed_free_dma; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci irq = platform_get_irq(dev, 0); 23298c2ecf20Sopenharmony_ci if (irq < 0) { 23308c2ecf20Sopenharmony_ci dev_err(&dev->dev, "no IRQ defined\n"); 23318c2ecf20Sopenharmony_ci ret = -ENODEV; 23328c2ecf20Sopenharmony_ci goto failed_free_mem; 23338c2ecf20Sopenharmony_ci } 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci ret = devm_request_irq(&dev->dev, irq, pxafb_handle_irq, 0, "LCD", fbi); 23368c2ecf20Sopenharmony_ci if (ret) { 23378c2ecf20Sopenharmony_ci dev_err(&dev->dev, "request_irq failed: %d\n", ret); 23388c2ecf20Sopenharmony_ci ret = -EBUSY; 23398c2ecf20Sopenharmony_ci goto failed_free_mem; 23408c2ecf20Sopenharmony_ci } 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci ret = pxafb_smart_init(fbi); 23438c2ecf20Sopenharmony_ci if (ret) { 23448c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to initialize smartpanel\n"); 23458c2ecf20Sopenharmony_ci goto failed_free_mem; 23468c2ecf20Sopenharmony_ci } 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci /* 23498c2ecf20Sopenharmony_ci * This makes sure that our colour bitfield 23508c2ecf20Sopenharmony_ci * descriptors are correctly initialised. 23518c2ecf20Sopenharmony_ci */ 23528c2ecf20Sopenharmony_ci ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); 23538c2ecf20Sopenharmony_ci if (ret) { 23548c2ecf20Sopenharmony_ci dev_err(&dev->dev, "failed to get suitable mode\n"); 23558c2ecf20Sopenharmony_ci goto failed_free_mem; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci ret = pxafb_set_par(&fbi->fb); 23598c2ecf20Sopenharmony_ci if (ret) { 23608c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to set parameters\n"); 23618c2ecf20Sopenharmony_ci goto failed_free_mem; 23628c2ecf20Sopenharmony_ci } 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci platform_set_drvdata(dev, fbi); 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci ret = register_framebuffer(&fbi->fb); 23678c2ecf20Sopenharmony_ci if (ret < 0) { 23688c2ecf20Sopenharmony_ci dev_err(&dev->dev, 23698c2ecf20Sopenharmony_ci "Failed to register framebuffer device: %d\n", ret); 23708c2ecf20Sopenharmony_ci goto failed_free_cmap; 23718c2ecf20Sopenharmony_ci } 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci pxafb_overlay_init(fbi); 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 23768c2ecf20Sopenharmony_ci fbi->freq_transition.notifier_call = pxafb_freq_transition; 23778c2ecf20Sopenharmony_ci cpufreq_register_notifier(&fbi->freq_transition, 23788c2ecf20Sopenharmony_ci CPUFREQ_TRANSITION_NOTIFIER); 23798c2ecf20Sopenharmony_ci#endif 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci /* 23828c2ecf20Sopenharmony_ci * Ok, now enable the LCD controller 23838c2ecf20Sopenharmony_ci */ 23848c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci return 0; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_cifailed_free_cmap: 23898c2ecf20Sopenharmony_ci if (fbi->fb.cmap.len) 23908c2ecf20Sopenharmony_ci fb_dealloc_cmap(&fbi->fb.cmap); 23918c2ecf20Sopenharmony_cifailed_free_mem: 23928c2ecf20Sopenharmony_ci free_pages_exact(fbi->video_mem, fbi->video_mem_size); 23938c2ecf20Sopenharmony_cifailed_free_dma: 23948c2ecf20Sopenharmony_ci dma_free_coherent(&dev->dev, fbi->dma_buff_size, 23958c2ecf20Sopenharmony_ci fbi->dma_buff, fbi->dma_buff_phys); 23968c2ecf20Sopenharmony_cifailed: 23978c2ecf20Sopenharmony_ci return ret; 23988c2ecf20Sopenharmony_ci} 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_cistatic int pxafb_remove(struct platform_device *dev) 24018c2ecf20Sopenharmony_ci{ 24028c2ecf20Sopenharmony_ci struct pxafb_info *fbi = platform_get_drvdata(dev); 24038c2ecf20Sopenharmony_ci struct fb_info *info; 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci if (!fbi) 24068c2ecf20Sopenharmony_ci return 0; 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci info = &fbi->fb; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci pxafb_overlay_exit(fbi); 24118c2ecf20Sopenharmony_ci unregister_framebuffer(info); 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci pxafb_disable_controller(fbi); 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci if (fbi->fb.cmap.len) 24168c2ecf20Sopenharmony_ci fb_dealloc_cmap(&fbi->fb.cmap); 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci free_pages_exact(fbi->video_mem, fbi->video_mem_size); 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci dma_free_coherent(&dev->dev, fbi->dma_buff_size, fbi->dma_buff, 24218c2ecf20Sopenharmony_ci fbi->dma_buff_phys); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci return 0; 24248c2ecf20Sopenharmony_ci} 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_cistatic const struct of_device_id pxafb_of_dev_id[] = { 24278c2ecf20Sopenharmony_ci { .compatible = "marvell,pxa270-lcdc", }, 24288c2ecf20Sopenharmony_ci { .compatible = "marvell,pxa300-lcdc", }, 24298c2ecf20Sopenharmony_ci { .compatible = "marvell,pxa2xx-lcdc", }, 24308c2ecf20Sopenharmony_ci { /* sentinel */ } 24318c2ecf20Sopenharmony_ci}; 24328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pxafb_of_dev_id); 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_cistatic struct platform_driver pxafb_driver = { 24358c2ecf20Sopenharmony_ci .probe = pxafb_probe, 24368c2ecf20Sopenharmony_ci .remove = pxafb_remove, 24378c2ecf20Sopenharmony_ci .driver = { 24388c2ecf20Sopenharmony_ci .name = "pxa2xx-fb", 24398c2ecf20Sopenharmony_ci .of_match_table = pxafb_of_dev_id, 24408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 24418c2ecf20Sopenharmony_ci .pm = &pxafb_pm_ops, 24428c2ecf20Sopenharmony_ci#endif 24438c2ecf20Sopenharmony_ci }, 24448c2ecf20Sopenharmony_ci}; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_cistatic int __init pxafb_init(void) 24478c2ecf20Sopenharmony_ci{ 24488c2ecf20Sopenharmony_ci if (pxafb_setup_options()) 24498c2ecf20Sopenharmony_ci return -EINVAL; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci return platform_driver_register(&pxafb_driver); 24528c2ecf20Sopenharmony_ci} 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_cistatic void __exit pxafb_exit(void) 24558c2ecf20Sopenharmony_ci{ 24568c2ecf20Sopenharmony_ci platform_driver_unregister(&pxafb_driver); 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_cimodule_init(pxafb_init); 24608c2ecf20Sopenharmony_cimodule_exit(pxafb_exit); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("loadable framebuffer driver for PXA"); 24638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2464