root/drivers/video/fbdev/cobalt_lcdfb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lcd_write_control
  2. lcd_read_control
  3. lcd_write_data
  4. lcd_read_data
  5. lcd_busy_wait
  6. lcd_clear
  7. cobalt_lcdfb_read
  8. cobalt_lcdfb_write
  9. cobalt_lcdfb_blank
  10. cobalt_lcdfb_cursor
  11. cobalt_lcdfb_probe
  12. cobalt_lcdfb_remove

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *  Cobalt/SEAD3 LCD frame buffer driver.
   4  *
   5  *  Copyright (C) 2008  Yoichi Yuasa <yuasa@linux-mips.org>
   6  *  Copyright (C) 2012  MIPS Technologies, Inc.
   7  */
   8 #include <linux/delay.h>
   9 #include <linux/fb.h>
  10 #include <linux/init.h>
  11 #include <linux/io.h>
  12 #include <linux/ioport.h>
  13 #include <linux/uaccess.h>
  14 #include <linux/platform_device.h>
  15 #include <linux/module.h>
  16 #include <linux/sched/signal.h>
  17 
  18 /*
  19  * Cursor position address
  20  * \X  0    1    2  ...  14   15
  21  * Y+----+----+----+---+----+----+
  22  * 0|0x00|0x01|0x02|...|0x0e|0x0f|
  23  *  +----+----+----+---+----+----+
  24  * 1|0x40|0x41|0x42|...|0x4e|0x4f|
  25  *  +----+----+----+---+----+----+
  26  */
  27 #define LCD_DATA_REG_OFFSET     0x10
  28 #define LCD_XRES_MAX            16
  29 #define LCD_YRES_MAX            2
  30 #define LCD_CHARS_MAX           32
  31 
  32 #define LCD_CLEAR               0x01
  33 #define LCD_CURSOR_MOVE_HOME    0x02
  34 #define LCD_RESET               0x06
  35 #define LCD_OFF                 0x08
  36 #define LCD_CURSOR_OFF          0x0c
  37 #define LCD_CURSOR_BLINK_OFF    0x0e
  38 #define LCD_CURSOR_ON           0x0f
  39 #define LCD_ON                  LCD_CURSOR_ON
  40 #define LCD_CURSOR_MOVE_LEFT    0x10
  41 #define LCD_CURSOR_MOVE_RIGHT   0x14
  42 #define LCD_DISPLAY_LEFT        0x18
  43 #define LCD_DISPLAY_RIGHT       0x1c
  44 #define LCD_PRERESET            0x3f    /* execute 4 times continuously */
  45 #define LCD_BUSY                0x80
  46 
  47 #define LCD_GRAPHIC_MODE        0x40
  48 #define LCD_TEXT_MODE           0x80
  49 #define LCD_CUR_POS_MASK        0x7f
  50 
  51 #define LCD_CUR_POS(x)          ((x) & LCD_CUR_POS_MASK)
  52 #define LCD_TEXT_POS(x)         ((x) | LCD_TEXT_MODE)
  53 
  54 static inline void lcd_write_control(struct fb_info *info, u8 control)
  55 {
  56         writel((u32)control << 24, info->screen_base);
  57 }
  58 
  59 static inline u8 lcd_read_control(struct fb_info *info)
  60 {
  61         return readl(info->screen_base) >> 24;
  62 }
  63 
  64 static inline void lcd_write_data(struct fb_info *info, u8 data)
  65 {
  66         writel((u32)data << 24, info->screen_base + LCD_DATA_REG_OFFSET);
  67 }
  68 
  69 static inline u8 lcd_read_data(struct fb_info *info)
  70 {
  71         return readl(info->screen_base + LCD_DATA_REG_OFFSET) >> 24;
  72 }
  73 
  74 static int lcd_busy_wait(struct fb_info *info)
  75 {
  76         u8 val = 0;
  77         int timeout = 10, retval = 0;
  78 
  79         do {
  80                 val = lcd_read_control(info);
  81                 val &= LCD_BUSY;
  82                 if (val != LCD_BUSY)
  83                         break;
  84 
  85                 if (msleep_interruptible(1))
  86                         return -EINTR;
  87 
  88                 timeout--;
  89         } while (timeout);
  90 
  91         if (val == LCD_BUSY)
  92                 retval = -EBUSY;
  93 
  94         return retval;
  95 }
  96 
  97 static void lcd_clear(struct fb_info *info)
  98 {
  99         int i;
 100 
 101         for (i = 0; i < 4; i++) {
 102                 udelay(150);
 103 
 104                 lcd_write_control(info, LCD_PRERESET);
 105         }
 106 
 107         udelay(150);
 108 
 109         lcd_write_control(info, LCD_CLEAR);
 110 
 111         udelay(150);
 112 
 113         lcd_write_control(info, LCD_RESET);
 114 }
 115 
 116 static const struct fb_fix_screeninfo cobalt_lcdfb_fix = {
 117         .id             = "cobalt-lcd",
 118         .type           = FB_TYPE_TEXT,
 119         .type_aux       = FB_AUX_TEXT_MDA,
 120         .visual         = FB_VISUAL_MONO01,
 121         .line_length    = LCD_XRES_MAX,
 122         .accel          = FB_ACCEL_NONE,
 123 };
 124 
 125 static ssize_t cobalt_lcdfb_read(struct fb_info *info, char __user *buf,
 126                                  size_t count, loff_t *ppos)
 127 {
 128         char src[LCD_CHARS_MAX];
 129         unsigned long pos;
 130         int len, retval = 0;
 131 
 132         pos = *ppos;
 133         if (pos >= LCD_CHARS_MAX || count == 0)
 134                 return 0;
 135 
 136         if (count > LCD_CHARS_MAX)
 137                 count = LCD_CHARS_MAX;
 138 
 139         if (pos + count > LCD_CHARS_MAX)
 140                 count = LCD_CHARS_MAX - pos;
 141 
 142         for (len = 0; len < count; len++) {
 143                 retval = lcd_busy_wait(info);
 144                 if (retval < 0)
 145                         break;
 146 
 147                 lcd_write_control(info, LCD_TEXT_POS(pos));
 148 
 149                 retval = lcd_busy_wait(info);
 150                 if (retval < 0)
 151                         break;
 152 
 153                 src[len] = lcd_read_data(info);
 154                 if (pos == 0x0f)
 155                         pos = 0x40;
 156                 else
 157                         pos++;
 158         }
 159 
 160         if (retval < 0 && signal_pending(current))
 161                 return -ERESTARTSYS;
 162 
 163         if (copy_to_user(buf, src, len))
 164                 return -EFAULT;
 165 
 166         *ppos += len;
 167 
 168         return len;
 169 }
 170 
 171 static ssize_t cobalt_lcdfb_write(struct fb_info *info, const char __user *buf,
 172                                   size_t count, loff_t *ppos)
 173 {
 174         char dst[LCD_CHARS_MAX];
 175         unsigned long pos;
 176         int len, retval = 0;
 177 
 178         pos = *ppos;
 179         if (pos >= LCD_CHARS_MAX || count == 0)
 180                 return 0;
 181 
 182         if (count > LCD_CHARS_MAX)
 183                 count = LCD_CHARS_MAX;
 184 
 185         if (pos + count > LCD_CHARS_MAX)
 186                 count = LCD_CHARS_MAX - pos;
 187 
 188         if (copy_from_user(dst, buf, count))
 189                 return -EFAULT;
 190 
 191         for (len = 0; len < count; len++) {
 192                 retval = lcd_busy_wait(info);
 193                 if (retval < 0)
 194                         break;
 195 
 196                 lcd_write_control(info, LCD_TEXT_POS(pos));
 197 
 198                 retval = lcd_busy_wait(info);
 199                 if (retval < 0)
 200                         break;
 201 
 202                 lcd_write_data(info, dst[len]);
 203                 if (pos == 0x0f)
 204                         pos = 0x40;
 205                 else
 206                         pos++;
 207         }
 208 
 209         if (retval < 0 && signal_pending(current))
 210                 return -ERESTARTSYS;
 211 
 212         *ppos += len;
 213 
 214         return len;
 215 }
 216 
 217 static int cobalt_lcdfb_blank(int blank_mode, struct fb_info *info)
 218 {
 219         int retval;
 220 
 221         retval = lcd_busy_wait(info);
 222         if (retval < 0)
 223                 return retval;
 224 
 225         switch (blank_mode) {
 226         case FB_BLANK_UNBLANK:
 227                 lcd_write_control(info, LCD_ON);
 228                 break;
 229         default:
 230                 lcd_write_control(info, LCD_OFF);
 231                 break;
 232         }
 233 
 234         return 0;
 235 }
 236 
 237 static int cobalt_lcdfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
 238 {
 239         u32 x, y;
 240         int retval;
 241 
 242         switch (cursor->set) {
 243         case FB_CUR_SETPOS:
 244                 x = cursor->image.dx;
 245                 y = cursor->image.dy;
 246                 if (x >= LCD_XRES_MAX || y >= LCD_YRES_MAX)
 247                         return -EINVAL;
 248 
 249                 retval = lcd_busy_wait(info);
 250                 if (retval < 0)
 251                         return retval;
 252 
 253                 lcd_write_control(info,
 254                                   LCD_TEXT_POS(info->fix.line_length * y + x));
 255                 break;
 256         default:
 257                 return -EINVAL;
 258         }
 259 
 260         retval = lcd_busy_wait(info);
 261         if (retval < 0)
 262                 return retval;
 263 
 264         if (cursor->enable)
 265                 lcd_write_control(info, LCD_CURSOR_ON);
 266         else
 267                 lcd_write_control(info, LCD_CURSOR_OFF);
 268 
 269         return 0;
 270 }
 271 
 272 static struct fb_ops cobalt_lcd_fbops = {
 273         .owner          = THIS_MODULE,
 274         .fb_read        = cobalt_lcdfb_read,
 275         .fb_write       = cobalt_lcdfb_write,
 276         .fb_blank       = cobalt_lcdfb_blank,
 277         .fb_cursor      = cobalt_lcdfb_cursor,
 278 };
 279 
 280 static int cobalt_lcdfb_probe(struct platform_device *dev)
 281 {
 282         struct fb_info *info;
 283         struct resource *res;
 284         int retval;
 285 
 286         info = framebuffer_alloc(0, &dev->dev);
 287         if (!info)
 288                 return -ENOMEM;
 289 
 290         res = platform_get_resource(dev, IORESOURCE_MEM, 0);
 291         if (!res) {
 292                 framebuffer_release(info);
 293                 return -EBUSY;
 294         }
 295 
 296         info->screen_size = resource_size(res);
 297         info->screen_base = devm_ioremap(&dev->dev, res->start,
 298                                          info->screen_size);
 299         if (!info->screen_base) {
 300                 framebuffer_release(info);
 301                 return -ENOMEM;
 302         }
 303 
 304         info->fbops = &cobalt_lcd_fbops;
 305         info->fix = cobalt_lcdfb_fix;
 306         info->fix.smem_start = res->start;
 307         info->fix.smem_len = info->screen_size;
 308         info->pseudo_palette = NULL;
 309         info->par = NULL;
 310         info->flags = FBINFO_DEFAULT;
 311 
 312         retval = register_framebuffer(info);
 313         if (retval < 0) {
 314                 framebuffer_release(info);
 315                 return retval;
 316         }
 317 
 318         platform_set_drvdata(dev, info);
 319 
 320         lcd_clear(info);
 321 
 322         fb_info(info, "Cobalt server LCD frame buffer device\n");
 323 
 324         return 0;
 325 }
 326 
 327 static int cobalt_lcdfb_remove(struct platform_device *dev)
 328 {
 329         struct fb_info *info;
 330 
 331         info = platform_get_drvdata(dev);
 332         if (info) {
 333                 unregister_framebuffer(info);
 334                 framebuffer_release(info);
 335         }
 336 
 337         return 0;
 338 }
 339 
 340 static struct platform_driver cobalt_lcdfb_driver = {
 341         .probe  = cobalt_lcdfb_probe,
 342         .remove = cobalt_lcdfb_remove,
 343         .driver = {
 344                 .name   = "cobalt-lcd",
 345         },
 346 };
 347 module_platform_driver(cobalt_lcdfb_driver);
 348 
 349 MODULE_LICENSE("GPL v2");
 350 MODULE_AUTHOR("Yoichi Yuasa");
 351 MODULE_DESCRIPTION("Cobalt server LCD frame buffer driver");

/* [<][>][^][v][top][bottom][index][help] */