162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* linux/drivers/video/sm501fb.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2006 Simtec Electronics 562306a36Sopenharmony_ci * Vincent Sanders <vince@simtec.co.uk> 662306a36Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Framebuffer driver for the Silicon Motion SM501 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/tty.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/fb.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/vmalloc.h> 2262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <linux/workqueue.h> 2562306a36Sopenharmony_ci#include <linux/wait.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/clk.h> 2862306a36Sopenharmony_ci#include <linux/console.h> 2962306a36Sopenharmony_ci#include <linux/io.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <asm/div64.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#ifdef CONFIG_PM 3562306a36Sopenharmony_ci#include <linux/pm.h> 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include <linux/sm501.h> 3962306a36Sopenharmony_ci#include <linux/sm501-regs.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "edid.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic char *fb_mode = "640x480-16@60"; 4462306a36Sopenharmony_cistatic unsigned long default_bpp = 16; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const struct fb_videomode sm501_default_mode = { 4762306a36Sopenharmony_ci .refresh = 60, 4862306a36Sopenharmony_ci .xres = 640, 4962306a36Sopenharmony_ci .yres = 480, 5062306a36Sopenharmony_ci .pixclock = 20833, 5162306a36Sopenharmony_ci .left_margin = 142, 5262306a36Sopenharmony_ci .right_margin = 13, 5362306a36Sopenharmony_ci .upper_margin = 21, 5462306a36Sopenharmony_ci .lower_margin = 1, 5562306a36Sopenharmony_ci .hsync_len = 69, 5662306a36Sopenharmony_ci .vsync_len = 3, 5762306a36Sopenharmony_ci .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 5862306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define NR_PALETTE 256 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cienum sm501_controller { 6462306a36Sopenharmony_ci HEAD_CRT = 0, 6562306a36Sopenharmony_ci HEAD_PANEL = 1, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* SM501 memory address. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * This structure is used to track memory usage within the SM501 framebuffer 7162306a36Sopenharmony_ci * allocation. The sm_addr field is stored as an offset as it is often used 7262306a36Sopenharmony_ci * against both the physical and mapped addresses. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_cistruct sm501_mem { 7562306a36Sopenharmony_ci unsigned long size; 7662306a36Sopenharmony_ci unsigned long sm_addr; /* offset from base of sm501 fb. */ 7762306a36Sopenharmony_ci void __iomem *k_addr; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* private data that is shared between all frambuffers* */ 8162306a36Sopenharmony_cistruct sm501fb_info { 8262306a36Sopenharmony_ci struct device *dev; 8362306a36Sopenharmony_ci struct fb_info *fb[2]; /* fb info for both heads */ 8462306a36Sopenharmony_ci struct resource *fbmem_res; /* framebuffer resource */ 8562306a36Sopenharmony_ci struct resource *regs_res; /* registers resource */ 8662306a36Sopenharmony_ci struct resource *regs2d_res; /* 2d registers resource */ 8762306a36Sopenharmony_ci struct sm501_platdata_fb *pdata; /* our platform data */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci unsigned long pm_crt_ctrl; /* pm: crt ctrl save */ 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci int irq; 9262306a36Sopenharmony_ci int swap_endian; /* set to swap rgb=>bgr */ 9362306a36Sopenharmony_ci void __iomem *regs; /* remapped registers */ 9462306a36Sopenharmony_ci void __iomem *regs2d; /* 2d remapped registers */ 9562306a36Sopenharmony_ci void __iomem *fbmem; /* remapped framebuffer */ 9662306a36Sopenharmony_ci size_t fbmem_len; /* length of remapped region */ 9762306a36Sopenharmony_ci u8 *edid_data; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* per-framebuffer private data */ 10162306a36Sopenharmony_cistruct sm501fb_par { 10262306a36Sopenharmony_ci u32 pseudo_palette[16]; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci enum sm501_controller head; 10562306a36Sopenharmony_ci struct sm501_mem cursor; 10662306a36Sopenharmony_ci struct sm501_mem screen; 10762306a36Sopenharmony_ci struct fb_ops ops; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci void *store_fb; 11062306a36Sopenharmony_ci void *store_cursor; 11162306a36Sopenharmony_ci void __iomem *cursor_regs; 11262306a36Sopenharmony_ci struct sm501fb_info *info; 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Helper functions */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic inline int h_total(struct fb_var_screeninfo *var) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return var->xres + var->left_margin + 12062306a36Sopenharmony_ci var->right_margin + var->hsync_len; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline int v_total(struct fb_var_screeninfo *var) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return var->yres + var->upper_margin + 12662306a36Sopenharmony_ci var->lower_margin + var->vsync_len; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* sm501fb_sync_regs() 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * This call is mainly for PCI bus systems where we need to 13262306a36Sopenharmony_ci * ensure that any writes to the bus are completed before the 13362306a36Sopenharmony_ci * next phase, or after completing a function. 13462306a36Sopenharmony_ci*/ 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic inline void sm501fb_sync_regs(struct sm501fb_info *info) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci smc501_readl(info->regs); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* sm501_alloc_mem 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * This is an attempt to lay out memory for the two framebuffers and 14462306a36Sopenharmony_ci * everything else 14562306a36Sopenharmony_ci * 14662306a36Sopenharmony_ci * |fbmem_res->start fbmem_res->end| 14762306a36Sopenharmony_ci * | | 14862306a36Sopenharmony_ci * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | 14962306a36Sopenharmony_ci * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * The "spare" space is for the 2d engine data 15262306a36Sopenharmony_ci * the fixed is space for the cursors (2x1Kbyte) 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * we need to allocate memory for the 2D acceleration engine 15562306a36Sopenharmony_ci * command list and the data for the engine to deal with. 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * - all allocations must be 128bit aligned 15862306a36Sopenharmony_ci * - cursors are 64x64x2 bits (1Kbyte) 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#define SM501_MEMF_CURSOR (1) 16362306a36Sopenharmony_ci#define SM501_MEMF_PANEL (2) 16462306a36Sopenharmony_ci#define SM501_MEMF_CRT (4) 16562306a36Sopenharmony_ci#define SM501_MEMF_ACCEL (8) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, 16862306a36Sopenharmony_ci unsigned int why, size_t size, u32 smem_len) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct sm501fb_par *par; 17162306a36Sopenharmony_ci struct fb_info *fbi; 17262306a36Sopenharmony_ci unsigned int ptr; 17362306a36Sopenharmony_ci unsigned int end; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci switch (why) { 17662306a36Sopenharmony_ci case SM501_MEMF_CURSOR: 17762306a36Sopenharmony_ci ptr = inf->fbmem_len - size; 17862306a36Sopenharmony_ci inf->fbmem_len = ptr; /* adjust available memory. */ 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci case SM501_MEMF_PANEL: 18262306a36Sopenharmony_ci if (size > inf->fbmem_len) 18362306a36Sopenharmony_ci return -ENOMEM; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ptr = inf->fbmem_len - size; 18662306a36Sopenharmony_ci fbi = inf->fb[HEAD_CRT]; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* round down, some programs such as directfb do not draw 18962306a36Sopenharmony_ci * 0,0 correctly unless the start is aligned to a page start. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (ptr > 0) 19362306a36Sopenharmony_ci ptr &= ~(PAGE_SIZE - 1); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (fbi && ptr < smem_len) 19662306a36Sopenharmony_ci return -ENOMEM; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci case SM501_MEMF_CRT: 20162306a36Sopenharmony_ci ptr = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* check to see if we have panel memory allocated 20462306a36Sopenharmony_ci * which would put an limit on available memory. */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci fbi = inf->fb[HEAD_PANEL]; 20762306a36Sopenharmony_ci if (fbi) { 20862306a36Sopenharmony_ci par = fbi->par; 20962306a36Sopenharmony_ci end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len; 21062306a36Sopenharmony_ci } else 21162306a36Sopenharmony_ci end = inf->fbmem_len; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if ((ptr + size) > end) 21462306a36Sopenharmony_ci return -ENOMEM; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci case SM501_MEMF_ACCEL: 21962306a36Sopenharmony_ci fbi = inf->fb[HEAD_CRT]; 22062306a36Sopenharmony_ci ptr = fbi ? smem_len : 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci fbi = inf->fb[HEAD_PANEL]; 22362306a36Sopenharmony_ci if (fbi) { 22462306a36Sopenharmony_ci par = fbi->par; 22562306a36Sopenharmony_ci end = par->screen.sm_addr; 22662306a36Sopenharmony_ci } else 22762306a36Sopenharmony_ci end = inf->fbmem_len; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if ((ptr + size) > end) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci default: 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci mem->size = size; 23962306a36Sopenharmony_ci mem->sm_addr = ptr; 24062306a36Sopenharmony_ci mem->k_addr = inf->fbmem + ptr; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n", 24362306a36Sopenharmony_ci __func__, mem->sm_addr, mem->k_addr, why, size); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* sm501fb_ps_to_hz 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * Converts a period in picoseconds to Hz. 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * Note, we try to keep this in Hz to minimise rounding with 25362306a36Sopenharmony_ci * the limited PLL settings on the SM501. 25462306a36Sopenharmony_ci*/ 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic unsigned long sm501fb_ps_to_hz(unsigned long psvalue) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci unsigned long long numerator=1000000000000ULL; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 10^12 / picosecond period gives frequency in Hz */ 26162306a36Sopenharmony_ci do_div(numerator, psvalue); 26262306a36Sopenharmony_ci return (unsigned long)numerator; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* sm501fb_hz_to_ps is identical to the opposite transform */ 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* sm501fb_setup_gamma 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * Programs a linear 1.0 gamma ramp in case the gamma 27262306a36Sopenharmony_ci * correction is enabled without programming anything else. 27362306a36Sopenharmony_ci*/ 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void sm501fb_setup_gamma(struct sm501fb_info *fbi, 27662306a36Sopenharmony_ci unsigned long palette) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci unsigned long value = 0; 27962306a36Sopenharmony_ci int offset; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* set gamma values */ 28262306a36Sopenharmony_ci for (offset = 0; offset < 256 * 4; offset += 4) { 28362306a36Sopenharmony_ci smc501_writel(value, fbi->regs + palette + offset); 28462306a36Sopenharmony_ci value += 0x010101; /* Advance RGB by 1,1,1.*/ 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* sm501fb_check_var 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * check common variables for both panel and crt 29162306a36Sopenharmony_ci*/ 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int sm501fb_check_var(struct fb_var_screeninfo *var, 29462306a36Sopenharmony_ci struct fb_info *info) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 29762306a36Sopenharmony_ci struct sm501fb_info *sm = par->info; 29862306a36Sopenharmony_ci unsigned long tmp; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* check we can fit these values into the registers */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (var->hsync_len > 255 || var->vsync_len > 63) 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* hdisplay end and hsync start */ 30662306a36Sopenharmony_ci if ((var->xres + var->right_margin) > 4096) 30762306a36Sopenharmony_ci return -EINVAL; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* vdisplay end and vsync start */ 31062306a36Sopenharmony_ci if ((var->yres + var->lower_margin) > 2048) 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* hard limits of device */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (h_total(var) > 4096 || v_total(var) > 2048) 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* check our line length is going to be 128 bit aligned */ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci tmp = (var->xres * var->bits_per_pixel) / 8; 32162306a36Sopenharmony_ci if ((tmp & 15) != 0) 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* check the virtual size */ 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (var->xres_virtual > 4096 || var->yres_virtual > 2048) 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* can cope with 8,16 or 32bpp */ 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (var->bits_per_pixel <= 8) 33262306a36Sopenharmony_ci var->bits_per_pixel = 8; 33362306a36Sopenharmony_ci else if (var->bits_per_pixel <= 16) 33462306a36Sopenharmony_ci var->bits_per_pixel = 16; 33562306a36Sopenharmony_ci else if (var->bits_per_pixel == 24) 33662306a36Sopenharmony_ci var->bits_per_pixel = 32; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* set r/g/b positions and validate bpp */ 33962306a36Sopenharmony_ci switch(var->bits_per_pixel) { 34062306a36Sopenharmony_ci case 8: 34162306a36Sopenharmony_ci var->red.length = var->bits_per_pixel; 34262306a36Sopenharmony_ci var->red.offset = 0; 34362306a36Sopenharmony_ci var->green.length = var->bits_per_pixel; 34462306a36Sopenharmony_ci var->green.offset = 0; 34562306a36Sopenharmony_ci var->blue.length = var->bits_per_pixel; 34662306a36Sopenharmony_ci var->blue.offset = 0; 34762306a36Sopenharmony_ci var->transp.length = 0; 34862306a36Sopenharmony_ci var->transp.offset = 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci case 16: 35362306a36Sopenharmony_ci if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { 35462306a36Sopenharmony_ci var->blue.offset = 11; 35562306a36Sopenharmony_ci var->green.offset = 5; 35662306a36Sopenharmony_ci var->red.offset = 0; 35762306a36Sopenharmony_ci } else { 35862306a36Sopenharmony_ci var->red.offset = 11; 35962306a36Sopenharmony_ci var->green.offset = 5; 36062306a36Sopenharmony_ci var->blue.offset = 0; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci var->transp.offset = 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci var->red.length = 5; 36562306a36Sopenharmony_ci var->green.length = 6; 36662306a36Sopenharmony_ci var->blue.length = 5; 36762306a36Sopenharmony_ci var->transp.length = 0; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci case 32: 37162306a36Sopenharmony_ci if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { 37262306a36Sopenharmony_ci var->transp.offset = 0; 37362306a36Sopenharmony_ci var->red.offset = 8; 37462306a36Sopenharmony_ci var->green.offset = 16; 37562306a36Sopenharmony_ci var->blue.offset = 24; 37662306a36Sopenharmony_ci } else { 37762306a36Sopenharmony_ci var->transp.offset = 24; 37862306a36Sopenharmony_ci var->red.offset = 16; 37962306a36Sopenharmony_ci var->green.offset = 8; 38062306a36Sopenharmony_ci var->blue.offset = 0; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci var->red.length = 8; 38462306a36Sopenharmony_ci var->green.length = 8; 38562306a36Sopenharmony_ci var->blue.length = 8; 38662306a36Sopenharmony_ci var->transp.length = 0; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci default: 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return 0; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* 39762306a36Sopenharmony_ci * sm501fb_check_var_crt(): 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * check the parameters for the CRT head, and either bring them 40062306a36Sopenharmony_ci * back into range, or return -EINVAL. 40162306a36Sopenharmony_ci*/ 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int sm501fb_check_var_crt(struct fb_var_screeninfo *var, 40462306a36Sopenharmony_ci struct fb_info *info) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci return sm501fb_check_var(var, info); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* sm501fb_check_var_pnl(): 41062306a36Sopenharmony_ci * 41162306a36Sopenharmony_ci * check the parameters for the CRT head, and either bring them 41262306a36Sopenharmony_ci * back into range, or return -EINVAL. 41362306a36Sopenharmony_ci*/ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int sm501fb_check_var_pnl(struct fb_var_screeninfo *var, 41662306a36Sopenharmony_ci struct fb_info *info) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci return sm501fb_check_var(var, info); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* sm501fb_set_par_common 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * set common registers for framebuffers 42462306a36Sopenharmony_ci*/ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int sm501fb_set_par_common(struct fb_info *info, 42762306a36Sopenharmony_ci struct fb_var_screeninfo *var) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 43062306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 43162306a36Sopenharmony_ci unsigned long pixclock; /* pixelclock in Hz */ 43262306a36Sopenharmony_ci unsigned long sm501pixclock; /* pixelclock the 501 can achieve in Hz */ 43362306a36Sopenharmony_ci unsigned int mem_type; 43462306a36Sopenharmony_ci unsigned int clock_type; 43562306a36Sopenharmony_ci unsigned int head_addr; 43662306a36Sopenharmony_ci unsigned int smem_len; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n", 43962306a36Sopenharmony_ci __func__, var->xres, var->yres, var->bits_per_pixel, 44062306a36Sopenharmony_ci var->xres_virtual, var->yres_virtual); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci switch (par->head) { 44362306a36Sopenharmony_ci case HEAD_CRT: 44462306a36Sopenharmony_ci mem_type = SM501_MEMF_CRT; 44562306a36Sopenharmony_ci clock_type = SM501_CLOCK_V2XCLK; 44662306a36Sopenharmony_ci head_addr = SM501_DC_CRT_FB_ADDR; 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci case HEAD_PANEL: 45062306a36Sopenharmony_ci mem_type = SM501_MEMF_PANEL; 45162306a36Sopenharmony_ci clock_type = SM501_CLOCK_P2XCLK; 45262306a36Sopenharmony_ci head_addr = SM501_DC_PANEL_FB_ADDR; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci default: 45662306a36Sopenharmony_ci mem_type = 0; /* stop compiler warnings */ 45762306a36Sopenharmony_ci head_addr = 0; 45862306a36Sopenharmony_ci clock_type = 0; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci switch (var->bits_per_pixel) { 46262306a36Sopenharmony_ci case 8: 46362306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci case 16: 46762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci case 32: 47162306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* allocate fb memory within 501 */ 47662306a36Sopenharmony_ci info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8; 47762306a36Sopenharmony_ci smem_len = info->fix.line_length * var->yres_virtual; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s: line length = %u\n", __func__, 48062306a36Sopenharmony_ci info->fix.line_length); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (sm501_alloc_mem(fbi, &par->screen, mem_type, smem_len, smem_len)) { 48362306a36Sopenharmony_ci dev_err(fbi->dev, "no memory available\n"); 48462306a36Sopenharmony_ci return -ENOMEM; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci mutex_lock(&info->mm_lock); 48862306a36Sopenharmony_ci info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr; 48962306a36Sopenharmony_ci info->fix.smem_len = smem_len; 49062306a36Sopenharmony_ci mutex_unlock(&info->mm_lock); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci info->screen_base = fbi->fbmem + par->screen.sm_addr; 49362306a36Sopenharmony_ci info->screen_size = info->fix.smem_len; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* set start of framebuffer to the screen */ 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci smc501_writel(par->screen.sm_addr | SM501_ADDR_FLIP, 49862306a36Sopenharmony_ci fbi->regs + head_addr); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* program CRT clock */ 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci pixclock = sm501fb_ps_to_hz(var->pixclock); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci sm501pixclock = sm501_set_clock(fbi->dev->parent, clock_type, 50562306a36Sopenharmony_ci pixclock); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* update fb layer with actual clock used */ 50862306a36Sopenharmony_ci var->pixclock = sm501fb_hz_to_ps(sm501pixclock); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, " 51162306a36Sopenharmony_ci "sm501pixclock = %lu, error = %ld%%\n", 51262306a36Sopenharmony_ci __func__, var->pixclock, pixclock, sm501pixclock, 51362306a36Sopenharmony_ci ((pixclock - sm501pixclock)*100)/pixclock); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/* sm501fb_set_par_geometry 51962306a36Sopenharmony_ci * 52062306a36Sopenharmony_ci * set the geometry registers for specified framebuffer. 52162306a36Sopenharmony_ci*/ 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void sm501fb_set_par_geometry(struct fb_info *info, 52462306a36Sopenharmony_ci struct fb_var_screeninfo *var) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 52762306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 52862306a36Sopenharmony_ci void __iomem *base = fbi->regs; 52962306a36Sopenharmony_ci unsigned long reg; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (par->head == HEAD_CRT) 53262306a36Sopenharmony_ci base += SM501_DC_CRT_H_TOT; 53362306a36Sopenharmony_ci else 53462306a36Sopenharmony_ci base += SM501_DC_PANEL_H_TOT; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* set framebuffer width and display width */ 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci reg = info->fix.line_length; 53962306a36Sopenharmony_ci reg |= ((var->xres * var->bits_per_pixel)/8) << 16; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci smc501_writel(reg, fbi->regs + (par->head == HEAD_CRT ? 54262306a36Sopenharmony_ci SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET)); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* program horizontal total */ 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci reg = (h_total(var) - 1) << 16; 54762306a36Sopenharmony_ci reg |= (var->xres - 1); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci smc501_writel(reg, base + SM501_OFF_DC_H_TOT); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* program horizontal sync */ 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci reg = var->hsync_len << 16; 55462306a36Sopenharmony_ci reg |= var->xres + var->right_margin - 1; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci smc501_writel(reg, base + SM501_OFF_DC_H_SYNC); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* program vertical total */ 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci reg = (v_total(var) - 1) << 16; 56162306a36Sopenharmony_ci reg |= (var->yres - 1); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci smc501_writel(reg, base + SM501_OFF_DC_V_TOT); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* program vertical sync */ 56662306a36Sopenharmony_ci reg = var->vsync_len << 16; 56762306a36Sopenharmony_ci reg |= var->yres + var->lower_margin - 1; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci smc501_writel(reg, base + SM501_OFF_DC_V_SYNC); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* sm501fb_pan_crt 57362306a36Sopenharmony_ci * 57462306a36Sopenharmony_ci * pan the CRT display output within an virtual framebuffer 57562306a36Sopenharmony_ci*/ 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int sm501fb_pan_crt(struct fb_var_screeninfo *var, 57862306a36Sopenharmony_ci struct fb_info *info) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 58162306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 58262306a36Sopenharmony_ci unsigned int bytes_pixel = info->var.bits_per_pixel / 8; 58362306a36Sopenharmony_ci unsigned long reg; 58462306a36Sopenharmony_ci unsigned long xoffs; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci xoffs = var->xoffset * bytes_pixel; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci reg = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK; 59162306a36Sopenharmony_ci reg |= ((xoffs & 15) / bytes_pixel) << 4; 59262306a36Sopenharmony_ci smc501_writel(reg, fbi->regs + SM501_DC_CRT_CONTROL); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci reg = (par->screen.sm_addr + xoffs + 59562306a36Sopenharmony_ci var->yoffset * info->fix.line_length); 59662306a36Sopenharmony_ci smc501_writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci/* sm501fb_pan_pnl 60362306a36Sopenharmony_ci * 60462306a36Sopenharmony_ci * pan the panel display output within an virtual framebuffer 60562306a36Sopenharmony_ci*/ 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int sm501fb_pan_pnl(struct fb_var_screeninfo *var, 60862306a36Sopenharmony_ci struct fb_info *info) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 61162306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 61262306a36Sopenharmony_ci unsigned long reg; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci reg = var->xoffset | (info->var.xres_virtual << 16); 61562306a36Sopenharmony_ci smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci reg = var->yoffset | (info->var.yres_virtual << 16); 61862306a36Sopenharmony_ci smc501_writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* sm501fb_set_par_crt 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * Set the CRT video mode from the fb_info structure 62762306a36Sopenharmony_ci*/ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int sm501fb_set_par_crt(struct fb_info *info) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 63262306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 63362306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 63462306a36Sopenharmony_ci unsigned long control; /* control register */ 63562306a36Sopenharmony_ci int ret; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* activate new configuration */ 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* enable CRT DAC - note 0 is on!*/ 64262306a36Sopenharmony_ci sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci control = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK | 64762306a36Sopenharmony_ci SM501_DC_CRT_CONTROL_GAMMA | 64862306a36Sopenharmony_ci SM501_DC_CRT_CONTROL_BLANK | 64962306a36Sopenharmony_ci SM501_DC_CRT_CONTROL_SEL | 65062306a36Sopenharmony_ci SM501_DC_CRT_CONTROL_CP | 65162306a36Sopenharmony_ci SM501_DC_CRT_CONTROL_TVP); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* set the sync polarities before we check data source */ 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) 65662306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_HSP; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) 65962306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_VSP; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) { 66262306a36Sopenharmony_ci /* the head is displaying panel data... */ 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0, 66562306a36Sopenharmony_ci info->fix.smem_len); 66662306a36Sopenharmony_ci goto out_update; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci ret = sm501fb_set_par_common(info, var); 67062306a36Sopenharmony_ci if (ret) { 67162306a36Sopenharmony_ci dev_err(fbi->dev, "failed to set common parameters\n"); 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci sm501fb_pan_crt(var, info); 67662306a36Sopenharmony_ci sm501fb_set_par_geometry(info, var); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci control |= SM501_FIFO_3; /* fill if >3 free slots */ 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci switch(var->bits_per_pixel) { 68162306a36Sopenharmony_ci case 8: 68262306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_8BPP; 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci case 16: 68662306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_16BPP; 68762306a36Sopenharmony_ci sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci case 32: 69162306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_32BPP; 69262306a36Sopenharmony_ci sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci default: 69662306a36Sopenharmony_ci BUG(); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */ 70062306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */ 70162306a36Sopenharmony_ci control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */ 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci out_update: 70462306a36Sopenharmony_ci dev_dbg(fbi->dev, "new control is %08lx\n", control); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci smc501_writel(control, fbi->regs + SM501_DC_CRT_CONTROL); 70762306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic void sm501fb_panel_power(struct sm501fb_info *fbi, int to) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci unsigned long control; 71562306a36Sopenharmony_ci void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL; 71662306a36Sopenharmony_ci struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci control = smc501_readl(ctrl_reg); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) { 72162306a36Sopenharmony_ci /* enable panel power */ 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */ 72462306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 72562306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 72662306a36Sopenharmony_ci mdelay(10); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */ 72962306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 73062306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 73162306a36Sopenharmony_ci mdelay(10); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* VBIASEN */ 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { 73662306a36Sopenharmony_ci if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) 73762306a36Sopenharmony_ci control &= ~SM501_DC_PANEL_CONTROL_BIAS; 73862306a36Sopenharmony_ci else 73962306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_BIAS; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 74262306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 74362306a36Sopenharmony_ci mdelay(10); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { 74762306a36Sopenharmony_ci if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) 74862306a36Sopenharmony_ci control &= ~SM501_DC_PANEL_CONTROL_FPEN; 74962306a36Sopenharmony_ci else 75062306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_FPEN; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 75362306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 75462306a36Sopenharmony_ci mdelay(10); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { 75762306a36Sopenharmony_ci /* disable panel power */ 75862306a36Sopenharmony_ci if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { 75962306a36Sopenharmony_ci if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) 76062306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_FPEN; 76162306a36Sopenharmony_ci else 76262306a36Sopenharmony_ci control &= ~SM501_DC_PANEL_CONTROL_FPEN; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 76562306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 76662306a36Sopenharmony_ci mdelay(10); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { 77062306a36Sopenharmony_ci if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) 77162306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_BIAS; 77262306a36Sopenharmony_ci else 77362306a36Sopenharmony_ci control &= ~SM501_DC_PANEL_CONTROL_BIAS; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 77662306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 77762306a36Sopenharmony_ci mdelay(10); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci control &= ~SM501_DC_PANEL_CONTROL_DATA; 78162306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 78262306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 78362306a36Sopenharmony_ci mdelay(10); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci control &= ~SM501_DC_PANEL_CONTROL_VDD; 78662306a36Sopenharmony_ci smc501_writel(control, ctrl_reg); 78762306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 78862306a36Sopenharmony_ci mdelay(10); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci/* sm501fb_set_par_pnl 79562306a36Sopenharmony_ci * 79662306a36Sopenharmony_ci * Set the panel video mode from the fb_info structure 79762306a36Sopenharmony_ci*/ 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic int sm501fb_set_par_pnl(struct fb_info *info) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 80262306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 80362306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 80462306a36Sopenharmony_ci unsigned long control; 80562306a36Sopenharmony_ci unsigned long reg; 80662306a36Sopenharmony_ci int ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* activate this new configuration */ 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = sm501fb_set_par_common(info, var); 81362306a36Sopenharmony_ci if (ret) 81462306a36Sopenharmony_ci return ret; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci sm501fb_pan_pnl(var, info); 81762306a36Sopenharmony_ci sm501fb_set_par_geometry(info, var); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* update control register */ 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci control = smc501_readl(fbi->regs + SM501_DC_PANEL_CONTROL); 82262306a36Sopenharmony_ci control &= (SM501_DC_PANEL_CONTROL_GAMMA | 82362306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_VDD | 82462306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_DATA | 82562306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_BIAS | 82662306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_FPEN | 82762306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_CP | 82862306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_CK | 82962306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_HP | 83062306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_VP | 83162306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_HPD | 83262306a36Sopenharmony_ci SM501_DC_PANEL_CONTROL_VPD); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci control |= SM501_FIFO_3; /* fill if >3 free slots */ 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci switch(var->bits_per_pixel) { 83762306a36Sopenharmony_ci case 8: 83862306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_8BPP; 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci case 16: 84262306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_16BPP; 84362306a36Sopenharmony_ci sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci case 32: 84762306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_32BPP; 84862306a36Sopenharmony_ci sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci default: 85262306a36Sopenharmony_ci BUG(); 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci smc501_writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* panel plane top left and bottom right location */ 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci smc501_writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci reg = var->xres - 1; 86262306a36Sopenharmony_ci reg |= (var->yres - 1) << 16; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci smc501_writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* program panel control register */ 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */ 86962306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */ 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) 87262306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_HSP; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) 87562306a36Sopenharmony_ci control |= SM501_DC_PANEL_CONTROL_VSP; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci smc501_writel(control, fbi->regs + SM501_DC_PANEL_CONTROL); 87862306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* ensure the panel interface is not tristated at this point */ 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci sm501_modify_reg(fbi->dev->parent, SM501_SYSTEM_CONTROL, 88362306a36Sopenharmony_ci 0, SM501_SYSCTRL_PANEL_TRISTATE); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* power the panel up */ 88662306a36Sopenharmony_ci sm501fb_panel_power(fbi, 1); 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci/* chan_to_field 89262306a36Sopenharmony_ci * 89362306a36Sopenharmony_ci * convert a colour value into a field position 89462306a36Sopenharmony_ci * 89562306a36Sopenharmony_ci * from pxafb.c 89662306a36Sopenharmony_ci*/ 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic inline unsigned int chan_to_field(unsigned int chan, 89962306a36Sopenharmony_ci struct fb_bitfield *bf) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci chan &= 0xffff; 90262306a36Sopenharmony_ci chan >>= 16 - bf->length; 90362306a36Sopenharmony_ci return chan << bf->offset; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci/* sm501fb_setcolreg 90762306a36Sopenharmony_ci * 90862306a36Sopenharmony_ci * set the colour mapping for modes that support palettised data 90962306a36Sopenharmony_ci*/ 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic int sm501fb_setcolreg(unsigned regno, 91262306a36Sopenharmony_ci unsigned red, unsigned green, unsigned blue, 91362306a36Sopenharmony_ci unsigned transp, struct fb_info *info) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 91662306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 91762306a36Sopenharmony_ci void __iomem *base = fbi->regs; 91862306a36Sopenharmony_ci unsigned int val; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (par->head == HEAD_CRT) 92162306a36Sopenharmony_ci base += SM501_DC_CRT_PALETTE; 92262306a36Sopenharmony_ci else 92362306a36Sopenharmony_ci base += SM501_DC_PANEL_PALETTE; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci switch (info->fix.visual) { 92662306a36Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 92762306a36Sopenharmony_ci /* true-colour, use pseuo-palette */ 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (regno < 16) { 93062306a36Sopenharmony_ci u32 *pal = par->pseudo_palette; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci val = chan_to_field(red, &info->var.red); 93362306a36Sopenharmony_ci val |= chan_to_field(green, &info->var.green); 93462306a36Sopenharmony_ci val |= chan_to_field(blue, &info->var.blue); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci pal[regno] = val; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci break; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 94162306a36Sopenharmony_ci if (regno < 256) { 94262306a36Sopenharmony_ci val = (red >> 8) << 16; 94362306a36Sopenharmony_ci val |= (green >> 8) << 8; 94462306a36Sopenharmony_ci val |= blue >> 8; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci smc501_writel(val, base + (regno * 4)); 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci default: 95262306a36Sopenharmony_ci return 1; /* unknown type */ 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci return 0; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/* sm501fb_blank_pnl 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * Blank or un-blank the panel interface 96162306a36Sopenharmony_ci*/ 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic int sm501fb_blank_pnl(int blank_mode, struct fb_info *info) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 96662306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci switch (blank_mode) { 97162306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 97262306a36Sopenharmony_ci sm501fb_panel_power(fbi, 0); 97362306a36Sopenharmony_ci break; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 97662306a36Sopenharmony_ci sm501fb_panel_power(fbi, 1); 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci case FB_BLANK_NORMAL: 98062306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 98162306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 98262306a36Sopenharmony_ci default: 98362306a36Sopenharmony_ci return 1; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci/* sm501fb_blank_crt 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * Blank or un-blank the crt interface 99262306a36Sopenharmony_ci*/ 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int sm501fb_blank_crt(int blank_mode, struct fb_info *info) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 99762306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 99862306a36Sopenharmony_ci unsigned long ctrl; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci ctrl = smc501_readl(fbi->regs + SM501_DC_CRT_CONTROL); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci switch (blank_mode) { 100562306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 100662306a36Sopenharmony_ci ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; 100762306a36Sopenharmony_ci sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0); 100862306a36Sopenharmony_ci fallthrough; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci case FB_BLANK_NORMAL: 101162306a36Sopenharmony_ci ctrl |= SM501_DC_CRT_CONTROL_BLANK; 101262306a36Sopenharmony_ci break; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 101562306a36Sopenharmony_ci ctrl &= ~SM501_DC_CRT_CONTROL_BLANK; 101662306a36Sopenharmony_ci ctrl |= SM501_DC_CRT_CONTROL_ENABLE; 101762306a36Sopenharmony_ci sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); 101862306a36Sopenharmony_ci break; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 102162306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 102262306a36Sopenharmony_ci default: 102362306a36Sopenharmony_ci return 1; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci smc501_writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL); 102862306a36Sopenharmony_ci sm501fb_sync_regs(fbi); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci} 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci/* sm501fb_cursor 103462306a36Sopenharmony_ci * 103562306a36Sopenharmony_ci * set or change the hardware cursor parameters 103662306a36Sopenharmony_ci*/ 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 104162306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 104262306a36Sopenharmony_ci void __iomem *base = fbi->regs; 104362306a36Sopenharmony_ci unsigned long hwc_addr; 104462306a36Sopenharmony_ci unsigned long fg, bg; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (par->head == HEAD_CRT) 104962306a36Sopenharmony_ci base += SM501_DC_CRT_HWC_BASE; 105062306a36Sopenharmony_ci else 105162306a36Sopenharmony_ci base += SM501_DC_PANEL_HWC_BASE; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci /* check not being asked to exceed capabilities */ 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci if (cursor->image.width > 64) 105662306a36Sopenharmony_ci return -EINVAL; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (cursor->image.height > 64) 105962306a36Sopenharmony_ci return -EINVAL; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (cursor->image.depth > 1) 106262306a36Sopenharmony_ci return -EINVAL; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci hwc_addr = smc501_readl(base + SM501_OFF_HWC_ADDR); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (cursor->enable) 106762306a36Sopenharmony_ci smc501_writel(hwc_addr | SM501_HWC_EN, 106862306a36Sopenharmony_ci base + SM501_OFF_HWC_ADDR); 106962306a36Sopenharmony_ci else 107062306a36Sopenharmony_ci smc501_writel(hwc_addr & ~SM501_HWC_EN, 107162306a36Sopenharmony_ci base + SM501_OFF_HWC_ADDR); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* set data */ 107462306a36Sopenharmony_ci if (cursor->set & FB_CUR_SETPOS) { 107562306a36Sopenharmony_ci unsigned int x = cursor->image.dx; 107662306a36Sopenharmony_ci unsigned int y = cursor->image.dy; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (x >= 2048 || y >= 2048 ) 107962306a36Sopenharmony_ci return -EINVAL; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci dev_dbg(fbi->dev, "set position %d,%d\n", x, y); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci //y += cursor->image.height; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci smc501_writel(x | (y << 16), base + SM501_OFF_HWC_LOC); 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (cursor->set & FB_CUR_SETCMAP) { 108962306a36Sopenharmony_ci unsigned int bg_col = cursor->image.bg_color; 109062306a36Sopenharmony_ci unsigned int fg_col = cursor->image.fg_color; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n", 109362306a36Sopenharmony_ci __func__, bg_col, fg_col); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci bg = ((info->cmap.red[bg_col] & 0xF8) << 8) | 109662306a36Sopenharmony_ci ((info->cmap.green[bg_col] & 0xFC) << 3) | 109762306a36Sopenharmony_ci ((info->cmap.blue[bg_col] & 0xF8) >> 3); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci fg = ((info->cmap.red[fg_col] & 0xF8) << 8) | 110062306a36Sopenharmony_ci ((info->cmap.green[fg_col] & 0xFC) << 3) | 110162306a36Sopenharmony_ci ((info->cmap.blue[fg_col] & 0xF8) >> 3); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci smc501_writel(bg, base + SM501_OFF_HWC_COLOR_1_2); 110662306a36Sopenharmony_ci smc501_writel(fg, base + SM501_OFF_HWC_COLOR_3); 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (cursor->set & FB_CUR_SETSIZE || 111062306a36Sopenharmony_ci cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { 111162306a36Sopenharmony_ci /* SM501 cursor is a two bpp 64x64 bitmap this routine 111262306a36Sopenharmony_ci * clears it to transparent then combines the cursor 111362306a36Sopenharmony_ci * shape plane with the colour plane to set the 111462306a36Sopenharmony_ci * cursor */ 111562306a36Sopenharmony_ci int x, y; 111662306a36Sopenharmony_ci const unsigned char *pcol = cursor->image.data; 111762306a36Sopenharmony_ci const unsigned char *pmsk = cursor->mask; 111862306a36Sopenharmony_ci void __iomem *dst = par->cursor.k_addr; 111962306a36Sopenharmony_ci unsigned char dcol = 0; 112062306a36Sopenharmony_ci unsigned char dmsk = 0; 112162306a36Sopenharmony_ci unsigned int op; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n", 112462306a36Sopenharmony_ci __func__, cursor->image.width, cursor->image.height); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci for (op = 0; op < (64*64*2)/8; op+=4) 112762306a36Sopenharmony_ci smc501_writel(0x0, dst + op); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci for (y = 0; y < cursor->image.height; y++) { 113062306a36Sopenharmony_ci for (x = 0; x < cursor->image.width; x++) { 113162306a36Sopenharmony_ci if ((x % 8) == 0) { 113262306a36Sopenharmony_ci dcol = *pcol++; 113362306a36Sopenharmony_ci dmsk = *pmsk++; 113462306a36Sopenharmony_ci } else { 113562306a36Sopenharmony_ci dcol >>= 1; 113662306a36Sopenharmony_ci dmsk >>= 1; 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (dmsk & 1) { 114062306a36Sopenharmony_ci op = (dcol & 1) ? 1 : 3; 114162306a36Sopenharmony_ci op <<= ((x % 4) * 2); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci op |= readb(dst + (x / 4)); 114462306a36Sopenharmony_ci writeb(op, dst + (x / 4)); 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci dst += (64*2)/8; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci sm501fb_sync_regs(fbi); /* ensure cursor data flushed */ 115262306a36Sopenharmony_ci return 0; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci/* sm501fb_crtsrc_show 115662306a36Sopenharmony_ci * 115762306a36Sopenharmony_ci * device attribute code to show where the crt output is sourced from 115862306a36Sopenharmony_ci*/ 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_cistatic ssize_t sm501fb_crtsrc_show(struct device *dev, 116162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci struct sm501fb_info *info = dev_get_drvdata(dev); 116462306a36Sopenharmony_ci unsigned long ctrl; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL); 116762306a36Sopenharmony_ci ctrl &= SM501_DC_CRT_CONTROL_SEL; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", ctrl ? "crt" : "panel"); 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci/* sm501fb_crtsrc_show 117362306a36Sopenharmony_ci * 117462306a36Sopenharmony_ci * device attribute code to set where the crt output is sourced from 117562306a36Sopenharmony_ci*/ 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cistatic ssize_t sm501fb_crtsrc_store(struct device *dev, 117862306a36Sopenharmony_ci struct device_attribute *attr, 117962306a36Sopenharmony_ci const char *buf, size_t len) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct sm501fb_info *info = dev_get_drvdata(dev); 118262306a36Sopenharmony_ci enum sm501_controller head; 118362306a36Sopenharmony_ci unsigned long ctrl; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (len < 1) 118662306a36Sopenharmony_ci return -EINVAL; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (strncasecmp(buf, "crt", 3) == 0) 118962306a36Sopenharmony_ci head = HEAD_CRT; 119062306a36Sopenharmony_ci else if (strncasecmp(buf, "panel", 5) == 0) 119162306a36Sopenharmony_ci head = HEAD_PANEL; 119262306a36Sopenharmony_ci else 119362306a36Sopenharmony_ci return -EINVAL; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci dev_info(dev, "setting crt source to head %d\n", head); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (head == HEAD_CRT) { 120062306a36Sopenharmony_ci ctrl |= SM501_DC_CRT_CONTROL_SEL; 120162306a36Sopenharmony_ci ctrl |= SM501_DC_CRT_CONTROL_ENABLE; 120262306a36Sopenharmony_ci ctrl |= SM501_DC_CRT_CONTROL_TE; 120362306a36Sopenharmony_ci } else { 120462306a36Sopenharmony_ci ctrl &= ~SM501_DC_CRT_CONTROL_SEL; 120562306a36Sopenharmony_ci ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; 120662306a36Sopenharmony_ci ctrl &= ~SM501_DC_CRT_CONTROL_TE; 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); 121062306a36Sopenharmony_ci sm501fb_sync_regs(info); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci return len; 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci/* Prepare the device_attr for registration with sysfs later */ 121662306a36Sopenharmony_cistatic DEVICE_ATTR(crt_src, 0664, sm501fb_crtsrc_show, sm501fb_crtsrc_store); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/* sm501fb_show_regs 121962306a36Sopenharmony_ci * 122062306a36Sopenharmony_ci * show the primary sm501 registers 122162306a36Sopenharmony_ci*/ 122262306a36Sopenharmony_cistatic int sm501fb_show_regs(struct sm501fb_info *info, char *ptr, 122362306a36Sopenharmony_ci unsigned int start, unsigned int len) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci void __iomem *mem = info->regs; 122662306a36Sopenharmony_ci char *buf = ptr; 122762306a36Sopenharmony_ci unsigned int reg; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci for (reg = start; reg < (len + start); reg += 4) 123062306a36Sopenharmony_ci ptr += sprintf(ptr, "%08x = %08x\n", reg, 123162306a36Sopenharmony_ci smc501_readl(mem + reg)); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return ptr - buf; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* sm501fb_debug_show_crt 123762306a36Sopenharmony_ci * 123862306a36Sopenharmony_ci * show the crt control and cursor registers 123962306a36Sopenharmony_ci*/ 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic ssize_t sm501fb_debug_show_crt(struct device *dev, 124262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci struct sm501fb_info *info = dev_get_drvdata(dev); 124562306a36Sopenharmony_ci char *ptr = buf; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40); 124862306a36Sopenharmony_ci ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci return ptr - buf; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci/* sm501fb_debug_show_pnl 125662306a36Sopenharmony_ci * 125762306a36Sopenharmony_ci * show the panel control and cursor registers 125862306a36Sopenharmony_ci*/ 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cistatic ssize_t sm501fb_debug_show_pnl(struct device *dev, 126162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct sm501fb_info *info = dev_get_drvdata(dev); 126462306a36Sopenharmony_ci char *ptr = buf; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40); 126762306a36Sopenharmony_ci ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci return ptr - buf; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic struct attribute *sm501fb_attrs[] = { 127562306a36Sopenharmony_ci &dev_attr_crt_src.attr, 127662306a36Sopenharmony_ci &dev_attr_fbregs_pnl.attr, 127762306a36Sopenharmony_ci &dev_attr_fbregs_crt.attr, 127862306a36Sopenharmony_ci NULL, 127962306a36Sopenharmony_ci}; 128062306a36Sopenharmony_ciATTRIBUTE_GROUPS(sm501fb); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci/* acceleration operations */ 128362306a36Sopenharmony_cistatic int sm501fb_sync(struct fb_info *info) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci int count = 1000000; 128662306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 128762306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* wait for the 2d engine to be ready */ 129062306a36Sopenharmony_ci while ((count > 0) && 129162306a36Sopenharmony_ci (smc501_readl(fbi->regs + SM501_SYSTEM_CONTROL) & 129262306a36Sopenharmony_ci SM501_SYSCTRL_2D_ENGINE_STATUS) != 0) 129362306a36Sopenharmony_ci count--; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (count <= 0) { 129662306a36Sopenharmony_ci fb_err(info, "Timeout waiting for 2d engine sync\n"); 129762306a36Sopenharmony_ci return 1; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 130562306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 130662306a36Sopenharmony_ci int width = area->width; 130762306a36Sopenharmony_ci int height = area->height; 130862306a36Sopenharmony_ci int sx = area->sx; 130962306a36Sopenharmony_ci int sy = area->sy; 131062306a36Sopenharmony_ci int dx = area->dx; 131162306a36Sopenharmony_ci int dy = area->dy; 131262306a36Sopenharmony_ci unsigned long rtl = 0; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* source clip */ 131562306a36Sopenharmony_ci if ((sx >= info->var.xres_virtual) || 131662306a36Sopenharmony_ci (sy >= info->var.yres_virtual)) 131762306a36Sopenharmony_ci /* source Area not within virtual screen, skipping */ 131862306a36Sopenharmony_ci return; 131962306a36Sopenharmony_ci if ((sx + width) >= info->var.xres_virtual) 132062306a36Sopenharmony_ci width = info->var.xres_virtual - sx - 1; 132162306a36Sopenharmony_ci if ((sy + height) >= info->var.yres_virtual) 132262306a36Sopenharmony_ci height = info->var.yres_virtual - sy - 1; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* dest clip */ 132562306a36Sopenharmony_ci if ((dx >= info->var.xres_virtual) || 132662306a36Sopenharmony_ci (dy >= info->var.yres_virtual)) 132762306a36Sopenharmony_ci /* Destination Area not within virtual screen, skipping */ 132862306a36Sopenharmony_ci return; 132962306a36Sopenharmony_ci if ((dx + width) >= info->var.xres_virtual) 133062306a36Sopenharmony_ci width = info->var.xres_virtual - dx - 1; 133162306a36Sopenharmony_ci if ((dy + height) >= info->var.yres_virtual) 133262306a36Sopenharmony_ci height = info->var.yres_virtual - dy - 1; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci if ((sx < dx) || (sy < dy)) { 133562306a36Sopenharmony_ci rtl = 1 << 27; 133662306a36Sopenharmony_ci sx += width - 1; 133762306a36Sopenharmony_ci dx += width - 1; 133862306a36Sopenharmony_ci sy += height - 1; 133962306a36Sopenharmony_ci dy += height - 1; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (sm501fb_sync(info)) 134362306a36Sopenharmony_ci return; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* set the base addresses */ 134662306a36Sopenharmony_ci smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE); 134762306a36Sopenharmony_ci smc501_writel(par->screen.sm_addr, 134862306a36Sopenharmony_ci fbi->regs2d + SM501_2D_DESTINATION_BASE); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci /* set the window width */ 135162306a36Sopenharmony_ci smc501_writel((info->var.xres << 16) | info->var.xres, 135262306a36Sopenharmony_ci fbi->regs2d + SM501_2D_WINDOW_WIDTH); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* set window stride */ 135562306a36Sopenharmony_ci smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual, 135662306a36Sopenharmony_ci fbi->regs2d + SM501_2D_PITCH); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* set data format */ 135962306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 136062306a36Sopenharmony_ci case 8: 136162306a36Sopenharmony_ci smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH); 136262306a36Sopenharmony_ci break; 136362306a36Sopenharmony_ci case 16: 136462306a36Sopenharmony_ci smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH); 136562306a36Sopenharmony_ci break; 136662306a36Sopenharmony_ci case 32: 136762306a36Sopenharmony_ci smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH); 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* 2d compare mask */ 137262306a36Sopenharmony_ci smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci /* 2d mask */ 137562306a36Sopenharmony_ci smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* source and destination x y */ 137862306a36Sopenharmony_ci smc501_writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE); 137962306a36Sopenharmony_ci smc501_writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* w/h */ 138262306a36Sopenharmony_ci smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* do area move */ 138562306a36Sopenharmony_ci smc501_writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL); 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) 138962306a36Sopenharmony_ci{ 139062306a36Sopenharmony_ci struct sm501fb_par *par = info->par; 139162306a36Sopenharmony_ci struct sm501fb_info *fbi = par->info; 139262306a36Sopenharmony_ci int width = rect->width, height = rect->height; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci if ((rect->dx >= info->var.xres_virtual) || 139562306a36Sopenharmony_ci (rect->dy >= info->var.yres_virtual)) 139662306a36Sopenharmony_ci /* Rectangle not within virtual screen, skipping */ 139762306a36Sopenharmony_ci return; 139862306a36Sopenharmony_ci if ((rect->dx + width) >= info->var.xres_virtual) 139962306a36Sopenharmony_ci width = info->var.xres_virtual - rect->dx - 1; 140062306a36Sopenharmony_ci if ((rect->dy + height) >= info->var.yres_virtual) 140162306a36Sopenharmony_ci height = info->var.yres_virtual - rect->dy - 1; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (sm501fb_sync(info)) 140462306a36Sopenharmony_ci return; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci /* set the base addresses */ 140762306a36Sopenharmony_ci smc501_writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE); 140862306a36Sopenharmony_ci smc501_writel(par->screen.sm_addr, 140962306a36Sopenharmony_ci fbi->regs2d + SM501_2D_DESTINATION_BASE); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* set the window width */ 141262306a36Sopenharmony_ci smc501_writel((info->var.xres << 16) | info->var.xres, 141362306a36Sopenharmony_ci fbi->regs2d + SM501_2D_WINDOW_WIDTH); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* set window stride */ 141662306a36Sopenharmony_ci smc501_writel((info->var.xres_virtual << 16) | info->var.xres_virtual, 141762306a36Sopenharmony_ci fbi->regs2d + SM501_2D_PITCH); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci /* set data format */ 142062306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 142162306a36Sopenharmony_ci case 8: 142262306a36Sopenharmony_ci smc501_writel(0, fbi->regs2d + SM501_2D_STRETCH); 142362306a36Sopenharmony_ci break; 142462306a36Sopenharmony_ci case 16: 142562306a36Sopenharmony_ci smc501_writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH); 142662306a36Sopenharmony_ci break; 142762306a36Sopenharmony_ci case 32: 142862306a36Sopenharmony_ci smc501_writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH); 142962306a36Sopenharmony_ci break; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci /* 2d compare mask */ 143362306a36Sopenharmony_ci smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* 2d mask */ 143662306a36Sopenharmony_ci smc501_writel(0xffffffff, fbi->regs2d + SM501_2D_MASK); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* colour */ 143962306a36Sopenharmony_ci smc501_writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* x y */ 144262306a36Sopenharmony_ci smc501_writel((rect->dx << 16) | rect->dy, 144362306a36Sopenharmony_ci fbi->regs2d + SM501_2D_DESTINATION); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci /* w/h */ 144662306a36Sopenharmony_ci smc501_writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci /* do rectangle fill */ 144962306a36Sopenharmony_ci smc501_writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL); 145062306a36Sopenharmony_ci} 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_cistatic struct fb_ops sm501fb_ops_crt = { 145462306a36Sopenharmony_ci .owner = THIS_MODULE, 145562306a36Sopenharmony_ci .fb_check_var = sm501fb_check_var_crt, 145662306a36Sopenharmony_ci .fb_set_par = sm501fb_set_par_crt, 145762306a36Sopenharmony_ci .fb_blank = sm501fb_blank_crt, 145862306a36Sopenharmony_ci .fb_setcolreg = sm501fb_setcolreg, 145962306a36Sopenharmony_ci .fb_pan_display = sm501fb_pan_crt, 146062306a36Sopenharmony_ci .fb_cursor = sm501fb_cursor, 146162306a36Sopenharmony_ci .fb_fillrect = sm501fb_fillrect, 146262306a36Sopenharmony_ci .fb_copyarea = sm501fb_copyarea, 146362306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 146462306a36Sopenharmony_ci .fb_sync = sm501fb_sync, 146562306a36Sopenharmony_ci}; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic struct fb_ops sm501fb_ops_pnl = { 146862306a36Sopenharmony_ci .owner = THIS_MODULE, 146962306a36Sopenharmony_ci .fb_check_var = sm501fb_check_var_pnl, 147062306a36Sopenharmony_ci .fb_set_par = sm501fb_set_par_pnl, 147162306a36Sopenharmony_ci .fb_pan_display = sm501fb_pan_pnl, 147262306a36Sopenharmony_ci .fb_blank = sm501fb_blank_pnl, 147362306a36Sopenharmony_ci .fb_setcolreg = sm501fb_setcolreg, 147462306a36Sopenharmony_ci .fb_cursor = sm501fb_cursor, 147562306a36Sopenharmony_ci .fb_fillrect = sm501fb_fillrect, 147662306a36Sopenharmony_ci .fb_copyarea = sm501fb_copyarea, 147762306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 147862306a36Sopenharmony_ci .fb_sync = sm501fb_sync, 147962306a36Sopenharmony_ci}; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci/* sm501_init_cursor 148262306a36Sopenharmony_ci * 148362306a36Sopenharmony_ci * initialise hw cursor parameters 148462306a36Sopenharmony_ci*/ 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base) 148762306a36Sopenharmony_ci{ 148862306a36Sopenharmony_ci struct sm501fb_par *par; 148962306a36Sopenharmony_ci struct sm501fb_info *info; 149062306a36Sopenharmony_ci int ret; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (fbi == NULL) 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci par = fbi->par; 149662306a36Sopenharmony_ci info = par->info; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci par->cursor_regs = info->regs + reg_base; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024, 150162306a36Sopenharmony_ci fbi->fix.smem_len); 150262306a36Sopenharmony_ci if (ret < 0) 150362306a36Sopenharmony_ci return ret; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci /* initialise the colour registers */ 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci smc501_writel(par->cursor.sm_addr, 150862306a36Sopenharmony_ci par->cursor_regs + SM501_OFF_HWC_ADDR); 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC); 151162306a36Sopenharmony_ci smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2); 151262306a36Sopenharmony_ci smc501_writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3); 151362306a36Sopenharmony_ci sm501fb_sync_regs(info); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci return 0; 151662306a36Sopenharmony_ci} 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci/* sm501fb_info_start 151962306a36Sopenharmony_ci * 152062306a36Sopenharmony_ci * fills the par structure claiming resources and remapping etc. 152162306a36Sopenharmony_ci*/ 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cistatic int sm501fb_start(struct sm501fb_info *info, 152462306a36Sopenharmony_ci struct platform_device *pdev) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct resource *res; 152762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 152862306a36Sopenharmony_ci int k; 152962306a36Sopenharmony_ci int ret; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci info->irq = ret = platform_get_irq(pdev, 0); 153262306a36Sopenharmony_ci if (ret < 0) { 153362306a36Sopenharmony_ci /* we currently do not use the IRQ */ 153462306a36Sopenharmony_ci dev_warn(dev, "no irq for device\n"); 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci /* allocate, reserve and remap resources for display 153862306a36Sopenharmony_ci * controller registers */ 153962306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 154062306a36Sopenharmony_ci if (res == NULL) { 154162306a36Sopenharmony_ci dev_err(dev, "no resource definition for registers\n"); 154262306a36Sopenharmony_ci ret = -ENOENT; 154362306a36Sopenharmony_ci goto err_release; 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci info->regs_res = request_mem_region(res->start, 154762306a36Sopenharmony_ci resource_size(res), 154862306a36Sopenharmony_ci pdev->name); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (info->regs_res == NULL) { 155162306a36Sopenharmony_ci dev_err(dev, "cannot claim registers\n"); 155262306a36Sopenharmony_ci ret = -ENXIO; 155362306a36Sopenharmony_ci goto err_release; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci info->regs = ioremap(res->start, resource_size(res)); 155762306a36Sopenharmony_ci if (info->regs == NULL) { 155862306a36Sopenharmony_ci dev_err(dev, "cannot remap registers\n"); 155962306a36Sopenharmony_ci ret = -ENXIO; 156062306a36Sopenharmony_ci goto err_regs_res; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci /* allocate, reserve and remap resources for 2d 156462306a36Sopenharmony_ci * controller registers */ 156562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 156662306a36Sopenharmony_ci if (res == NULL) { 156762306a36Sopenharmony_ci dev_err(dev, "no resource definition for 2d registers\n"); 156862306a36Sopenharmony_ci ret = -ENOENT; 156962306a36Sopenharmony_ci goto err_regs_map; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci info->regs2d_res = request_mem_region(res->start, 157362306a36Sopenharmony_ci resource_size(res), 157462306a36Sopenharmony_ci pdev->name); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (info->regs2d_res == NULL) { 157762306a36Sopenharmony_ci dev_err(dev, "cannot claim registers\n"); 157862306a36Sopenharmony_ci ret = -ENXIO; 157962306a36Sopenharmony_ci goto err_regs_map; 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci info->regs2d = ioremap(res->start, resource_size(res)); 158362306a36Sopenharmony_ci if (info->regs2d == NULL) { 158462306a36Sopenharmony_ci dev_err(dev, "cannot remap registers\n"); 158562306a36Sopenharmony_ci ret = -ENXIO; 158662306a36Sopenharmony_ci goto err_regs2d_res; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci /* allocate, reserve resources for framebuffer */ 159062306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 159162306a36Sopenharmony_ci if (res == NULL) { 159262306a36Sopenharmony_ci dev_err(dev, "no memory resource defined\n"); 159362306a36Sopenharmony_ci ret = -ENXIO; 159462306a36Sopenharmony_ci goto err_regs2d_map; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci info->fbmem_res = request_mem_region(res->start, 159862306a36Sopenharmony_ci resource_size(res), 159962306a36Sopenharmony_ci pdev->name); 160062306a36Sopenharmony_ci if (info->fbmem_res == NULL) { 160162306a36Sopenharmony_ci dev_err(dev, "cannot claim framebuffer\n"); 160262306a36Sopenharmony_ci ret = -ENXIO; 160362306a36Sopenharmony_ci goto err_regs2d_map; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci info->fbmem = ioremap(res->start, resource_size(res)); 160762306a36Sopenharmony_ci if (info->fbmem == NULL) { 160862306a36Sopenharmony_ci dev_err(dev, "cannot remap framebuffer\n"); 160962306a36Sopenharmony_ci ret = -ENXIO; 161062306a36Sopenharmony_ci goto err_mem_res; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci info->fbmem_len = resource_size(res); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* clear framebuffer memory - avoids garbage data on unused fb */ 161662306a36Sopenharmony_ci memset_io(info->fbmem, 0, info->fbmem_len); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci /* clear palette ram - undefined at power on */ 161962306a36Sopenharmony_ci for (k = 0; k < (256 * 3); k++) 162062306a36Sopenharmony_ci smc501_writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4)); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* enable display controller */ 162362306a36Sopenharmony_ci sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci /* enable 2d controller */ 162662306a36Sopenharmony_ci sm501_unit_power(dev->parent, SM501_GATE_2D_ENGINE, 1); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci /* setup cursors */ 162962306a36Sopenharmony_ci sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR); 163062306a36Sopenharmony_ci sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR); 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci return 0; /* everything is setup */ 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci err_mem_res: 163562306a36Sopenharmony_ci release_mem_region(info->fbmem_res->start, 163662306a36Sopenharmony_ci resource_size(info->fbmem_res)); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci err_regs2d_map: 163962306a36Sopenharmony_ci iounmap(info->regs2d); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci err_regs2d_res: 164262306a36Sopenharmony_ci release_mem_region(info->regs2d_res->start, 164362306a36Sopenharmony_ci resource_size(info->regs2d_res)); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci err_regs_map: 164662306a36Sopenharmony_ci iounmap(info->regs); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci err_regs_res: 164962306a36Sopenharmony_ci release_mem_region(info->regs_res->start, 165062306a36Sopenharmony_ci resource_size(info->regs_res)); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci err_release: 165362306a36Sopenharmony_ci return ret; 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic void sm501fb_stop(struct sm501fb_info *info) 165762306a36Sopenharmony_ci{ 165862306a36Sopenharmony_ci /* disable display controller */ 165962306a36Sopenharmony_ci sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci iounmap(info->fbmem); 166262306a36Sopenharmony_ci release_mem_region(info->fbmem_res->start, 166362306a36Sopenharmony_ci resource_size(info->fbmem_res)); 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci iounmap(info->regs2d); 166662306a36Sopenharmony_ci release_mem_region(info->regs2d_res->start, 166762306a36Sopenharmony_ci resource_size(info->regs2d_res)); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci iounmap(info->regs); 167062306a36Sopenharmony_ci release_mem_region(info->regs_res->start, 167162306a36Sopenharmony_ci resource_size(info->regs_res)); 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_cistatic int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head, 167562306a36Sopenharmony_ci const char *fbname) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci struct sm501_platdata_fbsub *pd; 167862306a36Sopenharmony_ci struct sm501fb_par *par = fb->par; 167962306a36Sopenharmony_ci struct sm501fb_info *info = par->info; 168062306a36Sopenharmony_ci unsigned long ctrl; 168162306a36Sopenharmony_ci unsigned int enable; 168262306a36Sopenharmony_ci int ret; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci switch (head) { 168562306a36Sopenharmony_ci case HEAD_CRT: 168662306a36Sopenharmony_ci pd = info->pdata->fb_crt; 168762306a36Sopenharmony_ci ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL); 168862306a36Sopenharmony_ci enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* ensure we set the correct source register */ 169162306a36Sopenharmony_ci if (info->pdata->fb_route != SM501_FB_CRT_PANEL) { 169262306a36Sopenharmony_ci ctrl |= SM501_DC_CRT_CONTROL_SEL; 169362306a36Sopenharmony_ci smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci break; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci case HEAD_PANEL: 169962306a36Sopenharmony_ci pd = info->pdata->fb_pnl; 170062306a36Sopenharmony_ci ctrl = smc501_readl(info->regs + SM501_DC_PANEL_CONTROL); 170162306a36Sopenharmony_ci enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0; 170262306a36Sopenharmony_ci break; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci default: 170562306a36Sopenharmony_ci pd = NULL; /* stop compiler warnings */ 170662306a36Sopenharmony_ci ctrl = 0; 170762306a36Sopenharmony_ci enable = 0; 170862306a36Sopenharmony_ci BUG(); 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci dev_info(info->dev, "fb %s %sabled at start\n", 171262306a36Sopenharmony_ci fbname, enable ? "en" : "dis"); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* check to see if our routing allows this */ 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) { 171762306a36Sopenharmony_ci ctrl &= ~SM501_DC_CRT_CONTROL_SEL; 171862306a36Sopenharmony_ci smc501_writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); 171962306a36Sopenharmony_ci enable = 0; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci strscpy(fb->fix.id, fbname, sizeof(fb->fix.id)); 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci memcpy(&par->ops, 172562306a36Sopenharmony_ci (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl, 172662306a36Sopenharmony_ci sizeof(struct fb_ops)); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci /* update ops dependent on what we've been passed */ 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0) 173162306a36Sopenharmony_ci par->ops.fb_cursor = NULL; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci fb->fbops = &par->ops; 173462306a36Sopenharmony_ci fb->flags = FBINFO_READS_FAST | 173562306a36Sopenharmony_ci FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | 173662306a36Sopenharmony_ci FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci#if defined(CONFIG_OF) 173962306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 174062306a36Sopenharmony_ci if (of_property_read_bool(info->dev->parent->of_node, "little-endian")) 174162306a36Sopenharmony_ci fb->flags |= FBINFO_FOREIGN_ENDIAN; 174262306a36Sopenharmony_ci#else 174362306a36Sopenharmony_ci if (of_property_read_bool(info->dev->parent->of_node, "big-endian")) 174462306a36Sopenharmony_ci fb->flags |= FBINFO_FOREIGN_ENDIAN; 174562306a36Sopenharmony_ci#endif 174662306a36Sopenharmony_ci#endif 174762306a36Sopenharmony_ci /* fixed data */ 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci fb->fix.type = FB_TYPE_PACKED_PIXELS; 175062306a36Sopenharmony_ci fb->fix.type_aux = 0; 175162306a36Sopenharmony_ci fb->fix.xpanstep = 1; 175262306a36Sopenharmony_ci fb->fix.ypanstep = 1; 175362306a36Sopenharmony_ci fb->fix.ywrapstep = 0; 175462306a36Sopenharmony_ci fb->fix.accel = FB_ACCEL_NONE; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci /* screenmode */ 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci fb->var.nonstd = 0; 175962306a36Sopenharmony_ci fb->var.activate = FB_ACTIVATE_NOW; 176062306a36Sopenharmony_ci fb->var.accel_flags = 0; 176162306a36Sopenharmony_ci fb->var.vmode = FB_VMODE_NONINTERLACED; 176262306a36Sopenharmony_ci fb->var.bits_per_pixel = 16; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (info->edid_data) { 176562306a36Sopenharmony_ci /* Now build modedb from EDID */ 176662306a36Sopenharmony_ci fb_edid_to_monspecs(info->edid_data, &fb->monspecs); 176762306a36Sopenharmony_ci fb_videomode_to_modelist(fb->monspecs.modedb, 176862306a36Sopenharmony_ci fb->monspecs.modedb_len, 176962306a36Sopenharmony_ci &fb->modelist); 177062306a36Sopenharmony_ci } 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) { 177362306a36Sopenharmony_ci /* TODO read the mode from the current display */ 177462306a36Sopenharmony_ci } else { 177562306a36Sopenharmony_ci if (pd->def_mode) { 177662306a36Sopenharmony_ci dev_info(info->dev, "using supplied mode\n"); 177762306a36Sopenharmony_ci fb_videomode_to_var(&fb->var, pd->def_mode); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci fb->var.bits_per_pixel = pd->def_bpp ? pd->def_bpp : 8; 178062306a36Sopenharmony_ci fb->var.xres_virtual = fb->var.xres; 178162306a36Sopenharmony_ci fb->var.yres_virtual = fb->var.yres; 178262306a36Sopenharmony_ci } else { 178362306a36Sopenharmony_ci if (info->edid_data) { 178462306a36Sopenharmony_ci ret = fb_find_mode(&fb->var, fb, fb_mode, 178562306a36Sopenharmony_ci fb->monspecs.modedb, 178662306a36Sopenharmony_ci fb->monspecs.modedb_len, 178762306a36Sopenharmony_ci &sm501_default_mode, default_bpp); 178862306a36Sopenharmony_ci /* edid_data is no longer needed, free it */ 178962306a36Sopenharmony_ci kfree(info->edid_data); 179062306a36Sopenharmony_ci } else { 179162306a36Sopenharmony_ci ret = fb_find_mode(&fb->var, fb, 179262306a36Sopenharmony_ci NULL, NULL, 0, NULL, 8); 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci switch (ret) { 179662306a36Sopenharmony_ci case 1: 179762306a36Sopenharmony_ci dev_info(info->dev, "using mode specified in " 179862306a36Sopenharmony_ci "@mode\n"); 179962306a36Sopenharmony_ci break; 180062306a36Sopenharmony_ci case 2: 180162306a36Sopenharmony_ci dev_info(info->dev, "using mode specified in " 180262306a36Sopenharmony_ci "@mode with ignored refresh rate\n"); 180362306a36Sopenharmony_ci break; 180462306a36Sopenharmony_ci case 3: 180562306a36Sopenharmony_ci dev_info(info->dev, "using mode default " 180662306a36Sopenharmony_ci "mode\n"); 180762306a36Sopenharmony_ci break; 180862306a36Sopenharmony_ci case 4: 180962306a36Sopenharmony_ci dev_info(info->dev, "using mode from list\n"); 181062306a36Sopenharmony_ci break; 181162306a36Sopenharmony_ci default: 181262306a36Sopenharmony_ci dev_info(info->dev, "ret = %d\n", ret); 181362306a36Sopenharmony_ci dev_info(info->dev, "failed to find mode\n"); 181462306a36Sopenharmony_ci return -EINVAL; 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci /* initialise and set the palette */ 182062306a36Sopenharmony_ci if (fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0)) { 182162306a36Sopenharmony_ci dev_err(info->dev, "failed to allocate cmap memory\n"); 182262306a36Sopenharmony_ci return -ENOMEM; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci fb_set_cmap(&fb->cmap, fb); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci ret = (fb->fbops->fb_check_var)(&fb->var, fb); 182762306a36Sopenharmony_ci if (ret) 182862306a36Sopenharmony_ci dev_err(info->dev, "check_var() failed on initial setup?\n"); 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci return 0; 183162306a36Sopenharmony_ci} 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci/* default platform data if none is supplied (ie, PCI device) */ 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_cistatic struct sm501_platdata_fbsub sm501fb_pdata_crt = { 183662306a36Sopenharmony_ci .flags = (SM501FB_FLAG_USE_INIT_MODE | 183762306a36Sopenharmony_ci SM501FB_FLAG_USE_HWCURSOR | 183862306a36Sopenharmony_ci SM501FB_FLAG_USE_HWACCEL | 183962306a36Sopenharmony_ci SM501FB_FLAG_DISABLE_AT_EXIT), 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci}; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_cistatic struct sm501_platdata_fbsub sm501fb_pdata_pnl = { 184462306a36Sopenharmony_ci .flags = (SM501FB_FLAG_USE_INIT_MODE | 184562306a36Sopenharmony_ci SM501FB_FLAG_USE_HWCURSOR | 184662306a36Sopenharmony_ci SM501FB_FLAG_USE_HWACCEL | 184762306a36Sopenharmony_ci SM501FB_FLAG_DISABLE_AT_EXIT), 184862306a36Sopenharmony_ci}; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_cistatic struct sm501_platdata_fb sm501fb_def_pdata = { 185162306a36Sopenharmony_ci .fb_route = SM501_FB_OWN, 185262306a36Sopenharmony_ci .fb_crt = &sm501fb_pdata_crt, 185362306a36Sopenharmony_ci .fb_pnl = &sm501fb_pdata_pnl, 185462306a36Sopenharmony_ci}; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_cistatic char driver_name_crt[] = "sm501fb-crt"; 185762306a36Sopenharmony_cistatic char driver_name_pnl[] = "sm501fb-panel"; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_cistatic int sm501fb_probe_one(struct sm501fb_info *info, 186062306a36Sopenharmony_ci enum sm501_controller head) 186162306a36Sopenharmony_ci{ 186262306a36Sopenharmony_ci unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel"; 186362306a36Sopenharmony_ci struct sm501_platdata_fbsub *pd; 186462306a36Sopenharmony_ci struct sm501fb_par *par; 186562306a36Sopenharmony_ci struct fb_info *fbi; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci /* Do not initialise if we've not been given any platform data */ 187062306a36Sopenharmony_ci if (pd == NULL) { 187162306a36Sopenharmony_ci dev_info(info->dev, "no data for fb %s (disabled)\n", name); 187262306a36Sopenharmony_ci return 0; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev); 187662306a36Sopenharmony_ci if (!fbi) 187762306a36Sopenharmony_ci return -ENOMEM; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci par = fbi->par; 188062306a36Sopenharmony_ci par->info = info; 188162306a36Sopenharmony_ci par->head = head; 188262306a36Sopenharmony_ci fbi->pseudo_palette = &par->pseudo_palette; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci info->fb[head] = fbi; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci return 0; 188762306a36Sopenharmony_ci} 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci/* Free up anything allocated by sm501fb_init_fb */ 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic void sm501_free_init_fb(struct sm501fb_info *info, 189262306a36Sopenharmony_ci enum sm501_controller head) 189362306a36Sopenharmony_ci{ 189462306a36Sopenharmony_ci struct fb_info *fbi = info->fb[head]; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci if (!fbi) 189762306a36Sopenharmony_ci return; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci fb_dealloc_cmap(&fbi->cmap); 190062306a36Sopenharmony_ci} 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_cistatic int sm501fb_start_one(struct sm501fb_info *info, 190362306a36Sopenharmony_ci enum sm501_controller head, const char *drvname) 190462306a36Sopenharmony_ci{ 190562306a36Sopenharmony_ci struct fb_info *fbi = info->fb[head]; 190662306a36Sopenharmony_ci int ret; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci if (!fbi) 190962306a36Sopenharmony_ci return 0; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci mutex_init(&info->fb[head]->mm_lock); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci ret = sm501fb_init_fb(info->fb[head], head, drvname); 191462306a36Sopenharmony_ci if (ret) { 191562306a36Sopenharmony_ci dev_err(info->dev, "cannot initialise fb %s\n", drvname); 191662306a36Sopenharmony_ci return ret; 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci ret = register_framebuffer(info->fb[head]); 192062306a36Sopenharmony_ci if (ret) { 192162306a36Sopenharmony_ci dev_err(info->dev, "failed to register fb %s\n", drvname); 192262306a36Sopenharmony_ci sm501_free_init_fb(info, head); 192362306a36Sopenharmony_ci return ret; 192462306a36Sopenharmony_ci } 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id); 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci return 0; 192962306a36Sopenharmony_ci} 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_cistatic int sm501fb_probe(struct platform_device *pdev) 193262306a36Sopenharmony_ci{ 193362306a36Sopenharmony_ci struct sm501fb_info *info; 193462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 193562306a36Sopenharmony_ci int ret; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci /* allocate our framebuffers */ 193862306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 193962306a36Sopenharmony_ci if (!info) { 194062306a36Sopenharmony_ci dev_err(dev, "failed to allocate state\n"); 194162306a36Sopenharmony_ci return -ENOMEM; 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci info->dev = dev = &pdev->dev; 194562306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci if (dev->parent->platform_data) { 194862306a36Sopenharmony_ci struct sm501_platdata *pd = dev->parent->platform_data; 194962306a36Sopenharmony_ci info->pdata = pd->fb; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci if (info->pdata == NULL) { 195362306a36Sopenharmony_ci int found = 0; 195462306a36Sopenharmony_ci#if defined(CONFIG_OF) 195562306a36Sopenharmony_ci struct device_node *np = pdev->dev.parent->of_node; 195662306a36Sopenharmony_ci const u8 *prop; 195762306a36Sopenharmony_ci const char *cp; 195862306a36Sopenharmony_ci int len; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci info->pdata = &sm501fb_def_pdata; 196162306a36Sopenharmony_ci if (np) { 196262306a36Sopenharmony_ci /* Get EDID */ 196362306a36Sopenharmony_ci cp = of_get_property(np, "mode", &len); 196462306a36Sopenharmony_ci if (cp) 196562306a36Sopenharmony_ci strcpy(fb_mode, cp); 196662306a36Sopenharmony_ci prop = of_get_property(np, "edid", &len); 196762306a36Sopenharmony_ci if (prop && len == EDID_LENGTH) { 196862306a36Sopenharmony_ci info->edid_data = kmemdup(prop, EDID_LENGTH, 196962306a36Sopenharmony_ci GFP_KERNEL); 197062306a36Sopenharmony_ci if (info->edid_data) 197162306a36Sopenharmony_ci found = 1; 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci#endif 197562306a36Sopenharmony_ci if (!found) { 197662306a36Sopenharmony_ci dev_info(dev, "using default configuration data\n"); 197762306a36Sopenharmony_ci info->pdata = &sm501fb_def_pdata; 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci } 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci /* probe for the presence of each panel */ 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci ret = sm501fb_probe_one(info, HEAD_CRT); 198462306a36Sopenharmony_ci if (ret < 0) { 198562306a36Sopenharmony_ci dev_err(dev, "failed to probe CRT\n"); 198662306a36Sopenharmony_ci goto err_alloc; 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci ret = sm501fb_probe_one(info, HEAD_PANEL); 199062306a36Sopenharmony_ci if (ret < 0) { 199162306a36Sopenharmony_ci dev_err(dev, "failed to probe PANEL\n"); 199262306a36Sopenharmony_ci goto err_probed_crt; 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci if (info->fb[HEAD_PANEL] == NULL && 199662306a36Sopenharmony_ci info->fb[HEAD_CRT] == NULL) { 199762306a36Sopenharmony_ci dev_err(dev, "no framebuffers found\n"); 199862306a36Sopenharmony_ci ret = -ENODEV; 199962306a36Sopenharmony_ci goto err_alloc; 200062306a36Sopenharmony_ci } 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci /* get the resources for both of the framebuffers */ 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci ret = sm501fb_start(info, pdev); 200562306a36Sopenharmony_ci if (ret) { 200662306a36Sopenharmony_ci dev_err(dev, "cannot initialise SM501\n"); 200762306a36Sopenharmony_ci goto err_probed_panel; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci ret = sm501fb_start_one(info, HEAD_CRT, driver_name_crt); 201162306a36Sopenharmony_ci if (ret) { 201262306a36Sopenharmony_ci dev_err(dev, "failed to start CRT\n"); 201362306a36Sopenharmony_ci goto err_started; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl); 201762306a36Sopenharmony_ci if (ret) { 201862306a36Sopenharmony_ci dev_err(dev, "failed to start Panel\n"); 201962306a36Sopenharmony_ci goto err_started_crt; 202062306a36Sopenharmony_ci } 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci /* we registered, return ok */ 202362306a36Sopenharmony_ci return 0; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cierr_started_crt: 202662306a36Sopenharmony_ci unregister_framebuffer(info->fb[HEAD_CRT]); 202762306a36Sopenharmony_ci sm501_free_init_fb(info, HEAD_CRT); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_cierr_started: 203062306a36Sopenharmony_ci sm501fb_stop(info); 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_cierr_probed_panel: 203362306a36Sopenharmony_ci framebuffer_release(info->fb[HEAD_PANEL]); 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_cierr_probed_crt: 203662306a36Sopenharmony_ci framebuffer_release(info->fb[HEAD_CRT]); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cierr_alloc: 203962306a36Sopenharmony_ci kfree(info); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci return ret; 204262306a36Sopenharmony_ci} 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci/* 204662306a36Sopenharmony_ci * Cleanup 204762306a36Sopenharmony_ci */ 204862306a36Sopenharmony_cistatic void sm501fb_remove(struct platform_device *pdev) 204962306a36Sopenharmony_ci{ 205062306a36Sopenharmony_ci struct sm501fb_info *info = platform_get_drvdata(pdev); 205162306a36Sopenharmony_ci struct fb_info *fbinfo_crt = info->fb[0]; 205262306a36Sopenharmony_ci struct fb_info *fbinfo_pnl = info->fb[1]; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci sm501_free_init_fb(info, HEAD_CRT); 205562306a36Sopenharmony_ci sm501_free_init_fb(info, HEAD_PANEL); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci if (fbinfo_crt) 205862306a36Sopenharmony_ci unregister_framebuffer(fbinfo_crt); 205962306a36Sopenharmony_ci if (fbinfo_pnl) 206062306a36Sopenharmony_ci unregister_framebuffer(fbinfo_pnl); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci sm501fb_stop(info); 206362306a36Sopenharmony_ci kfree(info); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci framebuffer_release(fbinfo_pnl); 206662306a36Sopenharmony_ci framebuffer_release(fbinfo_crt); 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci#ifdef CONFIG_PM 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_cistatic int sm501fb_suspend_fb(struct sm501fb_info *info, 207262306a36Sopenharmony_ci enum sm501_controller head) 207362306a36Sopenharmony_ci{ 207462306a36Sopenharmony_ci struct fb_info *fbi = info->fb[head]; 207562306a36Sopenharmony_ci struct sm501fb_par *par; 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci if (!fbi) 207862306a36Sopenharmony_ci return 0; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci par = fbi->par; 208162306a36Sopenharmony_ci if (par->screen.size == 0) 208262306a36Sopenharmony_ci return 0; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci /* blank the relevant interface to ensure unit power minimised */ 208562306a36Sopenharmony_ci (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* tell console/fb driver we are suspending */ 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci console_lock(); 209062306a36Sopenharmony_ci fb_set_suspend(fbi, 1); 209162306a36Sopenharmony_ci console_unlock(); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci /* backup copies in case chip is powered down over suspend */ 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci par->store_fb = vmalloc(par->screen.size); 209662306a36Sopenharmony_ci if (par->store_fb == NULL) { 209762306a36Sopenharmony_ci dev_err(info->dev, "no memory to store screen\n"); 209862306a36Sopenharmony_ci return -ENOMEM; 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci par->store_cursor = vmalloc(par->cursor.size); 210262306a36Sopenharmony_ci if (par->store_cursor == NULL) { 210362306a36Sopenharmony_ci dev_err(info->dev, "no memory to store cursor\n"); 210462306a36Sopenharmony_ci goto err_nocursor; 210562306a36Sopenharmony_ci } 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci dev_dbg(info->dev, "suspending screen to %p\n", par->store_fb); 210862306a36Sopenharmony_ci dev_dbg(info->dev, "suspending cursor to %p\n", par->store_cursor); 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size); 211162306a36Sopenharmony_ci memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size); 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci return 0; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci err_nocursor: 211662306a36Sopenharmony_ci vfree(par->store_fb); 211762306a36Sopenharmony_ci par->store_fb = NULL; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci return -ENOMEM; 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_cistatic void sm501fb_resume_fb(struct sm501fb_info *info, 212362306a36Sopenharmony_ci enum sm501_controller head) 212462306a36Sopenharmony_ci{ 212562306a36Sopenharmony_ci struct fb_info *fbi = info->fb[head]; 212662306a36Sopenharmony_ci struct sm501fb_par *par; 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (!fbi) 212962306a36Sopenharmony_ci return; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci par = fbi->par; 213262306a36Sopenharmony_ci if (par->screen.size == 0) 213362306a36Sopenharmony_ci return; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci /* re-activate the configuration */ 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci (par->ops.fb_set_par)(fbi); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci /* restore the data */ 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci dev_dbg(info->dev, "restoring screen from %p\n", par->store_fb); 214262306a36Sopenharmony_ci dev_dbg(info->dev, "restoring cursor from %p\n", par->store_cursor); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci if (par->store_fb) 214562306a36Sopenharmony_ci memcpy_toio(par->screen.k_addr, par->store_fb, 214662306a36Sopenharmony_ci par->screen.size); 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci if (par->store_cursor) 214962306a36Sopenharmony_ci memcpy_toio(par->cursor.k_addr, par->store_cursor, 215062306a36Sopenharmony_ci par->cursor.size); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci console_lock(); 215362306a36Sopenharmony_ci fb_set_suspend(fbi, 0); 215462306a36Sopenharmony_ci console_unlock(); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci vfree(par->store_fb); 215762306a36Sopenharmony_ci vfree(par->store_cursor); 215862306a36Sopenharmony_ci} 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci/* suspend and resume support */ 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic int sm501fb_suspend(struct platform_device *pdev, pm_message_t state) 216462306a36Sopenharmony_ci{ 216562306a36Sopenharmony_ci struct sm501fb_info *info = platform_get_drvdata(pdev); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci /* store crt control to resume with */ 216862306a36Sopenharmony_ci info->pm_crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci sm501fb_suspend_fb(info, HEAD_CRT); 217162306a36Sopenharmony_ci sm501fb_suspend_fb(info, HEAD_PANEL); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci /* turn off the clocks, in case the device is not powered down */ 217462306a36Sopenharmony_ci sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci return 0; 217762306a36Sopenharmony_ci} 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci#define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \ 218062306a36Sopenharmony_ci SM501_DC_CRT_CONTROL_SEL) 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_cistatic int sm501fb_resume(struct platform_device *pdev) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci struct sm501fb_info *info = platform_get_drvdata(pdev); 218662306a36Sopenharmony_ci unsigned long crt_ctrl; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci /* restore the items we want to be saved for crt control */ 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci crt_ctrl = smc501_readl(info->regs + SM501_DC_CRT_CONTROL); 219362306a36Sopenharmony_ci crt_ctrl &= ~SM501_CRT_CTRL_SAVE; 219462306a36Sopenharmony_ci crt_ctrl |= info->pm_crt_ctrl & SM501_CRT_CTRL_SAVE; 219562306a36Sopenharmony_ci smc501_writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci sm501fb_resume_fb(info, HEAD_CRT); 219862306a36Sopenharmony_ci sm501fb_resume_fb(info, HEAD_PANEL); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci return 0; 220162306a36Sopenharmony_ci} 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci#else 220462306a36Sopenharmony_ci#define sm501fb_suspend NULL 220562306a36Sopenharmony_ci#define sm501fb_resume NULL 220662306a36Sopenharmony_ci#endif 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_cistatic struct platform_driver sm501fb_driver = { 220962306a36Sopenharmony_ci .probe = sm501fb_probe, 221062306a36Sopenharmony_ci .remove_new = sm501fb_remove, 221162306a36Sopenharmony_ci .suspend = sm501fb_suspend, 221262306a36Sopenharmony_ci .resume = sm501fb_resume, 221362306a36Sopenharmony_ci .driver = { 221462306a36Sopenharmony_ci .name = "sm501-fb", 221562306a36Sopenharmony_ci .dev_groups = sm501fb_groups, 221662306a36Sopenharmony_ci }, 221762306a36Sopenharmony_ci}; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_cimodule_platform_driver(sm501fb_driver); 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_cimodule_param_named(mode, fb_mode, charp, 0); 222262306a36Sopenharmony_ciMODULE_PARM_DESC(mode, 222362306a36Sopenharmony_ci "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 222462306a36Sopenharmony_cimodule_param_named(bpp, default_bpp, ulong, 0); 222562306a36Sopenharmony_ciMODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode"); 222662306a36Sopenharmony_ciMODULE_AUTHOR("Ben Dooks, Vincent Sanders"); 222762306a36Sopenharmony_ciMODULE_DESCRIPTION("SM501 Framebuffer driver"); 222862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2229