18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/arcfb.c -- FB driver for Arc monochrome LCD board 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2005, Jaya Kumar <jayalk@intworks.biz> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 88c2ecf20Sopenharmony_ci * more details. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This driver was written to be used with the Arc LCD board. Arc uses a 138c2ecf20Sopenharmony_ci * set of KS108 chips that control individual 64x64 LCD matrices. The board 148c2ecf20Sopenharmony_ci * can be paneled in a variety of setups such as 2x1=128x64, 4x4=256x256 and 158c2ecf20Sopenharmony_ci * so on. The interface between the board and the host is TTL based GPIO. The 168c2ecf20Sopenharmony_ci * GPIO requirements are 8 writable data lines and 4+n lines for control. On a 178c2ecf20Sopenharmony_ci * GPIO-less system, the board can be tested by connecting the respective sigs 188c2ecf20Sopenharmony_ci * up to a parallel port connector. The driver requires the IO addresses for 198c2ecf20Sopenharmony_ci * data and control GPIO at load time. It is unable to probe for the 208c2ecf20Sopenharmony_ci * existence of the LCD so it must be told at load time whether it should 218c2ecf20Sopenharmony_ci * be enabled or not. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Todo: 248c2ecf20Sopenharmony_ci * - testing with 4x4 258c2ecf20Sopenharmony_ci * - testing with interrupt hw 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * General notes: 288c2ecf20Sopenharmony_ci * - User must set tuhold. It's in microseconds. According to the 108 spec, 298c2ecf20Sopenharmony_ci * the hold time is supposed to be at least 1 microsecond. 308c2ecf20Sopenharmony_ci * - User must set num_cols=x num_rows=y, eg: x=2 means 128 318c2ecf20Sopenharmony_ci * - User must set arcfb_enable=1 to enable it 328c2ecf20Sopenharmony_ci * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/errno.h> 398c2ecf20Sopenharmony_ci#include <linux/string.h> 408c2ecf20Sopenharmony_ci#include <linux/mm.h> 418c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 428c2ecf20Sopenharmony_ci#include <linux/delay.h> 438c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 448c2ecf20Sopenharmony_ci#include <linux/fb.h> 458c2ecf20Sopenharmony_ci#include <linux/init.h> 468c2ecf20Sopenharmony_ci#include <linux/arcfb.h> 478c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define floor8(a) (a&(~0x07)) 528c2ecf20Sopenharmony_ci#define floorXres(a,xres) (a&(~(xres - 1))) 538c2ecf20Sopenharmony_ci#define iceil8(a) (((int)((a+7)/8))*8) 548c2ecf20Sopenharmony_ci#define ceil64(a) (a|0x3F) 558c2ecf20Sopenharmony_ci#define ceilXres(a,xres) (a|(xres - 1)) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* ks108 chipset specific defines and code */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define KS_SET_DPY_START_LINE 0xC0 608c2ecf20Sopenharmony_ci#define KS_SET_PAGE_NUM 0xB8 618c2ecf20Sopenharmony_ci#define KS_SET_X 0x40 628c2ecf20Sopenharmony_ci#define KS_CEHI 0x01 638c2ecf20Sopenharmony_ci#define KS_CELO 0x00 648c2ecf20Sopenharmony_ci#define KS_SEL_CMD 0x08 658c2ecf20Sopenharmony_ci#define KS_SEL_DATA 0x00 668c2ecf20Sopenharmony_ci#define KS_DPY_ON 0x3F 678c2ecf20Sopenharmony_ci#define KS_DPY_OFF 0x3E 688c2ecf20Sopenharmony_ci#define KS_INTACK 0x40 698c2ecf20Sopenharmony_ci#define KS_CLRINT 0x02 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistruct arcfb_par { 728c2ecf20Sopenharmony_ci unsigned long dio_addr; 738c2ecf20Sopenharmony_ci unsigned long cio_addr; 748c2ecf20Sopenharmony_ci unsigned long c2io_addr; 758c2ecf20Sopenharmony_ci atomic_t ref_count; 768c2ecf20Sopenharmony_ci unsigned char cslut[9]; 778c2ecf20Sopenharmony_ci struct fb_info *info; 788c2ecf20Sopenharmony_ci unsigned int irq; 798c2ecf20Sopenharmony_ci spinlock_t lock; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo arcfb_fix = { 838c2ecf20Sopenharmony_ci .id = "arcfb", 848c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 858c2ecf20Sopenharmony_ci .visual = FB_VISUAL_MONO01, 868c2ecf20Sopenharmony_ci .xpanstep = 0, 878c2ecf20Sopenharmony_ci .ypanstep = 1, 888c2ecf20Sopenharmony_ci .ywrapstep = 0, 898c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo arcfb_var = { 938c2ecf20Sopenharmony_ci .xres = 128, 948c2ecf20Sopenharmony_ci .yres = 64, 958c2ecf20Sopenharmony_ci .xres_virtual = 128, 968c2ecf20Sopenharmony_ci .yres_virtual = 64, 978c2ecf20Sopenharmony_ci .bits_per_pixel = 1, 988c2ecf20Sopenharmony_ci .nonstd = 1, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic unsigned long num_cols; 1028c2ecf20Sopenharmony_cistatic unsigned long num_rows; 1038c2ecf20Sopenharmony_cistatic unsigned long dio_addr; 1048c2ecf20Sopenharmony_cistatic unsigned long cio_addr; 1058c2ecf20Sopenharmony_cistatic unsigned long c2io_addr; 1068c2ecf20Sopenharmony_cistatic unsigned long splashval; 1078c2ecf20Sopenharmony_cistatic unsigned long tuhold; 1088c2ecf20Sopenharmony_cistatic unsigned int nosplash; 1098c2ecf20Sopenharmony_cistatic unsigned int arcfb_enable; 1108c2ecf20Sopenharmony_cistatic unsigned int irq; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(arcfb_waitq); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void ks108_writeb_ctl(struct arcfb_par *par, 1158c2ecf20Sopenharmony_ci unsigned int chipindex, unsigned char value) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci unsigned char chipselval = par->cslut[chipindex]; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci outb(chipselval|KS_CEHI|KS_SEL_CMD, par->cio_addr); 1208c2ecf20Sopenharmony_ci outb(value, par->dio_addr); 1218c2ecf20Sopenharmony_ci udelay(tuhold); 1228c2ecf20Sopenharmony_ci outb(chipselval|KS_CELO|KS_SEL_CMD, par->cio_addr); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void ks108_writeb_mainctl(struct arcfb_par *par, unsigned char value) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci outb(value, par->cio_addr); 1298c2ecf20Sopenharmony_ci udelay(tuhold); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic unsigned char ks108_readb_ctl2(struct arcfb_par *par) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return inb(par->c2io_addr); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void ks108_writeb_data(struct arcfb_par *par, 1388c2ecf20Sopenharmony_ci unsigned int chipindex, unsigned char value) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci unsigned char chipselval = par->cslut[chipindex]; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci outb(chipselval|KS_CEHI|KS_SEL_DATA, par->cio_addr); 1438c2ecf20Sopenharmony_ci outb(value, par->dio_addr); 1448c2ecf20Sopenharmony_ci udelay(tuhold); 1458c2ecf20Sopenharmony_ci outb(chipselval|KS_CELO|KS_SEL_DATA, par->cio_addr); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void ks108_set_start_line(struct arcfb_par *par, 1498c2ecf20Sopenharmony_ci unsigned int chipindex, unsigned char y) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci ks108_writeb_ctl(par, chipindex, KS_SET_DPY_START_LINE|y); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void ks108_set_yaddr(struct arcfb_par *par, 1558c2ecf20Sopenharmony_ci unsigned int chipindex, unsigned char y) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci ks108_writeb_ctl(par, chipindex, KS_SET_PAGE_NUM|y); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void ks108_set_xaddr(struct arcfb_par *par, 1618c2ecf20Sopenharmony_ci unsigned int chipindex, unsigned char x) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci ks108_writeb_ctl(par, chipindex, KS_SET_X|x); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void ks108_clear_lcd(struct arcfb_par *par, unsigned int chipindex) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int i,j; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (i = 0; i <= 8; i++) { 1718c2ecf20Sopenharmony_ci ks108_set_yaddr(par, chipindex, i); 1728c2ecf20Sopenharmony_ci ks108_set_xaddr(par, chipindex, 0); 1738c2ecf20Sopenharmony_ci for (j = 0; j < 64; j++) { 1748c2ecf20Sopenharmony_ci ks108_writeb_data(par, chipindex, 1758c2ecf20Sopenharmony_ci (unsigned char) splashval); 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* main arcfb functions */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int arcfb_open(struct fb_info *info, int user) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci atomic_inc(&par->ref_count); 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int arcfb_release(struct fb_info *info, int user) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 1938c2ecf20Sopenharmony_ci int count = atomic_read(&par->ref_count); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (!count) 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci atomic_dec(&par->ref_count); 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int arcfb_pan_display(struct fb_var_screeninfo *var, 2028c2ecf20Sopenharmony_ci struct fb_info *info) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int i; 2058c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if ((var->vmode & FB_VMODE_YWRAP) && (var->yoffset < 64) 2088c2ecf20Sopenharmony_ci && (info->var.yres <= 64)) { 2098c2ecf20Sopenharmony_ci for (i = 0; i < num_cols; i++) { 2108c2ecf20Sopenharmony_ci ks108_set_start_line(par, i, var->yoffset); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci info->var.yoffset = var->yoffset; 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic irqreturn_t arcfb_interrupt(int vec, void *dev_instance) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct fb_info *info = dev_instance; 2228c2ecf20Sopenharmony_ci unsigned char ctl2status; 2238c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ctl2status = ks108_readb_ctl2(par); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!(ctl2status & KS_INTACK)) /* not arc generated interrupt */ 2288c2ecf20Sopenharmony_ci return IRQ_NONE; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ks108_writeb_mainctl(par, KS_CLRINT); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci spin_lock(&par->lock); 2338c2ecf20Sopenharmony_ci if (waitqueue_active(&arcfb_waitq)) { 2348c2ecf20Sopenharmony_ci wake_up(&arcfb_waitq); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci spin_unlock(&par->lock); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * here we handle a specific page on the lcd. the complexity comes from 2438c2ecf20Sopenharmony_ci * the fact that the fb is laidout in 8xX vertical columns. we extract 2448c2ecf20Sopenharmony_ci * each write of 8 vertical pixels. then we shift out as we move along 2458c2ecf20Sopenharmony_ci * X. That's what rightshift does. bitmask selects the desired input bit. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic void arcfb_lcd_update_page(struct arcfb_par *par, unsigned int upper, 2488c2ecf20Sopenharmony_ci unsigned int left, unsigned int right, unsigned int distance) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci unsigned char *src; 2518c2ecf20Sopenharmony_ci unsigned int xindex, yindex, chipindex, linesize; 2528c2ecf20Sopenharmony_ci int i; 2538c2ecf20Sopenharmony_ci unsigned char val; 2548c2ecf20Sopenharmony_ci unsigned char bitmask, rightshift; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci xindex = left >> 6; 2578c2ecf20Sopenharmony_ci yindex = upper >> 6; 2588c2ecf20Sopenharmony_ci chipindex = (xindex + (yindex*num_cols)); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ks108_set_yaddr(par, chipindex, upper/8); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci linesize = par->info->var.xres/8; 2638c2ecf20Sopenharmony_ci src = (unsigned char __force *) par->info->screen_base + (left/8) + 2648c2ecf20Sopenharmony_ci (upper * linesize); 2658c2ecf20Sopenharmony_ci ks108_set_xaddr(par, chipindex, left); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci bitmask=1; 2688c2ecf20Sopenharmony_ci rightshift=0; 2698c2ecf20Sopenharmony_ci while (left <= right) { 2708c2ecf20Sopenharmony_ci val = 0; 2718c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 2728c2ecf20Sopenharmony_ci if ( i > rightshift) { 2738c2ecf20Sopenharmony_ci val |= (*(src + (i*linesize)) & bitmask) 2748c2ecf20Sopenharmony_ci << (i - rightshift); 2758c2ecf20Sopenharmony_ci } else { 2768c2ecf20Sopenharmony_ci val |= (*(src + (i*linesize)) & bitmask) 2778c2ecf20Sopenharmony_ci >> (rightshift - i); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci ks108_writeb_data(par, chipindex, val); 2818c2ecf20Sopenharmony_ci left++; 2828c2ecf20Sopenharmony_ci if (bitmask == 0x80) { 2838c2ecf20Sopenharmony_ci bitmask = 1; 2848c2ecf20Sopenharmony_ci src++; 2858c2ecf20Sopenharmony_ci rightshift=0; 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci bitmask <<= 1; 2888c2ecf20Sopenharmony_ci rightshift++; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* 2948c2ecf20Sopenharmony_ci * here we handle the entire vertical page of the update. we write across 2958c2ecf20Sopenharmony_ci * lcd chips. update_page uses the upper/left values to decide which 2968c2ecf20Sopenharmony_ci * chip to select for the right. upper is needed for setting the page 2978c2ecf20Sopenharmony_ci * desired for the write. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_cistatic void arcfb_lcd_update_vert(struct arcfb_par *par, unsigned int top, 3008c2ecf20Sopenharmony_ci unsigned int bottom, unsigned int left, unsigned int right) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci unsigned int distance, upper, lower; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci distance = (bottom - top) + 1; 3058c2ecf20Sopenharmony_ci upper = top; 3068c2ecf20Sopenharmony_ci lower = top + 7; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci while (distance > 0) { 3098c2ecf20Sopenharmony_ci distance -= 8; 3108c2ecf20Sopenharmony_ci arcfb_lcd_update_page(par, upper, left, right, 8); 3118c2ecf20Sopenharmony_ci upper = lower + 1; 3128c2ecf20Sopenharmony_ci lower = upper + 7; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* 3178c2ecf20Sopenharmony_ci * here we handle horizontal blocks for the update. update_vert will 3188c2ecf20Sopenharmony_ci * handle spaning multiple pages. we break out each horizontal 3198c2ecf20Sopenharmony_ci * block in to individual blocks no taller than 64 pixels. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_cistatic void arcfb_lcd_update_horiz(struct arcfb_par *par, unsigned int left, 3228c2ecf20Sopenharmony_ci unsigned int right, unsigned int top, unsigned int h) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci unsigned int distance, upper, lower; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci distance = h; 3278c2ecf20Sopenharmony_ci upper = floor8(top); 3288c2ecf20Sopenharmony_ci lower = min(upper + distance - 1, ceil64(upper)); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci while (distance > 0) { 3318c2ecf20Sopenharmony_ci distance -= ((lower - upper) + 1 ); 3328c2ecf20Sopenharmony_ci arcfb_lcd_update_vert(par, upper, lower, left, right); 3338c2ecf20Sopenharmony_ci upper = lower + 1; 3348c2ecf20Sopenharmony_ci lower = min(upper + distance - 1, ceil64(upper)); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* 3398c2ecf20Sopenharmony_ci * here we start the process of splitting out the fb update into 3408c2ecf20Sopenharmony_ci * individual blocks of pixels. we end up splitting into 64x64 blocks 3418c2ecf20Sopenharmony_ci * and finally down to 64x8 pages. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_cistatic void arcfb_lcd_update(struct arcfb_par *par, unsigned int dx, 3448c2ecf20Sopenharmony_ci unsigned int dy, unsigned int w, unsigned int h) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci unsigned int left, right, distance, y; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* align the request first */ 3498c2ecf20Sopenharmony_ci y = floor8(dy); 3508c2ecf20Sopenharmony_ci h += dy - y; 3518c2ecf20Sopenharmony_ci h = iceil8(h); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci distance = w; 3548c2ecf20Sopenharmony_ci left = dx; 3558c2ecf20Sopenharmony_ci right = min(left + w - 1, ceil64(left)); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci while (distance > 0) { 3588c2ecf20Sopenharmony_ci arcfb_lcd_update_horiz(par, left, right, y, h); 3598c2ecf20Sopenharmony_ci distance -= ((right - left) + 1); 3608c2ecf20Sopenharmony_ci left = right + 1; 3618c2ecf20Sopenharmony_ci right = min(left + distance - 1, ceil64(left)); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void arcfb_fillrect(struct fb_info *info, 3668c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci sys_fillrect(info, rect); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* update the physical lcd */ 3738c2ecf20Sopenharmony_ci arcfb_lcd_update(par, rect->dx, rect->dy, rect->width, rect->height); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void arcfb_copyarea(struct fb_info *info, 3778c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci sys_copyarea(info, area); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* update the physical lcd */ 3848c2ecf20Sopenharmony_ci arcfb_lcd_update(par, area->dx, area->dy, area->width, area->height); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic void arcfb_imageblit(struct fb_info *info, const struct fb_image *image) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci sys_imageblit(info, image); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* update the physical lcd */ 3948c2ecf20Sopenharmony_ci arcfb_lcd_update(par, image->dx, image->dy, image->width, 3958c2ecf20Sopenharmony_ci image->height); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int arcfb_ioctl(struct fb_info *info, 3998c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 4028c2ecf20Sopenharmony_ci struct arcfb_par *par = info->par; 4038c2ecf20Sopenharmony_ci unsigned long flags; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci switch (cmd) { 4068c2ecf20Sopenharmony_ci case FBIO_WAITEVENT: 4078c2ecf20Sopenharmony_ci { 4088c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 4098c2ecf20Sopenharmony_ci /* illegal to wait on arc if no irq will occur */ 4108c2ecf20Sopenharmony_ci if (!par->irq) 4118c2ecf20Sopenharmony_ci return -EINVAL; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* wait until the Arc has generated an interrupt 4148c2ecf20Sopenharmony_ci * which will wake us up */ 4158c2ecf20Sopenharmony_ci spin_lock_irqsave(&par->lock, flags); 4168c2ecf20Sopenharmony_ci prepare_to_wait(&arcfb_waitq, &wait, 4178c2ecf20Sopenharmony_ci TASK_INTERRUPTIBLE); 4188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&par->lock, flags); 4198c2ecf20Sopenharmony_ci schedule(); 4208c2ecf20Sopenharmony_ci finish_wait(&arcfb_waitq, &wait); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci fallthrough; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci case FBIO_GETCONTROL2: 4258c2ecf20Sopenharmony_ci { 4268c2ecf20Sopenharmony_ci unsigned char ctl2; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ctl2 = ks108_readb_ctl2(info->par); 4298c2ecf20Sopenharmony_ci if (copy_to_user(argp, &ctl2, sizeof(ctl2))) 4308c2ecf20Sopenharmony_ci return -EFAULT; 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci return -EINVAL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* 4398c2ecf20Sopenharmony_ci * this is the access path from userspace. they can seek and write to 4408c2ecf20Sopenharmony_ci * the fb. it's inefficient for them to do anything less than 64*8 4418c2ecf20Sopenharmony_ci * writes since we update the lcd in each write() anyway. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_cistatic ssize_t arcfb_write(struct fb_info *info, const char __user *buf, 4448c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci /* modded from epson 1355 */ 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci unsigned long p; 4498c2ecf20Sopenharmony_ci int err=-EINVAL; 4508c2ecf20Sopenharmony_ci unsigned int fbmemlength,x,y,w,h, bitppos, startpos, endpos, bitcount; 4518c2ecf20Sopenharmony_ci struct arcfb_par *par; 4528c2ecf20Sopenharmony_ci unsigned int xres; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci p = *ppos; 4558c2ecf20Sopenharmony_ci par = info->par; 4568c2ecf20Sopenharmony_ci xres = info->var.xres; 4578c2ecf20Sopenharmony_ci fbmemlength = (xres * info->var.yres)/8; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (p > fbmemlength) 4608c2ecf20Sopenharmony_ci return -ENOSPC; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = 0; 4638c2ecf20Sopenharmony_ci if ((count + p) > fbmemlength) { 4648c2ecf20Sopenharmony_ci count = fbmemlength - p; 4658c2ecf20Sopenharmony_ci err = -ENOSPC; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (count) { 4698c2ecf20Sopenharmony_ci char *base_addr; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci base_addr = (char __force *)info->screen_base; 4728c2ecf20Sopenharmony_ci count -= copy_from_user(base_addr + p, buf, count); 4738c2ecf20Sopenharmony_ci *ppos += count; 4748c2ecf20Sopenharmony_ci err = -EFAULT; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci bitppos = p*8; 4798c2ecf20Sopenharmony_ci startpos = floorXres(bitppos, xres); 4808c2ecf20Sopenharmony_ci endpos = ceilXres((bitppos + (count*8)), xres); 4818c2ecf20Sopenharmony_ci bitcount = endpos - startpos; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci x = startpos % xres; 4848c2ecf20Sopenharmony_ci y = startpos / xres; 4858c2ecf20Sopenharmony_ci w = xres; 4868c2ecf20Sopenharmony_ci h = bitcount / xres; 4878c2ecf20Sopenharmony_ci arcfb_lcd_update(par, x, y, w, h); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (count) 4908c2ecf20Sopenharmony_ci return count; 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic const struct fb_ops arcfb_ops = { 4958c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4968c2ecf20Sopenharmony_ci .fb_open = arcfb_open, 4978c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 4988c2ecf20Sopenharmony_ci .fb_write = arcfb_write, 4998c2ecf20Sopenharmony_ci .fb_release = arcfb_release, 5008c2ecf20Sopenharmony_ci .fb_pan_display = arcfb_pan_display, 5018c2ecf20Sopenharmony_ci .fb_fillrect = arcfb_fillrect, 5028c2ecf20Sopenharmony_ci .fb_copyarea = arcfb_copyarea, 5038c2ecf20Sopenharmony_ci .fb_imageblit = arcfb_imageblit, 5048c2ecf20Sopenharmony_ci .fb_ioctl = arcfb_ioctl, 5058c2ecf20Sopenharmony_ci}; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int arcfb_probe(struct platform_device *dev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct fb_info *info; 5108c2ecf20Sopenharmony_ci int retval = -ENOMEM; 5118c2ecf20Sopenharmony_ci int videomemorysize; 5128c2ecf20Sopenharmony_ci unsigned char *videomemory; 5138c2ecf20Sopenharmony_ci struct arcfb_par *par; 5148c2ecf20Sopenharmony_ci int i; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci videomemorysize = (((64*64)*num_cols)*num_rows)/8; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* We need a flat backing store for the Arc's 5198c2ecf20Sopenharmony_ci less-flat actual paged framebuffer */ 5208c2ecf20Sopenharmony_ci videomemory = vzalloc(videomemorysize); 5218c2ecf20Sopenharmony_ci if (!videomemory) 5228c2ecf20Sopenharmony_ci return retval; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev); 5258c2ecf20Sopenharmony_ci if (!info) 5268c2ecf20Sopenharmony_ci goto err_fb_alloc; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci info->screen_base = (char __iomem *)videomemory; 5298c2ecf20Sopenharmony_ci info->fbops = &arcfb_ops; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci info->var = arcfb_var; 5328c2ecf20Sopenharmony_ci info->fix = arcfb_fix; 5338c2ecf20Sopenharmony_ci par = info->par; 5348c2ecf20Sopenharmony_ci par->info = info; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!dio_addr || !cio_addr || !c2io_addr) { 5378c2ecf20Sopenharmony_ci printk(KERN_WARNING "no IO addresses supplied\n"); 5388c2ecf20Sopenharmony_ci goto err_addr; 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci par->dio_addr = dio_addr; 5418c2ecf20Sopenharmony_ci par->cio_addr = cio_addr; 5428c2ecf20Sopenharmony_ci par->c2io_addr = c2io_addr; 5438c2ecf20Sopenharmony_ci par->cslut[0] = 0x00; 5448c2ecf20Sopenharmony_ci par->cslut[1] = 0x06; 5458c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT; 5468c2ecf20Sopenharmony_ci spin_lock_init(&par->lock); 5478c2ecf20Sopenharmony_ci if (irq) { 5488c2ecf20Sopenharmony_ci par->irq = irq; 5498c2ecf20Sopenharmony_ci if (request_irq(par->irq, &arcfb_interrupt, IRQF_SHARED, 5508c2ecf20Sopenharmony_ci "arcfb", info)) { 5518c2ecf20Sopenharmony_ci printk(KERN_INFO 5528c2ecf20Sopenharmony_ci "arcfb: Failed req IRQ %d\n", par->irq); 5538c2ecf20Sopenharmony_ci retval = -EBUSY; 5548c2ecf20Sopenharmony_ci goto err_addr; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci retval = register_framebuffer(info); 5588c2ecf20Sopenharmony_ci if (retval < 0) 5598c2ecf20Sopenharmony_ci goto err_register_fb; 5608c2ecf20Sopenharmony_ci platform_set_drvdata(dev, info); 5618c2ecf20Sopenharmony_ci fb_info(info, "Arc frame buffer device, using %dK of video memory\n", 5628c2ecf20Sopenharmony_ci videomemorysize >> 10); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* this inits the lcd but doesn't clear dirty pixels */ 5658c2ecf20Sopenharmony_ci for (i = 0; i < num_cols * num_rows; i++) { 5668c2ecf20Sopenharmony_ci ks108_writeb_ctl(par, i, KS_DPY_OFF); 5678c2ecf20Sopenharmony_ci ks108_set_start_line(par, i, 0); 5688c2ecf20Sopenharmony_ci ks108_set_yaddr(par, i, 0); 5698c2ecf20Sopenharmony_ci ks108_set_xaddr(par, i, 0); 5708c2ecf20Sopenharmony_ci ks108_writeb_ctl(par, i, KS_DPY_ON); 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* if we were told to splash the screen, we just clear it */ 5748c2ecf20Sopenharmony_ci if (!nosplash) { 5758c2ecf20Sopenharmony_ci for (i = 0; i < num_cols * num_rows; i++) { 5768c2ecf20Sopenharmony_ci fb_info(info, "splashing lcd %d\n", i); 5778c2ecf20Sopenharmony_ci ks108_set_start_line(par, i, 0); 5788c2ecf20Sopenharmony_ci ks108_clear_lcd(par, i); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cierr_register_fb: 5858c2ecf20Sopenharmony_ci free_irq(par->irq, info); 5868c2ecf20Sopenharmony_cierr_addr: 5878c2ecf20Sopenharmony_ci framebuffer_release(info); 5888c2ecf20Sopenharmony_cierr_fb_alloc: 5898c2ecf20Sopenharmony_ci vfree(videomemory); 5908c2ecf20Sopenharmony_ci return retval; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic int arcfb_remove(struct platform_device *dev) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (info) { 5988c2ecf20Sopenharmony_ci unregister_framebuffer(info); 5998c2ecf20Sopenharmony_ci if (irq) 6008c2ecf20Sopenharmony_ci free_irq(((struct arcfb_par *)(info->par))->irq, info); 6018c2ecf20Sopenharmony_ci vfree((void __force *)info->screen_base); 6028c2ecf20Sopenharmony_ci framebuffer_release(info); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci return 0; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic struct platform_driver arcfb_driver = { 6088c2ecf20Sopenharmony_ci .probe = arcfb_probe, 6098c2ecf20Sopenharmony_ci .remove = arcfb_remove, 6108c2ecf20Sopenharmony_ci .driver = { 6118c2ecf20Sopenharmony_ci .name = "arcfb", 6128c2ecf20Sopenharmony_ci }, 6138c2ecf20Sopenharmony_ci}; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic struct platform_device *arcfb_device; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int __init arcfb_init(void) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci int ret; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!arcfb_enable) 6228c2ecf20Sopenharmony_ci return -ENXIO; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci ret = platform_driver_register(&arcfb_driver); 6258c2ecf20Sopenharmony_ci if (!ret) { 6268c2ecf20Sopenharmony_ci arcfb_device = platform_device_alloc("arcfb", 0); 6278c2ecf20Sopenharmony_ci if (arcfb_device) { 6288c2ecf20Sopenharmony_ci ret = platform_device_add(arcfb_device); 6298c2ecf20Sopenharmony_ci } else { 6308c2ecf20Sopenharmony_ci ret = -ENOMEM; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci if (ret) { 6338c2ecf20Sopenharmony_ci platform_device_put(arcfb_device); 6348c2ecf20Sopenharmony_ci platform_driver_unregister(&arcfb_driver); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic void __exit arcfb_exit(void) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci platform_device_unregister(arcfb_device); 6448c2ecf20Sopenharmony_ci platform_driver_unregister(&arcfb_driver); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cimodule_param(num_cols, ulong, 0); 6488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(num_cols, "Num horiz panels, eg: 2 = 128 bit wide"); 6498c2ecf20Sopenharmony_cimodule_param(num_rows, ulong, 0); 6508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(num_rows, "Num vert panels, eg: 1 = 64 bit high"); 6518c2ecf20Sopenharmony_cimodule_param(nosplash, uint, 0); 6528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); 6538c2ecf20Sopenharmony_cimodule_param(arcfb_enable, uint, 0); 6548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(arcfb_enable, "Enable communication with Arc board"); 6558c2ecf20Sopenharmony_cimodule_param_hw(dio_addr, ulong, ioport, 0); 6568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); 6578c2ecf20Sopenharmony_cimodule_param_hw(cio_addr, ulong, ioport, 0); 6588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); 6598c2ecf20Sopenharmony_cimodule_param_hw(c2io_addr, ulong, ioport, 0); 6608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); 6618c2ecf20Sopenharmony_cimodule_param(splashval, ulong, 0); 6628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(splashval, "Splash pattern: 0xFF is black, 0x00 is green"); 6638c2ecf20Sopenharmony_cimodule_param(tuhold, ulong, 0); 6648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tuhold, "Time to hold between strobing data to Arc board"); 6658c2ecf20Sopenharmony_cimodule_param_hw(irq, uint, irq, 0); 6668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ for the Arc board"); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cimodule_init(arcfb_init); 6698c2ecf20Sopenharmony_cimodule_exit(arcfb_exit); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for Arc monochrome LCD board"); 6728c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 6738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6748c2ecf20Sopenharmony_ci 675