162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/arcfb.c -- FB driver for Arc monochrome LCD board 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2005, Jaya Kumar <jayalk@intworks.biz> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 862306a36Sopenharmony_ci * more details. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This driver was written to be used with the Arc LCD board. Arc uses a 1362306a36Sopenharmony_ci * set of KS108 chips that control individual 64x64 LCD matrices. The board 1462306a36Sopenharmony_ci * can be paneled in a variety of setups such as 2x1=128x64, 4x4=256x256 and 1562306a36Sopenharmony_ci * so on. The interface between the board and the host is TTL based GPIO. The 1662306a36Sopenharmony_ci * GPIO requirements are 8 writable data lines and 4+n lines for control. On a 1762306a36Sopenharmony_ci * GPIO-less system, the board can be tested by connecting the respective sigs 1862306a36Sopenharmony_ci * up to a parallel port connector. The driver requires the IO addresses for 1962306a36Sopenharmony_ci * data and control GPIO at load time. It is unable to probe for the 2062306a36Sopenharmony_ci * existence of the LCD so it must be told at load time whether it should 2162306a36Sopenharmony_ci * be enabled or not. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Todo: 2462306a36Sopenharmony_ci * - testing with 4x4 2562306a36Sopenharmony_ci * - testing with interrupt hw 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * General notes: 2862306a36Sopenharmony_ci * - User must set tuhold. It's in microseconds. According to the 108 spec, 2962306a36Sopenharmony_ci * the hold time is supposed to be at least 1 microsecond. 3062306a36Sopenharmony_ci * - User must set num_cols=x num_rows=y, eg: x=2 means 128 3162306a36Sopenharmony_ci * - User must set arcfb_enable=1 to enable it 3262306a36Sopenharmony_ci * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <linux/module.h> 3762306a36Sopenharmony_ci#include <linux/kernel.h> 3862306a36Sopenharmony_ci#include <linux/errno.h> 3962306a36Sopenharmony_ci#include <linux/string.h> 4062306a36Sopenharmony_ci#include <linux/mm.h> 4162306a36Sopenharmony_ci#include <linux/vmalloc.h> 4262306a36Sopenharmony_ci#include <linux/delay.h> 4362306a36Sopenharmony_ci#include <linux/interrupt.h> 4462306a36Sopenharmony_ci#include <linux/io.h> 4562306a36Sopenharmony_ci#include <linux/fb.h> 4662306a36Sopenharmony_ci#include <linux/init.h> 4762306a36Sopenharmony_ci#include <linux/arcfb.h> 4862306a36Sopenharmony_ci#include <linux/platform_device.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <linux/uaccess.h> 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define floor8(a) (a&(~0x07)) 5362306a36Sopenharmony_ci#define floorXres(a,xres) (a&(~(xres - 1))) 5462306a36Sopenharmony_ci#define iceil8(a) (((int)((a+7)/8))*8) 5562306a36Sopenharmony_ci#define ceil64(a) (a|0x3F) 5662306a36Sopenharmony_ci#define ceilXres(a,xres) (a|(xres - 1)) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* ks108 chipset specific defines and code */ 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define KS_SET_DPY_START_LINE 0xC0 6162306a36Sopenharmony_ci#define KS_SET_PAGE_NUM 0xB8 6262306a36Sopenharmony_ci#define KS_SET_X 0x40 6362306a36Sopenharmony_ci#define KS_CEHI 0x01 6462306a36Sopenharmony_ci#define KS_CELO 0x00 6562306a36Sopenharmony_ci#define KS_SEL_CMD 0x08 6662306a36Sopenharmony_ci#define KS_SEL_DATA 0x00 6762306a36Sopenharmony_ci#define KS_DPY_ON 0x3F 6862306a36Sopenharmony_ci#define KS_DPY_OFF 0x3E 6962306a36Sopenharmony_ci#define KS_INTACK 0x40 7062306a36Sopenharmony_ci#define KS_CLRINT 0x02 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct arcfb_par { 7362306a36Sopenharmony_ci unsigned long dio_addr; 7462306a36Sopenharmony_ci unsigned long cio_addr; 7562306a36Sopenharmony_ci unsigned long c2io_addr; 7662306a36Sopenharmony_ci atomic_t ref_count; 7762306a36Sopenharmony_ci unsigned char cslut[9]; 7862306a36Sopenharmony_ci struct fb_info *info; 7962306a36Sopenharmony_ci unsigned int irq; 8062306a36Sopenharmony_ci spinlock_t lock; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic const struct fb_fix_screeninfo arcfb_fix = { 8462306a36Sopenharmony_ci .id = "arcfb", 8562306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 8662306a36Sopenharmony_ci .visual = FB_VISUAL_MONO01, 8762306a36Sopenharmony_ci .xpanstep = 0, 8862306a36Sopenharmony_ci .ypanstep = 1, 8962306a36Sopenharmony_ci .ywrapstep = 0, 9062306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic const struct fb_var_screeninfo arcfb_var = { 9462306a36Sopenharmony_ci .xres = 128, 9562306a36Sopenharmony_ci .yres = 64, 9662306a36Sopenharmony_ci .xres_virtual = 128, 9762306a36Sopenharmony_ci .yres_virtual = 64, 9862306a36Sopenharmony_ci .bits_per_pixel = 1, 9962306a36Sopenharmony_ci .nonstd = 1, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic unsigned long num_cols; 10362306a36Sopenharmony_cistatic unsigned long num_rows; 10462306a36Sopenharmony_cistatic unsigned long dio_addr; 10562306a36Sopenharmony_cistatic unsigned long cio_addr; 10662306a36Sopenharmony_cistatic unsigned long c2io_addr; 10762306a36Sopenharmony_cistatic unsigned long splashval; 10862306a36Sopenharmony_cistatic unsigned long tuhold; 10962306a36Sopenharmony_cistatic unsigned int nosplash; 11062306a36Sopenharmony_cistatic unsigned int arcfb_enable; 11162306a36Sopenharmony_cistatic unsigned int irq; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(arcfb_waitq); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void ks108_writeb_ctl(struct arcfb_par *par, 11662306a36Sopenharmony_ci unsigned int chipindex, unsigned char value) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned char chipselval = par->cslut[chipindex]; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci outb(chipselval|KS_CEHI|KS_SEL_CMD, par->cio_addr); 12162306a36Sopenharmony_ci outb(value, par->dio_addr); 12262306a36Sopenharmony_ci udelay(tuhold); 12362306a36Sopenharmony_ci outb(chipselval|KS_CELO|KS_SEL_CMD, par->cio_addr); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void ks108_writeb_mainctl(struct arcfb_par *par, unsigned char value) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci outb(value, par->cio_addr); 13062306a36Sopenharmony_ci udelay(tuhold); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic unsigned char ks108_readb_ctl2(struct arcfb_par *par) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return inb(par->c2io_addr); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void ks108_writeb_data(struct arcfb_par *par, 13962306a36Sopenharmony_ci unsigned int chipindex, unsigned char value) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci unsigned char chipselval = par->cslut[chipindex]; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci outb(chipselval|KS_CEHI|KS_SEL_DATA, par->cio_addr); 14462306a36Sopenharmony_ci outb(value, par->dio_addr); 14562306a36Sopenharmony_ci udelay(tuhold); 14662306a36Sopenharmony_ci outb(chipselval|KS_CELO|KS_SEL_DATA, par->cio_addr); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void ks108_set_start_line(struct arcfb_par *par, 15062306a36Sopenharmony_ci unsigned int chipindex, unsigned char y) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci ks108_writeb_ctl(par, chipindex, KS_SET_DPY_START_LINE|y); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void ks108_set_yaddr(struct arcfb_par *par, 15662306a36Sopenharmony_ci unsigned int chipindex, unsigned char y) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci ks108_writeb_ctl(par, chipindex, KS_SET_PAGE_NUM|y); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void ks108_set_xaddr(struct arcfb_par *par, 16262306a36Sopenharmony_ci unsigned int chipindex, unsigned char x) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci ks108_writeb_ctl(par, chipindex, KS_SET_X|x); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void ks108_clear_lcd(struct arcfb_par *par, unsigned int chipindex) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int i,j; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i <= 8; i++) { 17262306a36Sopenharmony_ci ks108_set_yaddr(par, chipindex, i); 17362306a36Sopenharmony_ci ks108_set_xaddr(par, chipindex, 0); 17462306a36Sopenharmony_ci for (j = 0; j < 64; j++) { 17562306a36Sopenharmony_ci ks108_writeb_data(par, chipindex, 17662306a36Sopenharmony_ci (unsigned char) splashval); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* main arcfb functions */ 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int arcfb_open(struct fb_info *info, int user) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct arcfb_par *par = info->par; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci atomic_inc(&par->ref_count); 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int arcfb_release(struct fb_info *info, int user) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct arcfb_par *par = info->par; 19462306a36Sopenharmony_ci int count = atomic_read(&par->ref_count); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!count) 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci atomic_dec(&par->ref_count); 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int arcfb_pan_display(struct fb_var_screeninfo *var, 20362306a36Sopenharmony_ci struct fb_info *info) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci int i; 20662306a36Sopenharmony_ci struct arcfb_par *par = info->par; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if ((var->vmode & FB_VMODE_YWRAP) && (var->yoffset < 64) 20962306a36Sopenharmony_ci && (info->var.yres <= 64)) { 21062306a36Sopenharmony_ci for (i = 0; i < num_cols; i++) { 21162306a36Sopenharmony_ci ks108_set_start_line(par, i, var->yoffset); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci info->var.yoffset = var->yoffset; 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return -EINVAL; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic irqreturn_t arcfb_interrupt(int vec, void *dev_instance) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct fb_info *info = dev_instance; 22362306a36Sopenharmony_ci unsigned char ctl2status; 22462306a36Sopenharmony_ci struct arcfb_par *par = info->par; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ctl2status = ks108_readb_ctl2(par); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!(ctl2status & KS_INTACK)) /* not arc generated interrupt */ 22962306a36Sopenharmony_ci return IRQ_NONE; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ks108_writeb_mainctl(par, KS_CLRINT); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci spin_lock(&par->lock); 23462306a36Sopenharmony_ci if (waitqueue_active(&arcfb_waitq)) { 23562306a36Sopenharmony_ci wake_up(&arcfb_waitq); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci spin_unlock(&par->lock); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return IRQ_HANDLED; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* 24362306a36Sopenharmony_ci * here we handle a specific page on the lcd. the complexity comes from 24462306a36Sopenharmony_ci * the fact that the fb is laidout in 8xX vertical columns. we extract 24562306a36Sopenharmony_ci * each write of 8 vertical pixels. then we shift out as we move along 24662306a36Sopenharmony_ci * X. That's what rightshift does. bitmask selects the desired input bit. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistatic void arcfb_lcd_update_page(struct arcfb_par *par, unsigned int upper, 24962306a36Sopenharmony_ci unsigned int left, unsigned int right, unsigned int distance) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci unsigned char *src; 25262306a36Sopenharmony_ci unsigned int xindex, yindex, chipindex, linesize; 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci unsigned char val; 25562306a36Sopenharmony_ci unsigned char bitmask, rightshift; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci xindex = left >> 6; 25862306a36Sopenharmony_ci yindex = upper >> 6; 25962306a36Sopenharmony_ci chipindex = (xindex + (yindex*num_cols)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ks108_set_yaddr(par, chipindex, upper/8); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci linesize = par->info->var.xres/8; 26462306a36Sopenharmony_ci src = (unsigned char *)par->info->screen_buffer + (left/8) + 26562306a36Sopenharmony_ci (upper * linesize); 26662306a36Sopenharmony_ci ks108_set_xaddr(par, chipindex, left); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci bitmask=1; 26962306a36Sopenharmony_ci rightshift=0; 27062306a36Sopenharmony_ci while (left <= right) { 27162306a36Sopenharmony_ci val = 0; 27262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 27362306a36Sopenharmony_ci if ( i > rightshift) { 27462306a36Sopenharmony_ci val |= (*(src + (i*linesize)) & bitmask) 27562306a36Sopenharmony_ci << (i - rightshift); 27662306a36Sopenharmony_ci } else { 27762306a36Sopenharmony_ci val |= (*(src + (i*linesize)) & bitmask) 27862306a36Sopenharmony_ci >> (rightshift - i); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci ks108_writeb_data(par, chipindex, val); 28262306a36Sopenharmony_ci left++; 28362306a36Sopenharmony_ci if (bitmask == 0x80) { 28462306a36Sopenharmony_ci bitmask = 1; 28562306a36Sopenharmony_ci src++; 28662306a36Sopenharmony_ci rightshift=0; 28762306a36Sopenharmony_ci } else { 28862306a36Sopenharmony_ci bitmask <<= 1; 28962306a36Sopenharmony_ci rightshift++; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* 29562306a36Sopenharmony_ci * here we handle the entire vertical page of the update. we write across 29662306a36Sopenharmony_ci * lcd chips. update_page uses the upper/left values to decide which 29762306a36Sopenharmony_ci * chip to select for the right. upper is needed for setting the page 29862306a36Sopenharmony_ci * desired for the write. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic void arcfb_lcd_update_vert(struct arcfb_par *par, unsigned int top, 30162306a36Sopenharmony_ci unsigned int bottom, unsigned int left, unsigned int right) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci unsigned int distance, upper, lower; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci distance = (bottom - top) + 1; 30662306a36Sopenharmony_ci upper = top; 30762306a36Sopenharmony_ci lower = top + 7; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci while (distance > 0) { 31062306a36Sopenharmony_ci distance -= 8; 31162306a36Sopenharmony_ci arcfb_lcd_update_page(par, upper, left, right, 8); 31262306a36Sopenharmony_ci upper = lower + 1; 31362306a36Sopenharmony_ci lower = upper + 7; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* 31862306a36Sopenharmony_ci * here we handle horizontal blocks for the update. update_vert will 31962306a36Sopenharmony_ci * handle spaning multiple pages. we break out each horizontal 32062306a36Sopenharmony_ci * block in to individual blocks no taller than 64 pixels. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic void arcfb_lcd_update_horiz(struct arcfb_par *par, unsigned int left, 32362306a36Sopenharmony_ci unsigned int right, unsigned int top, unsigned int h) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci unsigned int distance, upper, lower; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci distance = h; 32862306a36Sopenharmony_ci upper = floor8(top); 32962306a36Sopenharmony_ci lower = min(upper + distance - 1, ceil64(upper)); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci while (distance > 0) { 33262306a36Sopenharmony_ci distance -= ((lower - upper) + 1 ); 33362306a36Sopenharmony_ci arcfb_lcd_update_vert(par, upper, lower, left, right); 33462306a36Sopenharmony_ci upper = lower + 1; 33562306a36Sopenharmony_ci lower = min(upper + distance - 1, ceil64(upper)); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* 34062306a36Sopenharmony_ci * here we start the process of splitting out the fb update into 34162306a36Sopenharmony_ci * individual blocks of pixels. we end up splitting into 64x64 blocks 34262306a36Sopenharmony_ci * and finally down to 64x8 pages. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_cistatic void arcfb_lcd_update(struct arcfb_par *par, unsigned int dx, 34562306a36Sopenharmony_ci unsigned int dy, unsigned int w, unsigned int h) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci unsigned int left, right, distance, y; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* align the request first */ 35062306a36Sopenharmony_ci y = floor8(dy); 35162306a36Sopenharmony_ci h += dy - y; 35262306a36Sopenharmony_ci h = iceil8(h); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci distance = w; 35562306a36Sopenharmony_ci left = dx; 35662306a36Sopenharmony_ci right = min(left + w - 1, ceil64(left)); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci while (distance > 0) { 35962306a36Sopenharmony_ci arcfb_lcd_update_horiz(par, left, right, y, h); 36062306a36Sopenharmony_ci distance -= ((right - left) + 1); 36162306a36Sopenharmony_ci left = right + 1; 36262306a36Sopenharmony_ci right = min(left + distance - 1, ceil64(left)); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void arcfb_fillrect(struct fb_info *info, 36762306a36Sopenharmony_ci const struct fb_fillrect *rect) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct arcfb_par *par = info->par; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci sys_fillrect(info, rect); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* update the physical lcd */ 37462306a36Sopenharmony_ci arcfb_lcd_update(par, rect->dx, rect->dy, rect->width, rect->height); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void arcfb_copyarea(struct fb_info *info, 37862306a36Sopenharmony_ci const struct fb_copyarea *area) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct arcfb_par *par = info->par; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci sys_copyarea(info, area); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* update the physical lcd */ 38562306a36Sopenharmony_ci arcfb_lcd_update(par, area->dx, area->dy, area->width, area->height); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void arcfb_imageblit(struct fb_info *info, const struct fb_image *image) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct arcfb_par *par = info->par; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci sys_imageblit(info, image); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* update the physical lcd */ 39562306a36Sopenharmony_ci arcfb_lcd_update(par, image->dx, image->dy, image->width, 39662306a36Sopenharmony_ci image->height); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int arcfb_ioctl(struct fb_info *info, 40062306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 40362306a36Sopenharmony_ci struct arcfb_par *par = info->par; 40462306a36Sopenharmony_ci unsigned long flags; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (cmd) { 40762306a36Sopenharmony_ci case FBIO_WAITEVENT: 40862306a36Sopenharmony_ci { 40962306a36Sopenharmony_ci DEFINE_WAIT(wait); 41062306a36Sopenharmony_ci /* illegal to wait on arc if no irq will occur */ 41162306a36Sopenharmony_ci if (!par->irq) 41262306a36Sopenharmony_ci return -EINVAL; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* wait until the Arc has generated an interrupt 41562306a36Sopenharmony_ci * which will wake us up */ 41662306a36Sopenharmony_ci spin_lock_irqsave(&par->lock, flags); 41762306a36Sopenharmony_ci prepare_to_wait(&arcfb_waitq, &wait, 41862306a36Sopenharmony_ci TASK_INTERRUPTIBLE); 41962306a36Sopenharmony_ci spin_unlock_irqrestore(&par->lock, flags); 42062306a36Sopenharmony_ci schedule(); 42162306a36Sopenharmony_ci finish_wait(&arcfb_waitq, &wait); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci fallthrough; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci case FBIO_GETCONTROL2: 42662306a36Sopenharmony_ci { 42762306a36Sopenharmony_ci unsigned char ctl2; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ctl2 = ks108_readb_ctl2(info->par); 43062306a36Sopenharmony_ci if (copy_to_user(argp, &ctl2, sizeof(ctl2))) 43162306a36Sopenharmony_ci return -EFAULT; 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci default: 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * this is the access path from userspace. they can seek and write to 44162306a36Sopenharmony_ci * the fb. it's inefficient for them to do anything less than 64*8 44262306a36Sopenharmony_ci * writes since we update the lcd in each write() anyway. 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic ssize_t arcfb_write(struct fb_info *info, const char __user *buf, 44562306a36Sopenharmony_ci size_t count, loff_t *ppos) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci /* modded from epson 1355 */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci unsigned long p; 45062306a36Sopenharmony_ci int err; 45162306a36Sopenharmony_ci unsigned int fbmemlength,x,y,w,h, bitppos, startpos, endpos, bitcount; 45262306a36Sopenharmony_ci struct arcfb_par *par; 45362306a36Sopenharmony_ci unsigned int xres; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!info->screen_buffer) 45662306a36Sopenharmony_ci return -ENODEV; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci p = *ppos; 45962306a36Sopenharmony_ci par = info->par; 46062306a36Sopenharmony_ci xres = info->var.xres; 46162306a36Sopenharmony_ci fbmemlength = (xres * info->var.yres)/8; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (p > fbmemlength) 46462306a36Sopenharmony_ci return -ENOSPC; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci err = 0; 46762306a36Sopenharmony_ci if ((count + p) > fbmemlength) { 46862306a36Sopenharmony_ci count = fbmemlength - p; 46962306a36Sopenharmony_ci err = -ENOSPC; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (count) { 47362306a36Sopenharmony_ci char *base_addr; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci base_addr = info->screen_buffer; 47662306a36Sopenharmony_ci count -= copy_from_user(base_addr + p, buf, count); 47762306a36Sopenharmony_ci *ppos += count; 47862306a36Sopenharmony_ci err = -EFAULT; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci bitppos = p*8; 48362306a36Sopenharmony_ci startpos = floorXres(bitppos, xres); 48462306a36Sopenharmony_ci endpos = ceilXres((bitppos + (count*8)), xres); 48562306a36Sopenharmony_ci bitcount = endpos - startpos; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci x = startpos % xres; 48862306a36Sopenharmony_ci y = startpos / xres; 48962306a36Sopenharmony_ci w = xres; 49062306a36Sopenharmony_ci h = bitcount / xres; 49162306a36Sopenharmony_ci arcfb_lcd_update(par, x, y, w, h); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (count) 49462306a36Sopenharmony_ci return count; 49562306a36Sopenharmony_ci return err; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic const struct fb_ops arcfb_ops = { 49962306a36Sopenharmony_ci .owner = THIS_MODULE, 50062306a36Sopenharmony_ci .fb_open = arcfb_open, 50162306a36Sopenharmony_ci .fb_read = fb_sys_read, 50262306a36Sopenharmony_ci .fb_write = arcfb_write, 50362306a36Sopenharmony_ci .fb_release = arcfb_release, 50462306a36Sopenharmony_ci .fb_pan_display = arcfb_pan_display, 50562306a36Sopenharmony_ci .fb_fillrect = arcfb_fillrect, 50662306a36Sopenharmony_ci .fb_copyarea = arcfb_copyarea, 50762306a36Sopenharmony_ci .fb_imageblit = arcfb_imageblit, 50862306a36Sopenharmony_ci .fb_ioctl = arcfb_ioctl, 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic int arcfb_probe(struct platform_device *dev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct fb_info *info; 51462306a36Sopenharmony_ci int retval = -ENOMEM; 51562306a36Sopenharmony_ci int videomemorysize; 51662306a36Sopenharmony_ci unsigned char *videomemory; 51762306a36Sopenharmony_ci struct arcfb_par *par; 51862306a36Sopenharmony_ci int i; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci videomemorysize = (((64*64)*num_cols)*num_rows)/8; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* We need a flat backing store for the Arc's 52362306a36Sopenharmony_ci less-flat actual paged framebuffer */ 52462306a36Sopenharmony_ci videomemory = vzalloc(videomemorysize); 52562306a36Sopenharmony_ci if (!videomemory) 52662306a36Sopenharmony_ci return retval; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev); 52962306a36Sopenharmony_ci if (!info) 53062306a36Sopenharmony_ci goto err_fb_alloc; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci info->screen_buffer = videomemory; 53362306a36Sopenharmony_ci info->fbops = &arcfb_ops; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci info->var = arcfb_var; 53662306a36Sopenharmony_ci info->fix = arcfb_fix; 53762306a36Sopenharmony_ci par = info->par; 53862306a36Sopenharmony_ci par->info = info; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!dio_addr || !cio_addr || !c2io_addr) { 54162306a36Sopenharmony_ci printk(KERN_WARNING "no IO addresses supplied\n"); 54262306a36Sopenharmony_ci goto err_addr; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci par->dio_addr = dio_addr; 54562306a36Sopenharmony_ci par->cio_addr = cio_addr; 54662306a36Sopenharmony_ci par->c2io_addr = c2io_addr; 54762306a36Sopenharmony_ci par->cslut[0] = 0x00; 54862306a36Sopenharmony_ci par->cslut[1] = 0x06; 54962306a36Sopenharmony_ci spin_lock_init(&par->lock); 55062306a36Sopenharmony_ci if (irq) { 55162306a36Sopenharmony_ci par->irq = irq; 55262306a36Sopenharmony_ci if (request_irq(par->irq, &arcfb_interrupt, IRQF_SHARED, 55362306a36Sopenharmony_ci "arcfb", info)) { 55462306a36Sopenharmony_ci printk(KERN_INFO 55562306a36Sopenharmony_ci "arcfb: Failed req IRQ %d\n", par->irq); 55662306a36Sopenharmony_ci retval = -EBUSY; 55762306a36Sopenharmony_ci goto err_addr; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci retval = register_framebuffer(info); 56162306a36Sopenharmony_ci if (retval < 0) 56262306a36Sopenharmony_ci goto err_register_fb; 56362306a36Sopenharmony_ci platform_set_drvdata(dev, info); 56462306a36Sopenharmony_ci fb_info(info, "Arc frame buffer device, using %dK of video memory\n", 56562306a36Sopenharmony_ci videomemorysize >> 10); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* this inits the lcd but doesn't clear dirty pixels */ 56862306a36Sopenharmony_ci for (i = 0; i < num_cols * num_rows; i++) { 56962306a36Sopenharmony_ci ks108_writeb_ctl(par, i, KS_DPY_OFF); 57062306a36Sopenharmony_ci ks108_set_start_line(par, i, 0); 57162306a36Sopenharmony_ci ks108_set_yaddr(par, i, 0); 57262306a36Sopenharmony_ci ks108_set_xaddr(par, i, 0); 57362306a36Sopenharmony_ci ks108_writeb_ctl(par, i, KS_DPY_ON); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* if we were told to splash the screen, we just clear it */ 57762306a36Sopenharmony_ci if (!nosplash) { 57862306a36Sopenharmony_ci for (i = 0; i < num_cols * num_rows; i++) { 57962306a36Sopenharmony_ci fb_info(info, "splashing lcd %d\n", i); 58062306a36Sopenharmony_ci ks108_set_start_line(par, i, 0); 58162306a36Sopenharmony_ci ks108_clear_lcd(par, i); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cierr_register_fb: 58862306a36Sopenharmony_ci free_irq(par->irq, info); 58962306a36Sopenharmony_cierr_addr: 59062306a36Sopenharmony_ci framebuffer_release(info); 59162306a36Sopenharmony_cierr_fb_alloc: 59262306a36Sopenharmony_ci vfree(videomemory); 59362306a36Sopenharmony_ci return retval; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic void arcfb_remove(struct platform_device *dev) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (info) { 60162306a36Sopenharmony_ci unregister_framebuffer(info); 60262306a36Sopenharmony_ci if (irq) 60362306a36Sopenharmony_ci free_irq(((struct arcfb_par *)(info->par))->irq, info); 60462306a36Sopenharmony_ci vfree(info->screen_buffer); 60562306a36Sopenharmony_ci framebuffer_release(info); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic struct platform_driver arcfb_driver = { 61062306a36Sopenharmony_ci .probe = arcfb_probe, 61162306a36Sopenharmony_ci .remove_new = arcfb_remove, 61262306a36Sopenharmony_ci .driver = { 61362306a36Sopenharmony_ci .name = "arcfb", 61462306a36Sopenharmony_ci }, 61562306a36Sopenharmony_ci}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic struct platform_device *arcfb_device; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int __init arcfb_init(void) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (!arcfb_enable) 62462306a36Sopenharmony_ci return -ENXIO; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci ret = platform_driver_register(&arcfb_driver); 62762306a36Sopenharmony_ci if (!ret) { 62862306a36Sopenharmony_ci arcfb_device = platform_device_alloc("arcfb", 0); 62962306a36Sopenharmony_ci if (arcfb_device) { 63062306a36Sopenharmony_ci ret = platform_device_add(arcfb_device); 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci ret = -ENOMEM; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci if (ret) { 63562306a36Sopenharmony_ci platform_device_put(arcfb_device); 63662306a36Sopenharmony_ci platform_driver_unregister(&arcfb_driver); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void __exit arcfb_exit(void) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci platform_device_unregister(arcfb_device); 64662306a36Sopenharmony_ci platform_driver_unregister(&arcfb_driver); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cimodule_param(num_cols, ulong, 0); 65062306a36Sopenharmony_ciMODULE_PARM_DESC(num_cols, "Num horiz panels, eg: 2 = 128 bit wide"); 65162306a36Sopenharmony_cimodule_param(num_rows, ulong, 0); 65262306a36Sopenharmony_ciMODULE_PARM_DESC(num_rows, "Num vert panels, eg: 1 = 64 bit high"); 65362306a36Sopenharmony_cimodule_param(nosplash, uint, 0); 65462306a36Sopenharmony_ciMODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); 65562306a36Sopenharmony_cimodule_param(arcfb_enable, uint, 0); 65662306a36Sopenharmony_ciMODULE_PARM_DESC(arcfb_enable, "Enable communication with Arc board"); 65762306a36Sopenharmony_cimodule_param_hw(dio_addr, ulong, ioport, 0); 65862306a36Sopenharmony_ciMODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); 65962306a36Sopenharmony_cimodule_param_hw(cio_addr, ulong, ioport, 0); 66062306a36Sopenharmony_ciMODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); 66162306a36Sopenharmony_cimodule_param_hw(c2io_addr, ulong, ioport, 0); 66262306a36Sopenharmony_ciMODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); 66362306a36Sopenharmony_cimodule_param(splashval, ulong, 0); 66462306a36Sopenharmony_ciMODULE_PARM_DESC(splashval, "Splash pattern: 0xFF is black, 0x00 is green"); 66562306a36Sopenharmony_cimodule_param(tuhold, ulong, 0); 66662306a36Sopenharmony_ciMODULE_PARM_DESC(tuhold, "Time to hold between strobing data to Arc board"); 66762306a36Sopenharmony_cimodule_param_hw(irq, uint, irq, 0); 66862306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ for the Arc board"); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cimodule_init(arcfb_init); 67162306a36Sopenharmony_cimodule_exit(arcfb_exit); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for Arc monochrome LCD board"); 67462306a36Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 67562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 67662306a36Sopenharmony_ci 677