18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/sa1100fb.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 1999 Eric A. Thomas 58c2ecf20Sopenharmony_ci * Based on acornfb.c Copyright (C) Russell King. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 88c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 98c2ecf20Sopenharmony_ci * more details. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * StrongARM 1100 LCD Controller Frame Buffer Driver 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Please direct your questions and comments on this driver to the following 148c2ecf20Sopenharmony_ci * email address: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * linux-arm-kernel@lists.arm.linux.org.uk 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Clean patches should be sent to the ARM Linux Patch System. Please see the 198c2ecf20Sopenharmony_ci * following web page for more information: 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * https://www.arm.linux.org.uk/developer/patches/info.shtml 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Thank you. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Known problems: 268c2ecf20Sopenharmony_ci * - With the Neponset plugged into an Assabet, LCD powerdown 278c2ecf20Sopenharmony_ci * doesn't work (LCD stays powered up). Therefore we shouldn't 288c2ecf20Sopenharmony_ci * blank the screen. 298c2ecf20Sopenharmony_ci * - We don't limit the CPU clock rate nor the mode selection 308c2ecf20Sopenharmony_ci * according to the available SDRAM bandwidth. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Other notes: 338c2ecf20Sopenharmony_ci * - Linear grayscale palettes and the kernel. 348c2ecf20Sopenharmony_ci * Such code does not belong in the kernel. The kernel frame buffer 358c2ecf20Sopenharmony_ci * drivers do not expect a linear colourmap, but a colourmap based on 368c2ecf20Sopenharmony_ci * the VT100 standard mapping. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * If your _userspace_ requires a linear colourmap, then the setup of 398c2ecf20Sopenharmony_ci * such a colourmap belongs _in userspace_, not in the kernel. Code 408c2ecf20Sopenharmony_ci * to set the colourmap correctly from user space has been sent to 418c2ecf20Sopenharmony_ci * David Neuer. It's around 8 lines of C code, plus another 4 to 428c2ecf20Sopenharmony_ci * detect if we are using grayscale. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * - The following must never be specified in a panel definition: 458c2ecf20Sopenharmony_ci * LCCR0_LtlEnd, LCCR3_PixClkDiv, LCCR3_VrtSnchL, LCCR3_HorSnchL 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * - The following should be specified: 488c2ecf20Sopenharmony_ci * either LCCR0_Color or LCCR0_Mono 498c2ecf20Sopenharmony_ci * either LCCR0_Sngl or LCCR0_Dual 508c2ecf20Sopenharmony_ci * either LCCR0_Act or LCCR0_Pas 518c2ecf20Sopenharmony_ci * either LCCR3_OutEnH or LCCD3_OutEnL 528c2ecf20Sopenharmony_ci * either LCCR3_PixRsEdg or LCCR3_PixFlEdg 538c2ecf20Sopenharmony_ci * either LCCR3_ACBsDiv or LCCR3_ACBsCntOff 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * Code Status: 568c2ecf20Sopenharmony_ci * 1999/04/01: 578c2ecf20Sopenharmony_ci * - Driver appears to be working for Brutus 320x200x8bpp mode. Other 588c2ecf20Sopenharmony_ci * resolutions are working, but only the 8bpp mode is supported. 598c2ecf20Sopenharmony_ci * Changes need to be made to the palette encode and decode routines 608c2ecf20Sopenharmony_ci * to support 4 and 16 bpp modes. 618c2ecf20Sopenharmony_ci * Driver is not designed to be a module. The FrameBuffer is statically 628c2ecf20Sopenharmony_ci * allocated since dynamic allocation of a 300k buffer cannot be 638c2ecf20Sopenharmony_ci * guaranteed. 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * 1999/06/17: 668c2ecf20Sopenharmony_ci * - FrameBuffer memory is now allocated at run-time when the 678c2ecf20Sopenharmony_ci * driver is initialized. 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * 2000/04/10: Nicolas Pitre <nico@fluxnic.net> 708c2ecf20Sopenharmony_ci * - Big cleanup for dynamic selection of machine type at run time. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * 2000/07/19: Jamey Hicks <jamey@crl.dec.com> 738c2ecf20Sopenharmony_ci * - Support for Bitsy aka Compaq iPAQ H3600 added. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * 2000/08/07: Tak-Shing Chan <tchan.rd@idthk.com> 768c2ecf20Sopenharmony_ci * Jeff Sutherland <jsutherland@accelent.com> 778c2ecf20Sopenharmony_ci * - Resolved an issue caused by a change made to the Assabet's PLD 788c2ecf20Sopenharmony_ci * earlier this year which broke the framebuffer driver for newer 798c2ecf20Sopenharmony_ci * Phase 4 Assabets. Some other parameters were changed to optimize 808c2ecf20Sopenharmony_ci * for the Sharp display. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * 2000/08/09: Kunihiko IMAI <imai@vasara.co.jp> 838c2ecf20Sopenharmony_ci * - XP860 support added 848c2ecf20Sopenharmony_ci * 858c2ecf20Sopenharmony_ci * 2000/08/19: Mark Huang <mhuang@livetoy.com> 868c2ecf20Sopenharmony_ci * - Allows standard options to be passed on the kernel command line 878c2ecf20Sopenharmony_ci * for most common passive displays. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * 2000/08/29: 908c2ecf20Sopenharmony_ci * - s/save_flags_cli/local_irq_save/ 918c2ecf20Sopenharmony_ci * - remove unneeded extra save_flags_cli in sa1100fb_enable_lcd_controller 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * 2000/10/10: Erik Mouw <J.A.K.Mouw@its.tudelft.nl> 948c2ecf20Sopenharmony_ci * - Updated LART stuff. Fixed some minor bugs. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * 2000/10/30: Murphy Chen <murphy@mail.dialogue.com.tw> 978c2ecf20Sopenharmony_ci * - Pangolin support added 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * 2000/10/31: Roman Jordan <jor@hoeft-wessel.de> 1008c2ecf20Sopenharmony_ci * - Huw Webpanel support added 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * 2000/11/23: Eric Peng <ericpeng@coventive.com> 1038c2ecf20Sopenharmony_ci * - Freebird add 1048c2ecf20Sopenharmony_ci * 1058c2ecf20Sopenharmony_ci * 2001/02/07: Jamey Hicks <jamey.hicks@compaq.com> 1068c2ecf20Sopenharmony_ci * Cliff Brake <cbrake@accelent.com> 1078c2ecf20Sopenharmony_ci * - Added PM callback 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * 2001/05/26: <rmk@arm.linux.org.uk> 1108c2ecf20Sopenharmony_ci * - Fix 16bpp so that (a) we use the right colours rather than some 1118c2ecf20Sopenharmony_ci * totally random colour depending on what was in page 0, and (b) 1128c2ecf20Sopenharmony_ci * we don't de-reference a NULL pointer. 1138c2ecf20Sopenharmony_ci * - remove duplicated implementation of consistent_alloc() 1148c2ecf20Sopenharmony_ci * - convert dma address types to dma_addr_t 1158c2ecf20Sopenharmony_ci * - remove unused 'montype' stuff 1168c2ecf20Sopenharmony_ci * - remove redundant zero inits of init_var after the initial 1178c2ecf20Sopenharmony_ci * memset. 1188c2ecf20Sopenharmony_ci * - remove allow_modeset (acornfb idea does not belong here) 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * 2001/05/28: <rmk@arm.linux.org.uk> 1218c2ecf20Sopenharmony_ci * - massive cleanup - move machine dependent data into structures 1228c2ecf20Sopenharmony_ci * - I've left various #warnings in - if you see one, and know 1238c2ecf20Sopenharmony_ci * the hardware concerned, please get in contact with me. 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * 2001/05/31: <rmk@arm.linux.org.uk> 1268c2ecf20Sopenharmony_ci * - Fix LCCR1 HSW value, fix all machine type specifications to 1278c2ecf20Sopenharmony_ci * keep values in line. (Please check your machine type specs) 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * 2001/06/10: <rmk@arm.linux.org.uk> 1308c2ecf20Sopenharmony_ci * - Fiddle with the LCD controller from task context only; mainly 1318c2ecf20Sopenharmony_ci * so that we can run with interrupts on, and sleep. 1328c2ecf20Sopenharmony_ci * - Convert #warnings into #errors. No pain, no gain. ;) 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * 2001/06/14: <rmk@arm.linux.org.uk> 1358c2ecf20Sopenharmony_ci * - Make the palette BPS value for 12bpp come out correctly. 1368c2ecf20Sopenharmony_ci * - Take notice of "greyscale" on any colour depth. 1378c2ecf20Sopenharmony_ci * - Make truecolor visuals use the RGB channel encoding information. 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * 2001/07/02: <rmk@arm.linux.org.uk> 1408c2ecf20Sopenharmony_ci * - Fix colourmap problems. 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * 2001/07/13: <abraham@2d3d.co.za> 1438c2ecf20Sopenharmony_ci * - Added support for the ICP LCD-Kit01 on LART. This LCD is 1448c2ecf20Sopenharmony_ci * manufactured by Prime View, model no V16C6448AB 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * 2001/07/23: <rmk@arm.linux.org.uk> 1478c2ecf20Sopenharmony_ci * - Hand merge version from handhelds.org CVS tree. See patch 1488c2ecf20Sopenharmony_ci * notes for 595/1 for more information. 1498c2ecf20Sopenharmony_ci * - Drop 12bpp (it's 16bpp with different colour register mappings). 1508c2ecf20Sopenharmony_ci * - This hardware can not do direct colour. Therefore we don't 1518c2ecf20Sopenharmony_ci * support it. 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * 2001/07/27: <rmk@arm.linux.org.uk> 1548c2ecf20Sopenharmony_ci * - Halve YRES on dual scan LCDs. 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * 2001/08/22: <rmk@arm.linux.org.uk> 1578c2ecf20Sopenharmony_ci * - Add b/w iPAQ pixclock value. 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * 2001/10/12: <rmk@arm.linux.org.uk> 1608c2ecf20Sopenharmony_ci * - Add patch 681/1 and clean up stork definitions. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#include <linux/module.h> 1648c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1658c2ecf20Sopenharmony_ci#include <linux/sched.h> 1668c2ecf20Sopenharmony_ci#include <linux/errno.h> 1678c2ecf20Sopenharmony_ci#include <linux/string.h> 1688c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 1698c2ecf20Sopenharmony_ci#include <linux/slab.h> 1708c2ecf20Sopenharmony_ci#include <linux/mm.h> 1718c2ecf20Sopenharmony_ci#include <linux/fb.h> 1728c2ecf20Sopenharmony_ci#include <linux/delay.h> 1738c2ecf20Sopenharmony_ci#include <linux/init.h> 1748c2ecf20Sopenharmony_ci#include <linux/ioport.h> 1758c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 1768c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 1778c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 1788c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 1798c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1808c2ecf20Sopenharmony_ci#include <linux/io.h> 1818c2ecf20Sopenharmony_ci#include <linux/clk.h> 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#include <video/sa1100fb.h> 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#include <mach/hardware.h> 1868c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 1878c2ecf20Sopenharmony_ci#include <mach/shannon.h> 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Complain if VAR is out of range. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci#define DEBUG_VAR 1 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci#include "sa1100fb.h" 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const struct sa1100fb_rgb rgb_4 = { 1978c2ecf20Sopenharmony_ci .red = { .offset = 0, .length = 4, }, 1988c2ecf20Sopenharmony_ci .green = { .offset = 0, .length = 4, }, 1998c2ecf20Sopenharmony_ci .blue = { .offset = 0, .length = 4, }, 2008c2ecf20Sopenharmony_ci .transp = { .offset = 0, .length = 0, }, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic const struct sa1100fb_rgb rgb_8 = { 2048c2ecf20Sopenharmony_ci .red = { .offset = 0, .length = 8, }, 2058c2ecf20Sopenharmony_ci .green = { .offset = 0, .length = 8, }, 2068c2ecf20Sopenharmony_ci .blue = { .offset = 0, .length = 8, }, 2078c2ecf20Sopenharmony_ci .transp = { .offset = 0, .length = 0, }, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct sa1100fb_rgb def_rgb_16 = { 2118c2ecf20Sopenharmony_ci .red = { .offset = 11, .length = 5, }, 2128c2ecf20Sopenharmony_ci .green = { .offset = 5, .length = 6, }, 2138c2ecf20Sopenharmony_ci .blue = { .offset = 0, .length = 5, }, 2148c2ecf20Sopenharmony_ci .transp = { .offset = 0, .length = 0, }, 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *); 2208c2ecf20Sopenharmony_cistatic void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline void sa1100fb_schedule_work(struct sa1100fb_info *fbi, u_int state) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci unsigned long flags; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci local_irq_save(flags); 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * We need to handle two requests being made at the same time. 2298c2ecf20Sopenharmony_ci * There are two important cases: 2308c2ecf20Sopenharmony_ci * 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE) 2318c2ecf20Sopenharmony_ci * We must perform the unblanking, which will do our REENABLE for us. 2328c2ecf20Sopenharmony_ci * 2. When we are blanking, but immediately unblank before we have 2338c2ecf20Sopenharmony_ci * blanked. We do the "REENABLE" thing here as well, just to be sure. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci if (fbi->task_state == C_ENABLE && state == C_REENABLE) 2368c2ecf20Sopenharmony_ci state = (u_int) -1; 2378c2ecf20Sopenharmony_ci if (fbi->task_state == C_DISABLE && state == C_ENABLE) 2388c2ecf20Sopenharmony_ci state = C_REENABLE; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (state != (u_int)-1) { 2418c2ecf20Sopenharmony_ci fbi->task_state = state; 2428c2ecf20Sopenharmony_ci schedule_work(&fbi->task); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci local_irq_restore(flags); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci chan &= 0xffff; 2508c2ecf20Sopenharmony_ci chan >>= 16 - bf->length; 2518c2ecf20Sopenharmony_ci return chan << bf->offset; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * Convert bits-per-pixel to a hardware palette PBS value. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic inline u_int palette_pbs(struct fb_var_screeninfo *var) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci int ret = 0; 2608c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 2618c2ecf20Sopenharmony_ci case 4: ret = 0 << 12; break; 2628c2ecf20Sopenharmony_ci case 8: ret = 1 << 12; break; 2638c2ecf20Sopenharmony_ci case 16: ret = 2 << 12; break; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci return ret; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int 2698c2ecf20Sopenharmony_cisa1100fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, 2708c2ecf20Sopenharmony_ci u_int trans, struct fb_info *info) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = 2738c2ecf20Sopenharmony_ci container_of(info, struct sa1100fb_info, fb); 2748c2ecf20Sopenharmony_ci u_int val, ret = 1; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (regno < fbi->palette_size) { 2778c2ecf20Sopenharmony_ci val = ((red >> 4) & 0xf00); 2788c2ecf20Sopenharmony_ci val |= ((green >> 8) & 0x0f0); 2798c2ecf20Sopenharmony_ci val |= ((blue >> 12) & 0x00f); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (regno == 0) 2828c2ecf20Sopenharmony_ci val |= palette_pbs(&fbi->fb.var); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci fbi->palette_cpu[regno] = val; 2858c2ecf20Sopenharmony_ci ret = 0; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int 2918c2ecf20Sopenharmony_cisa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 2928c2ecf20Sopenharmony_ci u_int trans, struct fb_info *info) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = 2958c2ecf20Sopenharmony_ci container_of(info, struct sa1100fb_info, fb); 2968c2ecf20Sopenharmony_ci unsigned int val; 2978c2ecf20Sopenharmony_ci int ret = 1; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * If inverse mode was selected, invert all the colours 3018c2ecf20Sopenharmony_ci * rather than the register number. The register number 3028c2ecf20Sopenharmony_ci * is what you poke into the framebuffer to produce the 3038c2ecf20Sopenharmony_ci * colour you requested. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci if (fbi->inf->cmap_inverse) { 3068c2ecf20Sopenharmony_ci red = 0xffff - red; 3078c2ecf20Sopenharmony_ci green = 0xffff - green; 3088c2ecf20Sopenharmony_ci blue = 0xffff - blue; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * If greyscale is true, then we convert the RGB value 3138c2ecf20Sopenharmony_ci * to greyscale no mater what visual we are using. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci if (fbi->fb.var.grayscale) 3168c2ecf20Sopenharmony_ci red = green = blue = (19595 * red + 38470 * green + 3178c2ecf20Sopenharmony_ci 7471 * blue) >> 16; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci switch (fbi->fb.fix.visual) { 3208c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 3218c2ecf20Sopenharmony_ci /* 3228c2ecf20Sopenharmony_ci * 12 or 16-bit True Colour. We encode the RGB value 3238c2ecf20Sopenharmony_ci * according to the RGB bitfield information. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci if (regno < 16) { 3268c2ecf20Sopenharmony_ci val = chan_to_field(red, &fbi->fb.var.red); 3278c2ecf20Sopenharmony_ci val |= chan_to_field(green, &fbi->fb.var.green); 3288c2ecf20Sopenharmony_ci val |= chan_to_field(blue, &fbi->fb.var.blue); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci fbi->pseudo_palette[regno] = val; 3318c2ecf20Sopenharmony_ci ret = 0; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci case FB_VISUAL_STATIC_PSEUDOCOLOR: 3368c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 3378c2ecf20Sopenharmony_ci ret = sa1100fb_setpalettereg(regno, red, green, blue, trans, info); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return ret; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 3458c2ecf20Sopenharmony_ci/* 3468c2ecf20Sopenharmony_ci * sa1100fb_display_dma_period() 3478c2ecf20Sopenharmony_ci * Calculate the minimum period (in picoseconds) between two DMA 3488c2ecf20Sopenharmony_ci * requests for the LCD controller. If we hit this, it means we're 3498c2ecf20Sopenharmony_ci * doing nothing but LCD DMA. 3508c2ecf20Sopenharmony_ci */ 3518c2ecf20Sopenharmony_cistatic inline unsigned int sa1100fb_display_dma_period(struct fb_var_screeninfo *var) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Period = pixclock * bits_per_byte * bytes_per_transfer 3558c2ecf20Sopenharmony_ci * / memory_bits_per_pixel; 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci return var->pixclock * 8 * 16 / var->bits_per_pixel; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci#endif 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* 3628c2ecf20Sopenharmony_ci * sa1100fb_check_var(): 3638c2ecf20Sopenharmony_ci * Round up in the following order: bits_per_pixel, xres, 3648c2ecf20Sopenharmony_ci * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, 3658c2ecf20Sopenharmony_ci * bitfields, horizontal timing, vertical timing. 3668c2ecf20Sopenharmony_ci */ 3678c2ecf20Sopenharmony_cistatic int 3688c2ecf20Sopenharmony_cisa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = 3718c2ecf20Sopenharmony_ci container_of(info, struct sa1100fb_info, fb); 3728c2ecf20Sopenharmony_ci int rgbidx; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (var->xres < MIN_XRES) 3758c2ecf20Sopenharmony_ci var->xres = MIN_XRES; 3768c2ecf20Sopenharmony_ci if (var->yres < MIN_YRES) 3778c2ecf20Sopenharmony_ci var->yres = MIN_YRES; 3788c2ecf20Sopenharmony_ci if (var->xres > fbi->inf->xres) 3798c2ecf20Sopenharmony_ci var->xres = fbi->inf->xres; 3808c2ecf20Sopenharmony_ci if (var->yres > fbi->inf->yres) 3818c2ecf20Sopenharmony_ci var->yres = fbi->inf->yres; 3828c2ecf20Sopenharmony_ci var->xres_virtual = max(var->xres_virtual, var->xres); 3838c2ecf20Sopenharmony_ci var->yres_virtual = max(var->yres_virtual, var->yres); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel); 3868c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 3878c2ecf20Sopenharmony_ci case 4: 3888c2ecf20Sopenharmony_ci rgbidx = RGB_4; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci case 8: 3918c2ecf20Sopenharmony_ci rgbidx = RGB_8; 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci case 16: 3948c2ecf20Sopenharmony_ci rgbidx = RGB_16; 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci default: 3978c2ecf20Sopenharmony_ci return -EINVAL; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * Copy the RGB parameters for this display 4028c2ecf20Sopenharmony_ci * from the machine specific parameters. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci var->red = fbi->rgb[rgbidx]->red; 4058c2ecf20Sopenharmony_ci var->green = fbi->rgb[rgbidx]->green; 4068c2ecf20Sopenharmony_ci var->blue = fbi->rgb[rgbidx]->blue; 4078c2ecf20Sopenharmony_ci var->transp = fbi->rgb[rgbidx]->transp; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n", 4108c2ecf20Sopenharmony_ci var->red.length, var->green.length, var->blue.length, 4118c2ecf20Sopenharmony_ci var->transp.length); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n", 4148c2ecf20Sopenharmony_ci var->red.offset, var->green.offset, var->blue.offset, 4158c2ecf20Sopenharmony_ci var->transp.offset); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 4188c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "dma period = %d ps, clock = %ld kHz\n", 4198c2ecf20Sopenharmony_ci sa1100fb_display_dma_period(var), 4208c2ecf20Sopenharmony_ci clk_get_rate(fbi->clk) / 1000); 4218c2ecf20Sopenharmony_ci#endif 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci if (fbi->inf->set_visual) 4298c2ecf20Sopenharmony_ci fbi->inf->set_visual(visual); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* 4338c2ecf20Sopenharmony_ci * sa1100fb_set_par(): 4348c2ecf20Sopenharmony_ci * Set the user defined part of the display for the specified console 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_cistatic int sa1100fb_set_par(struct fb_info *info) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = 4398c2ecf20Sopenharmony_ci container_of(info, struct sa1100fb_info, fb); 4408c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 4418c2ecf20Sopenharmony_ci unsigned long palette_mem_size; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "set_par\n"); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 16) 4468c2ecf20Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR; 4478c2ecf20Sopenharmony_ci else if (!fbi->inf->cmap_static) 4488c2ecf20Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; 4498c2ecf20Sopenharmony_ci else { 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * Some people have weird ideas about wanting static 4528c2ecf20Sopenharmony_ci * pseudocolor maps. I suspect their user space 4538c2ecf20Sopenharmony_ci * applications are broken. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci fbi->fb.fix.line_length = var->xres_virtual * 4598c2ecf20Sopenharmony_ci var->bits_per_pixel / 8; 4608c2ecf20Sopenharmony_ci fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci palette_mem_size = fbi->palette_size * sizeof(u16); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); 4678c2ecf20Sopenharmony_ci fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* 4708c2ecf20Sopenharmony_ci * Set (any) board control register to handle new color depth 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_ci sa1100fb_set_visual(fbi, fbi->fb.fix.visual); 4738c2ecf20Sopenharmony_ci sa1100fb_activate_var(var, fbi); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return 0; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci#if 0 4798c2ecf20Sopenharmony_cistatic int 4808c2ecf20Sopenharmony_cisa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, 4818c2ecf20Sopenharmony_ci struct fb_info *info) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = (struct sa1100fb_info *)info; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * Make sure the user isn't doing something stupid. 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static)) 4898c2ecf20Sopenharmony_ci return -EINVAL; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return gen_set_cmap(cmap, kspc, con, info); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci#endif 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci/* 4968c2ecf20Sopenharmony_ci * Formal definition of the VESA spec: 4978c2ecf20Sopenharmony_ci * On 4988c2ecf20Sopenharmony_ci * This refers to the state of the display when it is in full operation 4998c2ecf20Sopenharmony_ci * Stand-By 5008c2ecf20Sopenharmony_ci * This defines an optional operating state of minimal power reduction with 5018c2ecf20Sopenharmony_ci * the shortest recovery time 5028c2ecf20Sopenharmony_ci * Suspend 5038c2ecf20Sopenharmony_ci * This refers to a level of power management in which substantial power 5048c2ecf20Sopenharmony_ci * reduction is achieved by the display. The display can have a longer 5058c2ecf20Sopenharmony_ci * recovery time from this state than from the Stand-by state 5068c2ecf20Sopenharmony_ci * Off 5078c2ecf20Sopenharmony_ci * This indicates that the display is consuming the lowest level of power 5088c2ecf20Sopenharmony_ci * and is non-operational. Recovery from this state may optionally require 5098c2ecf20Sopenharmony_ci * the user to manually power on the monitor 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * Now, the fbdev driver adds an additional state, (blank), where they 5128c2ecf20Sopenharmony_ci * turn off the video (maybe by colormap tricks), but don't mess with the 5138c2ecf20Sopenharmony_ci * video itself: think of it semantically between on and Stand-By. 5148c2ecf20Sopenharmony_ci * 5158c2ecf20Sopenharmony_ci * So here's what we should do in our fbdev blank routine: 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * VESA_NO_BLANKING (mode 0) Video on, front/back light on 5188c2ecf20Sopenharmony_ci * VESA_VSYNC_SUSPEND (mode 1) Video on, front/back light off 5198c2ecf20Sopenharmony_ci * VESA_HSYNC_SUSPEND (mode 2) Video on, front/back light off 5208c2ecf20Sopenharmony_ci * VESA_POWERDOWN (mode 3) Video off, front/back light off 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * This will match the matrox implementation. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_ci/* 5258c2ecf20Sopenharmony_ci * sa1100fb_blank(): 5268c2ecf20Sopenharmony_ci * Blank the display by setting all palette values to zero. Note, the 5278c2ecf20Sopenharmony_ci * 12 and 16 bpp modes don't really use the palette, so this will not 5288c2ecf20Sopenharmony_ci * blank the display in all modes. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_cistatic int sa1100fb_blank(int blank, struct fb_info *info) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = 5338c2ecf20Sopenharmony_ci container_of(info, struct sa1100fb_info, fb); 5348c2ecf20Sopenharmony_ci int i; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "sa1100fb_blank: blank=%d\n", blank); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci switch (blank) { 5398c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 5408c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 5418c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 5428c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 5438c2ecf20Sopenharmony_ci if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 5448c2ecf20Sopenharmony_ci fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 5458c2ecf20Sopenharmony_ci for (i = 0; i < fbi->palette_size; i++) 5468c2ecf20Sopenharmony_ci sa1100fb_setpalettereg(i, 0, 0, 0, 0, info); 5478c2ecf20Sopenharmony_ci sa1100fb_schedule_work(fbi, C_DISABLE); 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 5518c2ecf20Sopenharmony_ci if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR || 5528c2ecf20Sopenharmony_ci fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) 5538c2ecf20Sopenharmony_ci fb_set_cmap(&fbi->fb.cmap, info); 5548c2ecf20Sopenharmony_ci sa1100fb_schedule_work(fbi, C_ENABLE); 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int sa1100fb_mmap(struct fb_info *info, 5608c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = 5638c2ecf20Sopenharmony_ci container_of(info, struct sa1100fb_info, fb); 5648c2ecf20Sopenharmony_ci unsigned long off = vma->vm_pgoff << PAGE_SHIFT; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (off < info->fix.smem_len) { 5678c2ecf20Sopenharmony_ci vma->vm_pgoff += 1; /* skip over the palette */ 5688c2ecf20Sopenharmony_ci return dma_mmap_wc(fbi->dev, vma, fbi->map_cpu, fbi->map_dma, 5698c2ecf20Sopenharmony_ci fbi->map_size); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return vm_iomap_memory(vma, info->fix.mmio_start, info->fix.mmio_len); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic const struct fb_ops sa1100fb_ops = { 5788c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5798c2ecf20Sopenharmony_ci .fb_check_var = sa1100fb_check_var, 5808c2ecf20Sopenharmony_ci .fb_set_par = sa1100fb_set_par, 5818c2ecf20Sopenharmony_ci// .fb_set_cmap = sa1100fb_set_cmap, 5828c2ecf20Sopenharmony_ci .fb_setcolreg = sa1100fb_setcolreg, 5838c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 5848c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 5858c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 5868c2ecf20Sopenharmony_ci .fb_blank = sa1100fb_blank, 5878c2ecf20Sopenharmony_ci .fb_mmap = sa1100fb_mmap, 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* 5918c2ecf20Sopenharmony_ci * Calculate the PCD value from the clock rate (in picoseconds). 5928c2ecf20Sopenharmony_ci * We take account of the PPCR clock setting. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_cistatic inline unsigned int get_pcd(struct sa1100fb_info *fbi, 5958c2ecf20Sopenharmony_ci unsigned int pixclock) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci unsigned int pcd = clk_get_rate(fbi->clk) / 100 / 1000; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci pcd *= pixclock; 6008c2ecf20Sopenharmony_ci pcd /= 10000000; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return pcd + 1; /* make up for integer math truncations */ 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci/* 6068c2ecf20Sopenharmony_ci * sa1100fb_activate_var(): 6078c2ecf20Sopenharmony_ci * Configures LCD Controller based on entries in var parameter. Settings are 6088c2ecf20Sopenharmony_ci * only written to the controller if changes were made. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_cistatic int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct sa1100fb_lcd_reg new_regs; 6138c2ecf20Sopenharmony_ci u_int half_screen_size, yres, pcd; 6148c2ecf20Sopenharmony_ci u_long flags; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "Configuring SA1100 LCD\n"); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "var: xres=%d hslen=%d lm=%d rm=%d\n", 6198c2ecf20Sopenharmony_ci var->xres, var->hsync_len, 6208c2ecf20Sopenharmony_ci var->left_margin, var->right_margin); 6218c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "var: yres=%d vslen=%d um=%d bm=%d\n", 6228c2ecf20Sopenharmony_ci var->yres, var->vsync_len, 6238c2ecf20Sopenharmony_ci var->upper_margin, var->lower_margin); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci#if DEBUG_VAR 6268c2ecf20Sopenharmony_ci if (var->xres < 16 || var->xres > 1024) 6278c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid xres %d\n", 6288c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->xres); 6298c2ecf20Sopenharmony_ci if (var->hsync_len < 1 || var->hsync_len > 64) 6308c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid hsync_len %d\n", 6318c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->hsync_len); 6328c2ecf20Sopenharmony_ci if (var->left_margin < 1 || var->left_margin > 255) 6338c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid left_margin %d\n", 6348c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->left_margin); 6358c2ecf20Sopenharmony_ci if (var->right_margin < 1 || var->right_margin > 255) 6368c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid right_margin %d\n", 6378c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->right_margin); 6388c2ecf20Sopenharmony_ci if (var->yres < 1 || var->yres > 1024) 6398c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid yres %d\n", 6408c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->yres); 6418c2ecf20Sopenharmony_ci if (var->vsync_len < 1 || var->vsync_len > 64) 6428c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid vsync_len %d\n", 6438c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->vsync_len); 6448c2ecf20Sopenharmony_ci if (var->upper_margin < 0 || var->upper_margin > 255) 6458c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid upper_margin %d\n", 6468c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->upper_margin); 6478c2ecf20Sopenharmony_ci if (var->lower_margin < 0 || var->lower_margin > 255) 6488c2ecf20Sopenharmony_ci dev_err(fbi->dev, "%s: invalid lower_margin %d\n", 6498c2ecf20Sopenharmony_ci fbi->fb.fix.id, var->lower_margin); 6508c2ecf20Sopenharmony_ci#endif 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci new_regs.lccr0 = fbi->inf->lccr0 | 6538c2ecf20Sopenharmony_ci LCCR0_LEN | LCCR0_LDM | LCCR0_BAM | 6548c2ecf20Sopenharmony_ci LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci new_regs.lccr1 = 6578c2ecf20Sopenharmony_ci LCCR1_DisWdth(var->xres) + 6588c2ecf20Sopenharmony_ci LCCR1_HorSnchWdth(var->hsync_len) + 6598c2ecf20Sopenharmony_ci LCCR1_BegLnDel(var->left_margin) + 6608c2ecf20Sopenharmony_ci LCCR1_EndLnDel(var->right_margin); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* 6638c2ecf20Sopenharmony_ci * If we have a dual scan LCD, then we need to halve 6648c2ecf20Sopenharmony_ci * the YRES parameter. 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_ci yres = var->yres; 6678c2ecf20Sopenharmony_ci if (fbi->inf->lccr0 & LCCR0_Dual) 6688c2ecf20Sopenharmony_ci yres /= 2; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci new_regs.lccr2 = 6718c2ecf20Sopenharmony_ci LCCR2_DisHght(yres) + 6728c2ecf20Sopenharmony_ci LCCR2_VrtSnchWdth(var->vsync_len) + 6738c2ecf20Sopenharmony_ci LCCR2_BegFrmDel(var->upper_margin) + 6748c2ecf20Sopenharmony_ci LCCR2_EndFrmDel(var->lower_margin); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci pcd = get_pcd(fbi, var->pixclock); 6778c2ecf20Sopenharmony_ci new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->inf->lccr3 | 6788c2ecf20Sopenharmony_ci (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) | 6798c2ecf20Sopenharmony_ci (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "nlccr0 = 0x%08lx\n", new_regs.lccr0); 6828c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "nlccr1 = 0x%08lx\n", new_regs.lccr1); 6838c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "nlccr2 = 0x%08lx\n", new_regs.lccr2); 6848c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "nlccr3 = 0x%08lx\n", new_regs.lccr3); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci half_screen_size = var->bits_per_pixel; 6878c2ecf20Sopenharmony_ci half_screen_size = half_screen_size * var->xres * var->yres / 16; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* Update shadow copy atomically */ 6908c2ecf20Sopenharmony_ci local_irq_save(flags); 6918c2ecf20Sopenharmony_ci fbi->dbar1 = fbi->palette_dma; 6928c2ecf20Sopenharmony_ci fbi->dbar2 = fbi->screen_dma + half_screen_size; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci fbi->reg_lccr0 = new_regs.lccr0; 6958c2ecf20Sopenharmony_ci fbi->reg_lccr1 = new_regs.lccr1; 6968c2ecf20Sopenharmony_ci fbi->reg_lccr2 = new_regs.lccr2; 6978c2ecf20Sopenharmony_ci fbi->reg_lccr3 = new_regs.lccr3; 6988c2ecf20Sopenharmony_ci local_irq_restore(flags); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * Only update the registers if the controller is enabled 7028c2ecf20Sopenharmony_ci * and something has changed. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci if (readl_relaxed(fbi->base + LCCR0) != fbi->reg_lccr0 || 7058c2ecf20Sopenharmony_ci readl_relaxed(fbi->base + LCCR1) != fbi->reg_lccr1 || 7068c2ecf20Sopenharmony_ci readl_relaxed(fbi->base + LCCR2) != fbi->reg_lccr2 || 7078c2ecf20Sopenharmony_ci readl_relaxed(fbi->base + LCCR3) != fbi->reg_lccr3 || 7088c2ecf20Sopenharmony_ci readl_relaxed(fbi->base + DBAR1) != fbi->dbar1 || 7098c2ecf20Sopenharmony_ci readl_relaxed(fbi->base + DBAR2) != fbi->dbar2) 7108c2ecf20Sopenharmony_ci sa1100fb_schedule_work(fbi, C_REENABLE); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/* 7168c2ecf20Sopenharmony_ci * NOTE! The following functions are purely helpers for set_ctrlr_state. 7178c2ecf20Sopenharmony_ci * Do not call them directly; set_ctrlr_state does the correct serialisation 7188c2ecf20Sopenharmony_ci * to ensure that things happen in the right way 100% of time time. 7198c2ecf20Sopenharmony_ci * -- rmk 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_cistatic inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "backlight o%s\n", on ? "n" : "ff"); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (fbi->inf->backlight_power) 7268c2ecf20Sopenharmony_ci fbi->inf->backlight_power(on); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "LCD power o%s\n", on ? "n" : "ff"); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (fbi->inf->lcd_power) 7348c2ecf20Sopenharmony_ci fbi->inf->lcd_power(on); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic void sa1100fb_setup_gpio(struct sa1100fb_info *fbi) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci u_int mask = 0; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* 7428c2ecf20Sopenharmony_ci * Enable GPIO<9:2> for LCD use if: 7438c2ecf20Sopenharmony_ci * 1. Active display, or 7448c2ecf20Sopenharmony_ci * 2. Color Dual Passive display 7458c2ecf20Sopenharmony_ci * 7468c2ecf20Sopenharmony_ci * see table 11.8 on page 11-27 in the SA1100 manual 7478c2ecf20Sopenharmony_ci * -- Erik. 7488c2ecf20Sopenharmony_ci * 7498c2ecf20Sopenharmony_ci * SA1110 spec update nr. 25 says we can and should 7508c2ecf20Sopenharmony_ci * clear LDD15 to 12 for 4 or 8bpp modes with active 7518c2ecf20Sopenharmony_ci * panels. 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_ci if ((fbi->reg_lccr0 & LCCR0_CMS) == LCCR0_Color && 7548c2ecf20Sopenharmony_ci (fbi->reg_lccr0 & (LCCR0_Dual|LCCR0_Act)) != 0) { 7558c2ecf20Sopenharmony_ci mask = GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (fbi->fb.var.bits_per_pixel > 8 || 7588c2ecf20Sopenharmony_ci (fbi->reg_lccr0 & (LCCR0_Dual|LCCR0_Act)) == LCCR0_Dual) 7598c2ecf20Sopenharmony_ci mask |= GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (mask) { 7648c2ecf20Sopenharmony_ci unsigned long flags; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* 7678c2ecf20Sopenharmony_ci * SA-1100 requires the GPIO direction register set 7688c2ecf20Sopenharmony_ci * appropriately for the alternate function. Hence 7698c2ecf20Sopenharmony_ci * we set it here via bitmask rather than excessive 7708c2ecf20Sopenharmony_ci * fiddling via the GPIO subsystem - and even then 7718c2ecf20Sopenharmony_ci * we'll still have to deal with GAFR. 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_ci local_irq_save(flags); 7748c2ecf20Sopenharmony_ci GPDR |= mask; 7758c2ecf20Sopenharmony_ci GAFR |= mask; 7768c2ecf20Sopenharmony_ci local_irq_restore(flags); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic void sa1100fb_enable_controller(struct sa1100fb_info *fbi) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "Enabling LCD controller\n"); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci /* 7858c2ecf20Sopenharmony_ci * Make sure the mode bits are present in the first palette entry 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_ci fbi->palette_cpu[0] &= 0xcfff; 7888c2ecf20Sopenharmony_ci fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* enable LCD controller clock */ 7918c2ecf20Sopenharmony_ci clk_prepare_enable(fbi->clk); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Sequence from 11.7.10 */ 7948c2ecf20Sopenharmony_ci writel_relaxed(fbi->reg_lccr3, fbi->base + LCCR3); 7958c2ecf20Sopenharmony_ci writel_relaxed(fbi->reg_lccr2, fbi->base + LCCR2); 7968c2ecf20Sopenharmony_ci writel_relaxed(fbi->reg_lccr1, fbi->base + LCCR1); 7978c2ecf20Sopenharmony_ci writel_relaxed(fbi->reg_lccr0 & ~LCCR0_LEN, fbi->base + LCCR0); 7988c2ecf20Sopenharmony_ci writel_relaxed(fbi->dbar1, fbi->base + DBAR1); 7998c2ecf20Sopenharmony_ci writel_relaxed(fbi->dbar2, fbi->base + DBAR2); 8008c2ecf20Sopenharmony_ci writel_relaxed(fbi->reg_lccr0 | LCCR0_LEN, fbi->base + LCCR0); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (fbi->shannon_lcden) 8038c2ecf20Sopenharmony_ci gpiod_set_value(fbi->shannon_lcden, 1); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "DBAR1: 0x%08x\n", readl_relaxed(fbi->base + DBAR1)); 8068c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "DBAR2: 0x%08x\n", readl_relaxed(fbi->base + DBAR2)); 8078c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "LCCR0: 0x%08x\n", readl_relaxed(fbi->base + LCCR0)); 8088c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "LCCR1: 0x%08x\n", readl_relaxed(fbi->base + LCCR1)); 8098c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "LCCR2: 0x%08x\n", readl_relaxed(fbi->base + LCCR2)); 8108c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "LCCR3: 0x%08x\n", readl_relaxed(fbi->base + LCCR3)); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic void sa1100fb_disable_controller(struct sa1100fb_info *fbi) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 8168c2ecf20Sopenharmony_ci u32 lccr0; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci dev_dbg(fbi->dev, "Disabling LCD controller\n"); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (fbi->shannon_lcden) 8218c2ecf20Sopenharmony_ci gpiod_set_value(fbi->shannon_lcden, 0); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 8248c2ecf20Sopenharmony_ci add_wait_queue(&fbi->ctrlr_wait, &wait); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* Clear LCD Status Register */ 8278c2ecf20Sopenharmony_ci writel_relaxed(~0, fbi->base + LCSR); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci lccr0 = readl_relaxed(fbi->base + LCCR0); 8308c2ecf20Sopenharmony_ci lccr0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ 8318c2ecf20Sopenharmony_ci writel_relaxed(lccr0, fbi->base + LCCR0); 8328c2ecf20Sopenharmony_ci lccr0 &= ~LCCR0_LEN; /* Disable LCD Controller */ 8338c2ecf20Sopenharmony_ci writel_relaxed(lccr0, fbi->base + LCCR0); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci schedule_timeout(20 * HZ / 1000); 8368c2ecf20Sopenharmony_ci remove_wait_queue(&fbi->ctrlr_wait, &wait); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* disable LCD controller clock */ 8398c2ecf20Sopenharmony_ci clk_disable_unprepare(fbi->clk); 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci/* 8438c2ecf20Sopenharmony_ci * sa1100fb_handle_irq: Handle 'LCD DONE' interrupts. 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_cistatic irqreturn_t sa1100fb_handle_irq(int irq, void *dev_id) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = dev_id; 8488c2ecf20Sopenharmony_ci unsigned int lcsr = readl_relaxed(fbi->base + LCSR); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (lcsr & LCSR_LDD) { 8518c2ecf20Sopenharmony_ci u32 lccr0 = readl_relaxed(fbi->base + LCCR0) | LCCR0_LDM; 8528c2ecf20Sopenharmony_ci writel_relaxed(lccr0, fbi->base + LCCR0); 8538c2ecf20Sopenharmony_ci wake_up(&fbi->ctrlr_wait); 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci writel_relaxed(lcsr, fbi->base + LCSR); 8578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci/* 8618c2ecf20Sopenharmony_ci * This function must be called from task context only, since it will 8628c2ecf20Sopenharmony_ci * sleep when disabling the LCD controller, or if we get two contending 8638c2ecf20Sopenharmony_ci * processes trying to alter state. 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_cistatic void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci u_int old_state; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci mutex_lock(&fbi->ctrlr_lock); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci old_state = fbi->state; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* 8748c2ecf20Sopenharmony_ci * Hack around fbcon initialisation. 8758c2ecf20Sopenharmony_ci */ 8768c2ecf20Sopenharmony_ci if (old_state == C_STARTUP && state == C_REENABLE) 8778c2ecf20Sopenharmony_ci state = C_ENABLE; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci switch (state) { 8808c2ecf20Sopenharmony_ci case C_DISABLE_CLKCHANGE: 8818c2ecf20Sopenharmony_ci /* 8828c2ecf20Sopenharmony_ci * Disable controller for clock change. If the 8838c2ecf20Sopenharmony_ci * controller is already disabled, then do nothing. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { 8868c2ecf20Sopenharmony_ci fbi->state = state; 8878c2ecf20Sopenharmony_ci sa1100fb_disable_controller(fbi); 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci break; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci case C_DISABLE_PM: 8928c2ecf20Sopenharmony_ci case C_DISABLE: 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * Disable controller 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_ci if (old_state != C_DISABLE) { 8978c2ecf20Sopenharmony_ci fbi->state = state; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci __sa1100fb_backlight_power(fbi, 0); 9008c2ecf20Sopenharmony_ci if (old_state != C_DISABLE_CLKCHANGE) 9018c2ecf20Sopenharmony_ci sa1100fb_disable_controller(fbi); 9028c2ecf20Sopenharmony_ci __sa1100fb_lcd_power(fbi, 0); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci break; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci case C_ENABLE_CLKCHANGE: 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * Enable the controller after clock change. Only 9098c2ecf20Sopenharmony_ci * do this if we were disabled for the clock change. 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_ci if (old_state == C_DISABLE_CLKCHANGE) { 9128c2ecf20Sopenharmony_ci fbi->state = C_ENABLE; 9138c2ecf20Sopenharmony_ci sa1100fb_enable_controller(fbi); 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci break; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci case C_REENABLE: 9188c2ecf20Sopenharmony_ci /* 9198c2ecf20Sopenharmony_ci * Re-enable the controller only if it was already 9208c2ecf20Sopenharmony_ci * enabled. This is so we reprogram the control 9218c2ecf20Sopenharmony_ci * registers. 9228c2ecf20Sopenharmony_ci */ 9238c2ecf20Sopenharmony_ci if (old_state == C_ENABLE) { 9248c2ecf20Sopenharmony_ci sa1100fb_disable_controller(fbi); 9258c2ecf20Sopenharmony_ci sa1100fb_setup_gpio(fbi); 9268c2ecf20Sopenharmony_ci sa1100fb_enable_controller(fbi); 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci case C_ENABLE_PM: 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * Re-enable the controller after PM. This is not 9338c2ecf20Sopenharmony_ci * perfect - think about the case where we were doing 9348c2ecf20Sopenharmony_ci * a clock change, and we suspended half-way through. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci if (old_state != C_DISABLE_PM) 9378c2ecf20Sopenharmony_ci break; 9388c2ecf20Sopenharmony_ci fallthrough; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci case C_ENABLE: 9418c2ecf20Sopenharmony_ci /* 9428c2ecf20Sopenharmony_ci * Power up the LCD screen, enable controller, and 9438c2ecf20Sopenharmony_ci * turn on the backlight. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci if (old_state != C_ENABLE) { 9468c2ecf20Sopenharmony_ci fbi->state = C_ENABLE; 9478c2ecf20Sopenharmony_ci sa1100fb_setup_gpio(fbi); 9488c2ecf20Sopenharmony_ci __sa1100fb_lcd_power(fbi, 1); 9498c2ecf20Sopenharmony_ci sa1100fb_enable_controller(fbi); 9508c2ecf20Sopenharmony_ci __sa1100fb_backlight_power(fbi, 1); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci mutex_unlock(&fbi->ctrlr_lock); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci/* 9588c2ecf20Sopenharmony_ci * Our LCD controller task (which is called when we blank or unblank) 9598c2ecf20Sopenharmony_ci * via keventd. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_cistatic void sa1100fb_task(struct work_struct *w) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = container_of(w, struct sa1100fb_info, task); 9648c2ecf20Sopenharmony_ci u_int state = xchg(&fbi->task_state, -1); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, state); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 9708c2ecf20Sopenharmony_ci/* 9718c2ecf20Sopenharmony_ci * CPU clock speed change handler. We need to adjust the LCD timing 9728c2ecf20Sopenharmony_ci * parameters when the CPU clock is adjusted by the power management 9738c2ecf20Sopenharmony_ci * subsystem. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_cistatic int 9768c2ecf20Sopenharmony_cisa1100fb_freq_transition(struct notifier_block *nb, unsigned long val, 9778c2ecf20Sopenharmony_ci void *data) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = TO_INF(nb, freq_transition); 9808c2ecf20Sopenharmony_ci u_int pcd; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci switch (val) { 9838c2ecf20Sopenharmony_ci case CPUFREQ_PRECHANGE: 9848c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE); 9858c2ecf20Sopenharmony_ci break; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci case CPUFREQ_POSTCHANGE: 9888c2ecf20Sopenharmony_ci pcd = get_pcd(fbi, fbi->fb.var.pixclock); 9898c2ecf20Sopenharmony_ci fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); 9908c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE); 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci#endif 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9988c2ecf20Sopenharmony_ci/* 9998c2ecf20Sopenharmony_ci * Power management hooks. Note that we won't be called from IRQ context, 10008c2ecf20Sopenharmony_ci * unlike the blank functions above, so we may sleep. 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_cistatic int sa1100fb_suspend(struct platform_device *dev, pm_message_t state) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = platform_get_drvdata(dev); 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_DISABLE_PM); 10078c2ecf20Sopenharmony_ci return 0; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic int sa1100fb_resume(struct platform_device *dev) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi = platform_get_drvdata(dev); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci set_ctrlr_state(fbi, C_ENABLE_PM); 10158c2ecf20Sopenharmony_ci return 0; 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci#else 10188c2ecf20Sopenharmony_ci#define sa1100fb_suspend NULL 10198c2ecf20Sopenharmony_ci#define sa1100fb_resume NULL 10208c2ecf20Sopenharmony_ci#endif 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci/* 10238c2ecf20Sopenharmony_ci * sa1100fb_map_video_memory(): 10248c2ecf20Sopenharmony_ci * Allocates the DRAM memory for the frame buffer. This buffer is 10258c2ecf20Sopenharmony_ci * remapped into a non-cached, non-buffered, memory region to 10268c2ecf20Sopenharmony_ci * allow palette and pixel writes to occur without flushing the 10278c2ecf20Sopenharmony_ci * cache. Once this area is remapped, all virtual memory 10288c2ecf20Sopenharmony_ci * access to the video memory should occur at the new region. 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_cistatic int sa1100fb_map_video_memory(struct sa1100fb_info *fbi) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * We reserve one page for the palette, plus the size 10348c2ecf20Sopenharmony_ci * of the framebuffer. 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE); 10378c2ecf20Sopenharmony_ci fbi->map_cpu = dma_alloc_wc(fbi->dev, fbi->map_size, &fbi->map_dma, 10388c2ecf20Sopenharmony_ci GFP_KERNEL); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (fbi->map_cpu) { 10418c2ecf20Sopenharmony_ci fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE; 10428c2ecf20Sopenharmony_ci fbi->screen_dma = fbi->map_dma + PAGE_SIZE; 10438c2ecf20Sopenharmony_ci /* 10448c2ecf20Sopenharmony_ci * FIXME: this is actually the wrong thing to place in 10458c2ecf20Sopenharmony_ci * smem_start. But fbdev suffers from the problem that 10468c2ecf20Sopenharmony_ci * it needs an API which doesn't exist (in this case, 10478c2ecf20Sopenharmony_ci * dma_writecombine_mmap) 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci fbi->fb.fix.smem_start = fbi->screen_dma; 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci return fbi->map_cpu ? 0 : -ENOMEM; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci/* Fake monspecs to fill in fbinfo structure */ 10568c2ecf20Sopenharmony_cistatic const struct fb_monspecs monspecs = { 10578c2ecf20Sopenharmony_ci .hfmin = 30000, 10588c2ecf20Sopenharmony_ci .hfmax = 70000, 10598c2ecf20Sopenharmony_ci .vfmin = 50, 10608c2ecf20Sopenharmony_ci .vfmax = 65, 10618c2ecf20Sopenharmony_ci}; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci struct sa1100fb_mach_info *inf = dev_get_platdata(dev); 10678c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi; 10688c2ecf20Sopenharmony_ci unsigned i; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci fbi = devm_kzalloc(dev, sizeof(struct sa1100fb_info), GFP_KERNEL); 10718c2ecf20Sopenharmony_ci if (!fbi) 10728c2ecf20Sopenharmony_ci return NULL; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci fbi->dev = dev; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci strcpy(fbi->fb.fix.id, SA1100_NAME); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; 10798c2ecf20Sopenharmony_ci fbi->fb.fix.type_aux = 0; 10808c2ecf20Sopenharmony_ci fbi->fb.fix.xpanstep = 0; 10818c2ecf20Sopenharmony_ci fbi->fb.fix.ypanstep = 0; 10828c2ecf20Sopenharmony_ci fbi->fb.fix.ywrapstep = 0; 10838c2ecf20Sopenharmony_ci fbi->fb.fix.accel = FB_ACCEL_NONE; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci fbi->fb.var.nonstd = 0; 10868c2ecf20Sopenharmony_ci fbi->fb.var.activate = FB_ACTIVATE_NOW; 10878c2ecf20Sopenharmony_ci fbi->fb.var.height = -1; 10888c2ecf20Sopenharmony_ci fbi->fb.var.width = -1; 10898c2ecf20Sopenharmony_ci fbi->fb.var.accel_flags = 0; 10908c2ecf20Sopenharmony_ci fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci fbi->fb.fbops = &sa1100fb_ops; 10938c2ecf20Sopenharmony_ci fbi->fb.flags = FBINFO_DEFAULT; 10948c2ecf20Sopenharmony_ci fbi->fb.monspecs = monspecs; 10958c2ecf20Sopenharmony_ci fbi->fb.pseudo_palette = fbi->pseudo_palette; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci fbi->rgb[RGB_4] = &rgb_4; 10988c2ecf20Sopenharmony_ci fbi->rgb[RGB_8] = &rgb_8; 10998c2ecf20Sopenharmony_ci fbi->rgb[RGB_16] = &def_rgb_16; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* 11028c2ecf20Sopenharmony_ci * People just don't seem to get this. We don't support 11038c2ecf20Sopenharmony_ci * anything but correct entries now, so panic if someone 11048c2ecf20Sopenharmony_ci * does something stupid. 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci if (inf->lccr3 & (LCCR3_VrtSnchL|LCCR3_HorSnchL|0xff) || 11078c2ecf20Sopenharmony_ci inf->pixclock == 0) 11088c2ecf20Sopenharmony_ci panic("sa1100fb error: invalid LCCR3 fields set or zero " 11098c2ecf20Sopenharmony_ci "pixclock."); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci fbi->fb.var.xres = inf->xres; 11128c2ecf20Sopenharmony_ci fbi->fb.var.xres_virtual = inf->xres; 11138c2ecf20Sopenharmony_ci fbi->fb.var.yres = inf->yres; 11148c2ecf20Sopenharmony_ci fbi->fb.var.yres_virtual = inf->yres; 11158c2ecf20Sopenharmony_ci fbi->fb.var.bits_per_pixel = inf->bpp; 11168c2ecf20Sopenharmony_ci fbi->fb.var.pixclock = inf->pixclock; 11178c2ecf20Sopenharmony_ci fbi->fb.var.hsync_len = inf->hsync_len; 11188c2ecf20Sopenharmony_ci fbi->fb.var.left_margin = inf->left_margin; 11198c2ecf20Sopenharmony_ci fbi->fb.var.right_margin = inf->right_margin; 11208c2ecf20Sopenharmony_ci fbi->fb.var.vsync_len = inf->vsync_len; 11218c2ecf20Sopenharmony_ci fbi->fb.var.upper_margin = inf->upper_margin; 11228c2ecf20Sopenharmony_ci fbi->fb.var.lower_margin = inf->lower_margin; 11238c2ecf20Sopenharmony_ci fbi->fb.var.sync = inf->sync; 11248c2ecf20Sopenharmony_ci fbi->fb.var.grayscale = inf->cmap_greyscale; 11258c2ecf20Sopenharmony_ci fbi->state = C_STARTUP; 11268c2ecf20Sopenharmony_ci fbi->task_state = (u_char)-1; 11278c2ecf20Sopenharmony_ci fbi->fb.fix.smem_len = inf->xres * inf->yres * 11288c2ecf20Sopenharmony_ci inf->bpp / 8; 11298c2ecf20Sopenharmony_ci fbi->inf = inf; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* Copy the RGB bitfield overrides */ 11328c2ecf20Sopenharmony_ci for (i = 0; i < NR_RGB; i++) 11338c2ecf20Sopenharmony_ci if (inf->rgb[i]) 11348c2ecf20Sopenharmony_ci fbi->rgb[i] = inf->rgb[i]; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci init_waitqueue_head(&fbi->ctrlr_wait); 11378c2ecf20Sopenharmony_ci INIT_WORK(&fbi->task, sa1100fb_task); 11388c2ecf20Sopenharmony_ci mutex_init(&fbi->ctrlr_lock); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return fbi; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int sa1100fb_probe(struct platform_device *pdev) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct sa1100fb_info *fbi; 11468c2ecf20Sopenharmony_ci int ret, irq; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (!dev_get_platdata(&pdev->dev)) { 11498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform LCD data\n"); 11508c2ecf20Sopenharmony_ci return -EINVAL; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 11548c2ecf20Sopenharmony_ci if (irq < 0) 11558c2ecf20Sopenharmony_ci return -EINVAL; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci fbi = sa1100fb_init_fbinfo(&pdev->dev); 11588c2ecf20Sopenharmony_ci if (!fbi) 11598c2ecf20Sopenharmony_ci return -ENOMEM; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci fbi->base = devm_platform_ioremap_resource(pdev, 0); 11628c2ecf20Sopenharmony_ci if (IS_ERR(fbi->base)) 11638c2ecf20Sopenharmony_ci return PTR_ERR(fbi->base); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci fbi->clk = devm_clk_get(&pdev->dev, NULL); 11668c2ecf20Sopenharmony_ci if (IS_ERR(fbi->clk)) 11678c2ecf20Sopenharmony_ci return PTR_ERR(fbi->clk); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, sa1100fb_handle_irq, 0, 11708c2ecf20Sopenharmony_ci "LCD", fbi); 11718c2ecf20Sopenharmony_ci if (ret) { 11728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_irq failed: %d\n", ret); 11738c2ecf20Sopenharmony_ci return ret; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci fbi->shannon_lcden = gpiod_get_optional(&pdev->dev, "shannon-lcden", 11778c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 11788c2ecf20Sopenharmony_ci if (IS_ERR(fbi->shannon_lcden)) 11798c2ecf20Sopenharmony_ci return PTR_ERR(fbi->shannon_lcden); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* Initialize video memory */ 11828c2ecf20Sopenharmony_ci ret = sa1100fb_map_video_memory(fbi); 11838c2ecf20Sopenharmony_ci if (ret) 11848c2ecf20Sopenharmony_ci return ret; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * This makes sure that our colour bitfield 11888c2ecf20Sopenharmony_ci * descriptors are correctly initialised. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_ci sa1100fb_check_var(&fbi->fb.var, &fbi->fb); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, fbi); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci ret = register_framebuffer(&fbi->fb); 11958c2ecf20Sopenharmony_ci if (ret < 0) { 11968c2ecf20Sopenharmony_ci dma_free_wc(fbi->dev, fbi->map_size, fbi->map_cpu, 11978c2ecf20Sopenharmony_ci fbi->map_dma); 11988c2ecf20Sopenharmony_ci return ret; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ 12028c2ecf20Sopenharmony_ci fbi->freq_transition.notifier_call = sa1100fb_freq_transition; 12038c2ecf20Sopenharmony_ci cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); 12048c2ecf20Sopenharmony_ci#endif 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* This driver cannot be unloaded at the moment */ 12078c2ecf20Sopenharmony_ci return 0; 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic struct platform_driver sa1100fb_driver = { 12118c2ecf20Sopenharmony_ci .probe = sa1100fb_probe, 12128c2ecf20Sopenharmony_ci .suspend = sa1100fb_suspend, 12138c2ecf20Sopenharmony_ci .resume = sa1100fb_resume, 12148c2ecf20Sopenharmony_ci .driver = { 12158c2ecf20Sopenharmony_ci .name = "sa11x0-fb", 12168c2ecf20Sopenharmony_ci }, 12178c2ecf20Sopenharmony_ci}; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ciint __init sa1100fb_init(void) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci if (fb_get_options("sa1100fb", NULL)) 12228c2ecf20Sopenharmony_ci return -ENODEV; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci return platform_driver_register(&sa1100fb_driver); 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ciint __init sa1100fb_setup(char *options) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci#if 0 12308c2ecf20Sopenharmony_ci char *this_opt; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (!options || !*options) 12338c2ecf20Sopenharmony_ci return 0; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "bpp:", 4)) 12388c2ecf20Sopenharmony_ci current_par.max_bpp = 12398c2ecf20Sopenharmony_ci simple_strtoul(this_opt + 4, NULL, 0); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "lccr0:", 6)) 12428c2ecf20Sopenharmony_ci lcd_shadow.lccr0 = 12438c2ecf20Sopenharmony_ci simple_strtoul(this_opt + 6, NULL, 0); 12448c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "lccr1:", 6)) { 12458c2ecf20Sopenharmony_ci lcd_shadow.lccr1 = 12468c2ecf20Sopenharmony_ci simple_strtoul(this_opt + 6, NULL, 0); 12478c2ecf20Sopenharmony_ci current_par.max_xres = 12488c2ecf20Sopenharmony_ci (lcd_shadow.lccr1 & 0x3ff) + 16; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "lccr2:", 6)) { 12518c2ecf20Sopenharmony_ci lcd_shadow.lccr2 = 12528c2ecf20Sopenharmony_ci simple_strtoul(this_opt + 6, NULL, 0); 12538c2ecf20Sopenharmony_ci current_par.max_yres = 12548c2ecf20Sopenharmony_ci (lcd_shadow. 12558c2ecf20Sopenharmony_ci lccr0 & LCCR0_SDS) ? ((lcd_shadow. 12568c2ecf20Sopenharmony_ci lccr2 & 0x3ff) + 12578c2ecf20Sopenharmony_ci 1) * 12588c2ecf20Sopenharmony_ci 2 : ((lcd_shadow.lccr2 & 0x3ff) + 1); 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "lccr3:", 6)) 12618c2ecf20Sopenharmony_ci lcd_shadow.lccr3 = 12628c2ecf20Sopenharmony_ci simple_strtoul(this_opt + 6, NULL, 0); 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci#endif 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cimodule_init(sa1100fb_init); 12698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("StrongARM-1100/1110 framebuffer driver"); 12708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1271