Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
0004  *
0005  *  Freescale DIU Frame Buffer device driver
0006  *
0007  *  Authors: Hongjun Chen <hong-jun.chen@freescale.com>
0008  *           Paul Widmer <paul.widmer@freescale.com>
0009  *           Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
0010  *           York Sun <yorksun@freescale.com>
0011  *
0012  *   Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/kernel.h>
0017 #include <linux/errno.h>
0018 #include <linux/string.h>
0019 #include <linux/slab.h>
0020 #include <linux/fb.h>
0021 #include <linux/init.h>
0022 #include <linux/dma-mapping.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/interrupt.h>
0025 #include <linux/clk.h>
0026 #include <linux/uaccess.h>
0027 #include <linux/vmalloc.h>
0028 #include <linux/spinlock.h>
0029 #include <linux/of_address.h>
0030 #include <linux/of_irq.h>
0031 
0032 #include <sysdev/fsl_soc.h>
0033 #include <linux/fsl-diu-fb.h>
0034 #include "edid.h"
0035 
0036 #define NUM_AOIS    5   /* 1 for plane 0, 2 for planes 1 & 2 each */
0037 
0038 /* HW cursor parameters */
0039 #define MAX_CURS        32
0040 
0041 /* INT_STATUS/INT_MASK field descriptions */
0042 #define INT_VSYNC   0x01    /* Vsync interrupt  */
0043 #define INT_VSYNC_WB    0x02    /* Vsync interrupt for write back operation */
0044 #define INT_UNDRUN  0x04    /* Under run exception interrupt */
0045 #define INT_PARERR  0x08    /* Display parameters error interrupt */
0046 #define INT_LS_BF_VS    0x10    /* Lines before vsync. interrupt */
0047 
0048 /*
0049  * List of supported video modes
0050  *
0051  * The first entry is the default video mode.  The remain entries are in
0052  * order if increasing resolution and frequency.  The 320x240-60 mode is
0053  * the initial AOI for the second and third planes.
0054  */
0055 static struct fb_videomode fsl_diu_mode_db[] = {
0056     {
0057         .refresh    = 60,
0058         .xres       = 1024,
0059         .yres       = 768,
0060         .pixclock   = 15385,
0061         .left_margin    = 160,
0062         .right_margin   = 24,
0063         .upper_margin   = 29,
0064         .lower_margin   = 3,
0065         .hsync_len  = 136,
0066         .vsync_len  = 6,
0067         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0068         .vmode      = FB_VMODE_NONINTERLACED
0069     },
0070     {
0071         .refresh    = 60,
0072         .xres       = 320,
0073         .yres       = 240,
0074         .pixclock   = 79440,
0075         .left_margin    = 16,
0076         .right_margin   = 16,
0077         .upper_margin   = 16,
0078         .lower_margin   = 5,
0079         .hsync_len  = 48,
0080         .vsync_len  = 1,
0081         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0082         .vmode      = FB_VMODE_NONINTERLACED
0083     },
0084     {
0085         .refresh        = 60,
0086         .xres           = 640,
0087         .yres           = 480,
0088         .pixclock       = 39722,
0089         .left_margin    = 48,
0090         .right_margin   = 16,
0091         .upper_margin   = 33,
0092         .lower_margin   = 10,
0093         .hsync_len      = 96,
0094         .vsync_len      = 2,
0095         .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0096         .vmode          = FB_VMODE_NONINTERLACED
0097     },
0098     {
0099         .refresh        = 72,
0100         .xres           = 640,
0101         .yres           = 480,
0102         .pixclock       = 32052,
0103         .left_margin    = 128,
0104         .right_margin   = 24,
0105         .upper_margin   = 28,
0106         .lower_margin   = 9,
0107         .hsync_len      = 40,
0108         .vsync_len      = 3,
0109         .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0110         .vmode          = FB_VMODE_NONINTERLACED
0111     },
0112     {
0113         .refresh        = 75,
0114         .xres           = 640,
0115         .yres           = 480,
0116         .pixclock       = 31747,
0117         .left_margin    = 120,
0118         .right_margin   = 16,
0119         .upper_margin   = 16,
0120         .lower_margin   = 1,
0121         .hsync_len      = 64,
0122         .vsync_len      = 3,
0123         .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0124         .vmode          = FB_VMODE_NONINTERLACED
0125     },
0126     {
0127         .refresh        = 90,
0128         .xres           = 640,
0129         .yres           = 480,
0130         .pixclock       = 25057,
0131         .left_margin    = 120,
0132         .right_margin   = 32,
0133         .upper_margin   = 14,
0134         .lower_margin   = 25,
0135         .hsync_len      = 40,
0136         .vsync_len      = 14,
0137         .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0138         .vmode          = FB_VMODE_NONINTERLACED
0139     },
0140     {
0141         .refresh        = 100,
0142         .xres           = 640,
0143         .yres           = 480,
0144         .pixclock       = 22272,
0145         .left_margin    = 48,
0146         .right_margin   = 32,
0147         .upper_margin   = 17,
0148         .lower_margin   = 22,
0149         .hsync_len      = 128,
0150         .vsync_len      = 12,
0151         .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0152         .vmode          = FB_VMODE_NONINTERLACED
0153     },
0154     {
0155         .refresh    = 60,
0156         .xres       = 800,
0157         .yres       = 480,
0158         .pixclock   = 33805,
0159         .left_margin    = 96,
0160         .right_margin   = 24,
0161         .upper_margin   = 10,
0162         .lower_margin   = 3,
0163         .hsync_len  = 72,
0164         .vsync_len  = 7,
0165         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0166         .vmode      = FB_VMODE_NONINTERLACED
0167     },
0168     {
0169         .refresh        = 60,
0170         .xres           = 800,
0171         .yres           = 600,
0172         .pixclock       = 25000,
0173         .left_margin    = 88,
0174         .right_margin   = 40,
0175         .upper_margin   = 23,
0176         .lower_margin   = 1,
0177         .hsync_len      = 128,
0178         .vsync_len      = 4,
0179         .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0180         .vmode          = FB_VMODE_NONINTERLACED
0181     },
0182     {
0183         .refresh    = 60,
0184         .xres       = 854,
0185         .yres       = 480,
0186         .pixclock   = 31518,
0187         .left_margin    = 104,
0188         .right_margin   = 16,
0189         .upper_margin   = 13,
0190         .lower_margin   = 1,
0191         .hsync_len  = 88,
0192         .vsync_len  = 3,
0193         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0194         .vmode      = FB_VMODE_NONINTERLACED
0195     },
0196     {
0197         .refresh    = 70,
0198         .xres       = 1024,
0199         .yres       = 768,
0200         .pixclock   = 16886,
0201         .left_margin    = 3,
0202         .right_margin   = 3,
0203         .upper_margin   = 2,
0204         .lower_margin   = 2,
0205         .hsync_len  = 40,
0206         .vsync_len  = 18,
0207         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0208         .vmode      = FB_VMODE_NONINTERLACED
0209     },
0210     {
0211         .refresh    = 75,
0212         .xres       = 1024,
0213         .yres       = 768,
0214         .pixclock   = 15009,
0215         .left_margin    = 3,
0216         .right_margin   = 3,
0217         .upper_margin   = 2,
0218         .lower_margin   = 2,
0219         .hsync_len  = 80,
0220         .vsync_len  = 32,
0221         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0222         .vmode      = FB_VMODE_NONINTERLACED
0223     },
0224     {
0225         .refresh    = 60,
0226         .xres       = 1280,
0227         .yres       = 480,
0228         .pixclock   = 18939,
0229         .left_margin    = 353,
0230         .right_margin   = 47,
0231         .upper_margin   = 39,
0232         .lower_margin   = 4,
0233         .hsync_len  = 8,
0234         .vsync_len  = 2,
0235         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0236         .vmode      = FB_VMODE_NONINTERLACED
0237     },
0238     {
0239         .refresh    = 60,
0240         .xres       = 1280,
0241         .yres       = 720,
0242         .pixclock   = 13426,
0243         .left_margin    = 192,
0244         .right_margin   = 64,
0245         .upper_margin   = 22,
0246         .lower_margin   = 1,
0247         .hsync_len  = 136,
0248         .vsync_len  = 3,
0249         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0250         .vmode      = FB_VMODE_NONINTERLACED
0251     },
0252     {
0253         .refresh    = 60,
0254         .xres       = 1280,
0255         .yres       = 1024,
0256         .pixclock   = 9375,
0257         .left_margin    = 38,
0258         .right_margin   = 128,
0259         .upper_margin   = 2,
0260         .lower_margin   = 7,
0261         .hsync_len  = 216,
0262         .vsync_len  = 37,
0263         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0264         .vmode      = FB_VMODE_NONINTERLACED
0265     },
0266     {
0267         .refresh    = 70,
0268         .xres       = 1280,
0269         .yres       = 1024,
0270         .pixclock   = 9380,
0271         .left_margin    = 6,
0272         .right_margin   = 6,
0273         .upper_margin   = 4,
0274         .lower_margin   = 4,
0275         .hsync_len  = 60,
0276         .vsync_len  = 94,
0277         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0278         .vmode      = FB_VMODE_NONINTERLACED
0279     },
0280     {
0281         .refresh    = 75,
0282         .xres       = 1280,
0283         .yres       = 1024,
0284         .pixclock   = 9380,
0285         .left_margin    = 6,
0286         .right_margin   = 6,
0287         .upper_margin   = 4,
0288         .lower_margin   = 4,
0289         .hsync_len  = 60,
0290         .vsync_len  = 15,
0291         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0292         .vmode      = FB_VMODE_NONINTERLACED
0293     },
0294     {
0295         .refresh    = 60,
0296         .xres       = 1920,
0297         .yres       = 1080,
0298         .pixclock   = 5787,
0299         .left_margin    = 328,
0300         .right_margin   = 120,
0301         .upper_margin   = 34,
0302         .lower_margin   = 1,
0303         .hsync_len  = 208,
0304         .vsync_len  = 3,
0305         .sync       = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
0306         .vmode      = FB_VMODE_NONINTERLACED
0307     },
0308 };
0309 
0310 static char *fb_mode;
0311 static unsigned long default_bpp = 32;
0312 static enum fsl_diu_monitor_port monitor_port;
0313 static char *monitor_string;
0314 
0315 #if defined(CONFIG_NOT_COHERENT_CACHE)
0316 static u8 *coherence_data;
0317 static size_t coherence_data_size;
0318 static unsigned int d_cache_line_size;
0319 #endif
0320 
0321 static DEFINE_SPINLOCK(diu_lock);
0322 
0323 enum mfb_index {
0324     PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */
0325     PLANE1_AOI0,    /* Plane 1, first AOI */
0326     PLANE1_AOI1,    /* Plane 1, second AOI */
0327     PLANE2_AOI0,    /* Plane 2, first AOI */
0328     PLANE2_AOI1,    /* Plane 2, second AOI */
0329 };
0330 
0331 struct mfb_info {
0332     enum mfb_index index;
0333     char *id;
0334     int registered;
0335     unsigned long pseudo_palette[16];
0336     struct diu_ad *ad;
0337     unsigned char g_alpha;
0338     unsigned int count;
0339     int x_aoi_d;        /* aoi display x offset to physical screen */
0340     int y_aoi_d;        /* aoi display y offset to physical screen */
0341     struct fsl_diu_data *parent;
0342 };
0343 
0344 /**
0345  * struct fsl_diu_data - per-DIU data structure
0346  * @dma_addr: DMA address of this structure
0347  * @fsl_diu_info: fb_info objects, one per AOI
0348  * @dev_attr: sysfs structure
0349  * @irq: IRQ
0350  * @monitor_port: the monitor port this DIU is connected to
0351  * @diu_reg: pointer to the DIU hardware registers
0352  * @reg_lock: spinlock for register access
0353  * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI
0354  * dummy_ad: DIU Area Descriptor for the dummy AOI
0355  * @ad[]: Area Descriptors for each real AOI
0356  * @gamma: gamma color table
0357  * @cursor: hardware cursor data
0358  * @blank_cursor: blank cursor for hiding cursor
0359  * @next_cursor: scratch space to build load cursor
0360  * @edid_data: EDID information buffer
0361  * @has_edid: whether or not the EDID buffer is valid
0362  *
0363  * This data structure must be allocated with 32-byte alignment, so that the
0364  * internal fields can be aligned properly.
0365  */
0366 struct fsl_diu_data {
0367     dma_addr_t dma_addr;
0368     struct fb_info fsl_diu_info[NUM_AOIS];
0369     struct mfb_info mfb[NUM_AOIS];
0370     struct device_attribute dev_attr;
0371     unsigned int irq;
0372     enum fsl_diu_monitor_port monitor_port;
0373     struct diu __iomem *diu_reg;
0374     spinlock_t reg_lock;
0375     u8 dummy_aoi[4 * 4 * 4];
0376     struct diu_ad dummy_ad __aligned(8);
0377     struct diu_ad ad[NUM_AOIS] __aligned(8);
0378     u8 gamma[256 * 3] __aligned(32);
0379     /* It's easier to parse the cursor data as little-endian */
0380     __le16 cursor[MAX_CURS * MAX_CURS] __aligned(32);
0381     /* Blank cursor data -- used to hide the cursor */
0382     __le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32);
0383     /* Scratch cursor data -- used to build new cursor */
0384     __le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32);
0385     uint8_t edid_data[EDID_LENGTH];
0386     bool has_edid;
0387 } __aligned(32);
0388 
0389 /* Determine the DMA address of a member of the fsl_diu_data structure */
0390 #define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))
0391 
0392 static const struct mfb_info mfb_template[] = {
0393     {
0394         .index = PLANE0,
0395         .id = "Panel0",
0396         .registered = 0,
0397         .count = 0,
0398         .x_aoi_d = 0,
0399         .y_aoi_d = 0,
0400     },
0401     {
0402         .index = PLANE1_AOI0,
0403         .id = "Panel1 AOI0",
0404         .registered = 0,
0405         .g_alpha = 0xff,
0406         .count = 0,
0407         .x_aoi_d = 0,
0408         .y_aoi_d = 0,
0409     },
0410     {
0411         .index = PLANE1_AOI1,
0412         .id = "Panel1 AOI1",
0413         .registered = 0,
0414         .g_alpha = 0xff,
0415         .count = 0,
0416         .x_aoi_d = 0,
0417         .y_aoi_d = 480,
0418     },
0419     {
0420         .index = PLANE2_AOI0,
0421         .id = "Panel2 AOI0",
0422         .registered = 0,
0423         .g_alpha = 0xff,
0424         .count = 0,
0425         .x_aoi_d = 640,
0426         .y_aoi_d = 0,
0427     },
0428     {
0429         .index = PLANE2_AOI1,
0430         .id = "Panel2 AOI1",
0431         .registered = 0,
0432         .g_alpha = 0xff,
0433         .count = 0,
0434         .x_aoi_d = 640,
0435         .y_aoi_d = 480,
0436     },
0437 };
0438 
0439 #ifdef DEBUG
0440 static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)
0441 {
0442     mb();
0443     pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x palette=%08x "
0444          "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "
0445          "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "
0446          "thresholds=%08x int_mask=%08x plut=%08x\n",
0447          hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,
0448          hw->palette, hw->cursor, hw->curs_pos, hw->diu_mode,
0449          hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,
0450          hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);
0451     rmb();
0452 }
0453 #endif
0454 
0455 /**
0456  * fsl_diu_name_to_port - convert a port name to a monitor port enum
0457  *
0458  * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns
0459  * the enum fsl_diu_monitor_port that corresponds to that string.
0460  *
0461  * For compatibility with older versions, a number ("0", "1", or "2") is also
0462  * supported.
0463  *
0464  * If the string is unknown, DVI is assumed.
0465  *
0466  * If the particular port is not supported by the platform, another port
0467  * (platform-specific) is chosen instead.
0468  */
0469 static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
0470 {
0471     enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;
0472     unsigned long val;
0473 
0474     if (s) {
0475         if (!kstrtoul(s, 10, &val) && (val <= 2))
0476             port = (enum fsl_diu_monitor_port) val;
0477         else if (strncmp(s, "lvds", 4) == 0)
0478             port = FSL_DIU_PORT_LVDS;
0479         else if (strncmp(s, "dlvds", 5) == 0)
0480             port = FSL_DIU_PORT_DLVDS;
0481     }
0482 
0483     if (diu_ops.valid_monitor_port)
0484         port = diu_ops.valid_monitor_port(port);
0485 
0486     return port;
0487 }
0488 
0489 /*
0490  * Workaround for failed writing desc register of planes.
0491  * Needed with MPC5121 DIU rev 2.0 silicon.
0492  */
0493 void wr_reg_wa(u32 *reg, u32 val)
0494 {
0495     do {
0496         out_be32(reg, val);
0497     } while (in_be32(reg) != val);
0498 }
0499 
0500 static void fsl_diu_enable_panel(struct fb_info *info)
0501 {
0502     struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
0503     struct diu_ad *ad = mfbi->ad;
0504     struct fsl_diu_data *data = mfbi->parent;
0505     struct diu __iomem *hw = data->diu_reg;
0506 
0507     switch (mfbi->index) {
0508     case PLANE0:
0509         wr_reg_wa(&hw->desc[0], ad->paddr);
0510         break;
0511     case PLANE1_AOI0:
0512         cmfbi = &data->mfb[2];
0513         if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
0514             if (cmfbi->count > 0)   /* AOI1 open */
0515                 ad->next_ad =
0516                     cpu_to_le32(cmfbi->ad->paddr);
0517             else
0518                 ad->next_ad = 0;
0519             wr_reg_wa(&hw->desc[1], ad->paddr);
0520         }
0521         break;
0522     case PLANE2_AOI0:
0523         cmfbi = &data->mfb[4];
0524         if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
0525             if (cmfbi->count > 0)   /* AOI1 open */
0526                 ad->next_ad =
0527                     cpu_to_le32(cmfbi->ad->paddr);
0528             else
0529                 ad->next_ad = 0;
0530             wr_reg_wa(&hw->desc[2], ad->paddr);
0531         }
0532         break;
0533     case PLANE1_AOI1:
0534         pmfbi = &data->mfb[1];
0535         ad->next_ad = 0;
0536         if (hw->desc[1] == data->dummy_ad.paddr)
0537             wr_reg_wa(&hw->desc[1], ad->paddr);
0538         else                    /* AOI0 open */
0539             pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
0540         break;
0541     case PLANE2_AOI1:
0542         pmfbi = &data->mfb[3];
0543         ad->next_ad = 0;
0544         if (hw->desc[2] == data->dummy_ad.paddr)
0545             wr_reg_wa(&hw->desc[2], ad->paddr);
0546         else                /* AOI0 was open */
0547             pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
0548         break;
0549     }
0550 }
0551 
0552 static void fsl_diu_disable_panel(struct fb_info *info)
0553 {
0554     struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
0555     struct diu_ad *ad = mfbi->ad;
0556     struct fsl_diu_data *data = mfbi->parent;
0557     struct diu __iomem *hw = data->diu_reg;
0558 
0559     switch (mfbi->index) {
0560     case PLANE0:
0561         wr_reg_wa(&hw->desc[0], 0);
0562         break;
0563     case PLANE1_AOI0:
0564         cmfbi = &data->mfb[2];
0565         if (cmfbi->count > 0)   /* AOI1 is open */
0566             wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
0567                     /* move AOI1 to the first */
0568         else            /* AOI1 was closed */
0569             wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
0570                     /* close AOI 0 */
0571         break;
0572     case PLANE2_AOI0:
0573         cmfbi = &data->mfb[4];
0574         if (cmfbi->count > 0)   /* AOI1 is open */
0575             wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
0576                     /* move AOI1 to the first */
0577         else            /* AOI1 was closed */
0578             wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
0579                     /* close AOI 0 */
0580         break;
0581     case PLANE1_AOI1:
0582         pmfbi = &data->mfb[1];
0583         if (hw->desc[1] != ad->paddr) {
0584                 /* AOI1 is not the first in the chain */
0585             if (pmfbi->count > 0)
0586                     /* AOI0 is open, must be the first */
0587                 pmfbi->ad->next_ad = 0;
0588         } else          /* AOI1 is the first in the chain */
0589             wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
0590                     /* close AOI 1 */
0591         break;
0592     case PLANE2_AOI1:
0593         pmfbi = &data->mfb[3];
0594         if (hw->desc[2] != ad->paddr) {
0595                 /* AOI1 is not the first in the chain */
0596             if (pmfbi->count > 0)
0597                 /* AOI0 is open, must be the first */
0598                 pmfbi->ad->next_ad = 0;
0599         } else      /* AOI1 is the first in the chain */
0600             wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
0601                 /* close AOI 1 */
0602         break;
0603     }
0604 }
0605 
0606 static void enable_lcdc(struct fb_info *info)
0607 {
0608     struct mfb_info *mfbi = info->par;
0609     struct fsl_diu_data *data = mfbi->parent;
0610     struct diu __iomem *hw = data->diu_reg;
0611 
0612     out_be32(&hw->diu_mode, MFB_MODE1);
0613 }
0614 
0615 static void disable_lcdc(struct fb_info *info)
0616 {
0617     struct mfb_info *mfbi = info->par;
0618     struct fsl_diu_data *data = mfbi->parent;
0619     struct diu __iomem *hw = data->diu_reg;
0620 
0621     out_be32(&hw->diu_mode, 0);
0622 }
0623 
0624 static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
0625                 struct fb_info *info)
0626 {
0627     struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
0628     struct fsl_diu_data *data = mfbi->parent;
0629     int available_height, upper_aoi_bottom;
0630     enum mfb_index index = mfbi->index;
0631     int lower_aoi_is_open, upper_aoi_is_open;
0632     __u32 base_plane_width, base_plane_height, upper_aoi_height;
0633 
0634     base_plane_width = data->fsl_diu_info[0].var.xres;
0635     base_plane_height = data->fsl_diu_info[0].var.yres;
0636 
0637     if (mfbi->x_aoi_d < 0)
0638         mfbi->x_aoi_d = 0;
0639     if (mfbi->y_aoi_d < 0)
0640         mfbi->y_aoi_d = 0;
0641     switch (index) {
0642     case PLANE0:
0643         if (mfbi->x_aoi_d != 0)
0644             mfbi->x_aoi_d = 0;
0645         if (mfbi->y_aoi_d != 0)
0646             mfbi->y_aoi_d = 0;
0647         break;
0648     case PLANE1_AOI0:
0649     case PLANE2_AOI0:
0650         lower_aoi_mfbi = data->fsl_diu_info[index+1].par;
0651         lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
0652         if (var->xres > base_plane_width)
0653             var->xres = base_plane_width;
0654         if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
0655             mfbi->x_aoi_d = base_plane_width - var->xres;
0656 
0657         if (lower_aoi_is_open)
0658             available_height = lower_aoi_mfbi->y_aoi_d;
0659         else
0660             available_height = base_plane_height;
0661         if (var->yres > available_height)
0662             var->yres = available_height;
0663         if ((mfbi->y_aoi_d + var->yres) > available_height)
0664             mfbi->y_aoi_d = available_height - var->yres;
0665         break;
0666     case PLANE1_AOI1:
0667     case PLANE2_AOI1:
0668         upper_aoi_mfbi = data->fsl_diu_info[index-1].par;
0669         upper_aoi_height = data->fsl_diu_info[index-1].var.yres;
0670         upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
0671         upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
0672         if (var->xres > base_plane_width)
0673             var->xres = base_plane_width;
0674         if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
0675             mfbi->x_aoi_d = base_plane_width - var->xres;
0676         if (mfbi->y_aoi_d < 0)
0677             mfbi->y_aoi_d = 0;
0678         if (upper_aoi_is_open) {
0679             if (mfbi->y_aoi_d < upper_aoi_bottom)
0680                 mfbi->y_aoi_d = upper_aoi_bottom;
0681             available_height = base_plane_height
0682                         - upper_aoi_bottom;
0683         } else
0684             available_height = base_plane_height;
0685         if (var->yres > available_height)
0686             var->yres = available_height;
0687         if ((mfbi->y_aoi_d + var->yres) > base_plane_height)
0688             mfbi->y_aoi_d = base_plane_height - var->yres;
0689         break;
0690     }
0691 }
0692 /*
0693  * Checks to see if the hardware supports the state requested by var passed
0694  * in. This function does not alter the hardware state! If the var passed in
0695  * is slightly off by what the hardware can support then we alter the var
0696  * PASSED in to what we can do. If the hardware doesn't support mode change
0697  * a -EINVAL will be returned by the upper layers.
0698  */
0699 static int fsl_diu_check_var(struct fb_var_screeninfo *var,
0700                 struct fb_info *info)
0701 {
0702     if (var->xres_virtual < var->xres)
0703         var->xres_virtual = var->xres;
0704     if (var->yres_virtual < var->yres)
0705         var->yres_virtual = var->yres;
0706 
0707     if (var->xoffset + info->var.xres > info->var.xres_virtual)
0708         var->xoffset = info->var.xres_virtual - info->var.xres;
0709 
0710     if (var->yoffset + info->var.yres > info->var.yres_virtual)
0711         var->yoffset = info->var.yres_virtual - info->var.yres;
0712 
0713     if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
0714         (var->bits_per_pixel != 16))
0715         var->bits_per_pixel = default_bpp;
0716 
0717     switch (var->bits_per_pixel) {
0718     case 16:
0719         var->red.length = 5;
0720         var->red.offset = 11;
0721         var->red.msb_right = 0;
0722 
0723         var->green.length = 6;
0724         var->green.offset = 5;
0725         var->green.msb_right = 0;
0726 
0727         var->blue.length = 5;
0728         var->blue.offset = 0;
0729         var->blue.msb_right = 0;
0730 
0731         var->transp.length = 0;
0732         var->transp.offset = 0;
0733         var->transp.msb_right = 0;
0734         break;
0735     case 24:
0736         var->red.length = 8;
0737         var->red.offset = 0;
0738         var->red.msb_right = 0;
0739 
0740         var->green.length = 8;
0741         var->green.offset = 8;
0742         var->green.msb_right = 0;
0743 
0744         var->blue.length = 8;
0745         var->blue.offset = 16;
0746         var->blue.msb_right = 0;
0747 
0748         var->transp.length = 0;
0749         var->transp.offset = 0;
0750         var->transp.msb_right = 0;
0751         break;
0752     case 32:
0753         var->red.length = 8;
0754         var->red.offset = 16;
0755         var->red.msb_right = 0;
0756 
0757         var->green.length = 8;
0758         var->green.offset = 8;
0759         var->green.msb_right = 0;
0760 
0761         var->blue.length = 8;
0762         var->blue.offset = 0;
0763         var->blue.msb_right = 0;
0764 
0765         var->transp.length = 8;
0766         var->transp.offset = 24;
0767         var->transp.msb_right = 0;
0768 
0769         break;
0770     }
0771 
0772     var->height = -1;
0773     var->width = -1;
0774     var->grayscale = 0;
0775 
0776     /* Copy nonstd field to/from sync for fbset usage */
0777     var->sync |= var->nonstd;
0778     var->nonstd |= var->sync;
0779 
0780     adjust_aoi_size_position(var, info);
0781     return 0;
0782 }
0783 
0784 static void set_fix(struct fb_info *info)
0785 {
0786     struct fb_fix_screeninfo *fix = &info->fix;
0787     struct fb_var_screeninfo *var = &info->var;
0788     struct mfb_info *mfbi = info->par;
0789 
0790     strncpy(fix->id, mfbi->id, sizeof(fix->id));
0791     fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
0792     fix->type = FB_TYPE_PACKED_PIXELS;
0793     fix->accel = FB_ACCEL_NONE;
0794     fix->visual = FB_VISUAL_TRUECOLOR;
0795     fix->xpanstep = 1;
0796     fix->ypanstep = 1;
0797 }
0798 
0799 static void update_lcdc(struct fb_info *info)
0800 {
0801     struct fb_var_screeninfo *var = &info->var;
0802     struct mfb_info *mfbi = info->par;
0803     struct fsl_diu_data *data = mfbi->parent;
0804     struct diu __iomem *hw;
0805     int i, j;
0806     u8 *gamma_table_base;
0807 
0808     u32 temp;
0809 
0810     hw = data->diu_reg;
0811 
0812     if (diu_ops.set_monitor_port)
0813         diu_ops.set_monitor_port(data->monitor_port);
0814     gamma_table_base = data->gamma;
0815 
0816     /* Prep for DIU init  - gamma table, cursor table */
0817 
0818     for (i = 0; i <= 2; i++)
0819         for (j = 0; j <= 255; j++)
0820             *gamma_table_base++ = j;
0821 
0822     if (diu_ops.set_gamma_table)
0823         diu_ops.set_gamma_table(data->monitor_port, data->gamma);
0824 
0825     disable_lcdc(info);
0826 
0827     /* Program DIU registers */
0828 
0829     out_be32(&hw->gamma, DMA_ADDR(data, gamma));
0830 
0831     out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */
0832     out_be32(&hw->disp_size, (var->yres << 16) | var->xres);
0833 
0834     /* Horizontal and vertical configuration register */
0835     temp = var->left_margin << 22 | /* BP_H */
0836            var->hsync_len << 11 |   /* PW_H */
0837            var->right_margin;       /* FP_H */
0838 
0839     out_be32(&hw->hsyn_para, temp);
0840 
0841     temp = var->upper_margin << 22 | /* BP_V */
0842            var->vsync_len << 11 |    /* PW_V  */
0843            var->lower_margin;        /* FP_V  */
0844 
0845     out_be32(&hw->vsyn_para, temp);
0846 
0847     diu_ops.set_pixel_clock(var->pixclock);
0848 
0849 #ifndef CONFIG_PPC_MPC512x
0850     /*
0851      * The PLUT register is defined differently on the MPC5121 than it
0852      * is on other SOCs.  Unfortunately, there's no documentation that
0853      * explains how it's supposed to be programmed, so for now, we leave
0854      * it at the default value on the MPC5121.
0855      *
0856      * For other SOCs, program it for the highest priority, which will
0857      * reduce the chance of underrun. Technically, we should scale the
0858      * priority to match the screen resolution, but doing that properly
0859      * requires delicate fine-tuning for each use-case.
0860      */
0861     out_be32(&hw->plut, 0x01F5F666);
0862 #endif
0863 
0864     /* Enable the DIU */
0865     enable_lcdc(info);
0866 }
0867 
0868 static int map_video_memory(struct fb_info *info)
0869 {
0870     u32 smem_len = info->fix.line_length * info->var.yres_virtual;
0871     void *p;
0872 
0873     p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO);
0874     if (!p) {
0875         dev_err(info->dev, "unable to allocate fb memory\n");
0876         return -ENOMEM;
0877     }
0878     mutex_lock(&info->mm_lock);
0879     info->screen_base = p;
0880     info->fix.smem_start = virt_to_phys(info->screen_base);
0881     info->fix.smem_len = smem_len;
0882     mutex_unlock(&info->mm_lock);
0883     info->screen_size = info->fix.smem_len;
0884 
0885     return 0;
0886 }
0887 
0888 static void unmap_video_memory(struct fb_info *info)
0889 {
0890     void *p = info->screen_base;
0891     size_t l = info->fix.smem_len;
0892 
0893     mutex_lock(&info->mm_lock);
0894     info->screen_base = NULL;
0895     info->fix.smem_start = 0;
0896     info->fix.smem_len = 0;
0897     mutex_unlock(&info->mm_lock);
0898 
0899     if (p)
0900         free_pages_exact(p, l);
0901 }
0902 
0903 /*
0904  * Using the fb_var_screeninfo in fb_info we set the aoi of this
0905  * particular framebuffer. It is a light version of fsl_diu_set_par.
0906  */
0907 static int fsl_diu_set_aoi(struct fb_info *info)
0908 {
0909     struct fb_var_screeninfo *var = &info->var;
0910     struct mfb_info *mfbi = info->par;
0911     struct diu_ad *ad = mfbi->ad;
0912 
0913     /* AOI should not be greater than display size */
0914     ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
0915     ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
0916     return 0;
0917 }
0918 
0919 /**
0920  * fsl_diu_get_pixel_format: return the pixel format for a given color depth
0921  *
0922  * The pixel format is a 32-bit value that determine which bits in each
0923  * pixel are to be used for each color.  This is the default function used
0924  * if the platform does not define its own version.
0925  */
0926 static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)
0927 {
0928 #define PF_BYTE_F       0x10000000
0929 #define PF_ALPHA_C_MASK     0x0E000000
0930 #define PF_ALPHA_C_SHIFT    25
0931 #define PF_BLUE_C_MASK      0x01800000
0932 #define PF_BLUE_C_SHIFT     23
0933 #define PF_GREEN_C_MASK     0x00600000
0934 #define PF_GREEN_C_SHIFT    21
0935 #define PF_RED_C_MASK       0x00180000
0936 #define PF_RED_C_SHIFT      19
0937 #define PF_PALETTE      0x00040000
0938 #define PF_PIXEL_S_MASK     0x00030000
0939 #define PF_PIXEL_S_SHIFT    16
0940 #define PF_COMP_3_MASK      0x0000F000
0941 #define PF_COMP_3_SHIFT     12
0942 #define PF_COMP_2_MASK      0x00000F00
0943 #define PF_COMP_2_SHIFT     8
0944 #define PF_COMP_1_MASK      0x000000F0
0945 #define PF_COMP_1_SHIFT     4
0946 #define PF_COMP_0_MASK      0x0000000F
0947 #define PF_COMP_0_SHIFT     0
0948 
0949 #define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \
0950     cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \
0951     (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \
0952     (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \
0953     (c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \
0954     (c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT))
0955 
0956     switch (bits_per_pixel) {
0957     case 32:
0958         /* 0x88883316 */
0959         return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8);
0960     case 24:
0961         /* 0x88082219 */
0962         return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0);
0963     case 16:
0964         /* 0x65053118 */
0965         return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);
0966     default:
0967         pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel);
0968         return 0;
0969     }
0970 }
0971 
0972 /*
0973  * Copies a cursor image from user space to the proper place in driver
0974  * memory so that the hardware can display the cursor image.
0975  *
0976  * Cursor data is represented as a sequence of 'width' bits packed into bytes.
0977  * That is, the first 8 bits are in the first byte, the second 8 bits in the
0978  * second byte, and so on.  Therefore, the each row of the cursor is (width +
0979  * 7) / 8 bytes of 'data'
0980  *
0981  * The DIU only supports cursors up to 32x32 (MAX_CURS).  We reject cursors
0982  * larger than this, so we already know that 'width' <= 32.  Therefore, we can
0983  * simplify our code by using a 32-bit big-endian integer ("line") to read in
0984  * a single line of pixels, and only look at the top 'width' bits of that
0985  * integer.
0986  *
0987  * This could result in an unaligned 32-bit read.  For example, if the cursor
0988  * is 24x24, then the first three bytes of 'image' contain the pixel data for
0989  * the top line of the cursor.  We do a 32-bit read of 'image', but we look
0990  * only at the top 24 bits.  Then we increment 'image' by 3 bytes.  The next
0991  * read is unaligned.  The only problem is that we might read past the end of
0992  * 'image' by 1-3 bytes, but that should not cause any problems.
0993  */
0994 static void fsl_diu_load_cursor_image(struct fb_info *info,
0995     const void *image, uint16_t bg, uint16_t fg,
0996     unsigned int width, unsigned int height)
0997 {
0998     struct mfb_info *mfbi = info->par;
0999     struct fsl_diu_data *data = mfbi->parent;
1000     __le16 *cursor = data->cursor;
1001     __le16 _fg = cpu_to_le16(fg);
1002     __le16 _bg = cpu_to_le16(bg);
1003     unsigned int h, w;
1004 
1005     for (h = 0; h < height; h++) {
1006         uint32_t mask = 1 << 31;
1007         uint32_t line = be32_to_cpup(image);
1008 
1009         for (w = 0; w < width; w++) {
1010             cursor[w] = (line & mask) ? _fg : _bg;
1011             mask >>= 1;
1012         }
1013 
1014         cursor += MAX_CURS;
1015         image += DIV_ROUND_UP(width, 8);
1016     }
1017 }
1018 
1019 /*
1020  * Set a hardware cursor.  The image data for the cursor is passed via the
1021  * fb_cursor object.
1022  */
1023 static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor)
1024 {
1025     struct mfb_info *mfbi = info->par;
1026     struct fsl_diu_data *data = mfbi->parent;
1027     struct diu __iomem *hw = data->diu_reg;
1028 
1029     if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
1030         return -EINVAL;
1031 
1032     /* The cursor size has changed */
1033     if (cursor->set & FB_CUR_SETSIZE) {
1034         /*
1035          * The DIU cursor is a fixed size, so when we get this
1036          * message, instead of resizing the cursor, we just clear
1037          * all the image data, in expectation of new data.  However,
1038          * in tests this control does not appear to be normally
1039          * called.
1040          */
1041         memset(data->cursor, 0, sizeof(data->cursor));
1042     }
1043 
1044     /* The cursor position has changed (cursor->image.dx|dy) */
1045     if (cursor->set & FB_CUR_SETPOS) {
1046         uint32_t xx, yy;
1047 
1048         yy = (cursor->image.dy - info->var.yoffset) & 0x7ff;
1049         xx = (cursor->image.dx - info->var.xoffset) & 0x7ff;
1050 
1051         out_be32(&hw->curs_pos, yy << 16 | xx);
1052     }
1053 
1054     /*
1055      * FB_CUR_SETIMAGE - the cursor image has changed
1056      * FB_CUR_SETCMAP  - the cursor colors has changed
1057      * FB_CUR_SETSHAPE - the cursor bitmask has changed
1058      */
1059     if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
1060         /*
1061          * Determine the size of the cursor image data.  Normally,
1062          * it's 8x16.
1063          */
1064         unsigned int image_size =
1065             DIV_ROUND_UP(cursor->image.width, 8) *
1066             cursor->image.height;
1067         unsigned int image_words =
1068             DIV_ROUND_UP(image_size, sizeof(uint32_t));
1069         unsigned int bg_idx = cursor->image.bg_color;
1070         unsigned int fg_idx = cursor->image.fg_color;
1071         uint32_t *image, *source, *mask;
1072         uint16_t fg, bg;
1073         unsigned int i;
1074 
1075         if (info->state != FBINFO_STATE_RUNNING)
1076             return 0;
1077 
1078         bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
1079              ((info->cmap.green[bg_idx] & 0xf8) << 2) |
1080              ((info->cmap.blue[bg_idx] & 0xf8) >> 3) |
1081              1 << 15;
1082 
1083         fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
1084              ((info->cmap.green[fg_idx] & 0xf8) << 2) |
1085              ((info->cmap.blue[fg_idx] & 0xf8) >> 3) |
1086              1 << 15;
1087 
1088         /* Use 32-bit operations on the data to improve performance */
1089         image = (uint32_t *)data->next_cursor;
1090         source = (uint32_t *)cursor->image.data;
1091         mask = (uint32_t *)cursor->mask;
1092 
1093         if (cursor->rop == ROP_XOR)
1094             for (i = 0; i < image_words; i++)
1095                 image[i] = source[i] ^ mask[i];
1096         else
1097             for (i = 0; i < image_words; i++)
1098                 image[i] = source[i] & mask[i];
1099 
1100         fsl_diu_load_cursor_image(info, image, bg, fg,
1101             cursor->image.width, cursor->image.height);
1102     }
1103 
1104     /*
1105      * Show or hide the cursor.  The cursor data is always stored in the
1106      * 'cursor' memory block, and the actual cursor position is always in
1107      * the DIU's CURS_POS register.  To hide the cursor, we redirect the
1108      * CURSOR register to a blank cursor.  The show the cursor, we
1109      * redirect the CURSOR register to the real cursor data.
1110      */
1111     if (cursor->enable)
1112         out_be32(&hw->cursor, DMA_ADDR(data, cursor));
1113     else
1114         out_be32(&hw->cursor, DMA_ADDR(data, blank_cursor));
1115 
1116     return 0;
1117 }
1118 
1119 /*
1120  * Using the fb_var_screeninfo in fb_info we set the resolution of this
1121  * particular framebuffer. This function alters the fb_fix_screeninfo stored
1122  * in fb_info. It does not alter var in fb_info since we are using that
1123  * data. This means we depend on the data in var inside fb_info to be
1124  * supported by the hardware. fsl_diu_check_var is always called before
1125  * fsl_diu_set_par to ensure this.
1126  */
1127 static int fsl_diu_set_par(struct fb_info *info)
1128 {
1129     unsigned long len;
1130     struct fb_var_screeninfo *var = &info->var;
1131     struct mfb_info *mfbi = info->par;
1132     struct fsl_diu_data *data = mfbi->parent;
1133     struct diu_ad *ad = mfbi->ad;
1134     struct diu __iomem *hw;
1135 
1136     hw = data->diu_reg;
1137 
1138     set_fix(info);
1139 
1140     len = info->var.yres_virtual * info->fix.line_length;
1141     /* Alloc & dealloc each time resolution/bpp change */
1142     if (len != info->fix.smem_len) {
1143         if (info->fix.smem_start)
1144             unmap_video_memory(info);
1145 
1146         /* Memory allocation for framebuffer */
1147         if (map_video_memory(info)) {
1148             dev_err(info->dev, "unable to allocate fb memory 1\n");
1149             return -ENOMEM;
1150         }
1151     }
1152 
1153     if (diu_ops.get_pixel_format)
1154         ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port,
1155                                var->bits_per_pixel);
1156     else
1157         ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel);
1158 
1159     ad->addr    = cpu_to_le32(info->fix.smem_start);
1160     ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
1161                 var->xres_virtual) | mfbi->g_alpha;
1162     /* AOI should not be greater than display size */
1163     ad->aoi_size    = cpu_to_le32((var->yres << 16) | var->xres);
1164     ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);
1165     ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
1166 
1167     /* Disable chroma keying function */
1168     ad->ckmax_r = 0;
1169     ad->ckmax_g = 0;
1170     ad->ckmax_b = 0;
1171 
1172     ad->ckmin_r = 255;
1173     ad->ckmin_g = 255;
1174     ad->ckmin_b = 255;
1175 
1176     if (mfbi->index == PLANE0)
1177         update_lcdc(info);
1178     return 0;
1179 }
1180 
1181 static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
1182 {
1183     return ((val << width) + 0x7FFF - val) >> 16;
1184 }
1185 
1186 /*
1187  * Set a single color register. The values supplied have a 16 bit magnitude
1188  * which needs to be scaled in this function for the hardware. Things to take
1189  * into consideration are how many color registers, if any, are supported with
1190  * the current color visual. With truecolor mode no color palettes are
1191  * supported. Here a pseudo palette is created which we store the value in
1192  * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
1193  * color palette.
1194  */
1195 static int fsl_diu_setcolreg(unsigned int regno, unsigned int red,
1196                  unsigned int green, unsigned int blue,
1197                  unsigned int transp, struct fb_info *info)
1198 {
1199     int ret = 1;
1200 
1201     /*
1202      * If greyscale is true, then we convert the RGB value
1203      * to greyscale no matter what visual we are using.
1204      */
1205     if (info->var.grayscale)
1206         red = green = blue = (19595 * red + 38470 * green +
1207                       7471 * blue) >> 16;
1208     switch (info->fix.visual) {
1209     case FB_VISUAL_TRUECOLOR:
1210         /*
1211          * 16-bit True Colour.  We encode the RGB value
1212          * according to the RGB bitfield information.
1213          */
1214         if (regno < 16) {
1215             u32 *pal = info->pseudo_palette;
1216             u32 v;
1217 
1218             red = CNVT_TOHW(red, info->var.red.length);
1219             green = CNVT_TOHW(green, info->var.green.length);
1220             blue = CNVT_TOHW(blue, info->var.blue.length);
1221             transp = CNVT_TOHW(transp, info->var.transp.length);
1222 
1223             v = (red << info->var.red.offset) |
1224                 (green << info->var.green.offset) |
1225                 (blue << info->var.blue.offset) |
1226                 (transp << info->var.transp.offset);
1227 
1228             pal[regno] = v;
1229             ret = 0;
1230         }
1231         break;
1232     }
1233 
1234     return ret;
1235 }
1236 
1237 /*
1238  * Pan (or wrap, depending on the `vmode' field) the display using the
1239  * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
1240  * don't fit, return -EINVAL.
1241  */
1242 static int fsl_diu_pan_display(struct fb_var_screeninfo *var,
1243                  struct fb_info *info)
1244 {
1245     if ((info->var.xoffset == var->xoffset) &&
1246         (info->var.yoffset == var->yoffset))
1247         return 0;   /* No change, do nothing */
1248 
1249     if (var->xoffset + info->var.xres > info->var.xres_virtual
1250         || var->yoffset + info->var.yres > info->var.yres_virtual)
1251         return -EINVAL;
1252 
1253     info->var.xoffset = var->xoffset;
1254     info->var.yoffset = var->yoffset;
1255 
1256     if (var->vmode & FB_VMODE_YWRAP)
1257         info->var.vmode |= FB_VMODE_YWRAP;
1258     else
1259         info->var.vmode &= ~FB_VMODE_YWRAP;
1260 
1261     fsl_diu_set_aoi(info);
1262 
1263     return 0;
1264 }
1265 
1266 static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
1267                unsigned long arg)
1268 {
1269     struct mfb_info *mfbi = info->par;
1270     struct diu_ad *ad = mfbi->ad;
1271     struct mfb_chroma_key ck;
1272     unsigned char global_alpha;
1273     struct aoi_display_offset aoi_d;
1274     __u32 pix_fmt;
1275     void __user *buf = (void __user *)arg;
1276 
1277     if (!arg)
1278         return -EINVAL;
1279 
1280     dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd,
1281         _IOC_DIR(cmd) & _IOC_READ ? "R" : "",
1282         _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "",
1283         _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
1284 
1285     switch (cmd) {
1286     case MFB_SET_PIXFMT_OLD:
1287         dev_warn(info->dev,
1288              "MFB_SET_PIXFMT value of 0x%08x is deprecated.\n",
1289              MFB_SET_PIXFMT_OLD);
1290         fallthrough;
1291     case MFB_SET_PIXFMT:
1292         if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
1293             return -EFAULT;
1294         ad->pix_fmt = pix_fmt;
1295         break;
1296     case MFB_GET_PIXFMT_OLD:
1297         dev_warn(info->dev,
1298              "MFB_GET_PIXFMT value of 0x%08x is deprecated.\n",
1299              MFB_GET_PIXFMT_OLD);
1300         fallthrough;
1301     case MFB_GET_PIXFMT:
1302         pix_fmt = ad->pix_fmt;
1303         if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
1304             return -EFAULT;
1305         break;
1306     case MFB_SET_AOID:
1307         if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
1308             return -EFAULT;
1309         mfbi->x_aoi_d = aoi_d.x_aoi_d;
1310         mfbi->y_aoi_d = aoi_d.y_aoi_d;
1311         fsl_diu_check_var(&info->var, info);
1312         fsl_diu_set_aoi(info);
1313         break;
1314     case MFB_GET_AOID:
1315         aoi_d.x_aoi_d = mfbi->x_aoi_d;
1316         aoi_d.y_aoi_d = mfbi->y_aoi_d;
1317         if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
1318             return -EFAULT;
1319         break;
1320     case MFB_GET_ALPHA:
1321         global_alpha = mfbi->g_alpha;
1322         if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
1323             return -EFAULT;
1324         break;
1325     case MFB_SET_ALPHA:
1326         /* set panel information */
1327         if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
1328             return -EFAULT;
1329         ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
1330                             (global_alpha & 0xff);
1331         mfbi->g_alpha = global_alpha;
1332         break;
1333     case MFB_SET_CHROMA_KEY:
1334         /* set panel winformation */
1335         if (copy_from_user(&ck, buf, sizeof(ck)))
1336             return -EFAULT;
1337 
1338         if (ck.enable &&
1339            (ck.red_max < ck.red_min ||
1340             ck.green_max < ck.green_min ||
1341             ck.blue_max < ck.blue_min))
1342             return -EINVAL;
1343 
1344         if (!ck.enable) {
1345             ad->ckmax_r = 0;
1346             ad->ckmax_g = 0;
1347             ad->ckmax_b = 0;
1348             ad->ckmin_r = 255;
1349             ad->ckmin_g = 255;
1350             ad->ckmin_b = 255;
1351         } else {
1352             ad->ckmax_r = ck.red_max;
1353             ad->ckmax_g = ck.green_max;
1354             ad->ckmax_b = ck.blue_max;
1355             ad->ckmin_r = ck.red_min;
1356             ad->ckmin_g = ck.green_min;
1357             ad->ckmin_b = ck.blue_min;
1358         }
1359         break;
1360 #ifdef CONFIG_PPC_MPC512x
1361     case MFB_SET_GAMMA: {
1362         struct fsl_diu_data *data = mfbi->parent;
1363 
1364         if (copy_from_user(data->gamma, buf, sizeof(data->gamma)))
1365             return -EFAULT;
1366         setbits32(&data->diu_reg->gamma, 0); /* Force table reload */
1367         break;
1368     }
1369     case MFB_GET_GAMMA: {
1370         struct fsl_diu_data *data = mfbi->parent;
1371 
1372         if (copy_to_user(buf, data->gamma, sizeof(data->gamma)))
1373             return -EFAULT;
1374         break;
1375     }
1376 #endif
1377     default:
1378         dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
1379         return -ENOIOCTLCMD;
1380     }
1381 
1382     return 0;
1383 }
1384 
1385 static inline void fsl_diu_enable_interrupts(struct fsl_diu_data *data)
1386 {
1387     u32 int_mask = INT_UNDRUN; /* enable underrun detection */
1388 
1389     if (IS_ENABLED(CONFIG_NOT_COHERENT_CACHE))
1390         int_mask |= INT_VSYNC; /* enable vertical sync */
1391 
1392     clrbits32(&data->diu_reg->int_mask, int_mask);
1393 }
1394 
1395 /* turn on fb if count == 1
1396  */
1397 static int fsl_diu_open(struct fb_info *info, int user)
1398 {
1399     struct mfb_info *mfbi = info->par;
1400     int res = 0;
1401 
1402     /* free boot splash memory on first /dev/fb0 open */
1403     if ((mfbi->index == PLANE0) && diu_ops.release_bootmem)
1404         diu_ops.release_bootmem();
1405 
1406     spin_lock(&diu_lock);
1407     mfbi->count++;
1408     if (mfbi->count == 1) {
1409         fsl_diu_check_var(&info->var, info);
1410         res = fsl_diu_set_par(info);
1411         if (res < 0)
1412             mfbi->count--;
1413         else {
1414             fsl_diu_enable_interrupts(mfbi->parent);
1415             fsl_diu_enable_panel(info);
1416         }
1417     }
1418 
1419     spin_unlock(&diu_lock);
1420     return res;
1421 }
1422 
1423 /* turn off fb if count == 0
1424  */
1425 static int fsl_diu_release(struct fb_info *info, int user)
1426 {
1427     struct mfb_info *mfbi = info->par;
1428 
1429     spin_lock(&diu_lock);
1430     mfbi->count--;
1431     if (mfbi->count == 0) {
1432         struct fsl_diu_data *data = mfbi->parent;
1433         bool disable = true;
1434         int i;
1435 
1436         /* Disable interrupts only if all AOIs are closed */
1437         for (i = 0; i < NUM_AOIS; i++) {
1438             struct mfb_info *mi = data->fsl_diu_info[i].par;
1439 
1440             if (mi->count)
1441                 disable = false;
1442         }
1443         if (disable)
1444             out_be32(&data->diu_reg->int_mask, 0xffffffff);
1445         fsl_diu_disable_panel(info);
1446     }
1447 
1448     spin_unlock(&diu_lock);
1449     return 0;
1450 }
1451 
1452 static const struct fb_ops fsl_diu_ops = {
1453     .owner = THIS_MODULE,
1454     .fb_check_var = fsl_diu_check_var,
1455     .fb_set_par = fsl_diu_set_par,
1456     .fb_setcolreg = fsl_diu_setcolreg,
1457     .fb_pan_display = fsl_diu_pan_display,
1458     .fb_fillrect = cfb_fillrect,
1459     .fb_copyarea = cfb_copyarea,
1460     .fb_imageblit = cfb_imageblit,
1461     .fb_ioctl = fsl_diu_ioctl,
1462     .fb_open = fsl_diu_open,
1463     .fb_release = fsl_diu_release,
1464     .fb_cursor = fsl_diu_cursor,
1465 };
1466 
1467 static int install_fb(struct fb_info *info)
1468 {
1469     int rc;
1470     struct mfb_info *mfbi = info->par;
1471     struct fsl_diu_data *data = mfbi->parent;
1472     const char *aoi_mode, *init_aoi_mode = "320x240";
1473     struct fb_videomode *db = fsl_diu_mode_db;
1474     unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
1475     int has_default_mode = 1;
1476 
1477     info->var.activate = FB_ACTIVATE_NOW;
1478     info->fbops = &fsl_diu_ops;
1479     info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK |
1480         FBINFO_READS_FAST;
1481     info->pseudo_palette = mfbi->pseudo_palette;
1482 
1483     rc = fb_alloc_cmap(&info->cmap, 16, 0);
1484     if (rc)
1485         return rc;
1486 
1487     if (mfbi->index == PLANE0) {
1488         if (data->has_edid) {
1489             /* Now build modedb from EDID */
1490             fb_edid_to_monspecs(data->edid_data, &info->monspecs);
1491             fb_videomode_to_modelist(info->monspecs.modedb,
1492                          info->monspecs.modedb_len,
1493                          &info->modelist);
1494             db = info->monspecs.modedb;
1495             dbsize = info->monspecs.modedb_len;
1496         }
1497         aoi_mode = fb_mode;
1498     } else {
1499         aoi_mode = init_aoi_mode;
1500     }
1501     rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,
1502               default_bpp);
1503     if (!rc) {
1504         /*
1505          * For plane 0 we continue and look into
1506          * driver's internal modedb.
1507          */
1508         if ((mfbi->index == PLANE0) && data->has_edid)
1509             has_default_mode = 0;
1510         else
1511             return -EINVAL;
1512     }
1513 
1514     if (!has_default_mode) {
1515         rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
1516             ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);
1517         if (rc)
1518             has_default_mode = 1;
1519     }
1520 
1521     /* Still not found, use preferred mode from database if any */
1522     if (!has_default_mode && info->monspecs.modedb) {
1523         struct fb_monspecs *specs = &info->monspecs;
1524         struct fb_videomode *modedb = &specs->modedb[0];
1525 
1526         /*
1527          * Get preferred timing. If not found,
1528          * first mode in database will be used.
1529          */
1530         if (specs->misc & FB_MISC_1ST_DETAIL) {
1531             int i;
1532 
1533             for (i = 0; i < specs->modedb_len; i++) {
1534                 if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
1535                     modedb = &specs->modedb[i];
1536                     break;
1537                 }
1538             }
1539         }
1540 
1541         info->var.bits_per_pixel = default_bpp;
1542         fb_videomode_to_var(&info->var, modedb);
1543     }
1544 
1545     if (fsl_diu_check_var(&info->var, info)) {
1546         dev_err(info->dev, "fsl_diu_check_var failed\n");
1547         unmap_video_memory(info);
1548         fb_dealloc_cmap(&info->cmap);
1549         return -EINVAL;
1550     }
1551 
1552     if (register_framebuffer(info) < 0) {
1553         dev_err(info->dev, "register_framebuffer failed\n");
1554         unmap_video_memory(info);
1555         fb_dealloc_cmap(&info->cmap);
1556         return -EINVAL;
1557     }
1558 
1559     mfbi->registered = 1;
1560     dev_info(info->dev, "%s registered successfully\n", mfbi->id);
1561 
1562     return 0;
1563 }
1564 
1565 static void uninstall_fb(struct fb_info *info)
1566 {
1567     struct mfb_info *mfbi = info->par;
1568 
1569     if (!mfbi->registered)
1570         return;
1571 
1572     unregister_framebuffer(info);
1573     unmap_video_memory(info);
1574     fb_dealloc_cmap(&info->cmap);
1575 
1576     mfbi->registered = 0;
1577 }
1578 
1579 static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
1580 {
1581     struct diu __iomem *hw = dev_id;
1582     uint32_t status = in_be32(&hw->int_status);
1583 
1584     if (status) {
1585         /* This is the workaround for underrun */
1586         if (status & INT_UNDRUN) {
1587             out_be32(&hw->diu_mode, 0);
1588             udelay(1);
1589             out_be32(&hw->diu_mode, 1);
1590         }
1591 #if defined(CONFIG_NOT_COHERENT_CACHE)
1592         else if (status & INT_VSYNC) {
1593             unsigned int i;
1594 
1595             for (i = 0; i < coherence_data_size;
1596                 i += d_cache_line_size)
1597                 __asm__ __volatile__ (
1598                     "dcbz 0, %[input]"
1599                 ::[input]"r"(&coherence_data[i]));
1600         }
1601 #endif
1602         return IRQ_HANDLED;
1603     }
1604     return IRQ_NONE;
1605 }
1606 
1607 #ifdef CONFIG_PM
1608 /*
1609  * Power management hooks. Note that we won't be called from IRQ context,
1610  * unlike the blank functions above, so we may sleep.
1611  */
1612 static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
1613 {
1614     struct fsl_diu_data *data;
1615 
1616     data = dev_get_drvdata(&ofdev->dev);
1617     disable_lcdc(data->fsl_diu_info);
1618 
1619     return 0;
1620 }
1621 
1622 static int fsl_diu_resume(struct platform_device *ofdev)
1623 {
1624     struct fsl_diu_data *data;
1625     unsigned int i;
1626 
1627     data = dev_get_drvdata(&ofdev->dev);
1628 
1629     fsl_diu_enable_interrupts(data);
1630     update_lcdc(data->fsl_diu_info);
1631     for (i = 0; i < NUM_AOIS; i++) {
1632         if (data->mfb[i].count)
1633             fsl_diu_enable_panel(&data->fsl_diu_info[i]);
1634     }
1635 
1636     return 0;
1637 }
1638 
1639 #else
1640 #define fsl_diu_suspend NULL
1641 #define fsl_diu_resume NULL
1642 #endif              /* CONFIG_PM */
1643 
1644 static ssize_t store_monitor(struct device *device,
1645     struct device_attribute *attr, const char *buf, size_t count)
1646 {
1647     enum fsl_diu_monitor_port old_monitor_port;
1648     struct fsl_diu_data *data =
1649         container_of(attr, struct fsl_diu_data, dev_attr);
1650 
1651     old_monitor_port = data->monitor_port;
1652     data->monitor_port = fsl_diu_name_to_port(buf);
1653 
1654     if (old_monitor_port != data->monitor_port) {
1655         /* All AOIs need adjust pixel format
1656          * fsl_diu_set_par only change the pixsel format here
1657          * unlikely to fail. */
1658         unsigned int i;
1659 
1660         for (i=0; i < NUM_AOIS; i++)
1661             fsl_diu_set_par(&data->fsl_diu_info[i]);
1662     }
1663     return count;
1664 }
1665 
1666 static ssize_t show_monitor(struct device *device,
1667     struct device_attribute *attr, char *buf)
1668 {
1669     struct fsl_diu_data *data =
1670         container_of(attr, struct fsl_diu_data, dev_attr);
1671 
1672     switch (data->monitor_port) {
1673     case FSL_DIU_PORT_DVI:
1674         return sprintf(buf, "DVI\n");
1675     case FSL_DIU_PORT_LVDS:
1676         return sprintf(buf, "Single-link LVDS\n");
1677     case FSL_DIU_PORT_DLVDS:
1678         return sprintf(buf, "Dual-link LVDS\n");
1679     }
1680 
1681     return 0;
1682 }
1683 
1684 static int fsl_diu_probe(struct platform_device *pdev)
1685 {
1686     struct device_node *np = pdev->dev.of_node;
1687     struct mfb_info *mfbi;
1688     struct fsl_diu_data *data;
1689     dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */
1690     const void *prop;
1691     unsigned int i;
1692     int ret;
1693 
1694     data = dmam_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
1695                    &dma_addr, GFP_DMA | __GFP_ZERO);
1696     if (!data)
1697         return -ENOMEM;
1698     data->dma_addr = dma_addr;
1699 
1700     /*
1701      * dma_alloc_coherent() uses a page allocator, so the address is
1702      * always page-aligned.  We need the memory to be 32-byte aligned,
1703      * so that's good.  However, if one day the allocator changes, we
1704      * need to catch that.  It's not worth the effort to handle unaligned
1705      * alloctions now because it's highly unlikely to ever be a problem.
1706      */
1707     if ((unsigned long)data & 31) {
1708         dev_err(&pdev->dev, "misaligned allocation");
1709         ret = -ENOMEM;
1710         goto error;
1711     }
1712 
1713     spin_lock_init(&data->reg_lock);
1714 
1715     for (i = 0; i < NUM_AOIS; i++) {
1716         struct fb_info *info = &data->fsl_diu_info[i];
1717 
1718         info->device = &pdev->dev;
1719         info->par = &data->mfb[i];
1720 
1721         /*
1722          * We store the physical address of the AD in the reserved
1723          * 'paddr' field of the AD itself.
1724          */
1725         data->ad[i].paddr = DMA_ADDR(data, ad[i]);
1726 
1727         info->fix.smem_start = 0;
1728 
1729         /* Initialize the AOI data structure */
1730         mfbi = info->par;
1731         memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
1732         mfbi->parent = data;
1733         mfbi->ad = &data->ad[i];
1734     }
1735 
1736     /* Get the EDID data from the device tree, if present */
1737     prop = of_get_property(np, "edid", &ret);
1738     if (prop && ret == EDID_LENGTH) {
1739         memcpy(data->edid_data, prop, EDID_LENGTH);
1740         data->has_edid = true;
1741     }
1742 
1743     data->diu_reg = of_iomap(np, 0);
1744     if (!data->diu_reg) {
1745         dev_err(&pdev->dev, "cannot map DIU registers\n");
1746         ret = -EFAULT;
1747         goto error;
1748     }
1749 
1750     /* Get the IRQ of the DIU */
1751     data->irq = irq_of_parse_and_map(np, 0);
1752 
1753     if (!data->irq) {
1754         dev_err(&pdev->dev, "could not get DIU IRQ\n");
1755         ret = -EINVAL;
1756         goto error;
1757     }
1758     data->monitor_port = monitor_port;
1759 
1760     /* Initialize the dummy Area Descriptor */
1761     data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi));
1762     data->dummy_ad.pix_fmt = 0x88882317;
1763     data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
1764     data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) |  2);
1765     data->dummy_ad.offset_xyi = 0;
1766     data->dummy_ad.offset_xyd = 0;
1767     data->dummy_ad.next_ad = 0;
1768     data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);
1769 
1770     /*
1771      * Let DIU continue to display splash screen if it was pre-initialized
1772      * by the bootloader; otherwise, clear the display.
1773      */
1774     if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)
1775         out_be32(&data->diu_reg->desc[0], 0);
1776 
1777     out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);
1778     out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);
1779 
1780     /*
1781      * Older versions of U-Boot leave interrupts enabled, so disable
1782      * all of them and clear the status register.
1783      */
1784     out_be32(&data->diu_reg->int_mask, 0xffffffff);
1785     in_be32(&data->diu_reg->int_status);
1786 
1787     ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",
1788               data->diu_reg);
1789     if (ret) {
1790         dev_err(&pdev->dev, "could not claim irq\n");
1791         goto error;
1792     }
1793 
1794     for (i = 0; i < NUM_AOIS; i++) {
1795         ret = install_fb(&data->fsl_diu_info[i]);
1796         if (ret) {
1797             dev_err(&pdev->dev, "could not register fb %d\n", i);
1798             free_irq(data->irq, data->diu_reg);
1799             goto error;
1800         }
1801     }
1802 
1803     sysfs_attr_init(&data->dev_attr.attr);
1804     data->dev_attr.attr.name = "monitor";
1805     data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
1806     data->dev_attr.show = show_monitor;
1807     data->dev_attr.store = store_monitor;
1808     ret = device_create_file(&pdev->dev, &data->dev_attr);
1809     if (ret) {
1810         dev_err(&pdev->dev, "could not create sysfs file %s\n",
1811             data->dev_attr.attr.name);
1812     }
1813 
1814     dev_set_drvdata(&pdev->dev, data);
1815     return 0;
1816 
1817 error:
1818     for (i = 0; i < NUM_AOIS; i++)
1819         uninstall_fb(&data->fsl_diu_info[i]);
1820 
1821     iounmap(data->diu_reg);
1822 
1823     return ret;
1824 }
1825 
1826 static int fsl_diu_remove(struct platform_device *pdev)
1827 {
1828     struct fsl_diu_data *data;
1829     int i;
1830 
1831     data = dev_get_drvdata(&pdev->dev);
1832     disable_lcdc(&data->fsl_diu_info[0]);
1833 
1834     free_irq(data->irq, data->diu_reg);
1835 
1836     for (i = 0; i < NUM_AOIS; i++)
1837         uninstall_fb(&data->fsl_diu_info[i]);
1838 
1839     iounmap(data->diu_reg);
1840 
1841     return 0;
1842 }
1843 
1844 #ifndef MODULE
1845 static int __init fsl_diu_setup(char *options)
1846 {
1847     char *opt;
1848     unsigned long val;
1849 
1850     if (!options || !*options)
1851         return 0;
1852 
1853     while ((opt = strsep(&options, ",")) != NULL) {
1854         if (!*opt)
1855             continue;
1856         if (!strncmp(opt, "monitor=", 8)) {
1857             monitor_port = fsl_diu_name_to_port(opt + 8);
1858         } else if (!strncmp(opt, "bpp=", 4)) {
1859             if (!kstrtoul(opt + 4, 10, &val))
1860                 default_bpp = val;
1861         } else
1862             fb_mode = opt;
1863     }
1864 
1865     return 0;
1866 }
1867 #endif
1868 
1869 static const struct of_device_id fsl_diu_match[] = {
1870 #ifdef CONFIG_PPC_MPC512x
1871     {
1872         .compatible = "fsl,mpc5121-diu",
1873     },
1874 #endif
1875     {
1876         .compatible = "fsl,diu",
1877     },
1878     {}
1879 };
1880 MODULE_DEVICE_TABLE(of, fsl_diu_match);
1881 
1882 static struct platform_driver fsl_diu_driver = {
1883     .driver = {
1884         .name = "fsl-diu-fb",
1885         .of_match_table = fsl_diu_match,
1886     },
1887     .probe      = fsl_diu_probe,
1888     .remove     = fsl_diu_remove,
1889     .suspend    = fsl_diu_suspend,
1890     .resume     = fsl_diu_resume,
1891 };
1892 
1893 static int __init fsl_diu_init(void)
1894 {
1895 #ifdef CONFIG_NOT_COHERENT_CACHE
1896     struct device_node *np;
1897     const u32 *prop;
1898 #endif
1899     int ret;
1900 #ifndef MODULE
1901     char *option;
1902 
1903     /*
1904      * For kernel boot options (in 'video=xxxfb:<options>' format)
1905      */
1906     if (fb_get_options("fslfb", &option))
1907         return -ENODEV;
1908     fsl_diu_setup(option);
1909 #else
1910     monitor_port = fsl_diu_name_to_port(monitor_string);
1911 #endif
1912 
1913     /*
1914      * Must to verify set_pixel_clock. If not implement on platform,
1915      * then that means that there is no platform support for the DIU.
1916      */
1917     if (!diu_ops.set_pixel_clock)
1918         return -ENODEV;
1919 
1920     pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");
1921 
1922 #ifdef CONFIG_NOT_COHERENT_CACHE
1923     np = of_get_cpu_node(0, NULL);
1924     if (!np) {
1925         pr_err("fsl-diu-fb: can't find 'cpu' device node\n");
1926         return -ENODEV;
1927     }
1928 
1929     prop = of_get_property(np, "d-cache-size", NULL);
1930     if (prop == NULL) {
1931         pr_err("fsl-diu-fb: missing 'd-cache-size' property' "
1932                "in 'cpu' node\n");
1933         of_node_put(np);
1934         return -ENODEV;
1935     }
1936 
1937     /*
1938      * Freescale PLRU requires 13/8 times the cache size to do a proper
1939      * displacement flush
1940      */
1941     coherence_data_size = be32_to_cpup(prop) * 13;
1942     coherence_data_size /= 8;
1943 
1944     pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",
1945          coherence_data_size);
1946 
1947     prop = of_get_property(np, "d-cache-line-size", NULL);
1948     if (prop == NULL) {
1949         pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
1950                "in 'cpu' node\n");
1951         of_node_put(np);
1952         return -ENODEV;
1953     }
1954     d_cache_line_size = be32_to_cpup(prop);
1955 
1956     pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",
1957          d_cache_line_size);
1958 
1959     of_node_put(np);
1960     coherence_data = vmalloc(coherence_data_size);
1961     if (!coherence_data)
1962         return -ENOMEM;
1963 #endif
1964 
1965     ret = platform_driver_register(&fsl_diu_driver);
1966     if (ret) {
1967         pr_err("fsl-diu-fb: failed to register platform driver\n");
1968 #if defined(CONFIG_NOT_COHERENT_CACHE)
1969         vfree(coherence_data);
1970 #endif
1971     }
1972     return ret;
1973 }
1974 
1975 static void __exit fsl_diu_exit(void)
1976 {
1977     platform_driver_unregister(&fsl_diu_driver);
1978 #if defined(CONFIG_NOT_COHERENT_CACHE)
1979     vfree(coherence_data);
1980 #endif
1981 }
1982 
1983 module_init(fsl_diu_init);
1984 module_exit(fsl_diu_exit);
1985 
1986 MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
1987 MODULE_DESCRIPTION("Freescale DIU framebuffer driver");
1988 MODULE_LICENSE("GPL");
1989 
1990 module_param_named(mode, fb_mode, charp, 0);
1991 MODULE_PARM_DESC(mode,
1992     "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
1993 module_param_named(bpp, default_bpp, ulong, 0);
1994 MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");
1995 module_param_named(monitor, monitor_string, charp, 0);
1996 MODULE_PARM_DESC(monitor, "Specify the monitor port "
1997     "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");
1998