18c2ecf20Sopenharmony_ci/* linux/drivers/video/s3c2410fb.c 28c2ecf20Sopenharmony_ci * Copyright (c) 2004,2005 Arnaud Patard 38c2ecf20Sopenharmony_ci * Copyright (c) 2004-2008 Ben Dooks 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * S3C2410 LCD Framebuffer Driver 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 * Driver based on skeletonfb.c, sa1100fb.c and others. 128c2ecf20Sopenharmony_ci*/ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/mm.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/fb.h> 258c2ecf20Sopenharmony_ci#include <linux/init.h> 268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci#include <linux/clk.h> 308c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 318c2ecf20Sopenharmony_ci#include <linux/io.h> 328c2ecf20Sopenharmony_ci#include <linux/platform_data/fb-s3c2410.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <asm/div64.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <asm/mach/map.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 398c2ecf20Sopenharmony_ci#include <linux/pm.h> 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "s3c2410fb.h" 438c2ecf20Sopenharmony_ci#include "s3c2410fb-regs-lcd.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Debugging stuff */ 468c2ecf20Sopenharmony_cistatic int debug = IS_BUILTIN(CONFIG_FB_S3C2410_DEBUG); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define dprintk(msg...) \ 498c2ecf20Sopenharmony_cido { \ 508c2ecf20Sopenharmony_ci if (debug) \ 518c2ecf20Sopenharmony_ci pr_debug(msg); \ 528c2ecf20Sopenharmony_ci} while (0) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* useful functions */ 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int is_s3c2412(struct s3c2410fb_info *fbi) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return (fbi->drv_type == DRV_S3C2412); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* s3c2410fb_set_lcdaddr 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * initialise lcd controller address pointers 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_cistatic void s3c2410fb_set_lcdaddr(struct fb_info *info) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci unsigned long saddr1, saddr2, saddr3; 688c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 698c2ecf20Sopenharmony_ci void __iomem *regs = fbi->io; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci saddr1 = info->fix.smem_start >> 1; 728c2ecf20Sopenharmony_ci saddr2 = info->fix.smem_start; 738c2ecf20Sopenharmony_ci saddr2 += info->fix.line_length * info->var.yres; 748c2ecf20Sopenharmony_ci saddr2 >>= 1; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci saddr3 = S3C2410_OFFSIZE(0) | 778c2ecf20Sopenharmony_ci S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci dprintk("LCDSADDR1 = 0x%08lx\n", saddr1); 808c2ecf20Sopenharmony_ci dprintk("LCDSADDR2 = 0x%08lx\n", saddr2); 818c2ecf20Sopenharmony_ci dprintk("LCDSADDR3 = 0x%08lx\n", saddr3); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci writel(saddr1, regs + S3C2410_LCDSADDR1); 848c2ecf20Sopenharmony_ci writel(saddr2, regs + S3C2410_LCDSADDR2); 858c2ecf20Sopenharmony_ci writel(saddr3, regs + S3C2410_LCDSADDR3); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* s3c2410fb_calc_pixclk() 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * calculate divisor for clk->pixclk 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_cistatic unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi, 938c2ecf20Sopenharmony_ci unsigned long pixclk) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned long clk = fbi->clk_rate; 968c2ecf20Sopenharmony_ci unsigned long long div; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* pixclk is in picoseconds, our clock is in Hz 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci * Hz -> picoseconds is / 10^-12 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci div = (unsigned long long)clk * pixclk; 1048c2ecf20Sopenharmony_ci div >>= 12; /* div / 2^12 */ 1058c2ecf20Sopenharmony_ci do_div(div, 625 * 625UL * 625); /* div / 5^12 */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div); 1088c2ecf20Sopenharmony_ci return div; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * s3c2410fb_check_var(): 1138c2ecf20Sopenharmony_ci * Get the video params out of 'var'. If a value doesn't fit, round it up, 1148c2ecf20Sopenharmony_ci * if it's too big, return -EINVAL. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic int s3c2410fb_check_var(struct fb_var_screeninfo *var, 1188c2ecf20Sopenharmony_ci struct fb_info *info) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 1218c2ecf20Sopenharmony_ci struct s3c2410fb_mach_info *mach_info = dev_get_platdata(fbi->dev); 1228c2ecf20Sopenharmony_ci struct s3c2410fb_display *display = NULL; 1238c2ecf20Sopenharmony_ci struct s3c2410fb_display *default_display = mach_info->displays + 1248c2ecf20Sopenharmony_ci mach_info->default_display; 1258c2ecf20Sopenharmony_ci int type = default_display->type; 1268c2ecf20Sopenharmony_ci unsigned i; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci dprintk("check_var(var=%p, info=%p)\n", var, info); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* validate x/y resolution */ 1318c2ecf20Sopenharmony_ci /* choose default mode if possible */ 1328c2ecf20Sopenharmony_ci if (var->yres == default_display->yres && 1338c2ecf20Sopenharmony_ci var->xres == default_display->xres && 1348c2ecf20Sopenharmony_ci var->bits_per_pixel == default_display->bpp) 1358c2ecf20Sopenharmony_ci display = default_display; 1368c2ecf20Sopenharmony_ci else 1378c2ecf20Sopenharmony_ci for (i = 0; i < mach_info->num_displays; i++) 1388c2ecf20Sopenharmony_ci if (type == mach_info->displays[i].type && 1398c2ecf20Sopenharmony_ci var->yres == mach_info->displays[i].yres && 1408c2ecf20Sopenharmony_ci var->xres == mach_info->displays[i].xres && 1418c2ecf20Sopenharmony_ci var->bits_per_pixel == mach_info->displays[i].bpp) { 1428c2ecf20Sopenharmony_ci display = mach_info->displays + i; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!display) { 1478c2ecf20Sopenharmony_ci dprintk("wrong resolution or depth %dx%d at %d bpp\n", 1488c2ecf20Sopenharmony_ci var->xres, var->yres, var->bits_per_pixel); 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* it is always the size as the display */ 1538c2ecf20Sopenharmony_ci var->xres_virtual = display->xres; 1548c2ecf20Sopenharmony_ci var->yres_virtual = display->yres; 1558c2ecf20Sopenharmony_ci var->height = display->height; 1568c2ecf20Sopenharmony_ci var->width = display->width; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* copy lcd settings */ 1598c2ecf20Sopenharmony_ci var->pixclock = display->pixclock; 1608c2ecf20Sopenharmony_ci var->left_margin = display->left_margin; 1618c2ecf20Sopenharmony_ci var->right_margin = display->right_margin; 1628c2ecf20Sopenharmony_ci var->upper_margin = display->upper_margin; 1638c2ecf20Sopenharmony_ci var->lower_margin = display->lower_margin; 1648c2ecf20Sopenharmony_ci var->vsync_len = display->vsync_len; 1658c2ecf20Sopenharmony_ci var->hsync_len = display->hsync_len; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci fbi->regs.lcdcon5 = display->lcdcon5; 1688c2ecf20Sopenharmony_ci /* set display type */ 1698c2ecf20Sopenharmony_ci fbi->regs.lcdcon1 = display->type; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci var->transp.offset = 0; 1728c2ecf20Sopenharmony_ci var->transp.length = 0; 1738c2ecf20Sopenharmony_ci /* set r/g/b positions */ 1748c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 1758c2ecf20Sopenharmony_ci case 1: 1768c2ecf20Sopenharmony_ci case 2: 1778c2ecf20Sopenharmony_ci case 4: 1788c2ecf20Sopenharmony_ci var->red.offset = 0; 1798c2ecf20Sopenharmony_ci var->red.length = var->bits_per_pixel; 1808c2ecf20Sopenharmony_ci var->green = var->red; 1818c2ecf20Sopenharmony_ci var->blue = var->red; 1828c2ecf20Sopenharmony_ci break; 1838c2ecf20Sopenharmony_ci case 8: 1848c2ecf20Sopenharmony_ci if (display->type != S3C2410_LCDCON1_TFT) { 1858c2ecf20Sopenharmony_ci /* 8 bpp 332 */ 1868c2ecf20Sopenharmony_ci var->red.length = 3; 1878c2ecf20Sopenharmony_ci var->red.offset = 5; 1888c2ecf20Sopenharmony_ci var->green.length = 3; 1898c2ecf20Sopenharmony_ci var->green.offset = 2; 1908c2ecf20Sopenharmony_ci var->blue.length = 2; 1918c2ecf20Sopenharmony_ci var->blue.offset = 0; 1928c2ecf20Sopenharmony_ci } else { 1938c2ecf20Sopenharmony_ci var->red.offset = 0; 1948c2ecf20Sopenharmony_ci var->red.length = 8; 1958c2ecf20Sopenharmony_ci var->green = var->red; 1968c2ecf20Sopenharmony_ci var->blue = var->red; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case 12: 2008c2ecf20Sopenharmony_ci /* 12 bpp 444 */ 2018c2ecf20Sopenharmony_ci var->red.length = 4; 2028c2ecf20Sopenharmony_ci var->red.offset = 8; 2038c2ecf20Sopenharmony_ci var->green.length = 4; 2048c2ecf20Sopenharmony_ci var->green.offset = 4; 2058c2ecf20Sopenharmony_ci var->blue.length = 4; 2068c2ecf20Sopenharmony_ci var->blue.offset = 0; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci default: 2108c2ecf20Sopenharmony_ci case 16: 2118c2ecf20Sopenharmony_ci if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) { 2128c2ecf20Sopenharmony_ci /* 16 bpp, 565 format */ 2138c2ecf20Sopenharmony_ci var->red.offset = 11; 2148c2ecf20Sopenharmony_ci var->green.offset = 5; 2158c2ecf20Sopenharmony_ci var->blue.offset = 0; 2168c2ecf20Sopenharmony_ci var->red.length = 5; 2178c2ecf20Sopenharmony_ci var->green.length = 6; 2188c2ecf20Sopenharmony_ci var->blue.length = 5; 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci /* 16 bpp, 5551 format */ 2218c2ecf20Sopenharmony_ci var->red.offset = 11; 2228c2ecf20Sopenharmony_ci var->green.offset = 6; 2238c2ecf20Sopenharmony_ci var->blue.offset = 1; 2248c2ecf20Sopenharmony_ci var->red.length = 5; 2258c2ecf20Sopenharmony_ci var->green.length = 5; 2268c2ecf20Sopenharmony_ci var->blue.length = 5; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci case 32: 2308c2ecf20Sopenharmony_ci /* 24 bpp 888 and 8 dummy */ 2318c2ecf20Sopenharmony_ci var->red.length = 8; 2328c2ecf20Sopenharmony_ci var->red.offset = 16; 2338c2ecf20Sopenharmony_ci var->green.length = 8; 2348c2ecf20Sopenharmony_ci var->green.offset = 8; 2358c2ecf20Sopenharmony_ci var->blue.length = 8; 2368c2ecf20Sopenharmony_ci var->blue.offset = 0; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* s3c2410fb_calculate_stn_lcd_regs 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci * calculate register values from var settings 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info, 2478c2ecf20Sopenharmony_ci struct s3c2410fb_hw *regs) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci const struct s3c2410fb_info *fbi = info->par; 2508c2ecf20Sopenharmony_ci const struct fb_var_screeninfo *var = &info->var; 2518c2ecf20Sopenharmony_ci int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT; 2528c2ecf20Sopenharmony_ci int hs = var->xres >> 2; 2538c2ecf20Sopenharmony_ci unsigned wdly = (var->left_margin >> 4) - 1; 2548c2ecf20Sopenharmony_ci unsigned wlh = (var->hsync_len >> 4) - 1; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (type != S3C2410_LCDCON1_STN4) 2578c2ecf20Sopenharmony_ci hs >>= 1; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 2608c2ecf20Sopenharmony_ci case 1: 2618c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_STN1BPP; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case 2: 2648c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_STN2GREY; 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci case 4: 2678c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_STN4GREY; 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci case 8: 2708c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_STN8BPP; 2718c2ecf20Sopenharmony_ci hs *= 3; 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci case 12: 2748c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_STN12BPP; 2758c2ecf20Sopenharmony_ci hs *= 3; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci /* invalid pixel depth */ 2808c2ecf20Sopenharmony_ci dev_err(fbi->dev, "invalid bpp %d\n", 2818c2ecf20Sopenharmony_ci var->bits_per_pixel); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci /* update X/Y info */ 2848c2ecf20Sopenharmony_ci dprintk("setting horz: lft=%d, rt=%d, sync=%d\n", 2858c2ecf20Sopenharmony_ci var->left_margin, var->right_margin, var->hsync_len); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (wdly > 3) 2908c2ecf20Sopenharmony_ci wdly = 3; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (wlh > 3) 2938c2ecf20Sopenharmony_ci wlh = 3; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) | 2968c2ecf20Sopenharmony_ci S3C2410_LCDCON3_LINEBLANK(var->right_margin / 8) | 2978c2ecf20Sopenharmony_ci S3C2410_LCDCON3_HOZVAL(hs - 1); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* s3c2410fb_calculate_tft_lcd_regs 3038c2ecf20Sopenharmony_ci * 3048c2ecf20Sopenharmony_ci * calculate register values from var settings 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_cistatic void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info, 3078c2ecf20Sopenharmony_ci struct s3c2410fb_hw *regs) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci const struct s3c2410fb_info *fbi = info->par; 3108c2ecf20Sopenharmony_ci const struct fb_var_screeninfo *var = &info->var; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 3138c2ecf20Sopenharmony_ci case 1: 3148c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_TFT1BPP; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci case 2: 3178c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_TFT2BPP; 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case 4: 3208c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_TFT4BPP; 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case 8: 3238c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_TFT8BPP; 3248c2ecf20Sopenharmony_ci regs->lcdcon5 |= S3C2410_LCDCON5_BSWP | 3258c2ecf20Sopenharmony_ci S3C2410_LCDCON5_FRM565; 3268c2ecf20Sopenharmony_ci regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci case 16: 3298c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_TFT16BPP; 3308c2ecf20Sopenharmony_ci regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP; 3318c2ecf20Sopenharmony_ci regs->lcdcon5 |= S3C2410_LCDCON5_HWSWP; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case 32: 3348c2ecf20Sopenharmony_ci regs->lcdcon1 |= S3C2410_LCDCON1_TFT24BPP; 3358c2ecf20Sopenharmony_ci regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP | 3368c2ecf20Sopenharmony_ci S3C2410_LCDCON5_HWSWP | 3378c2ecf20Sopenharmony_ci S3C2410_LCDCON5_BPP24BL); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci default: 3408c2ecf20Sopenharmony_ci /* invalid pixel depth */ 3418c2ecf20Sopenharmony_ci dev_err(fbi->dev, "invalid bpp %d\n", 3428c2ecf20Sopenharmony_ci var->bits_per_pixel); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci /* update X/Y info */ 3458c2ecf20Sopenharmony_ci dprintk("setting vert: up=%d, low=%d, sync=%d\n", 3468c2ecf20Sopenharmony_ci var->upper_margin, var->lower_margin, var->vsync_len); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci dprintk("setting horz: lft=%d, rt=%d, sync=%d\n", 3498c2ecf20Sopenharmony_ci var->left_margin, var->right_margin, var->hsync_len); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) | 3528c2ecf20Sopenharmony_ci S3C2410_LCDCON2_VBPD(var->upper_margin - 1) | 3538c2ecf20Sopenharmony_ci S3C2410_LCDCON2_VFPD(var->lower_margin - 1) | 3548c2ecf20Sopenharmony_ci S3C2410_LCDCON2_VSPW(var->vsync_len - 1); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) | 3578c2ecf20Sopenharmony_ci S3C2410_LCDCON3_HFPD(var->left_margin - 1) | 3588c2ecf20Sopenharmony_ci S3C2410_LCDCON3_HOZVAL(var->xres - 1); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* s3c2410fb_activate_var 3648c2ecf20Sopenharmony_ci * 3658c2ecf20Sopenharmony_ci * activate (set) the controller from the given framebuffer 3668c2ecf20Sopenharmony_ci * information 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_cistatic void s3c2410fb_activate_var(struct fb_info *info) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 3718c2ecf20Sopenharmony_ci void __iomem *regs = fbi->io; 3728c2ecf20Sopenharmony_ci int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT; 3738c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 3748c2ecf20Sopenharmony_ci int clkdiv; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci dprintk("%s: var->xres = %d\n", __func__, var->xres); 3798c2ecf20Sopenharmony_ci dprintk("%s: var->yres = %d\n", __func__, var->yres); 3808c2ecf20Sopenharmony_ci dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (type == S3C2410_LCDCON1_TFT) { 3838c2ecf20Sopenharmony_ci s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs); 3848c2ecf20Sopenharmony_ci --clkdiv; 3858c2ecf20Sopenharmony_ci if (clkdiv < 0) 3868c2ecf20Sopenharmony_ci clkdiv = 0; 3878c2ecf20Sopenharmony_ci } else { 3888c2ecf20Sopenharmony_ci s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs); 3898c2ecf20Sopenharmony_ci if (clkdiv < 2) 3908c2ecf20Sopenharmony_ci clkdiv = 2; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* write new registers */ 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci dprintk("new register set:\n"); 3988c2ecf20Sopenharmony_ci dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1); 3998c2ecf20Sopenharmony_ci dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2); 4008c2ecf20Sopenharmony_ci dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3); 4018c2ecf20Sopenharmony_ci dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4); 4028c2ecf20Sopenharmony_ci dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID, 4058c2ecf20Sopenharmony_ci regs + S3C2410_LCDCON1); 4068c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2); 4078c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3); 4088c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4); 4098c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* set lcd address pointers */ 4128c2ecf20Sopenharmony_ci s3c2410fb_set_lcdaddr(info); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID, 4158c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* 4198c2ecf20Sopenharmony_ci * s3c2410fb_set_par - Alters the hardware state. 4208c2ecf20Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_cistatic int s3c2410fb_set_par(struct fb_info *info) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 4288c2ecf20Sopenharmony_ci case 32: 4298c2ecf20Sopenharmony_ci case 16: 4308c2ecf20Sopenharmony_ci case 12: 4318c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case 1: 4348c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_MONO01; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci default: 4378c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* activate this new configuration */ 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci s3c2410fb_activate_var(info); 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void schedule_palette_update(struct s3c2410fb_info *fbi, 4508c2ecf20Sopenharmony_ci unsigned int regno, unsigned int val) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci unsigned long flags; 4538c2ecf20Sopenharmony_ci unsigned long irqen; 4548c2ecf20Sopenharmony_ci void __iomem *irq_base = fbi->irq_base; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci local_irq_save(flags); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci fbi->palette_buffer[regno] = val; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (!fbi->palette_ready) { 4618c2ecf20Sopenharmony_ci fbi->palette_ready = 1; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* enable IRQ */ 4648c2ecf20Sopenharmony_ci irqen = readl(irq_base + S3C24XX_LCDINTMSK); 4658c2ecf20Sopenharmony_ci irqen &= ~S3C2410_LCDINT_FRSYNC; 4668c2ecf20Sopenharmony_ci writel(irqen, irq_base + S3C24XX_LCDINTMSK); 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci local_irq_restore(flags); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/* from pxafb.c */ 4738c2ecf20Sopenharmony_cistatic inline unsigned int chan_to_field(unsigned int chan, 4748c2ecf20Sopenharmony_ci struct fb_bitfield *bf) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci chan &= 0xffff; 4778c2ecf20Sopenharmony_ci chan >>= 16 - bf->length; 4788c2ecf20Sopenharmony_ci return chan << bf->offset; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic int s3c2410fb_setcolreg(unsigned regno, 4828c2ecf20Sopenharmony_ci unsigned red, unsigned green, unsigned blue, 4838c2ecf20Sopenharmony_ci unsigned transp, struct fb_info *info) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 4868c2ecf20Sopenharmony_ci void __iomem *regs = fbi->io; 4878c2ecf20Sopenharmony_ci unsigned int val; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n", 4908c2ecf20Sopenharmony_ci regno, red, green, blue); */ 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci switch (info->fix.visual) { 4938c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 4948c2ecf20Sopenharmony_ci /* true-colour, use pseudo-palette */ 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (regno < 16) { 4978c2ecf20Sopenharmony_ci u32 *pal = info->pseudo_palette; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci val = chan_to_field(red, &info->var.red); 5008c2ecf20Sopenharmony_ci val |= chan_to_field(green, &info->var.green); 5018c2ecf20Sopenharmony_ci val |= chan_to_field(blue, &info->var.blue); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci pal[regno] = val; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 5088c2ecf20Sopenharmony_ci if (regno < 256) { 5098c2ecf20Sopenharmony_ci /* currently assume RGB 5-6-5 mode */ 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci val = (red >> 0) & 0xf800; 5128c2ecf20Sopenharmony_ci val |= (green >> 5) & 0x07e0; 5138c2ecf20Sopenharmony_ci val |= (blue >> 11) & 0x001f; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci writel(val, regs + S3C2410_TFTPAL(regno)); 5168c2ecf20Sopenharmony_ci schedule_palette_update(fbi, regno, val); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci default: 5228c2ecf20Sopenharmony_ci return 1; /* unknown type */ 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/* s3c2410fb_lcd_enable 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * shutdown the lcd controller 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_cistatic void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci unsigned long flags; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci local_irq_save(flags); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (enable) 5398c2ecf20Sopenharmony_ci fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID; 5408c2ecf20Sopenharmony_ci else 5418c2ecf20Sopenharmony_ci fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci local_irq_restore(flags); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * s3c2410fb_blank 5518c2ecf20Sopenharmony_ci * @blank_mode: the blank mode we want. 5528c2ecf20Sopenharmony_ci * @info: frame buffer structure that represents a single frame buffer 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * Blank the screen if blank_mode != 0, else unblank. Return 0 if 5558c2ecf20Sopenharmony_ci * blanking succeeded, != 0 if un-/blanking failed due to e.g. a 5568c2ecf20Sopenharmony_ci * video mode which doesn't support it. Implements VESA suspend 5578c2ecf20Sopenharmony_ci * and powerdown modes on hardware that supports disabling hsync/vsync: 5588c2ecf20Sopenharmony_ci * 5598c2ecf20Sopenharmony_ci * Returns negative errno on error, or zero on success. 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_cistatic int s3c2410fb_blank(int blank_mode, struct fb_info *info) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 5658c2ecf20Sopenharmony_ci void __iomem *tpal_reg = fbi->io; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci dprintk("blank(mode=%d, info=%p)\n", blank_mode, info); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (blank_mode == FB_BLANK_POWERDOWN) 5728c2ecf20Sopenharmony_ci s3c2410fb_lcd_enable(fbi, 0); 5738c2ecf20Sopenharmony_ci else 5748c2ecf20Sopenharmony_ci s3c2410fb_lcd_enable(fbi, 1); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (blank_mode == FB_BLANK_UNBLANK) 5778c2ecf20Sopenharmony_ci writel(0x0, tpal_reg); 5788c2ecf20Sopenharmony_ci else { 5798c2ecf20Sopenharmony_ci dprintk("setting TPAL to output 0x000000\n"); 5808c2ecf20Sopenharmony_ci writel(S3C2410_TPAL_EN, tpal_reg); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_cistatic int s3c2410fb_debug_show(struct device *dev, 5878c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off"); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int s3c2410fb_debug_store(struct device *dev, 5938c2ecf20Sopenharmony_ci struct device_attribute *attr, 5948c2ecf20Sopenharmony_ci const char *buf, size_t len) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci if (len < 1) 5978c2ecf20Sopenharmony_ci return -EINVAL; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (strncasecmp(buf, "on", 2) == 0 || 6008c2ecf20Sopenharmony_ci strncasecmp(buf, "1", 1) == 0) { 6018c2ecf20Sopenharmony_ci debug = 1; 6028c2ecf20Sopenharmony_ci dev_dbg(dev, "s3c2410fb: Debug On"); 6038c2ecf20Sopenharmony_ci } else if (strncasecmp(buf, "off", 3) == 0 || 6048c2ecf20Sopenharmony_ci strncasecmp(buf, "0", 1) == 0) { 6058c2ecf20Sopenharmony_ci debug = 0; 6068c2ecf20Sopenharmony_ci dev_dbg(dev, "s3c2410fb: Debug Off"); 6078c2ecf20Sopenharmony_ci } else { 6088c2ecf20Sopenharmony_ci return -EINVAL; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return len; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic DEVICE_ATTR(debug, 0664, s3c2410fb_debug_show, s3c2410fb_debug_store); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic const struct fb_ops s3c2410fb_ops = { 6178c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6188c2ecf20Sopenharmony_ci .fb_check_var = s3c2410fb_check_var, 6198c2ecf20Sopenharmony_ci .fb_set_par = s3c2410fb_set_par, 6208c2ecf20Sopenharmony_ci .fb_blank = s3c2410fb_blank, 6218c2ecf20Sopenharmony_ci .fb_setcolreg = s3c2410fb_setcolreg, 6228c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 6238c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 6248c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 6258c2ecf20Sopenharmony_ci}; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* 6288c2ecf20Sopenharmony_ci * s3c2410fb_map_video_memory(): 6298c2ecf20Sopenharmony_ci * Allocates the DRAM memory for the frame buffer. This buffer is 6308c2ecf20Sopenharmony_ci * remapped into a non-cached, non-buffered, memory region to 6318c2ecf20Sopenharmony_ci * allow palette and pixel writes to occur without flushing the 6328c2ecf20Sopenharmony_ci * cache. Once this area is remapped, all virtual memory 6338c2ecf20Sopenharmony_ci * access to the video memory should occur at the new region. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_cistatic int s3c2410fb_map_video_memory(struct fb_info *info) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 6388c2ecf20Sopenharmony_ci dma_addr_t map_dma; 6398c2ecf20Sopenharmony_ci unsigned map_size = PAGE_ALIGN(info->fix.smem_len); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci info->screen_base = dma_alloc_wc(fbi->dev, map_size, &map_dma, 6448c2ecf20Sopenharmony_ci GFP_KERNEL); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (info->screen_base) { 6478c2ecf20Sopenharmony_ci /* prevent initial garbage on screen */ 6488c2ecf20Sopenharmony_ci dprintk("map_video_memory: clear %p:%08x\n", 6498c2ecf20Sopenharmony_ci info->screen_base, map_size); 6508c2ecf20Sopenharmony_ci memset(info->screen_base, 0x00, map_size); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci info->fix.smem_start = map_dma; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n", 6558c2ecf20Sopenharmony_ci info->fix.smem_start, info->screen_base, map_size); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return info->screen_base ? 0 : -ENOMEM; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic inline void s3c2410fb_unmap_video_memory(struct fb_info *info) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len), 6668c2ecf20Sopenharmony_ci info->screen_base, info->fix.smem_start); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic inline void modify_gpio(void __iomem *reg, 6708c2ecf20Sopenharmony_ci unsigned long set, unsigned long mask) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci unsigned long tmp; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (!reg) 6758c2ecf20Sopenharmony_ci return; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci tmp = readl(reg) & ~mask; 6788c2ecf20Sopenharmony_ci writel(tmp | set, reg); 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci/* 6828c2ecf20Sopenharmony_ci * s3c2410fb_init_registers - Initialise all LCD-related registers 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_cistatic int s3c2410fb_init_registers(struct fb_info *info) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = info->par; 6878c2ecf20Sopenharmony_ci struct s3c2410fb_mach_info *mach_info = dev_get_platdata(fbi->dev); 6888c2ecf20Sopenharmony_ci unsigned long flags; 6898c2ecf20Sopenharmony_ci void __iomem *regs = fbi->io; 6908c2ecf20Sopenharmony_ci void __iomem *tpal; 6918c2ecf20Sopenharmony_ci void __iomem *lpcsel; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (is_s3c2412(fbi)) { 6948c2ecf20Sopenharmony_ci tpal = regs + S3C2412_TPAL; 6958c2ecf20Sopenharmony_ci lpcsel = regs + S3C2412_TCONSEL; 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci tpal = regs + S3C2410_TPAL; 6988c2ecf20Sopenharmony_ci lpcsel = regs + S3C2410_LPCSEL; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Initialise LCD with values from haret */ 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci local_irq_save(flags); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* modify the gpio(s) with interrupts set (bjd) */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci modify_gpio(mach_info->gpcup_reg, mach_info->gpcup, mach_info->gpcup_mask); 7088c2ecf20Sopenharmony_ci modify_gpio(mach_info->gpccon_reg, mach_info->gpccon, mach_info->gpccon_mask); 7098c2ecf20Sopenharmony_ci modify_gpio(mach_info->gpdup_reg, mach_info->gpdup, mach_info->gpdup_mask); 7108c2ecf20Sopenharmony_ci modify_gpio(mach_info->gpdcon_reg, mach_info->gpdcon, mach_info->gpdcon_mask); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci local_irq_restore(flags); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel); 7158c2ecf20Sopenharmony_ci writel(mach_info->lpcsel, lpcsel); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci dprintk("replacing TPAL %08x\n", readl(tpal)); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* ensure temporary palette disabled */ 7208c2ecf20Sopenharmony_ci writel(0x00, tpal); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci return 0; 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_cistatic void s3c2410fb_write_palette(struct s3c2410fb_info *fbi) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci unsigned int i; 7288c2ecf20Sopenharmony_ci void __iomem *regs = fbi->io; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci fbi->palette_ready = 0; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 7338c2ecf20Sopenharmony_ci unsigned long ent = fbi->palette_buffer[i]; 7348c2ecf20Sopenharmony_ci if (ent == PALETTE_BUFF_CLEAR) 7358c2ecf20Sopenharmony_ci continue; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci writel(ent, regs + S3C2410_TFTPAL(i)); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* it seems the only way to know exactly 7408c2ecf20Sopenharmony_ci * if the palette wrote ok, is to check 7418c2ecf20Sopenharmony_ci * to see if the value verifies ok 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (readw(regs + S3C2410_TFTPAL(i)) == ent) 7458c2ecf20Sopenharmony_ci fbi->palette_buffer[i] = PALETTE_BUFF_CLEAR; 7468c2ecf20Sopenharmony_ci else 7478c2ecf20Sopenharmony_ci fbi->palette_ready = 1; /* retry */ 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic irqreturn_t s3c2410fb_irq(int irq, void *dev_id) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci struct s3c2410fb_info *fbi = dev_id; 7548c2ecf20Sopenharmony_ci void __iomem *irq_base = fbi->irq_base; 7558c2ecf20Sopenharmony_ci unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (lcdirq & S3C2410_LCDINT_FRSYNC) { 7588c2ecf20Sopenharmony_ci if (fbi->palette_ready) 7598c2ecf20Sopenharmony_ci s3c2410fb_write_palette(fbi); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND); 7628c2ecf20Sopenharmony_ci writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_S3C24XX_CPUFREQ 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int s3c2410fb_cpufreq_transition(struct notifier_block *nb, 7718c2ecf20Sopenharmony_ci unsigned long val, void *data) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct s3c2410fb_info *info; 7748c2ecf20Sopenharmony_ci struct fb_info *fbinfo; 7758c2ecf20Sopenharmony_ci long delta_f; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci info = container_of(nb, struct s3c2410fb_info, freq_transition); 7788c2ecf20Sopenharmony_ci fbinfo = dev_get_drvdata(info->dev); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* work out change, <0 for speed-up */ 7818c2ecf20Sopenharmony_ci delta_f = info->clk_rate - clk_get_rate(info->clk); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) || 7848c2ecf20Sopenharmony_ci (val == CPUFREQ_PRECHANGE && delta_f < 0)) { 7858c2ecf20Sopenharmony_ci info->clk_rate = clk_get_rate(info->clk); 7868c2ecf20Sopenharmony_ci s3c2410fb_activate_var(fbinfo); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci return 0; 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci info->freq_transition.notifier_call = s3c2410fb_cpufreq_transition; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return cpufreq_register_notifier(&info->freq_transition, 7978c2ecf20Sopenharmony_ci CPUFREQ_TRANSITION_NOTIFIER); 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci cpufreq_unregister_notifier(&info->freq_transition, 8038c2ecf20Sopenharmony_ci CPUFREQ_TRANSITION_NOTIFIER); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci#else 8078c2ecf20Sopenharmony_cistatic inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci return 0; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci#endif 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic const char driver_name[] = "s3c2410fb"; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic int s3c24xxfb_probe(struct platform_device *pdev, 8218c2ecf20Sopenharmony_ci enum s3c_drv_type drv_type) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct s3c2410fb_info *info; 8248c2ecf20Sopenharmony_ci struct s3c2410fb_display *display; 8258c2ecf20Sopenharmony_ci struct fb_info *fbinfo; 8268c2ecf20Sopenharmony_ci struct s3c2410fb_mach_info *mach_info; 8278c2ecf20Sopenharmony_ci struct resource *res; 8288c2ecf20Sopenharmony_ci int ret; 8298c2ecf20Sopenharmony_ci int irq; 8308c2ecf20Sopenharmony_ci int i; 8318c2ecf20Sopenharmony_ci int size; 8328c2ecf20Sopenharmony_ci u32 lcdcon1; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci mach_info = dev_get_platdata(&pdev->dev); 8358c2ecf20Sopenharmony_ci if (mach_info == NULL) { 8368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 8378c2ecf20Sopenharmony_ci "no platform data for lcd, cannot attach\n"); 8388c2ecf20Sopenharmony_ci return -EINVAL; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (mach_info->default_display >= mach_info->num_displays) { 8428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "default is %d but only %d displays\n", 8438c2ecf20Sopenharmony_ci mach_info->default_display, mach_info->num_displays); 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci display = mach_info->displays + mach_info->default_display; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 8508c2ecf20Sopenharmony_ci if (irq < 0) { 8518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no irq for device\n"); 8528c2ecf20Sopenharmony_ci return -ENOENT; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev); 8568c2ecf20Sopenharmony_ci if (!fbinfo) 8578c2ecf20Sopenharmony_ci return -ENOMEM; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, fbinfo); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci info = fbinfo->par; 8628c2ecf20Sopenharmony_ci info->dev = &pdev->dev; 8638c2ecf20Sopenharmony_ci info->drv_type = drv_type; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 8668c2ecf20Sopenharmony_ci if (res == NULL) { 8678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get memory registers\n"); 8688c2ecf20Sopenharmony_ci ret = -ENXIO; 8698c2ecf20Sopenharmony_ci goto dealloc_fb; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci size = resource_size(res); 8738c2ecf20Sopenharmony_ci info->mem = request_mem_region(res->start, size, pdev->name); 8748c2ecf20Sopenharmony_ci if (info->mem == NULL) { 8758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get memory region\n"); 8768c2ecf20Sopenharmony_ci ret = -ENOENT; 8778c2ecf20Sopenharmony_ci goto dealloc_fb; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci info->io = ioremap(res->start, size); 8818c2ecf20Sopenharmony_ci if (info->io == NULL) { 8828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ioremap() of registers failed\n"); 8838c2ecf20Sopenharmony_ci ret = -ENXIO; 8848c2ecf20Sopenharmony_ci goto release_mem; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (drv_type == DRV_S3C2412) 8888c2ecf20Sopenharmony_ci info->irq_base = info->io + S3C2412_LCDINTBASE; 8898c2ecf20Sopenharmony_ci else 8908c2ecf20Sopenharmony_ci info->irq_base = info->io + S3C2410_LCDINTBASE; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci dprintk("devinit\n"); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci strcpy(fbinfo->fix.id, driver_name); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* Stop the video */ 8978c2ecf20Sopenharmony_ci lcdcon1 = readl(info->io + S3C2410_LCDCON1); 8988c2ecf20Sopenharmony_ci writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 9018c2ecf20Sopenharmony_ci fbinfo->fix.type_aux = 0; 9028c2ecf20Sopenharmony_ci fbinfo->fix.xpanstep = 0; 9038c2ecf20Sopenharmony_ci fbinfo->fix.ypanstep = 0; 9048c2ecf20Sopenharmony_ci fbinfo->fix.ywrapstep = 0; 9058c2ecf20Sopenharmony_ci fbinfo->fix.accel = FB_ACCEL_NONE; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci fbinfo->var.nonstd = 0; 9088c2ecf20Sopenharmony_ci fbinfo->var.activate = FB_ACTIVATE_NOW; 9098c2ecf20Sopenharmony_ci fbinfo->var.accel_flags = 0; 9108c2ecf20Sopenharmony_ci fbinfo->var.vmode = FB_VMODE_NONINTERLACED; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci fbinfo->fbops = &s3c2410fb_ops; 9138c2ecf20Sopenharmony_ci fbinfo->flags = FBINFO_FLAG_DEFAULT; 9148c2ecf20Sopenharmony_ci fbinfo->pseudo_palette = &info->pseudo_pal; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) 9178c2ecf20Sopenharmony_ci info->palette_buffer[i] = PALETTE_BUFF_CLEAR; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info); 9208c2ecf20Sopenharmony_ci if (ret) { 9218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); 9228c2ecf20Sopenharmony_ci ret = -EBUSY; 9238c2ecf20Sopenharmony_ci goto release_regs; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci info->clk = clk_get(NULL, "lcd"); 9278c2ecf20Sopenharmony_ci if (IS_ERR(info->clk)) { 9288c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get lcd clock source\n"); 9298c2ecf20Sopenharmony_ci ret = PTR_ERR(info->clk); 9308c2ecf20Sopenharmony_ci goto release_irq; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci clk_prepare_enable(info->clk); 9348c2ecf20Sopenharmony_ci dprintk("got and enabled clock\n"); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci info->clk_rate = clk_get_rate(info->clk); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* find maximum required memory size for display */ 9418c2ecf20Sopenharmony_ci for (i = 0; i < mach_info->num_displays; i++) { 9428c2ecf20Sopenharmony_ci unsigned long smem_len = mach_info->displays[i].xres; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci smem_len *= mach_info->displays[i].yres; 9458c2ecf20Sopenharmony_ci smem_len *= mach_info->displays[i].bpp; 9468c2ecf20Sopenharmony_ci smem_len >>= 3; 9478c2ecf20Sopenharmony_ci if (fbinfo->fix.smem_len < smem_len) 9488c2ecf20Sopenharmony_ci fbinfo->fix.smem_len = smem_len; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* Initialize video memory */ 9528c2ecf20Sopenharmony_ci ret = s3c2410fb_map_video_memory(fbinfo); 9538c2ecf20Sopenharmony_ci if (ret) { 9548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret); 9558c2ecf20Sopenharmony_ci ret = -ENOMEM; 9568c2ecf20Sopenharmony_ci goto release_clock; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci dprintk("got video memory\n"); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci fbinfo->var.xres = display->xres; 9628c2ecf20Sopenharmony_ci fbinfo->var.yres = display->yres; 9638c2ecf20Sopenharmony_ci fbinfo->var.bits_per_pixel = display->bpp; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci s3c2410fb_init_registers(fbinfo); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci s3c2410fb_check_var(&fbinfo->var, fbinfo); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci ret = s3c2410fb_cpufreq_register(info); 9708c2ecf20Sopenharmony_ci if (ret < 0) { 9718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register cpufreq\n"); 9728c2ecf20Sopenharmony_ci goto free_video_memory; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci ret = register_framebuffer(fbinfo); 9768c2ecf20Sopenharmony_ci if (ret < 0) { 9778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register framebuffer device: %d\n", 9788c2ecf20Sopenharmony_ci ret); 9798c2ecf20Sopenharmony_ci goto free_cpufreq; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* create device files */ 9838c2ecf20Sopenharmony_ci ret = device_create_file(&pdev->dev, &dev_attr_debug); 9848c2ecf20Sopenharmony_ci if (ret) 9858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to add debug attribute\n"); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "fb%d: %s frame buffer device\n", 9888c2ecf20Sopenharmony_ci fbinfo->node, fbinfo->fix.id); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci free_cpufreq: 9938c2ecf20Sopenharmony_ci s3c2410fb_cpufreq_deregister(info); 9948c2ecf20Sopenharmony_cifree_video_memory: 9958c2ecf20Sopenharmony_ci s3c2410fb_unmap_video_memory(fbinfo); 9968c2ecf20Sopenharmony_cirelease_clock: 9978c2ecf20Sopenharmony_ci clk_disable_unprepare(info->clk); 9988c2ecf20Sopenharmony_ci clk_put(info->clk); 9998c2ecf20Sopenharmony_cirelease_irq: 10008c2ecf20Sopenharmony_ci free_irq(irq, info); 10018c2ecf20Sopenharmony_cirelease_regs: 10028c2ecf20Sopenharmony_ci iounmap(info->io); 10038c2ecf20Sopenharmony_cirelease_mem: 10048c2ecf20Sopenharmony_ci release_mem_region(res->start, size); 10058c2ecf20Sopenharmony_cidealloc_fb: 10068c2ecf20Sopenharmony_ci framebuffer_release(fbinfo); 10078c2ecf20Sopenharmony_ci return ret; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic int s3c2410fb_probe(struct platform_device *pdev) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci return s3c24xxfb_probe(pdev, DRV_S3C2410); 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic int s3c2412fb_probe(struct platform_device *pdev) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci return s3c24xxfb_probe(pdev, DRV_S3C2412); 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci/* 10228c2ecf20Sopenharmony_ci * Cleanup 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_cistatic int s3c2410fb_remove(struct platform_device *pdev) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct fb_info *fbinfo = platform_get_drvdata(pdev); 10278c2ecf20Sopenharmony_ci struct s3c2410fb_info *info = fbinfo->par; 10288c2ecf20Sopenharmony_ci int irq; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci unregister_framebuffer(fbinfo); 10318c2ecf20Sopenharmony_ci s3c2410fb_cpufreq_deregister(info); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci s3c2410fb_lcd_enable(info, 0); 10348c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci s3c2410fb_unmap_video_memory(fbinfo); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (info->clk) { 10398c2ecf20Sopenharmony_ci clk_disable_unprepare(info->clk); 10408c2ecf20Sopenharmony_ci clk_put(info->clk); 10418c2ecf20Sopenharmony_ci info->clk = NULL; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 10458c2ecf20Sopenharmony_ci free_irq(irq, info); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci iounmap(info->io); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci release_mem_region(info->mem->start, resource_size(info->mem)); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci framebuffer_release(fbinfo); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/* suspend and resume support for the lcd controller */ 10598c2ecf20Sopenharmony_cistatic int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct fb_info *fbinfo = platform_get_drvdata(dev); 10628c2ecf20Sopenharmony_ci struct s3c2410fb_info *info = fbinfo->par; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci s3c2410fb_lcd_enable(info, 0); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* sleep before disabling the clock, we need to ensure 10678c2ecf20Sopenharmony_ci * the LCD DMA engine is not going to get back on the bus 10688c2ecf20Sopenharmony_ci * before the clock goes off again (bjd) */ 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 10718c2ecf20Sopenharmony_ci clk_disable_unprepare(info->clk); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci return 0; 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int s3c2410fb_resume(struct platform_device *dev) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct fb_info *fbinfo = platform_get_drvdata(dev); 10798c2ecf20Sopenharmony_ci struct s3c2410fb_info *info = fbinfo->par; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci clk_prepare_enable(info->clk); 10828c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci s3c2410fb_init_registers(fbinfo); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* re-activate our display after resume */ 10878c2ecf20Sopenharmony_ci s3c2410fb_activate_var(fbinfo); 10888c2ecf20Sopenharmony_ci s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci#else 10948c2ecf20Sopenharmony_ci#define s3c2410fb_suspend NULL 10958c2ecf20Sopenharmony_ci#define s3c2410fb_resume NULL 10968c2ecf20Sopenharmony_ci#endif 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic struct platform_driver s3c2410fb_driver = { 10998c2ecf20Sopenharmony_ci .probe = s3c2410fb_probe, 11008c2ecf20Sopenharmony_ci .remove = s3c2410fb_remove, 11018c2ecf20Sopenharmony_ci .suspend = s3c2410fb_suspend, 11028c2ecf20Sopenharmony_ci .resume = s3c2410fb_resume, 11038c2ecf20Sopenharmony_ci .driver = { 11048c2ecf20Sopenharmony_ci .name = "s3c2410-lcd", 11058c2ecf20Sopenharmony_ci }, 11068c2ecf20Sopenharmony_ci}; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic struct platform_driver s3c2412fb_driver = { 11098c2ecf20Sopenharmony_ci .probe = s3c2412fb_probe, 11108c2ecf20Sopenharmony_ci .remove = s3c2410fb_remove, 11118c2ecf20Sopenharmony_ci .suspend = s3c2410fb_suspend, 11128c2ecf20Sopenharmony_ci .resume = s3c2410fb_resume, 11138c2ecf20Sopenharmony_ci .driver = { 11148c2ecf20Sopenharmony_ci .name = "s3c2412-lcd", 11158c2ecf20Sopenharmony_ci }, 11168c2ecf20Sopenharmony_ci}; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ciint __init s3c2410fb_init(void) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci int ret = platform_driver_register(&s3c2410fb_driver); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (ret == 0) 11238c2ecf20Sopenharmony_ci ret = platform_driver_register(&s3c2412fb_driver); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return ret; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cistatic void __exit s3c2410fb_cleanup(void) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci platform_driver_unregister(&s3c2410fb_driver); 11318c2ecf20Sopenharmony_ci platform_driver_unregister(&s3c2412fb_driver); 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cimodule_init(s3c2410fb_init); 11358c2ecf20Sopenharmony_cimodule_exit(s3c2410fb_cleanup); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); 11388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>"); 11398c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for the s3c2410"); 11408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11418c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c2410-lcd"); 11428c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:s3c2412-lcd"); 1143