LCOV - code coverage report
Current view: top level - drivers/video - arm-hdlcd.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 279 0.0 %
Date: 2014-02-18 Functions: 0 20 0.0 %
Branches: 0 109 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * drivers/video/arm-hdlcd.c
       3                 :            :  *
       4                 :            :  * Copyright (C) 2011 ARM Limited
       5                 :            :  *
       6                 :            :  * This file is subject to the terms and conditions of the GNU General Public
       7                 :            :  * License.  See the file COPYING in the main directory of this archive
       8                 :            :  * for more details.
       9                 :            :  *
      10                 :            :  *  ARM HDLCD Controller
      11                 :            :  */
      12                 :            : 
      13                 :            : #include <linux/module.h>
      14                 :            : #include <linux/kernel.h>
      15                 :            : #include <linux/errno.h>
      16                 :            : #include <linux/string.h>
      17                 :            : #include <linux/ctype.h>
      18                 :            : #include <linux/mm.h>
      19                 :            : #include <linux/delay.h>
      20                 :            : #include <linux/of.h>
      21                 :            : #include <linux/fb.h>
      22                 :            : #include <linux/clk.h>
      23                 :            : #include <linux/init.h>
      24                 :            : #include <linux/interrupt.h>
      25                 :            : #include <linux/ioport.h>
      26                 :            : #include <linux/dma-mapping.h>
      27                 :            : #include <linux/platform_device.h>
      28                 :            : #include <linux/memblock.h>
      29                 :            : #include <linux/arm-hdlcd.h>
      30                 :            : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
      31                 :            : #include <linux/proc_fs.h>
      32                 :            : #include <linux/seq_file.h>
      33                 :            : #endif
      34                 :            : 
      35                 :            : #include "edid.h"
      36                 :            : 
      37                 :            : #ifdef CONFIG_SERIAL_AMBA_PCU_UART
      38                 :            : int get_edid(u8 *msgbuf);
      39                 :            : #else
      40                 :            : #endif
      41                 :            : 
      42                 :            : #define to_hdlcd_device(info)   container_of(info, struct hdlcd_device, fb)
      43                 :            : 
      44                 :            : static struct of_device_id  hdlcd_of_matches[] = {
      45                 :            :         { .compatible   = "arm,hdlcd" },
      46                 :            :         {},
      47                 :            : };
      48                 :            : 
      49                 :            : /* Framebuffer size.  */
      50                 :            : static unsigned long framebuffer_size;
      51                 :            : 
      52                 :            : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
      53                 :            : static unsigned long buffer_underrun_events;
      54                 :            : static DEFINE_SPINLOCK(hdlcd_underrun_lock);
      55                 :            : 
      56                 :            : static void hdlcd_underrun_set(unsigned long val)
      57                 :            : {
      58                 :            :         spin_lock(&hdlcd_underrun_lock);
      59                 :            :         buffer_underrun_events = val;
      60                 :            :         spin_unlock(&hdlcd_underrun_lock);
      61                 :            : }
      62                 :            : 
      63                 :            : static unsigned long hdlcd_underrun_get(void)
      64                 :            : {
      65                 :            :         unsigned long val;
      66                 :            :         spin_lock(&hdlcd_underrun_lock);
      67                 :            :         val = buffer_underrun_events;
      68                 :            :         spin_unlock(&hdlcd_underrun_lock);
      69                 :            :         return val;
      70                 :            : }
      71                 :            : 
      72                 :            : #ifdef CONFIG_PROC_FS
      73                 :            : static int hdlcd_underrun_show(struct seq_file *m, void *v)
      74                 :            : {
      75                 :            :         unsigned char underrun_string[32];
      76                 :            :         snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get());
      77                 :            :         seq_puts(m, underrun_string);
      78                 :            :         return 0;
      79                 :            : }
      80                 :            : 
      81                 :            : static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
      82                 :            : {
      83                 :            :         return single_open(file, hdlcd_underrun_show, NULL);
      84                 :            : }
      85                 :            : 
      86                 :            : static const struct file_operations proc_hdlcd_underrun_operations = {
      87                 :            :         .open           = proc_hdlcd_underrun_open,
      88                 :            :         .read           = seq_read,
      89                 :            :         .llseek         = seq_lseek,
      90                 :            :         .release        = single_release,
      91                 :            : };
      92                 :            : 
      93                 :            : static int hdlcd_underrun_init(void)
      94                 :            : {
      95                 :            :         hdlcd_underrun_set(0);
      96                 :            :         proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations);
      97                 :            :         return 0;
      98                 :            : }
      99                 :            : static void hdlcd_underrun_close(void)
     100                 :            : {
     101                 :            :         remove_proc_entry("hdlcd_underrun", NULL);
     102                 :            : }
     103                 :            : #else
     104                 :            : static int hdlcd_underrun_init(void) { return 0; }
     105                 :            : static void hdlcd_underrun_close(void) { }
     106                 :            : #endif
     107                 :            : #endif
     108                 :            : 
     109                 :            : static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
     110                 :            : 
     111                 :            : static struct fb_var_screeninfo cached_var_screeninfo;
     112                 :            : 
     113                 :            : static struct fb_videomode hdlcd_default_mode = {
     114                 :            :         .refresh        = 60,
     115                 :            :         .xres           = 1680,
     116                 :            :         .yres           = 1050,
     117                 :            :         .pixclock       = 8403,
     118                 :            :         .left_margin    = 80,
     119                 :            :         .right_margin   = 48,
     120                 :            :         .upper_margin   = 21,
     121                 :            :         .lower_margin   = 3,
     122                 :            :         .hsync_len      = 32,
     123                 :            :         .vsync_len      = 6,
     124                 :            :         .sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
     125                 :            :         .vmode          = FB_VMODE_NONINTERLACED
     126                 :            : };
     127                 :            : 
     128                 :            : static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
     129                 :            : {
     130                 :            :         dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
     131                 :          0 :         writel(1, hdlcd->base + HDLCD_REG_COMMAND);
     132                 :            : }
     133                 :            : 
     134                 :            : static inline void hdlcd_disable(struct hdlcd_device *hdlcd)
     135                 :            : {
     136                 :            :         dev_dbg(hdlcd->dev, "HDLCD: output disabled\n");
     137                 :          0 :         writel(0, hdlcd->base + HDLCD_REG_COMMAND);
     138                 :            : }
     139                 :            : 
     140                 :          0 : static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd,
     141                 :            :                                 struct fb_var_screeninfo *var)
     142                 :            : {
     143                 :            :         int ret = 0;
     144                 :            : 
     145                 :          0 :         memset(&var->transp, 0, sizeof(var->transp));
     146                 :          0 :         var->red.msb_right = 0;
     147                 :          0 :         var->green.msb_right = 0;
     148                 :          0 :         var->blue.msb_right = 0;
     149                 :          0 :         var->blue.offset = 0;
     150                 :            : 
     151   [ #  #  #  #  :          0 :         switch (var->bits_per_pixel) {
                      # ]
     152                 :            :         case 8:
     153                 :            :                 /* pseudocolor */
     154                 :          0 :                 var->red.length = 8;
     155                 :          0 :                 var->green.length = 8;
     156                 :          0 :                 var->blue.length = 8;
     157                 :            :                 break;
     158                 :            :         case 16:
     159                 :            :                 /* 565 format */
     160                 :          0 :                 var->red.length = 5;
     161                 :          0 :                 var->green.length = 6;
     162                 :          0 :                 var->blue.length = 5;
     163                 :            :                 break;
     164                 :            :         case 32:
     165                 :          0 :                 var->transp.length = 8;
     166                 :            :         case 24:
     167                 :          0 :                 var->red.length = 8;
     168                 :          0 :                 var->green.length = 8;
     169                 :          0 :                 var->blue.length = 8;
     170                 :            :                 break;
     171                 :            :         default:
     172                 :            :                 ret = -EINVAL;
     173                 :            :                 break;
     174                 :            :         }
     175                 :            : 
     176         [ #  # ]:          0 :         if (!ret) {
     177         [ #  # ]:          0 :                 if(var->bits_per_pixel != 32)
     178                 :            :                 {
     179                 :          0 :                         var->green.offset = var->blue.length;
     180                 :          0 :                         var->red.offset = var->green.offset + var->green.length;
     181                 :            :                 }
     182                 :            :                 else
     183                 :            :                 {
     184                 :            :                         /* Previously, the byte ordering for 32-bit color was
     185                 :            :                          * (msb)<alpha><red><green><blue>(lsb)
     186                 :            :                          * but this does not match what android expects and
     187                 :            :                          * the colors are odd. Instead, use
     188                 :            :                          * <alpha><blue><green><red>
     189                 :            :                          * Since we tell fb what we are doing, console
     190                 :            :                          * , X and directfb access should work fine.
     191                 :            :                          */
     192                 :          0 :                         var->green.offset = var->red.length;
     193                 :          0 :                         var->blue.offset = var->green.offset + var->green.length;
     194                 :          0 :                         var->transp.offset = var->blue.offset + var->blue.length;
     195                 :            :                 }
     196                 :            :         }
     197                 :            : 
     198                 :          0 :         return ret;
     199                 :            : }
     200                 :            : 
     201                 :          0 : static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
     202                 :            : {
     203                 :            :         struct hdlcd_device *hdlcd = to_hdlcd_device(info);
     204                 :          0 :         int bytes_per_pixel = var->bits_per_pixel / 8;
     205                 :            : 
     206                 :            : #ifdef HDLCD_NO_VIRTUAL_SCREEN
     207                 :            :         var->yres_virtual = var->yres;
     208                 :            : #else
     209                 :          0 :         var->yres_virtual = 2 * var->yres;
     210                 :            : #endif
     211                 :            : 
     212         [ #  # ]:          0 :         if ((var->xres_virtual * bytes_per_pixel * var->yres_virtual) > hdlcd->fb.fix.smem_len)
     213                 :            :                 return -ENOMEM;
     214                 :            : 
     215 [ #  # ][ #  # ]:          0 :         if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
     216                 :            :                 return -EINVAL;
     217                 :            : 
     218                 :            :         /* make sure the bitfields are set appropriately */
     219                 :          0 :         return hdlcd_set_bitfields(hdlcd, var);
     220                 :            : }
     221                 :            : 
     222                 :            : /* prototype */
     223                 :            : static int hdlcd_pan_display(struct fb_var_screeninfo *var,
     224                 :            :         struct fb_info *info);
     225                 :            : 
     226                 :            : #define WRITE_HDLCD_REG(reg, value)     writel((value), hdlcd->base + (reg))
     227                 :            : #define READ_HDLCD_REG(reg)             readl(hdlcd->base + (reg))
     228                 :            : 
     229                 :          0 : static int hdlcd_set_par(struct fb_info *info)
     230                 :            : {
     231                 :            :         struct hdlcd_device *hdlcd = to_hdlcd_device(info);
     232                 :          0 :         int bytes_per_pixel = hdlcd->fb.var.bits_per_pixel / 8;
     233                 :            :         int polarities;
     234                 :            :         int old_yoffset;
     235                 :            : 
     236                 :            :         /* check for shortcuts */
     237                 :          0 :         old_yoffset = cached_var_screeninfo.yoffset;
     238                 :          0 :         cached_var_screeninfo.yoffset = info->var.yoffset;
     239         [ #  # ]:          0 :         if (!memcmp(&info->var, &cached_var_screeninfo,
     240                 :            :                                 sizeof(struct fb_var_screeninfo))) {
     241         [ #  # ]:          0 :                 if(old_yoffset != info->var.yoffset) {
     242                 :            :                         /* we only changed yoffset, and we already
     243                 :            :                          * already recorded it a couple lines up
     244                 :            :                          */
     245                 :          0 :                         hdlcd_pan_display(&info->var, info);
     246                 :            :                 }
     247                 :            :                 /* or no change */
     248                 :            :                 return 0;
     249                 :            :         }
     250                 :            : 
     251                 :          0 :         hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bytes_per_pixel;
     252                 :            : 
     253         [ #  # ]:          0 :         if (hdlcd->fb.var.bits_per_pixel >= 16)
     254                 :          0 :                 hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
     255                 :            :         else
     256                 :          0 :                 hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
     257                 :            : 
     258                 :          0 :         memcpy(&cached_var_screeninfo, &info->var, sizeof(struct fb_var_screeninfo));
     259                 :            : 
     260                 :            :         polarities = HDLCD_POLARITY_DATAEN |
     261                 :            : #ifndef CONFIG_ARCH_TUSCAN
     262                 :            :                 HDLCD_POLARITY_PIXELCLK |
     263                 :            : #endif
     264                 :            :                 HDLCD_POLARITY_DATA;
     265         [ #  # ]:          0 :         polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
     266                 :          0 :         polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
     267                 :            : 
     268                 :            :         hdlcd_disable(hdlcd);
     269                 :            : 
     270                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bytes_per_pixel);
     271                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bytes_per_pixel);
     272                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
     273                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
     274                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
     275                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
     276                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
     277                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
     278                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
     279                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
     280                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
     281                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
     282                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bytes_per_pixel - 1) << 3);
     283                 :            : #ifdef HDLCD_RED_DEFAULT_COLOUR
     284                 :            :         WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 | (hdlcd->fb.var.red.length & 0xf) << 8) \
     285                 :            :                                                                                                           | hdlcd->fb.var.red.offset);
     286                 :            : #else
     287                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, ((hdlcd->fb.var.red.length & 0xf) << 8) | hdlcd->fb.var.red.offset);
     288                 :            : #endif
     289                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, ((hdlcd->fb.var.green.length & 0xf) << 8) | hdlcd->fb.var.green.offset);
     290                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, ((hdlcd->fb.var.blue.length & 0xf) << 8) | hdlcd->fb.var.blue.offset);
     291                 :            : 
     292                 :          0 :         clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
     293                 :          0 :         clk_enable(hdlcd->clk);
     294                 :            : 
     295                 :            :         hdlcd_enable(hdlcd);
     296                 :            : 
     297                 :          0 :         return 0;
     298                 :            : }
     299                 :            : 
     300                 :          0 : static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
     301                 :            :                 unsigned int blue, unsigned int transp, struct fb_info *info)
     302                 :            : {
     303         [ #  # ]:          0 :         if (regno < 16) {
     304                 :          0 :                 u32 *pal = info->pseudo_palette;
     305                 :            : 
     306                 :          0 :                 pal[regno] = ((red >> 8) << info->var.red.offset) |
     307                 :          0 :                         ((green >> 8) << info->var.green.offset) |
     308                 :          0 :                         ((blue >> 8) << info->var.blue.offset);
     309                 :            :         }
     310                 :            : 
     311                 :          0 :         return 0;
     312                 :            : }
     313                 :            : 
     314                 :          0 : static irqreturn_t hdlcd_irq(int irq, void *data)
     315                 :            : {
     316                 :            :         struct hdlcd_device *hdlcd = data;
     317                 :            :         unsigned long irq_mask, irq_status;
     318                 :            : 
     319                 :          0 :         irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
     320                 :          0 :         irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
     321                 :            : 
     322                 :            :         /* acknowledge interrupt(s) */
     323                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
     324                 :            : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
     325                 :            :         if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
     326                 :            :                 /* increment the count */
     327                 :            :                 hdlcd_underrun_set(hdlcd_underrun_get() + 1);
     328                 :            :         }
     329                 :            : #endif
     330         [ #  # ]:          0 :         if (irq_status & HDLCD_INTERRUPT_VSYNC) {
     331                 :            :                 /* disable future VSYNC interrupts */
     332                 :          0 :                 WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
     333                 :            : 
     334                 :          0 :                 complete(&hdlcd->vsync_completion);
     335                 :            :         }
     336                 :            : 
     337                 :          0 :         return IRQ_HANDLED;
     338                 :            : }
     339                 :            : 
     340                 :          0 : static int hdlcd_wait_for_vsync(struct fb_info *info)
     341                 :            : {
     342                 :            :         struct hdlcd_device *hdlcd = to_hdlcd_device(info);
     343                 :            :         unsigned long irq_mask;
     344                 :            :         int err;
     345                 :            : 
     346                 :            :         /* enable VSYNC interrupt */
     347                 :          0 :         irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
     348                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
     349                 :            : 
     350                 :          0 :         err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
     351                 :            :                                                         msecs_to_jiffies(100));
     352                 :            : 
     353         [ #  # ]:          0 :         if (!err)
     354                 :            :                 return -ETIMEDOUT;
     355                 :            : 
     356                 :          0 :         return 0;
     357                 :            : }
     358                 :            : 
     359                 :          0 : static int hdlcd_blank(int blank_mode, struct fb_info *info)
     360                 :            : {
     361                 :            :         struct hdlcd_device *hdlcd = to_hdlcd_device(info);
     362                 :            : 
     363   [ #  #  #  # ]:          0 :         switch (blank_mode) {
     364                 :            :         case FB_BLANK_POWERDOWN:
     365                 :          0 :                 clk_disable(hdlcd->clk);
     366                 :            :         case FB_BLANK_NORMAL:
     367                 :            :                 hdlcd_disable(hdlcd);
     368                 :            :                 break;
     369                 :            :         case FB_BLANK_UNBLANK:
     370                 :          0 :                 clk_enable(hdlcd->clk);
     371                 :            :                 hdlcd_enable(hdlcd);
     372                 :            :                 break;
     373                 :            :         case FB_BLANK_VSYNC_SUSPEND:
     374                 :            :         case FB_BLANK_HSYNC_SUSPEND:
     375                 :            :         default:
     376                 :            :                 return 1;
     377                 :            :         }
     378                 :            : 
     379                 :            :         return 0;
     380                 :            : }
     381                 :            : 
     382                 :          0 : static void hdlcd_mmap_open(struct vm_area_struct *vma)
     383                 :            : {
     384                 :          0 : }
     385                 :            : 
     386                 :          0 : static void hdlcd_mmap_close(struct vm_area_struct *vma)
     387                 :            : {
     388                 :          0 : }
     389                 :            : 
     390                 :            : static struct vm_operations_struct hdlcd_mmap_ops = {
     391                 :            :         .open   = hdlcd_mmap_open,
     392                 :            :         .close  = hdlcd_mmap_close,
     393                 :            : };
     394                 :            : 
     395                 :          0 : static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma)
     396                 :            : {
     397                 :            :         struct hdlcd_device *hdlcd = to_hdlcd_device(info);
     398                 :            :         unsigned long off;
     399                 :            :         unsigned long start;
     400                 :          0 :         unsigned long len = hdlcd->fb.fix.smem_len;
     401                 :            : 
     402         [ #  # ]:          0 :         if (vma->vm_end - vma->vm_start == 0)
     403                 :            :                 return 0;
     404         [ #  # ]:          0 :         if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
     405                 :            :                 return -EINVAL;
     406                 :            : 
     407                 :          0 :         off = vma->vm_pgoff << PAGE_SHIFT;
     408 [ #  # ][ #  # ]:          0 :         if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
     409                 :            :                 return -EINVAL;
     410                 :            : 
     411                 :          0 :         start = hdlcd->fb.fix.smem_start;
     412                 :          0 :         off += start;
     413                 :            : 
     414                 :          0 :         vma->vm_pgoff = off >> PAGE_SHIFT;
     415                 :          0 :         vma->vm_flags |= VM_IO;
     416                 :          0 :         vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
     417                 :          0 :         vma->vm_ops = &hdlcd_mmap_ops;
     418         [ #  # ]:          0 :         if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
     419                 :            :                                 vma->vm_end - vma->vm_start,
     420                 :            :                                 vma->vm_page_prot))
     421                 :            :                 return -EAGAIN;
     422                 :            : 
     423                 :          0 :         return 0;
     424                 :            : }
     425                 :            : 
     426                 :          0 : static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
     427                 :            : {
     428                 :            :         struct hdlcd_device *hdlcd = to_hdlcd_device(info);
     429                 :            : 
     430                 :          0 :         hdlcd->fb.var.yoffset = var->yoffset;
     431                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
     432                 :            :                         (var->yoffset * hdlcd->fb.fix.line_length));
     433                 :            : 
     434                 :          0 :         hdlcd_wait_for_vsync(info);
     435                 :            : 
     436                 :          0 :         return 0;
     437                 :            : }
     438                 :            : 
     439                 :          0 : static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
     440                 :            : {
     441                 :            :         int err;
     442                 :            : 
     443         [ #  # ]:          0 :         switch (cmd) {
     444                 :            :         case FBIO_WAITFORVSYNC:
     445                 :          0 :                 err = hdlcd_wait_for_vsync(info);
     446                 :          0 :                 break;
     447                 :            :         default:
     448                 :            :                 err = -ENOIOCTLCMD;
     449                 :            :                 break;
     450                 :            :         }
     451                 :            : 
     452                 :          0 :         return err;
     453                 :            : }
     454                 :            : 
     455                 :            : static struct fb_ops hdlcd_ops = {
     456                 :            :         .owner                  = THIS_MODULE,
     457                 :            :         .fb_check_var           = hdlcd_check_var,
     458                 :            :         .fb_set_par             = hdlcd_set_par,
     459                 :            :         .fb_setcolreg           = hdlcd_setcolreg,
     460                 :            :         .fb_blank               = hdlcd_blank,
     461                 :            :         .fb_fillrect            = cfb_fillrect,
     462                 :            :         .fb_copyarea            = cfb_copyarea,
     463                 :            :         .fb_imageblit           = cfb_imageblit,
     464                 :            :         .fb_mmap                = hdlcd_mmap,
     465                 :            :         .fb_pan_display         = hdlcd_pan_display,
     466                 :            :         .fb_ioctl               = hdlcd_ioctl,
     467                 :            :         .fb_compat_ioctl        = hdlcd_ioctl
     468                 :            : };
     469                 :            : 
     470                 :          0 : static int hdlcd_setup(struct hdlcd_device *hdlcd)
     471                 :            : {
     472                 :            :         u32 version;
     473                 :            :         int err = -EFAULT;
     474                 :            : 
     475                 :          0 :         hdlcd->fb.device = hdlcd->dev;
     476                 :            : 
     477                 :          0 :         hdlcd->clk = clk_get(hdlcd->dev, NULL);
     478         [ #  # ]:          0 :         if (IS_ERR(hdlcd->clk)) {
     479                 :          0 :                 dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
     480                 :          0 :                 return PTR_ERR(hdlcd->clk);
     481                 :            :         }
     482                 :            : 
     483                 :          0 :         err = clk_prepare(hdlcd->clk);
     484         [ #  # ]:          0 :         if (err)
     485                 :            :                 goto clk_prepare_err;
     486                 :            : 
     487                 :          0 :         hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
     488         [ #  # ]:          0 :         if (!hdlcd->base) {
     489                 :          0 :                 dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
     490                 :          0 :                 goto remap_err;
     491                 :            :         }
     492                 :            : 
     493                 :          0 :         hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
     494         [ #  # ]:          0 :         if (!hdlcd->fb.pseudo_palette) {
     495                 :          0 :                 dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
     496                 :            :                 err = -ENOMEM;
     497                 :          0 :                 goto kmalloc_err;
     498                 :            :         }
     499                 :            : 
     500                 :          0 :         version = readl(hdlcd->base + HDLCD_REG_VERSION);
     501         [ #  # ]:          0 :         if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
     502                 :          0 :                 dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
     503                 :            :                 err = -EINVAL;
     504                 :          0 :                 goto kmalloc_err;
     505                 :            :         }
     506                 :          0 :         dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n",
     507                 :            :                 (version & HDLCD_VERSION_MAJOR_MASK) >> 8,
     508                 :            :                 version & HDLCD_VERSION_MINOR_MASK);
     509                 :            : 
     510                 :          0 :         strcpy(hdlcd->fb.fix.id, "hdlcd");
     511                 :          0 :         hdlcd->fb.fbops                      = &hdlcd_ops;
     512                 :          0 :         hdlcd->fb.flags                      = FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
     513                 :            : 
     514                 :          0 :         hdlcd->fb.fix.type           = FB_TYPE_PACKED_PIXELS;
     515                 :          0 :         hdlcd->fb.fix.type_aux               = 0;
     516                 :          0 :         hdlcd->fb.fix.xpanstep               = 0;
     517                 :          0 :         hdlcd->fb.fix.ypanstep               = 1;
     518                 :          0 :         hdlcd->fb.fix.ywrapstep              = 0;
     519                 :          0 :         hdlcd->fb.fix.accel          = FB_ACCEL_NONE;
     520                 :            : 
     521                 :          0 :         hdlcd->fb.var.nonstd         = 0;
     522                 :          0 :         hdlcd->fb.var.activate               = FB_ACTIVATE_NOW;
     523                 :          0 :         hdlcd->fb.var.height         = -1;
     524                 :          0 :         hdlcd->fb.var.width          = -1;
     525                 :          0 :         hdlcd->fb.var.accel_flags    = 0;
     526                 :            : 
     527                 :            :         init_completion(&hdlcd->vsync_completion);
     528                 :            : 
     529         [ #  # ]:          0 :         if (hdlcd->edid) {
     530                 :            :                 /* build modedb from EDID */
     531                 :          0 :                 fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
     532                 :          0 :                 fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
     533                 :          0 :                                         hdlcd->fb.monspecs.modedb_len,
     534                 :            :                                         &hdlcd->fb.modelist);
     535                 :          0 :                 fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
     536                 :          0 :                         hdlcd->fb.monspecs.modedb,
     537                 :            :                         hdlcd->fb.monspecs.modedb_len,
     538                 :            :                         &hdlcd_default_mode, 32);
     539                 :            :         } else {
     540                 :          0 :                 hdlcd->fb.monspecs.hfmin     = 0;
     541                 :          0 :                 hdlcd->fb.monspecs.hfmax     = 100000;
     542                 :          0 :                 hdlcd->fb.monspecs.vfmin     = 0;
     543                 :          0 :                 hdlcd->fb.monspecs.vfmax     = 400;
     544                 :          0 :                 hdlcd->fb.monspecs.dclkmin   = 1000000;
     545                 :          0 :                 hdlcd->fb.monspecs.dclkmax   = 100000000;
     546                 :          0 :                 fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
     547                 :            :         }
     548                 :            : 
     549         [ #  # ]:          0 :         dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres,
     550                 :            :                 hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel,
     551                 :            :                 hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60);
     552                 :          0 :         hdlcd->fb.var.xres_virtual   = hdlcd->fb.var.xres;
     553                 :            : #ifdef HDLCD_NO_VIRTUAL_SCREEN
     554                 :            :         hdlcd->fb.var.yres_virtual   = hdlcd->fb.var.yres;
     555                 :            : #else
     556                 :          0 :         hdlcd->fb.var.yres_virtual   = hdlcd->fb.var.yres * 2;
     557                 :            : #endif
     558                 :            : 
     559                 :            :         /* initialise and set the palette */
     560         [ #  # ]:          0 :         if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
     561                 :          0 :                 dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
     562                 :            :                 err = -ENOMEM;
     563                 :          0 :                 goto setup_err;
     564                 :            :         }
     565                 :          0 :         fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
     566                 :            : 
     567                 :            :         /* Allow max number of outstanding requests with the largest beat burst */
     568                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
     569                 :            :         /* Set the framebuffer base to start of allocated memory */
     570                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
     571                 :            : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
     572                 :            :         /* turn on underrun interrupt for counting */
     573                 :            :         WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
     574                 :            : #else
     575                 :            :         /* Ensure interrupts are disabled */
     576                 :          0 :         WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
     577                 :            : #endif  
     578                 :          0 :         fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
     579                 :            : 
     580         [ #  # ]:          0 :         if (!register_framebuffer(&hdlcd->fb)) {
     581                 :            :                 return 0;
     582                 :            :         }
     583                 :            : 
     584                 :          0 :         dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
     585                 :            : 
     586                 :          0 :         fb_dealloc_cmap(&hdlcd->fb.cmap);
     587                 :            : setup_err:
     588                 :          0 :         iounmap(hdlcd->base);
     589                 :            : kmalloc_err:
     590                 :          0 :         kfree(hdlcd->fb.pseudo_palette);
     591                 :            : remap_err:
     592                 :          0 :         clk_unprepare(hdlcd->clk);
     593                 :            : clk_prepare_err:
     594                 :          0 :         clk_put(hdlcd->clk);
     595                 :          0 :         return err;
     596                 :            : }
     597                 :            : 
     598                 :            : static inline unsigned char atohex(u8 data)
     599                 :            : {
     600         [ #  # ]:          0 :         if (!isxdigit(data))
     601                 :            :                 return 0;
     602                 :            :         /* truncate the upper nibble and add 9 to non-digit values */
     603         [ #  # ]:          0 :         return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf);
     604                 :            : }
     605                 :            : 
     606                 :            : /* EDID data is passed from devicetree in a literal string that can contain spaces and
     607                 :            :    the hexadecimal dump of the data */
     608                 :          0 : static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
     609                 :            : {
     610                 :            :         int i, j;
     611                 :            : 
     612         [ #  # ]:          0 :         if (!edid_data)
     613                 :            :                 return -EINVAL;
     614                 :            : 
     615                 :          0 :         hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
     616         [ #  # ]:          0 :         if (!hdlcd->edid)
     617                 :            :                 return -ENOMEM;
     618                 :            : 
     619         [ #  # ]:          0 :         for (i = 0, j = 0; i < data_len; i++) {
     620         [ #  # ]:          0 :                 if (isspace(edid_data[i]))
     621                 :          0 :                         continue;
     622                 :          0 :                 hdlcd->edid[j++] = atohex(edid_data[i]);
     623         [ #  # ]:          0 :                 if (j >= EDID_LENGTH)
     624                 :            :                         break;
     625                 :            :         }
     626                 :            : 
     627         [ #  # ]:          0 :         if (j < EDID_LENGTH) {
     628                 :          0 :                 kfree(hdlcd->edid);
     629                 :          0 :                 hdlcd->edid = NULL;
     630                 :            :                 return -EINVAL;
     631                 :            :         }
     632                 :            : 
     633                 :            :         return 0;
     634                 :            : }
     635                 :            : 
     636                 :          0 : static int hdlcd_probe(struct platform_device *pdev)
     637                 :            : {
     638                 :            :         int err = 0, i;
     639                 :            :         struct hdlcd_device *hdlcd;
     640                 :          0 :         struct resource *mem;
     641                 :            : #ifdef CONFIG_OF
     642                 :            :         struct device_node *of_node;
     643                 :            : #endif
     644                 :            : 
     645                 :          0 :         memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo));
     646                 :            : 
     647                 :            :         dev_dbg(&pdev->dev, "HDLCD: probing\n");
     648                 :            : 
     649                 :            :         hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL);
     650         [ #  # ]:          0 :         if (!hdlcd)
     651                 :            :                 return -ENOMEM;
     652                 :            : 
     653                 :            : #ifdef CONFIG_OF
     654                 :          0 :         of_node = pdev->dev.of_node;
     655         [ #  # ]:          0 :         if (of_node) {
     656                 :            :                 int len;
     657                 :            :                 const u8 *edid;
     658                 :          0 :                 const __be32 *prop = of_get_property(of_node, "mode", &len);
     659         [ #  # ]:          0 :                 if (prop)
     660                 :          0 :                         strncpy(fb_mode, (char *)prop, len);
     661                 :          0 :                 prop = of_get_property(of_node, "framebuffer", &len);
     662         [ #  # ]:          0 :                 if (prop) {
     663                 :          0 :                         hdlcd->fb.fix.smem_start = of_read_ulong(prop,
     664                 :            :                                         of_n_addr_cells(of_node));
     665                 :          0 :                         prop += of_n_addr_cells(of_node);
     666                 :          0 :                         framebuffer_size = of_read_ulong(prop,
     667                 :            :                                         of_n_size_cells(of_node));
     668         [ #  # ]:          0 :                         if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
     669                 :          0 :                                 framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE;
     670                 :            :                         dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
     671                 :            :                                 hdlcd->fb.fix.smem_start, framebuffer_size);
     672                 :            :                 }
     673                 :          0 :                 edid = of_get_property(of_node, "edid", &len);
     674         [ #  # ]:          0 :                 if (edid) {
     675                 :          0 :                         err = parse_edid_data(hdlcd, edid, len);
     676                 :            : #ifdef CONFIG_SERIAL_AMBA_PCU_UART
     677                 :            :                 } else {
     678                 :            :                         /* ask the firmware to fetch the EDID */
     679                 :            :                         dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n");
     680                 :            :                         hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
     681                 :            :                         if (!hdlcd->edid)
     682                 :            :                                 return -ENOMEM;
     683                 :            :                         err = get_edid(hdlcd->edid);
     684                 :            : #endif /* CONFIG_SERIAL_AMBA_PCU_UART */
     685                 :            :                 }
     686         [ #  # ]:          0 :                 if (err)
     687                 :          0 :                         dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
     688                 :            :         }
     689                 :            : #endif /* CONFIG_OF */
     690                 :            : 
     691                 :          0 :         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     692         [ #  # ]:          0 :         if (!mem) {
     693                 :          0 :                 dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
     694                 :            :                 err = -EINVAL;
     695                 :          0 :                 goto resource_err;
     696                 :            :         }
     697                 :            : 
     698                 :          0 :         i = platform_get_irq(pdev, 0);
     699         [ #  # ]:          0 :         if (i < 0) {
     700                 :          0 :                 dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
     701                 :            :                 err = -ENOENT;
     702                 :          0 :                 goto resource_err;
     703                 :            :         } else {
     704                 :          0 :                 err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
     705         [ #  # ]:          0 :                 if (err) {
     706                 :          0 :                         dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
     707                 :          0 :                         goto resource_err;
     708                 :            :                 }
     709                 :          0 :                 hdlcd->irq = i;
     710                 :            :         }
     711                 :            : 
     712         [ #  # ]:          0 :         if (!request_mem_region(mem->start, resource_size(mem),      dev_name(&pdev->dev))) {
     713                 :            :                 err = -ENXIO;
     714                 :            :                 goto request_err;
     715                 :            :         }
     716                 :            : 
     717         [ #  # ]:          0 :         if (!hdlcd->fb.fix.smem_start) {
     718                 :          0 :                 dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
     719                 :            :                 err = -ENOMEM;
     720                 :          0 :                 goto memalloc_err;
     721                 :            :         }
     722                 :          0 :         hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start, framebuffer_size);
     723         [ #  # ]:          0 :         if (!hdlcd->fb.screen_base) {
     724                 :          0 :                 dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
     725                 :            :                 err = -ENOMEM;
     726                 :          0 :                 goto probe_err;
     727                 :            :         }
     728                 :            : 
     729                 :          0 :         hdlcd->fb.screen_size = framebuffer_size;
     730                 :          0 :         hdlcd->fb.fix.smem_len = framebuffer_size;
     731                 :          0 :         hdlcd->fb.fix.mmio_start = mem->start;
     732                 :          0 :         hdlcd->fb.fix.mmio_len = resource_size(mem);
     733                 :            : 
     734                 :            :         /* Clear the framebuffer */
     735         [ #  # ]:          0 :         memset(hdlcd->fb.screen_base, 0, framebuffer_size);
     736                 :            : 
     737                 :          0 :         hdlcd->dev = &pdev->dev;
     738                 :            : 
     739                 :            :         dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n",
     740                 :            :                 hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start);
     741                 :            : 
     742                 :          0 :         err = hdlcd_setup(hdlcd);
     743                 :            : 
     744         [ #  # ]:          0 :         if (err)
     745                 :            :                 goto probe_err;
     746                 :            : 
     747                 :            :         platform_set_drvdata(pdev, hdlcd);
     748                 :          0 :         return 0;
     749                 :            : 
     750                 :            : probe_err:
     751                 :          0 :         iounmap(hdlcd->fb.screen_base);
     752                 :          0 :         memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
     753                 :            : 
     754                 :            : memalloc_err:
     755                 :          0 :         release_mem_region(mem->start, resource_size(mem));
     756                 :            : 
     757                 :            : request_err:
     758                 :          0 :         free_irq(hdlcd->irq, hdlcd);
     759                 :            : 
     760                 :            : resource_err:
     761                 :          0 :         kfree(hdlcd);
     762                 :            : 
     763                 :          0 :         return err;
     764                 :            : }
     765                 :            : 
     766                 :          0 : static int hdlcd_remove(struct platform_device *pdev)
     767                 :            : {
     768                 :            :         struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
     769                 :            : 
     770                 :          0 :         clk_disable(hdlcd->clk);
     771                 :          0 :         clk_unprepare(hdlcd->clk);
     772                 :          0 :         clk_put(hdlcd->clk);
     773                 :            : 
     774                 :            :         /* unmap memory */
     775                 :          0 :         iounmap(hdlcd->fb.screen_base);
     776                 :          0 :         iounmap(hdlcd->base);
     777                 :            : 
     778                 :            :         /* deallocate fb memory */
     779                 :          0 :         fb_dealloc_cmap(&hdlcd->fb.cmap);
     780                 :          0 :         kfree(hdlcd->fb.pseudo_palette);
     781                 :          0 :         memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
     782                 :          0 :         release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
     783                 :            : 
     784                 :          0 :         free_irq(hdlcd->irq, NULL);
     785                 :          0 :         kfree(hdlcd);
     786                 :            : 
     787                 :          0 :         return 0;
     788                 :            : }
     789                 :            : 
     790                 :            : #ifdef CONFIG_PM
     791                 :          0 : static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
     792                 :            : {
     793                 :            :         /* not implemented yet */
     794                 :          0 :         return 0;
     795                 :            : }
     796                 :            : 
     797                 :          0 : static int hdlcd_resume(struct platform_device *pdev)
     798                 :            : {
     799                 :            :         /* not implemented yet */
     800                 :          0 :         return 0;
     801                 :            : }
     802                 :            : #else
     803                 :            : #define hdlcd_suspend   NULL
     804                 :            : #define hdlcd_resume    NULL
     805                 :            : #endif
     806                 :            : 
     807                 :            : static struct platform_driver hdlcd_driver = {
     808                 :            :         .probe          = hdlcd_probe,
     809                 :            :         .remove         = hdlcd_remove,
     810                 :            :         .suspend        = hdlcd_suspend,
     811                 :            :         .resume         = hdlcd_resume,
     812                 :            :         .driver = {
     813                 :            :                 .name           = "hdlcd",
     814                 :            :                 .owner          = THIS_MODULE,
     815                 :            :                 .of_match_table = hdlcd_of_matches,
     816                 :            :         },
     817                 :            : };
     818                 :            : 
     819                 :          0 : static int __init hdlcd_init(void)
     820                 :            : {
     821                 :            : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
     822                 :            :         int err = platform_driver_register(&hdlcd_driver);
     823                 :            :         if (!err)
     824                 :            :                 hdlcd_underrun_init();
     825                 :            :         return err;
     826                 :            : #else
     827                 :          0 :         return platform_driver_register(&hdlcd_driver);
     828                 :            : #endif
     829                 :            : }
     830                 :            : 
     831                 :          0 : void __exit hdlcd_exit(void)
     832                 :            : {
     833                 :            : #ifdef HDLCD_COUNT_BUFFERUNDERRUNS
     834                 :            :         hdlcd_underrun_close();
     835                 :            : #endif
     836                 :          0 :         platform_driver_unregister(&hdlcd_driver);
     837                 :          0 : }
     838                 :            : 
     839                 :            : module_init(hdlcd_init);
     840                 :            : module_exit(hdlcd_exit);
     841                 :            : 
     842                 :            : MODULE_AUTHOR("Liviu Dudau");
     843                 :            : MODULE_DESCRIPTION("ARM HDLCD core driver");
     844                 :            : MODULE_LICENSE("GPL v2");

Generated by: LCOV version 1.9