linux/drivers/video/arcfb.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/arcfb.c -- FB driver for Arc monochrome LCD board
   3 *
   4 * Copyright (C) 2005, Jaya Kumar <jayalk@intworks.biz>
   5 * http://www.intworks.biz/arclcd
   6 *
   7 * This file is subject to the terms and conditions of the GNU General Public
   8 * License. See the file COPYING in the main directory of this archive for
   9 * more details.
  10 *
  11 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
  12 *
  13 * This driver was written to be used with the Arc LCD board. Arc uses a
  14 * set of KS108 chips that control individual 64x64 LCD matrices. The board
  15 * can be paneled in a variety of setups such as 2x1=128x64, 4x4=256x256 and
  16 * so on. The interface between the board and the host is TTL based GPIO. The
  17 * GPIO requirements are 8 writable data lines and 4+n lines for control. On a
  18 * GPIO-less system, the board can be tested by connecting the respective sigs
  19 * up to a parallel port connector. The driver requires the IO addresses for
  20 * data and control GPIO at load time. It is unable to probe for the
  21 * existence of the LCD so it must be told at load time whether it should
  22 * be enabled or not.
  23 *
  24 * Todo:
  25 * - testing with 4x4
  26 * - testing with interrupt hw
  27 *
  28 * General notes:
  29 * - User must set tuhold. It's in microseconds. According to the 108 spec,
  30 *   the hold time is supposed to be at least 1 microsecond.
  31 * - User must set num_cols=x num_rows=y, eg: x=2 means 128
  32 * - User must set arcfb_enable=1 to enable it
  33 * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR
  34 *
  35 */
  36
  37#include <linux/module.h>
  38#include <linux/kernel.h>
  39#include <linux/errno.h>
  40#include <linux/string.h>
  41#include <linux/mm.h>
  42#include <linux/vmalloc.h>
  43#include <linux/delay.h>
  44#include <linux/interrupt.h>
  45#include <linux/fb.h>
  46#include <linux/init.h>
  47#include <linux/arcfb.h>
  48#include <linux/platform_device.h>
  49
  50#include <linux/uaccess.h>
  51
  52#define floor8(a) (a&(~0x07))
  53#define floorXres(a,xres) (a&(~(xres - 1)))
  54#define iceil8(a) (((int)((a+7)/8))*8)
  55#define ceil64(a) (a|0x3F)
  56#define ceilXres(a,xres) (a|(xres - 1))
  57
  58/* ks108 chipset specific defines and code */
  59
  60#define KS_SET_DPY_START_LINE   0xC0
  61#define KS_SET_PAGE_NUM         0xB8
  62#define KS_SET_X                0x40
  63#define KS_CEHI                 0x01
  64#define KS_CELO                 0x00
  65#define KS_SEL_CMD              0x08
  66#define KS_SEL_DATA             0x00
  67#define KS_DPY_ON               0x3F
  68#define KS_DPY_OFF              0x3E
  69#define KS_INTACK               0x40
  70#define KS_CLRINT               0x02
  71
  72struct arcfb_par {
  73        unsigned long dio_addr;
  74        unsigned long cio_addr;
  75        unsigned long c2io_addr;
  76        atomic_t ref_count;
  77        unsigned char cslut[9];
  78        struct fb_info *info;
  79        unsigned int irq;
  80        spinlock_t lock;
  81};
  82
  83static struct fb_fix_screeninfo arcfb_fix __devinitdata = {
  84        .id =           "arcfb",
  85        .type =         FB_TYPE_PACKED_PIXELS,
  86        .visual =       FB_VISUAL_MONO01,
  87        .xpanstep =     0,
  88        .ypanstep =     1,
  89        .ywrapstep =    0,
  90        .accel =        FB_ACCEL_NONE,
  91};
  92
  93static struct fb_var_screeninfo arcfb_var __devinitdata = {
  94        .xres           = 128,
  95        .yres           = 64,
  96        .xres_virtual   = 128,
  97        .yres_virtual   = 64,
  98        .bits_per_pixel = 1,
  99        .nonstd         = 1,
 100};
 101
 102static unsigned long num_cols;
 103static unsigned long num_rows;
 104static unsigned long dio_addr;
 105static unsigned long cio_addr;
 106static unsigned long c2io_addr;
 107static unsigned long splashval;
 108static unsigned long tuhold;
 109static unsigned int nosplash;
 110static unsigned int arcfb_enable;
 111static unsigned int irq;
 112
 113static DECLARE_WAIT_QUEUE_HEAD(arcfb_waitq);
 114
 115static void ks108_writeb_ctl(struct arcfb_par *par,
 116                                unsigned int chipindex, unsigned char value)
 117{
 118        unsigned char chipselval = par->cslut[chipindex];
 119
 120        outb(chipselval|KS_CEHI|KS_SEL_CMD, par->cio_addr);
 121        outb(value, par->dio_addr);
 122        udelay(tuhold);
 123        outb(chipselval|KS_CELO|KS_SEL_CMD, par->cio_addr);
 124}
 125
 126static void ks108_writeb_mainctl(struct arcfb_par *par, unsigned char value)
 127{
 128
 129        outb(value, par->cio_addr);
 130        udelay(tuhold);
 131}
 132
 133static unsigned char ks108_readb_ctl2(struct arcfb_par *par)
 134{
 135        return inb(par->c2io_addr);
 136}
 137
 138static void ks108_writeb_data(struct arcfb_par *par,
 139                                unsigned int chipindex, unsigned char value)
 140{
 141        unsigned char chipselval = par->cslut[chipindex];
 142
 143        outb(chipselval|KS_CEHI|KS_SEL_DATA, par->cio_addr);
 144        outb(value, par->dio_addr);
 145        udelay(tuhold);
 146        outb(chipselval|KS_CELO|KS_SEL_DATA, par->cio_addr);
 147}
 148
 149static void ks108_set_start_line(struct arcfb_par *par,
 150                                unsigned int chipindex, unsigned char y)
 151{
 152        ks108_writeb_ctl(par, chipindex, KS_SET_DPY_START_LINE|y);
 153}
 154
 155static void ks108_set_yaddr(struct arcfb_par *par,
 156                                unsigned int chipindex, unsigned char y)
 157{
 158        ks108_writeb_ctl(par, chipindex, KS_SET_PAGE_NUM|y);
 159}
 160
 161static void ks108_set_xaddr(struct arcfb_par *par,
 162                                unsigned int chipindex, unsigned char x)
 163{
 164        ks108_writeb_ctl(par, chipindex, KS_SET_X|x);
 165}
 166
 167static void ks108_clear_lcd(struct arcfb_par *par, unsigned int chipindex)
 168{
 169        int i,j;
 170
 171        for (i = 0; i <= 8; i++) {
 172                ks108_set_yaddr(par, chipindex, i);
 173                ks108_set_xaddr(par, chipindex, 0);
 174                for (j = 0; j < 64; j++) {
 175                        ks108_writeb_data(par, chipindex,
 176                                (unsigned char) splashval);
 177                }
 178        }
 179}
 180
 181/* main arcfb functions */
 182
 183static int arcfb_open(struct fb_info *info, int user)
 184{
 185        struct arcfb_par *par = info->par;
 186
 187        atomic_inc(&par->ref_count);
 188        return 0;
 189}
 190
 191static int arcfb_release(struct fb_info *info, int user)
 192{
 193        struct arcfb_par *par = info->par;
 194        int count = atomic_read(&par->ref_count);
 195
 196        if (!count)
 197                return -EINVAL;
 198        atomic_dec(&par->ref_count);
 199        return 0;
 200}
 201
 202static int arcfb_pan_display(struct fb_var_screeninfo *var,
 203                                struct fb_info *info)
 204{
 205        int i;
 206        struct arcfb_par *par = info->par;
 207
 208        if ((var->vmode & FB_VMODE_YWRAP) && (var->yoffset < 64)
 209                && (info->var.yres <= 64)) {
 210                for (i = 0; i < num_cols; i++) {
 211                        ks108_set_start_line(par, i, var->yoffset);
 212                }
 213                info->var.yoffset = var->yoffset;
 214                return 0;
 215        }
 216
 217        return -EINVAL;
 218}
 219
 220static irqreturn_t arcfb_interrupt(int vec, void *dev_instance)
 221{
 222        struct fb_info *info = dev_instance;
 223        unsigned char ctl2status;
 224        struct arcfb_par *par = info->par;
 225
 226        ctl2status = ks108_readb_ctl2(par);
 227
 228        if (!(ctl2status & KS_INTACK)) /* not arc generated interrupt */
 229                return IRQ_NONE;
 230
 231        ks108_writeb_mainctl(par, KS_CLRINT);
 232
 233        spin_lock(&par->lock);
 234        if (waitqueue_active(&arcfb_waitq)) {
 235                wake_up(&arcfb_waitq);
 236        }
 237        spin_unlock(&par->lock);
 238
 239        return IRQ_HANDLED;
 240}
 241
 242/*
 243 * here we handle a specific page on the lcd. the complexity comes from
 244 * the fact that the fb is laidout in 8xX vertical columns. we extract
 245 * each write of 8 vertical pixels. then we shift out as we move along
 246 * X. That's what rightshift does. bitmask selects the desired input bit.
 247 */
 248static void arcfb_lcd_update_page(struct arcfb_par *par, unsigned int upper,
 249                unsigned int left, unsigned int right, unsigned int distance)
 250{
 251        unsigned char *src;
 252        unsigned int xindex, yindex, chipindex, linesize;
 253        int i;
 254        unsigned char val;
 255        unsigned char bitmask, rightshift;
 256
 257        xindex = left >> 6;
 258        yindex = upper >> 6;
 259        chipindex = (xindex + (yindex*num_cols));
 260
 261        ks108_set_yaddr(par, chipindex, upper/8);
 262
 263        linesize = par->info->var.xres/8;
 264        src = (unsigned char __force *) par->info->screen_base + (left/8) +
 265                (upper * linesize);
 266        ks108_set_xaddr(par, chipindex, left);
 267
 268        bitmask=1;
 269        rightshift=0;
 270        while (left <= right) {
 271                val = 0;
 272                for (i = 0; i < 8; i++) {
 273                        if ( i > rightshift) {
 274                                val |= (*(src + (i*linesize)) & bitmask)
 275                                                << (i - rightshift);
 276                        } else {
 277                                val |= (*(src + (i*linesize)) & bitmask)
 278                                                 >> (rightshift - i);
 279                        }
 280                }
 281                ks108_writeb_data(par, chipindex, val);
 282                left++;
 283                if (bitmask == 0x80) {
 284                        bitmask = 1;
 285                        src++;
 286                        rightshift=0;
 287                } else {
 288                        bitmask <<= 1;
 289                        rightshift++;
 290                }
 291        }
 292}
 293
 294/*
 295 * here we handle the entire vertical page of the update. we write across
 296 * lcd chips. update_page uses the upper/left values to decide which
 297 * chip to select for the right. upper is needed for setting the page
 298 * desired for the write.
 299 */
 300static void arcfb_lcd_update_vert(struct arcfb_par *par, unsigned int top,
 301                unsigned int bottom, unsigned int left, unsigned int right)
 302{
 303        unsigned int distance, upper, lower;
 304
 305        distance = (bottom - top) + 1;
 306        upper = top;
 307        lower = top + 7;
 308
 309        while (distance > 0) {
 310                distance -= 8;
 311                arcfb_lcd_update_page(par, upper, left, right, 8);
 312                upper = lower + 1;
 313                lower = upper + 7;
 314        }
 315}
 316
 317/*
 318 * here we handle horizontal blocks for the update. update_vert will
 319 * handle spaning multiple pages. we break out each horizontal
 320 * block in to individual blocks no taller than 64 pixels.
 321 */
 322static void arcfb_lcd_update_horiz(struct arcfb_par *par, unsigned int left,
 323                        unsigned int right, unsigned int top, unsigned int h)
 324{
 325        unsigned int distance, upper, lower;
 326
 327        distance = h;
 328        upper = floor8(top);
 329        lower = min(upper + distance - 1, ceil64(upper));
 330
 331        while (distance > 0) {
 332                distance -= ((lower - upper) + 1 );
 333                arcfb_lcd_update_vert(par, upper, lower, left, right);
 334                upper = lower + 1;
 335                lower = min(upper + distance - 1, ceil64(upper));
 336        }
 337}
 338
 339/*
 340 * here we start the process of spliting out the fb update into
 341 * individual blocks of pixels. we end up spliting into 64x64 blocks
 342 * and finally down to 64x8 pages.
 343 */
 344static void arcfb_lcd_update(struct arcfb_par *par, unsigned int dx,
 345                        unsigned int dy, unsigned int w, unsigned int h)
 346{
 347        unsigned int left, right, distance, y;
 348
 349        /* align the request first */
 350        y = floor8(dy);
 351        h += dy - y;
 352        h = iceil8(h);
 353
 354        distance = w;
 355        left = dx;
 356        right = min(left + w - 1, ceil64(left));
 357
 358        while (distance > 0) {
 359                arcfb_lcd_update_horiz(par, left, right, y, h);
 360                distance -= ((right - left) + 1);
 361                left = right + 1;
 362                right = min(left + distance - 1, ceil64(left));
 363        }
 364}
 365
 366static void arcfb_fillrect(struct fb_info *info,
 367                           const struct fb_fillrect *rect)
 368{
 369        struct arcfb_par *par = info->par;
 370
 371        sys_fillrect(info, rect);
 372
 373        /* update the physical lcd */
 374        arcfb_lcd_update(par, rect->dx, rect->dy, rect->width, rect->height);
 375}
 376
 377static void arcfb_copyarea(struct fb_info *info,
 378                           const struct fb_copyarea *area)
 379{
 380        struct arcfb_par *par = info->par;
 381
 382        sys_copyarea(info, area);
 383
 384        /* update the physical lcd */
 385        arcfb_lcd_update(par, area->dx, area->dy, area->width, area->height);
 386}
 387
 388static void arcfb_imageblit(struct fb_info *info, const struct fb_image *image)
 389{
 390        struct arcfb_par *par = info->par;
 391
 392        sys_imageblit(info, image);
 393
 394        /* update the physical lcd */
 395        arcfb_lcd_update(par, image->dx, image->dy, image->width,
 396                                image->height);
 397}
 398
 399static int arcfb_ioctl(struct fb_info *info,
 400                          unsigned int cmd, unsigned long arg)
 401{
 402        void __user *argp = (void __user *)arg;
 403        struct arcfb_par *par = info->par;
 404        unsigned long flags;
 405
 406        switch (cmd) {
 407                case FBIO_WAITEVENT:
 408                {
 409                        DEFINE_WAIT(wait);
 410                        /* illegal to wait on arc if no irq will occur */
 411                        if (!par->irq)
 412                                return -EINVAL;
 413
 414                        /* wait until the Arc has generated an interrupt
 415                         * which will wake us up */
 416                        spin_lock_irqsave(&par->lock, flags);
 417                        prepare_to_wait(&arcfb_waitq, &wait,
 418                                        TASK_INTERRUPTIBLE);
 419                        spin_unlock_irqrestore(&par->lock, flags);
 420                        schedule();
 421                        finish_wait(&arcfb_waitq, &wait);
 422                }
 423                case FBIO_GETCONTROL2:
 424                {
 425                        unsigned char ctl2;
 426
 427                        ctl2 = ks108_readb_ctl2(info->par);
 428                        if (copy_to_user(argp, &ctl2, sizeof(ctl2)))
 429                                return -EFAULT;
 430                        return 0;
 431                }
 432                default:
 433                        return -EINVAL;
 434        }
 435}
 436
 437/*
 438 * this is the access path from userspace. they can seek and write to
 439 * the fb. it's inefficient for them to do anything less than 64*8
 440 * writes since we update the lcd in each write() anyway.
 441 */
 442static ssize_t arcfb_write(struct fb_info *info, const char __user *buf,
 443                           size_t count, loff_t *ppos)
 444{
 445        /* modded from epson 1355 */
 446
 447        unsigned long p;
 448        int err=-EINVAL;
 449        unsigned int fbmemlength,x,y,w,h, bitppos, startpos, endpos, bitcount;
 450        struct arcfb_par *par;
 451        unsigned int xres;
 452
 453        p = *ppos;
 454        par = info->par;
 455        xres = info->var.xres;
 456        fbmemlength = (xres * info->var.yres)/8;
 457
 458        if (p > fbmemlength)
 459                return -ENOSPC;
 460
 461        err = 0;
 462        if ((count + p) > fbmemlength) {
 463                count = fbmemlength - p;
 464                err = -ENOSPC;
 465        }
 466
 467        if (count) {
 468                char *base_addr;
 469
 470                base_addr = (char __force *)info->screen_base;
 471                count -= copy_from_user(base_addr + p, buf, count);
 472                *ppos += count;
 473                err = -EFAULT;
 474        }
 475
 476
 477        bitppos = p*8;
 478        startpos = floorXres(bitppos, xres);
 479        endpos = ceilXres((bitppos + (count*8)), xres);
 480        bitcount = endpos - startpos;
 481
 482        x = startpos % xres;
 483        y = startpos / xres;
 484        w = xres;
 485        h = bitcount / xres;
 486        arcfb_lcd_update(par, x, y, w, h);
 487
 488        if (count)
 489                return count;
 490        return err;
 491}
 492
 493static struct fb_ops arcfb_ops = {
 494        .owner          = THIS_MODULE,
 495        .fb_open        = arcfb_open,
 496        .fb_read        = fb_sys_read,
 497        .fb_write       = arcfb_write,
 498        .fb_release     = arcfb_release,
 499        .fb_pan_display = arcfb_pan_display,
 500        .fb_fillrect    = arcfb_fillrect,
 501        .fb_copyarea    = arcfb_copyarea,
 502        .fb_imageblit   = arcfb_imageblit,
 503        .fb_ioctl       = arcfb_ioctl,
 504};
 505
 506static int __devinit arcfb_probe(struct platform_device *dev)
 507{
 508        struct fb_info *info;
 509        int retval = -ENOMEM;
 510        int videomemorysize;
 511        unsigned char *videomemory;
 512        struct arcfb_par *par;
 513        int i;
 514
 515        videomemorysize = (((64*64)*num_cols)*num_rows)/8;
 516
 517        /* We need a flat backing store for the Arc's
 518           less-flat actual paged framebuffer */
 519        if (!(videomemory = vmalloc(videomemorysize)))
 520                return retval;
 521
 522        memset(videomemory, 0, videomemorysize);
 523
 524        info = framebuffer_alloc(sizeof(struct arcfb_par), &dev->dev);
 525        if (!info)
 526                goto err;
 527
 528        info->screen_base = (char __iomem *)videomemory;
 529        info->fbops = &arcfb_ops;
 530
 531        info->var = arcfb_var;
 532        info->fix = arcfb_fix;
 533        par = info->par;
 534        par->info = info;
 535
 536        if (!dio_addr || !cio_addr || !c2io_addr) {
 537                printk(KERN_WARNING "no IO addresses supplied\n");
 538                goto err1;
 539        }
 540        par->dio_addr = dio_addr;
 541        par->cio_addr = cio_addr;
 542        par->c2io_addr = c2io_addr;
 543        par->cslut[0] = 0x00;
 544        par->cslut[1] = 0x06;
 545        info->flags = FBINFO_FLAG_DEFAULT;
 546        spin_lock_init(&par->lock);
 547        retval = register_framebuffer(info);
 548        if (retval < 0)
 549                goto err1;
 550        platform_set_drvdata(dev, info);
 551        if (irq) {
 552                par->irq = irq;
 553                if (request_irq(par->irq, &arcfb_interrupt, IRQF_SHARED,
 554                                "arcfb", info)) {
 555                        printk(KERN_INFO
 556                                "arcfb: Failed req IRQ %d\n", par->irq);
 557                        goto err1;
 558                }
 559        }
 560        printk(KERN_INFO
 561               "fb%d: Arc frame buffer device, using %dK of video memory\n",
 562               info->node, videomemorysize >> 10);
 563
 564        /* this inits the lcd but doesn't clear dirty pixels */
 565        for (i = 0; i < num_cols * num_rows; i++) {
 566                ks108_writeb_ctl(par, i, KS_DPY_OFF);
 567                ks108_set_start_line(par, i, 0);
 568                ks108_set_yaddr(par, i, 0);
 569                ks108_set_xaddr(par, i, 0);
 570                ks108_writeb_ctl(par, i, KS_DPY_ON);
 571        }
 572
 573        /* if we were told to splash the screen, we just clear it */
 574        if (!nosplash) {
 575                for (i = 0; i < num_cols * num_rows; i++) {
 576                        printk(KERN_INFO "fb%d: splashing lcd %d\n",
 577                                info->node, i);
 578                        ks108_set_start_line(par, i, 0);
 579                        ks108_clear_lcd(par, i);
 580                }
 581        }
 582
 583        return 0;
 584err1:
 585        framebuffer_release(info);
 586err:
 587        vfree(videomemory);
 588        return retval;
 589}
 590
 591static int __devexit arcfb_remove(struct platform_device *dev)
 592{
 593        struct fb_info *info = platform_get_drvdata(dev);
 594
 595        if (info) {
 596                unregister_framebuffer(info);
 597                vfree((void __force *)info->screen_base);
 598                framebuffer_release(info);
 599        }
 600        return 0;
 601}
 602
 603static struct platform_driver arcfb_driver = {
 604        .probe  = arcfb_probe,
 605        .remove = __devexit_p(arcfb_remove),
 606        .driver = {
 607                .name   = "arcfb",
 608        },
 609};
 610
 611static struct platform_device *arcfb_device;
 612
 613static int __init arcfb_init(void)
 614{
 615        int ret;
 616
 617        if (!arcfb_enable)
 618                return -ENXIO;
 619
 620        ret = platform_driver_register(&arcfb_driver);
 621        if (!ret) {
 622                arcfb_device = platform_device_alloc("arcfb", 0);
 623                if (arcfb_device) {
 624                        ret = platform_device_add(arcfb_device);
 625                } else {
 626                        ret = -ENOMEM;
 627                }
 628                if (ret) {
 629                        platform_device_put(arcfb_device);
 630                        platform_driver_unregister(&arcfb_driver);
 631                }
 632        }
 633        return ret;
 634
 635}
 636
 637static void __exit arcfb_exit(void)
 638{
 639        platform_device_unregister(arcfb_device);
 640        platform_driver_unregister(&arcfb_driver);
 641}
 642
 643module_param(num_cols, ulong, 0);
 644MODULE_PARM_DESC(num_cols, "Num horiz panels, eg: 2 = 128 bit wide");
 645module_param(num_rows, ulong, 0);
 646MODULE_PARM_DESC(num_rows, "Num vert panels, eg: 1 = 64 bit high");
 647module_param(nosplash, uint, 0);
 648MODULE_PARM_DESC(nosplash, "Disable doing the splash screen");
 649module_param(arcfb_enable, uint, 0);
 650MODULE_PARM_DESC(arcfb_enable, "Enable communication with Arc board");
 651module_param(dio_addr, ulong, 0);
 652MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480");
 653module_param(cio_addr, ulong, 0);
 654MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400");
 655module_param(c2io_addr, ulong, 0);
 656MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408");
 657module_param(splashval, ulong, 0);
 658MODULE_PARM_DESC(splashval, "Splash pattern: 0xFF is black, 0x00 is green");
 659module_param(tuhold, ulong, 0);
 660MODULE_PARM_DESC(tuhold, "Time to hold between strobing data to Arc board");
 661module_param(irq, uint, 0);
 662MODULE_PARM_DESC(irq, "IRQ for the Arc board");
 663
 664module_init(arcfb_init);
 665module_exit(arcfb_exit);
 666
 667MODULE_DESCRIPTION("fbdev driver for Arc monochrome LCD board");
 668MODULE_AUTHOR("Jaya Kumar");
 669MODULE_LICENSE("GPL");
 670
 671
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.