1995 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.4 *5 * Freescale DIU Frame Buffer device driver6 *7 * Authors: Hongjun Chen <hong-jun.chen@freescale.com>8 * Paul Widmer <paul.widmer@freescale.com>9 * Srikanth Srinivasan <srikanth.srinivasan@freescale.com>10 * York Sun <yorksun@freescale.com>11 *12 * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix13 */14 15#include <linux/module.h>16#include <linux/kernel.h>17#include <linux/errno.h>18#include <linux/string.h>19#include <linux/slab.h>20#include <linux/fb.h>21#include <linux/init.h>22#include <linux/dma-mapping.h>23#include <linux/platform_device.h>24#include <linux/interrupt.h>25#include <linux/clk.h>26#include <linux/uaccess.h>27#include <linux/vmalloc.h>28#include <linux/spinlock.h>29#include <linux/of_address.h>30#include <linux/of_irq.h>31 32#include <sysdev/fsl_soc.h>33#include <linux/fsl-diu-fb.h>34#include "edid.h"35 36#define NUM_AOIS 5 /* 1 for plane 0, 2 for planes 1 & 2 each */37 38/* HW cursor parameters */39#define MAX_CURS 3240 41/* INT_STATUS/INT_MASK field descriptions */42#define INT_VSYNC 0x01 /* Vsync interrupt */43#define INT_VSYNC_WB 0x02 /* Vsync interrupt for write back operation */44#define INT_UNDRUN 0x04 /* Under run exception interrupt */45#define INT_PARERR 0x08 /* Display parameters error interrupt */46#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */47 48/*49 * List of supported video modes50 *51 * The first entry is the default video mode. The remain entries are in52 * order if increasing resolution and frequency. The 320x240-60 mode is53 * the initial AOI for the second and third planes.54 */55static struct fb_videomode fsl_diu_mode_db[] = {56 {57 .refresh = 60,58 .xres = 1024,59 .yres = 768,60 .pixclock = 15385,61 .left_margin = 160,62 .right_margin = 24,63 .upper_margin = 29,64 .lower_margin = 3,65 .hsync_len = 136,66 .vsync_len = 6,67 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,68 .vmode = FB_VMODE_NONINTERLACED69 },70 {71 .refresh = 60,72 .xres = 320,73 .yres = 240,74 .pixclock = 79440,75 .left_margin = 16,76 .right_margin = 16,77 .upper_margin = 16,78 .lower_margin = 5,79 .hsync_len = 48,80 .vsync_len = 1,81 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,82 .vmode = FB_VMODE_NONINTERLACED83 },84 {85 .refresh = 60,86 .xres = 640,87 .yres = 480,88 .pixclock = 39722,89 .left_margin = 48,90 .right_margin = 16,91 .upper_margin = 33,92 .lower_margin = 10,93 .hsync_len = 96,94 .vsync_len = 2,95 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,96 .vmode = FB_VMODE_NONINTERLACED97 },98 {99 .refresh = 72,100 .xres = 640,101 .yres = 480,102 .pixclock = 32052,103 .left_margin = 128,104 .right_margin = 24,105 .upper_margin = 28,106 .lower_margin = 9,107 .hsync_len = 40,108 .vsync_len = 3,109 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,110 .vmode = FB_VMODE_NONINTERLACED111 },112 {113 .refresh = 75,114 .xres = 640,115 .yres = 480,116 .pixclock = 31747,117 .left_margin = 120,118 .right_margin = 16,119 .upper_margin = 16,120 .lower_margin = 1,121 .hsync_len = 64,122 .vsync_len = 3,123 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,124 .vmode = FB_VMODE_NONINTERLACED125 },126 {127 .refresh = 90,128 .xres = 640,129 .yres = 480,130 .pixclock = 25057,131 .left_margin = 120,132 .right_margin = 32,133 .upper_margin = 14,134 .lower_margin = 25,135 .hsync_len = 40,136 .vsync_len = 14,137 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,138 .vmode = FB_VMODE_NONINTERLACED139 },140 {141 .refresh = 100,142 .xres = 640,143 .yres = 480,144 .pixclock = 22272,145 .left_margin = 48,146 .right_margin = 32,147 .upper_margin = 17,148 .lower_margin = 22,149 .hsync_len = 128,150 .vsync_len = 12,151 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,152 .vmode = FB_VMODE_NONINTERLACED153 },154 {155 .refresh = 60,156 .xres = 800,157 .yres = 480,158 .pixclock = 33805,159 .left_margin = 96,160 .right_margin = 24,161 .upper_margin = 10,162 .lower_margin = 3,163 .hsync_len = 72,164 .vsync_len = 7,165 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,166 .vmode = FB_VMODE_NONINTERLACED167 },168 {169 .refresh = 60,170 .xres = 800,171 .yres = 600,172 .pixclock = 25000,173 .left_margin = 88,174 .right_margin = 40,175 .upper_margin = 23,176 .lower_margin = 1,177 .hsync_len = 128,178 .vsync_len = 4,179 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,180 .vmode = FB_VMODE_NONINTERLACED181 },182 {183 .refresh = 60,184 .xres = 854,185 .yres = 480,186 .pixclock = 31518,187 .left_margin = 104,188 .right_margin = 16,189 .upper_margin = 13,190 .lower_margin = 1,191 .hsync_len = 88,192 .vsync_len = 3,193 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,194 .vmode = FB_VMODE_NONINTERLACED195 },196 {197 .refresh = 70,198 .xres = 1024,199 .yres = 768,200 .pixclock = 16886,201 .left_margin = 3,202 .right_margin = 3,203 .upper_margin = 2,204 .lower_margin = 2,205 .hsync_len = 40,206 .vsync_len = 18,207 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,208 .vmode = FB_VMODE_NONINTERLACED209 },210 {211 .refresh = 75,212 .xres = 1024,213 .yres = 768,214 .pixclock = 15009,215 .left_margin = 3,216 .right_margin = 3,217 .upper_margin = 2,218 .lower_margin = 2,219 .hsync_len = 80,220 .vsync_len = 32,221 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,222 .vmode = FB_VMODE_NONINTERLACED223 },224 {225 .refresh = 60,226 .xres = 1280,227 .yres = 480,228 .pixclock = 18939,229 .left_margin = 353,230 .right_margin = 47,231 .upper_margin = 39,232 .lower_margin = 4,233 .hsync_len = 8,234 .vsync_len = 2,235 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,236 .vmode = FB_VMODE_NONINTERLACED237 },238 {239 .refresh = 60,240 .xres = 1280,241 .yres = 720,242 .pixclock = 13426,243 .left_margin = 192,244 .right_margin = 64,245 .upper_margin = 22,246 .lower_margin = 1,247 .hsync_len = 136,248 .vsync_len = 3,249 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,250 .vmode = FB_VMODE_NONINTERLACED251 },252 {253 .refresh = 60,254 .xres = 1280,255 .yres = 1024,256 .pixclock = 9375,257 .left_margin = 38,258 .right_margin = 128,259 .upper_margin = 2,260 .lower_margin = 7,261 .hsync_len = 216,262 .vsync_len = 37,263 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,264 .vmode = FB_VMODE_NONINTERLACED265 },266 {267 .refresh = 70,268 .xres = 1280,269 .yres = 1024,270 .pixclock = 9380,271 .left_margin = 6,272 .right_margin = 6,273 .upper_margin = 4,274 .lower_margin = 4,275 .hsync_len = 60,276 .vsync_len = 94,277 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,278 .vmode = FB_VMODE_NONINTERLACED279 },280 {281 .refresh = 75,282 .xres = 1280,283 .yres = 1024,284 .pixclock = 9380,285 .left_margin = 6,286 .right_margin = 6,287 .upper_margin = 4,288 .lower_margin = 4,289 .hsync_len = 60,290 .vsync_len = 15,291 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,292 .vmode = FB_VMODE_NONINTERLACED293 },294 {295 .refresh = 60,296 .xres = 1920,297 .yres = 1080,298 .pixclock = 5787,299 .left_margin = 328,300 .right_margin = 120,301 .upper_margin = 34,302 .lower_margin = 1,303 .hsync_len = 208,304 .vsync_len = 3,305 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,306 .vmode = FB_VMODE_NONINTERLACED307 },308};309 310static char *fb_mode;311static unsigned long default_bpp = 32;312static enum fsl_diu_monitor_port monitor_port;313static char *monitor_string;314 315#if defined(CONFIG_NOT_COHERENT_CACHE)316static u8 *coherence_data;317static size_t coherence_data_size;318static unsigned int d_cache_line_size;319#endif320 321static DEFINE_SPINLOCK(diu_lock);322 323enum mfb_index {324 PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */325 PLANE1_AOI0, /* Plane 1, first AOI */326 PLANE1_AOI1, /* Plane 1, second AOI */327 PLANE2_AOI0, /* Plane 2, first AOI */328 PLANE2_AOI1, /* Plane 2, second AOI */329};330 331struct mfb_info {332 enum mfb_index index;333 char *id;334 int registered;335 unsigned long pseudo_palette[16];336 struct diu_ad *ad;337 unsigned char g_alpha;338 unsigned int count;339 int x_aoi_d; /* aoi display x offset to physical screen */340 int y_aoi_d; /* aoi display y offset to physical screen */341 struct fsl_diu_data *parent;342};343 344/**345 * struct fsl_diu_data - per-DIU data structure346 * @dma_addr: DMA address of this structure347 * @fsl_diu_info: fb_info objects, one per AOI348 * @dev_attr: sysfs structure349 * @irq: IRQ350 * @monitor_port: the monitor port this DIU is connected to351 * @diu_reg: pointer to the DIU hardware registers352 * @reg_lock: spinlock for register access353 * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI354 * dummy_ad: DIU Area Descriptor for the dummy AOI355 * @ad[]: Area Descriptors for each real AOI356 * @gamma: gamma color table357 * @cursor: hardware cursor data358 * @blank_cursor: blank cursor for hiding cursor359 * @next_cursor: scratch space to build load cursor360 * @edid_data: EDID information buffer361 * @has_edid: whether or not the EDID buffer is valid362 *363 * This data structure must be allocated with 32-byte alignment, so that the364 * internal fields can be aligned properly.365 */366struct fsl_diu_data {367 dma_addr_t dma_addr;368 struct fb_info fsl_diu_info[NUM_AOIS];369 struct mfb_info mfb[NUM_AOIS];370 struct device_attribute dev_attr;371 unsigned int irq;372 enum fsl_diu_monitor_port monitor_port;373 struct diu __iomem *diu_reg;374 spinlock_t reg_lock;375 u8 dummy_aoi[4 * 4 * 4];376 struct diu_ad dummy_ad __aligned(8);377 struct diu_ad ad[NUM_AOIS] __aligned(8);378 u8 gamma[256 * 3] __aligned(32);379 /* It's easier to parse the cursor data as little-endian */380 __le16 cursor[MAX_CURS * MAX_CURS] __aligned(32);381 /* Blank cursor data -- used to hide the cursor */382 __le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32);383 /* Scratch cursor data -- used to build new cursor */384 __le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32);385 uint8_t edid_data[EDID_LENGTH];386 bool has_edid;387} __aligned(32);388 389/* Determine the DMA address of a member of the fsl_diu_data structure */390#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))391 392static const struct mfb_info mfb_template[] = {393 {394 .index = PLANE0,395 .id = "Panel0",396 .registered = 0,397 .count = 0,398 .x_aoi_d = 0,399 .y_aoi_d = 0,400 },401 {402 .index = PLANE1_AOI0,403 .id = "Panel1 AOI0",404 .registered = 0,405 .g_alpha = 0xff,406 .count = 0,407 .x_aoi_d = 0,408 .y_aoi_d = 0,409 },410 {411 .index = PLANE1_AOI1,412 .id = "Panel1 AOI1",413 .registered = 0,414 .g_alpha = 0xff,415 .count = 0,416 .x_aoi_d = 0,417 .y_aoi_d = 480,418 },419 {420 .index = PLANE2_AOI0,421 .id = "Panel2 AOI0",422 .registered = 0,423 .g_alpha = 0xff,424 .count = 0,425 .x_aoi_d = 640,426 .y_aoi_d = 0,427 },428 {429 .index = PLANE2_AOI1,430 .id = "Panel2 AOI1",431 .registered = 0,432 .g_alpha = 0xff,433 .count = 0,434 .x_aoi_d = 640,435 .y_aoi_d = 480,436 },437};438 439#ifdef DEBUG440static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)441{442 mb();443 pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x palette=%08x "444 "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "445 "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "446 "thresholds=%08x int_mask=%08x plut=%08x\n",447 hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,448 hw->palette, hw->cursor, hw->curs_pos, hw->diu_mode,449 hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,450 hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);451 rmb();452}453#endif454 455/**456 * fsl_diu_name_to_port - convert a port name to a monitor port enum457 *458 * Takes the name of a monitor port ("dvi", "lvds", or "dlvds") and returns459 * the enum fsl_diu_monitor_port that corresponds to that string.460 *461 * For compatibility with older versions, a number ("0", "1", or "2") is also462 * supported.463 *464 * If the string is unknown, DVI is assumed.465 *466 * If the particular port is not supported by the platform, another port467 * (platform-specific) is chosen instead.468 */469static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)470{471 enum fsl_diu_monitor_port port = FSL_DIU_PORT_DVI;472 unsigned long val;473 474 if (s) {475 if (!kstrtoul(s, 10, &val) && (val <= 2))476 port = (enum fsl_diu_monitor_port) val;477 else if (strncmp(s, "lvds", 4) == 0)478 port = FSL_DIU_PORT_LVDS;479 else if (strncmp(s, "dlvds", 5) == 0)480 port = FSL_DIU_PORT_DLVDS;481 }482 483 if (diu_ops.valid_monitor_port)484 port = diu_ops.valid_monitor_port(port);485 486 return port;487}488 489/*490 * Workaround for failed writing desc register of planes.491 * Needed with MPC5121 DIU rev 2.0 silicon.492 */493static void wr_reg_wa(u32 *reg, u32 val)494{495 do {496 out_be32(reg, val);497 } while (in_be32(reg) != val);498}499 500static void fsl_diu_enable_panel(struct fb_info *info)501{502 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;503 struct diu_ad *ad = mfbi->ad;504 struct fsl_diu_data *data = mfbi->parent;505 struct diu __iomem *hw = data->diu_reg;506 507 switch (mfbi->index) {508 case PLANE0:509 wr_reg_wa(&hw->desc[0], ad->paddr);510 break;511 case PLANE1_AOI0:512 cmfbi = &data->mfb[2];513 if (hw->desc[1] != ad->paddr) { /* AOI0 closed */514 if (cmfbi->count > 0) /* AOI1 open */515 ad->next_ad =516 cpu_to_le32(cmfbi->ad->paddr);517 else518 ad->next_ad = 0;519 wr_reg_wa(&hw->desc[1], ad->paddr);520 }521 break;522 case PLANE2_AOI0:523 cmfbi = &data->mfb[4];524 if (hw->desc[2] != ad->paddr) { /* AOI0 closed */525 if (cmfbi->count > 0) /* AOI1 open */526 ad->next_ad =527 cpu_to_le32(cmfbi->ad->paddr);528 else529 ad->next_ad = 0;530 wr_reg_wa(&hw->desc[2], ad->paddr);531 }532 break;533 case PLANE1_AOI1:534 pmfbi = &data->mfb[1];535 ad->next_ad = 0;536 if (hw->desc[1] == data->dummy_ad.paddr)537 wr_reg_wa(&hw->desc[1], ad->paddr);538 else /* AOI0 open */539 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);540 break;541 case PLANE2_AOI1:542 pmfbi = &data->mfb[3];543 ad->next_ad = 0;544 if (hw->desc[2] == data->dummy_ad.paddr)545 wr_reg_wa(&hw->desc[2], ad->paddr);546 else /* AOI0 was open */547 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);548 break;549 }550}551 552static void fsl_diu_disable_panel(struct fb_info *info)553{554 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;555 struct diu_ad *ad = mfbi->ad;556 struct fsl_diu_data *data = mfbi->parent;557 struct diu __iomem *hw = data->diu_reg;558 559 switch (mfbi->index) {560 case PLANE0:561 wr_reg_wa(&hw->desc[0], 0);562 break;563 case PLANE1_AOI0:564 cmfbi = &data->mfb[2];565 if (cmfbi->count > 0) /* AOI1 is open */566 wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);567 /* move AOI1 to the first */568 else /* AOI1 was closed */569 wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);570 /* close AOI 0 */571 break;572 case PLANE2_AOI0:573 cmfbi = &data->mfb[4];574 if (cmfbi->count > 0) /* AOI1 is open */575 wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);576 /* move AOI1 to the first */577 else /* AOI1 was closed */578 wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);579 /* close AOI 0 */580 break;581 case PLANE1_AOI1:582 pmfbi = &data->mfb[1];583 if (hw->desc[1] != ad->paddr) {584 /* AOI1 is not the first in the chain */585 if (pmfbi->count > 0)586 /* AOI0 is open, must be the first */587 pmfbi->ad->next_ad = 0;588 } else /* AOI1 is the first in the chain */589 wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);590 /* close AOI 1 */591 break;592 case PLANE2_AOI1:593 pmfbi = &data->mfb[3];594 if (hw->desc[2] != ad->paddr) {595 /* AOI1 is not the first in the chain */596 if (pmfbi->count > 0)597 /* AOI0 is open, must be the first */598 pmfbi->ad->next_ad = 0;599 } else /* AOI1 is the first in the chain */600 wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);601 /* close AOI 1 */602 break;603 }604}605 606static void enable_lcdc(struct fb_info *info)607{608 struct mfb_info *mfbi = info->par;609 struct fsl_diu_data *data = mfbi->parent;610 struct diu __iomem *hw = data->diu_reg;611 612 out_be32(&hw->diu_mode, MFB_MODE1);613}614 615static void disable_lcdc(struct fb_info *info)616{617 struct mfb_info *mfbi = info->par;618 struct fsl_diu_data *data = mfbi->parent;619 struct diu __iomem *hw = data->diu_reg;620 621 out_be32(&hw->diu_mode, 0);622}623 624static void adjust_aoi_size_position(struct fb_var_screeninfo *var,625 struct fb_info *info)626{627 struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;628 struct fsl_diu_data *data = mfbi->parent;629 int available_height, upper_aoi_bottom;630 enum mfb_index index = mfbi->index;631 int lower_aoi_is_open, upper_aoi_is_open;632 __u32 base_plane_width, base_plane_height, upper_aoi_height;633 634 base_plane_width = data->fsl_diu_info[0].var.xres;635 base_plane_height = data->fsl_diu_info[0].var.yres;636 637 if (mfbi->x_aoi_d < 0)638 mfbi->x_aoi_d = 0;639 if (mfbi->y_aoi_d < 0)640 mfbi->y_aoi_d = 0;641 switch (index) {642 case PLANE0:643 if (mfbi->x_aoi_d != 0)644 mfbi->x_aoi_d = 0;645 if (mfbi->y_aoi_d != 0)646 mfbi->y_aoi_d = 0;647 break;648 case PLANE1_AOI0:649 case PLANE2_AOI0:650 lower_aoi_mfbi = data->fsl_diu_info[index+1].par;651 lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;652 if (var->xres > base_plane_width)653 var->xres = base_plane_width;654 if ((mfbi->x_aoi_d + var->xres) > base_plane_width)655 mfbi->x_aoi_d = base_plane_width - var->xres;656 657 if (lower_aoi_is_open)658 available_height = lower_aoi_mfbi->y_aoi_d;659 else660 available_height = base_plane_height;661 if (var->yres > available_height)662 var->yres = available_height;663 if ((mfbi->y_aoi_d + var->yres) > available_height)664 mfbi->y_aoi_d = available_height - var->yres;665 break;666 case PLANE1_AOI1:667 case PLANE2_AOI1:668 upper_aoi_mfbi = data->fsl_diu_info[index-1].par;669 upper_aoi_height = data->fsl_diu_info[index-1].var.yres;670 upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;671 upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;672 if (var->xres > base_plane_width)673 var->xres = base_plane_width;674 if ((mfbi->x_aoi_d + var->xres) > base_plane_width)675 mfbi->x_aoi_d = base_plane_width - var->xres;676 if (mfbi->y_aoi_d < 0)677 mfbi->y_aoi_d = 0;678 if (upper_aoi_is_open) {679 if (mfbi->y_aoi_d < upper_aoi_bottom)680 mfbi->y_aoi_d = upper_aoi_bottom;681 available_height = base_plane_height682 - upper_aoi_bottom;683 } else684 available_height = base_plane_height;685 if (var->yres > available_height)686 var->yres = available_height;687 if ((mfbi->y_aoi_d + var->yres) > base_plane_height)688 mfbi->y_aoi_d = base_plane_height - var->yres;689 break;690 }691}692/*693 * Checks to see if the hardware supports the state requested by var passed694 * in. This function does not alter the hardware state! If the var passed in695 * is slightly off by what the hardware can support then we alter the var696 * PASSED in to what we can do. If the hardware doesn't support mode change697 * a -EINVAL will be returned by the upper layers.698 */699static int fsl_diu_check_var(struct fb_var_screeninfo *var,700 struct fb_info *info)701{702 if (var->xres_virtual < var->xres)703 var->xres_virtual = var->xres;704 if (var->yres_virtual < var->yres)705 var->yres_virtual = var->yres;706 707 if (var->xoffset + info->var.xres > info->var.xres_virtual)708 var->xoffset = info->var.xres_virtual - info->var.xres;709 710 if (var->yoffset + info->var.yres > info->var.yres_virtual)711 var->yoffset = info->var.yres_virtual - info->var.yres;712 713 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&714 (var->bits_per_pixel != 16))715 var->bits_per_pixel = default_bpp;716 717 switch (var->bits_per_pixel) {718 case 16:719 var->red.length = 5;720 var->red.offset = 11;721 var->red.msb_right = 0;722 723 var->green.length = 6;724 var->green.offset = 5;725 var->green.msb_right = 0;726 727 var->blue.length = 5;728 var->blue.offset = 0;729 var->blue.msb_right = 0;730 731 var->transp.length = 0;732 var->transp.offset = 0;733 var->transp.msb_right = 0;734 break;735 case 24:736 var->red.length = 8;737 var->red.offset = 0;738 var->red.msb_right = 0;739 740 var->green.length = 8;741 var->green.offset = 8;742 var->green.msb_right = 0;743 744 var->blue.length = 8;745 var->blue.offset = 16;746 var->blue.msb_right = 0;747 748 var->transp.length = 0;749 var->transp.offset = 0;750 var->transp.msb_right = 0;751 break;752 case 32:753 var->red.length = 8;754 var->red.offset = 16;755 var->red.msb_right = 0;756 757 var->green.length = 8;758 var->green.offset = 8;759 var->green.msb_right = 0;760 761 var->blue.length = 8;762 var->blue.offset = 0;763 var->blue.msb_right = 0;764 765 var->transp.length = 8;766 var->transp.offset = 24;767 var->transp.msb_right = 0;768 769 break;770 }771 772 var->height = -1;773 var->width = -1;774 var->grayscale = 0;775 776 /* Copy nonstd field to/from sync for fbset usage */777 var->sync |= var->nonstd;778 var->nonstd |= var->sync;779 780 adjust_aoi_size_position(var, info);781 return 0;782}783 784static void set_fix(struct fb_info *info)785{786 struct fb_fix_screeninfo *fix = &info->fix;787 struct fb_var_screeninfo *var = &info->var;788 struct mfb_info *mfbi = info->par;789 790 strscpy_pad(fix->id, mfbi->id);791 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;792 fix->type = FB_TYPE_PACKED_PIXELS;793 fix->accel = FB_ACCEL_NONE;794 fix->visual = FB_VISUAL_TRUECOLOR;795 fix->xpanstep = 1;796 fix->ypanstep = 1;797}798 799static void update_lcdc(struct fb_info *info)800{801 struct fb_var_screeninfo *var = &info->var;802 struct mfb_info *mfbi = info->par;803 struct fsl_diu_data *data = mfbi->parent;804 struct diu __iomem *hw;805 int i, j;806 u8 *gamma_table_base;807 808 u32 temp;809 810 hw = data->diu_reg;811 812 if (diu_ops.set_monitor_port)813 diu_ops.set_monitor_port(data->monitor_port);814 gamma_table_base = data->gamma;815 816 /* Prep for DIU init - gamma table, cursor table */817 818 for (i = 0; i <= 2; i++)819 for (j = 0; j <= 255; j++)820 *gamma_table_base++ = j;821 822 if (diu_ops.set_gamma_table)823 diu_ops.set_gamma_table(data->monitor_port, data->gamma);824 825 disable_lcdc(info);826 827 /* Program DIU registers */828 829 out_be32(&hw->gamma, DMA_ADDR(data, gamma));830 831 out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */832 out_be32(&hw->disp_size, (var->yres << 16) | var->xres);833 834 /* Horizontal and vertical configuration register */835 temp = var->left_margin << 22 | /* BP_H */836 var->hsync_len << 11 | /* PW_H */837 var->right_margin; /* FP_H */838 839 out_be32(&hw->hsyn_para, temp);840 841 temp = var->upper_margin << 22 | /* BP_V */842 var->vsync_len << 11 | /* PW_V */843 var->lower_margin; /* FP_V */844 845 out_be32(&hw->vsyn_para, temp);846 847 diu_ops.set_pixel_clock(var->pixclock);848 849#ifndef CONFIG_PPC_MPC512x850 /*851 * The PLUT register is defined differently on the MPC5121 than it852 * is on other SOCs. Unfortunately, there's no documentation that853 * explains how it's supposed to be programmed, so for now, we leave854 * it at the default value on the MPC5121.855 *856 * For other SOCs, program it for the highest priority, which will857 * reduce the chance of underrun. Technically, we should scale the858 * priority to match the screen resolution, but doing that properly859 * requires delicate fine-tuning for each use-case.860 */861 out_be32(&hw->plut, 0x01F5F666);862#endif863 864 /* Enable the DIU */865 enable_lcdc(info);866}867 868static int map_video_memory(struct fb_info *info)869{870 u32 smem_len = info->fix.line_length * info->var.yres_virtual;871 void *p;872 873 p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO);874 if (!p) {875 fb_err(info, "unable to allocate fb memory\n");876 return -ENOMEM;877 }878 mutex_lock(&info->mm_lock);879 info->screen_base = p;880 info->fix.smem_start = virt_to_phys((__force const void *)info->screen_base);881 info->fix.smem_len = smem_len;882 mutex_unlock(&info->mm_lock);883 info->screen_size = info->fix.smem_len;884 885 return 0;886}887 888static void unmap_video_memory(struct fb_info *info)889{890 void *p = info->screen_base;891 size_t l = info->fix.smem_len;892 893 mutex_lock(&info->mm_lock);894 info->screen_base = NULL;895 info->fix.smem_start = 0;896 info->fix.smem_len = 0;897 mutex_unlock(&info->mm_lock);898 899 if (p)900 free_pages_exact(p, l);901}902 903/*904 * Using the fb_var_screeninfo in fb_info we set the aoi of this905 * particular framebuffer. It is a light version of fsl_diu_set_par.906 */907static int fsl_diu_set_aoi(struct fb_info *info)908{909 struct fb_var_screeninfo *var = &info->var;910 struct mfb_info *mfbi = info->par;911 struct diu_ad *ad = mfbi->ad;912 913 /* AOI should not be greater than display size */914 ad->offset_xyi = cpu_to_le32((var->yoffset << 16) | var->xoffset);915 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);916 return 0;917}918 919/**920 * fsl_diu_get_pixel_format: return the pixel format for a given color depth921 *922 * The pixel format is a 32-bit value that determine which bits in each923 * pixel are to be used for each color. This is the default function used924 * if the platform does not define its own version.925 */926static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)927{928#define PF_BYTE_F 0x10000000929#define PF_ALPHA_C_MASK 0x0E000000930#define PF_ALPHA_C_SHIFT 25931#define PF_BLUE_C_MASK 0x01800000932#define PF_BLUE_C_SHIFT 23933#define PF_GREEN_C_MASK 0x00600000934#define PF_GREEN_C_SHIFT 21935#define PF_RED_C_MASK 0x00180000936#define PF_RED_C_SHIFT 19937#define PF_PALETTE 0x00040000938#define PF_PIXEL_S_MASK 0x00030000939#define PF_PIXEL_S_SHIFT 16940#define PF_COMP_3_MASK 0x0000F000941#define PF_COMP_3_SHIFT 12942#define PF_COMP_2_MASK 0x00000F00943#define PF_COMP_2_SHIFT 8944#define PF_COMP_1_MASK 0x000000F0945#define PF_COMP_1_SHIFT 4946#define PF_COMP_0_MASK 0x0000000F947#define PF_COMP_0_SHIFT 0948 949#define MAKE_PF(alpha, red, green, blue, size, c0, c1, c2, c3) \950 cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \951 (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \952 (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \953 (c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \954 (c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT))955 956 switch (bits_per_pixel) {957 case 32:958 /* 0x88883316 */959 return MAKE_PF(3, 2, 1, 0, 3, 8, 8, 8, 8);960 case 24:961 /* 0x88082219 */962 return MAKE_PF(4, 0, 1, 2, 2, 8, 8, 8, 0);963 case 16:964 /* 0x65053118 */965 return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);966 default:967 pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel);968 return 0;969 }970}971 972/*973 * Copies a cursor image from user space to the proper place in driver974 * memory so that the hardware can display the cursor image.975 *976 * Cursor data is represented as a sequence of 'width' bits packed into bytes.977 * That is, the first 8 bits are in the first byte, the second 8 bits in the978 * second byte, and so on. Therefore, the each row of the cursor is (width +979 * 7) / 8 bytes of 'data'980 *981 * The DIU only supports cursors up to 32x32 (MAX_CURS). We reject cursors982 * larger than this, so we already know that 'width' <= 32. Therefore, we can983 * simplify our code by using a 32-bit big-endian integer ("line") to read in984 * a single line of pixels, and only look at the top 'width' bits of that985 * integer.986 *987 * This could result in an unaligned 32-bit read. For example, if the cursor988 * is 24x24, then the first three bytes of 'image' contain the pixel data for989 * the top line of the cursor. We do a 32-bit read of 'image', but we look990 * only at the top 24 bits. Then we increment 'image' by 3 bytes. The next991 * read is unaligned. The only problem is that we might read past the end of992 * 'image' by 1-3 bytes, but that should not cause any problems.993 */994static void fsl_diu_load_cursor_image(struct fb_info *info,995 const void *image, uint16_t bg, uint16_t fg,996 unsigned int width, unsigned int height)997{998 struct mfb_info *mfbi = info->par;999 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 the1021 * fb_cursor object.1022 */1023static 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 this1036 * message, instead of resizing the cursor, we just clear1037 * all the image data, in expectation of new data. However,1038 * in tests this control does not appear to be normally1039 * 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 changed1056 * FB_CUR_SETCMAP - the cursor colors has changed1057 * FB_CUR_SETSHAPE - the cursor bitmask has changed1058 */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 else1097 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 the1106 * 'cursor' memory block, and the actual cursor position is always in1107 * the DIU's CURS_POS register. To hide the cursor, we redirect the1108 * CURSOR register to a blank cursor. The show the cursor, we1109 * redirect the CURSOR register to the real cursor data.1110 */1111 if (cursor->enable)1112 out_be32(&hw->cursor, DMA_ADDR(data, cursor));1113 else1114 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 this1121 * particular framebuffer. This function alters the fb_fix_screeninfo stored1122 * in fb_info. It does not alter var in fb_info since we are using that1123 * data. This means we depend on the data in var inside fb_info to be1124 * supported by the hardware. fsl_diu_check_var is always called before1125 * fsl_diu_set_par to ensure this.1126 */1127static 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 fb_err(info, "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 else1157 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 1181static 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 magnitude1188 * which needs to be scaled in this function for the hardware. Things to take1189 * into consideration are how many color registers, if any, are supported with1190 * the current color visual. With truecolor mode no color palettes are1191 * supported. Here a pseudo palette is created which we store the value in1192 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited1193 * color palette.1194 */1195static 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 value1203 * 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 value1212 * 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 the1239 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values1240 * don't fit, return -EINVAL.1241 */1242static 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_virtual1250 || 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 else1259 info->var.vmode &= ~FB_VMODE_YWRAP;1260 1261 fsl_diu_set_aoi(info);1262 1263 return 0;1264}1265 1266static 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 fb_dbg(info, "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 fb_warn(info,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 fb_warn(info,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_MPC512x1361 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#endif1377 default:1378 fb_err(info, "unknown ioctl command (0x%08X)\n", cmd);1379 return -ENOIOCTLCMD;1380 }1381 1382 return 0;1383}1384 1385static 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 == 11396 */1397static 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 == 01424 */1425static 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 1452static const struct fb_ops fsl_diu_ops = {1453 .owner = THIS_MODULE,1454 FB_DEFAULT_IOMEM_OPS,1455 .fb_check_var = fsl_diu_check_var,1456 .fb_set_par = fsl_diu_set_par,1457 .fb_setcolreg = fsl_diu_setcolreg,1458 .fb_pan_display = fsl_diu_pan_display,1459 .fb_ioctl = fsl_diu_ioctl,1460 .fb_open = fsl_diu_open,1461 .fb_release = fsl_diu_release,1462 .fb_cursor = fsl_diu_cursor,1463};1464 1465static int install_fb(struct fb_info *info)1466{1467 int rc;1468 struct mfb_info *mfbi = info->par;1469 struct fsl_diu_data *data = mfbi->parent;1470 const char *aoi_mode, *init_aoi_mode = "320x240";1471 struct fb_videomode *db = fsl_diu_mode_db;1472 unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);1473 int has_default_mode = 1;1474 1475 info->var.activate = FB_ACTIVATE_NOW;1476 info->fbops = &fsl_diu_ops;1477 info->flags = FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK |1478 FBINFO_READS_FAST;1479 info->pseudo_palette = mfbi->pseudo_palette;1480 1481 rc = fb_alloc_cmap(&info->cmap, 16, 0);1482 if (rc)1483 return rc;1484 1485 if (mfbi->index == PLANE0) {1486 if (data->has_edid) {1487 /* Now build modedb from EDID */1488 fb_edid_to_monspecs(data->edid_data, &info->monspecs);1489 fb_videomode_to_modelist(info->monspecs.modedb,1490 info->monspecs.modedb_len,1491 &info->modelist);1492 db = info->monspecs.modedb;1493 dbsize = info->monspecs.modedb_len;1494 }1495 aoi_mode = fb_mode;1496 } else {1497 aoi_mode = init_aoi_mode;1498 }1499 rc = fb_find_mode(&info->var, info, aoi_mode, db, dbsize, NULL,1500 default_bpp);1501 if (!rc) {1502 /*1503 * For plane 0 we continue and look into1504 * driver's internal modedb.1505 */1506 if ((mfbi->index == PLANE0) && data->has_edid)1507 has_default_mode = 0;1508 else1509 return -EINVAL;1510 }1511 1512 if (!has_default_mode) {1513 rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,1514 ARRAY_SIZE(fsl_diu_mode_db), NULL, default_bpp);1515 if (rc)1516 has_default_mode = 1;1517 }1518 1519 /* Still not found, use preferred mode from database if any */1520 if (!has_default_mode && info->monspecs.modedb) {1521 struct fb_monspecs *specs = &info->monspecs;1522 struct fb_videomode *modedb = &specs->modedb[0];1523 1524 /*1525 * Get preferred timing. If not found,1526 * first mode in database will be used.1527 */1528 if (specs->misc & FB_MISC_1ST_DETAIL) {1529 int i;1530 1531 for (i = 0; i < specs->modedb_len; i++) {1532 if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {1533 modedb = &specs->modedb[i];1534 break;1535 }1536 }1537 }1538 1539 info->var.bits_per_pixel = default_bpp;1540 fb_videomode_to_var(&info->var, modedb);1541 }1542 1543 if (fsl_diu_check_var(&info->var, info)) {1544 fb_err(info, "fsl_diu_check_var failed\n");1545 unmap_video_memory(info);1546 fb_dealloc_cmap(&info->cmap);1547 return -EINVAL;1548 }1549 1550 if (register_framebuffer(info) < 0) {1551 fb_err(info, "register_framebuffer failed\n");1552 unmap_video_memory(info);1553 fb_dealloc_cmap(&info->cmap);1554 return -EINVAL;1555 }1556 1557 mfbi->registered = 1;1558 fb_info(info, "%s registered successfully\n", mfbi->id);1559 1560 return 0;1561}1562 1563static void uninstall_fb(struct fb_info *info)1564{1565 struct mfb_info *mfbi = info->par;1566 1567 if (!mfbi->registered)1568 return;1569 1570 unregister_framebuffer(info);1571 unmap_video_memory(info);1572 fb_dealloc_cmap(&info->cmap);1573 1574 mfbi->registered = 0;1575}1576 1577static irqreturn_t fsl_diu_isr(int irq, void *dev_id)1578{1579 struct diu __iomem *hw = dev_id;1580 uint32_t status = in_be32(&hw->int_status);1581 1582 if (status) {1583 /* This is the workaround for underrun */1584 if (status & INT_UNDRUN) {1585 out_be32(&hw->diu_mode, 0);1586 udelay(1);1587 out_be32(&hw->diu_mode, 1);1588 }1589#if defined(CONFIG_NOT_COHERENT_CACHE)1590 else if (status & INT_VSYNC) {1591 unsigned int i;1592 1593 for (i = 0; i < coherence_data_size;1594 i += d_cache_line_size)1595 __asm__ __volatile__ (1596 "dcbz 0, %[input]"1597 ::[input]"r"(&coherence_data[i]));1598 }1599#endif1600 return IRQ_HANDLED;1601 }1602 return IRQ_NONE;1603}1604 1605#ifdef CONFIG_PM1606/*1607 * Power management hooks. Note that we won't be called from IRQ context,1608 * unlike the blank functions above, so we may sleep.1609 */1610static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)1611{1612 struct fsl_diu_data *data;1613 1614 data = dev_get_drvdata(&ofdev->dev);1615 disable_lcdc(data->fsl_diu_info);1616 1617 return 0;1618}1619 1620static int fsl_diu_resume(struct platform_device *ofdev)1621{1622 struct fsl_diu_data *data;1623 unsigned int i;1624 1625 data = dev_get_drvdata(&ofdev->dev);1626 1627 fsl_diu_enable_interrupts(data);1628 update_lcdc(data->fsl_diu_info);1629 for (i = 0; i < NUM_AOIS; i++) {1630 if (data->mfb[i].count)1631 fsl_diu_enable_panel(&data->fsl_diu_info[i]);1632 }1633 1634 return 0;1635}1636 1637#else1638#define fsl_diu_suspend NULL1639#define fsl_diu_resume NULL1640#endif /* CONFIG_PM */1641 1642static ssize_t store_monitor(struct device *device,1643 struct device_attribute *attr, const char *buf, size_t count)1644{1645 enum fsl_diu_monitor_port old_monitor_port;1646 struct fsl_diu_data *data =1647 container_of(attr, struct fsl_diu_data, dev_attr);1648 1649 old_monitor_port = data->monitor_port;1650 data->monitor_port = fsl_diu_name_to_port(buf);1651 1652 if (old_monitor_port != data->monitor_port) {1653 /* All AOIs need adjust pixel format1654 * fsl_diu_set_par only change the pixsel format here1655 * unlikely to fail. */1656 unsigned int i;1657 1658 for (i=0; i < NUM_AOIS; i++)1659 fsl_diu_set_par(&data->fsl_diu_info[i]);1660 }1661 return count;1662}1663 1664static ssize_t show_monitor(struct device *device,1665 struct device_attribute *attr, char *buf)1666{1667 struct fsl_diu_data *data =1668 container_of(attr, struct fsl_diu_data, dev_attr);1669 1670 switch (data->monitor_port) {1671 case FSL_DIU_PORT_DVI:1672 return sprintf(buf, "DVI\n");1673 case FSL_DIU_PORT_LVDS:1674 return sprintf(buf, "Single-link LVDS\n");1675 case FSL_DIU_PORT_DLVDS:1676 return sprintf(buf, "Dual-link LVDS\n");1677 }1678 1679 return 0;1680}1681 1682static int fsl_diu_probe(struct platform_device *pdev)1683{1684 struct device_node *np = pdev->dev.of_node;1685 struct mfb_info *mfbi;1686 struct fsl_diu_data *data;1687 dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */1688 const void *prop;1689 unsigned int i;1690 int ret;1691 1692 data = dmam_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data),1693 &dma_addr, GFP_DMA | __GFP_ZERO);1694 if (!data)1695 return -ENOMEM;1696 data->dma_addr = dma_addr;1697 1698 /*1699 * dma_alloc_coherent() uses a page allocator, so the address is1700 * always page-aligned. We need the memory to be 32-byte aligned,1701 * so that's good. However, if one day the allocator changes, we1702 * need to catch that. It's not worth the effort to handle unaligned1703 * alloctions now because it's highly unlikely to ever be a problem.1704 */1705 if ((unsigned long)data & 31) {1706 dev_err(&pdev->dev, "misaligned allocation");1707 ret = -ENOMEM;1708 goto error;1709 }1710 1711 spin_lock_init(&data->reg_lock);1712 1713 for (i = 0; i < NUM_AOIS; i++) {1714 struct fb_info *info = &data->fsl_diu_info[i];1715 1716 info->device = &pdev->dev;1717 info->par = &data->mfb[i];1718 1719 /*1720 * We store the physical address of the AD in the reserved1721 * 'paddr' field of the AD itself.1722 */1723 data->ad[i].paddr = DMA_ADDR(data, ad[i]);1724 1725 info->fix.smem_start = 0;1726 1727 /* Initialize the AOI data structure */1728 mfbi = info->par;1729 memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));1730 mfbi->parent = data;1731 mfbi->ad = &data->ad[i];1732 }1733 1734 /* Get the EDID data from the device tree, if present */1735 prop = of_get_property(np, "edid", &ret);1736 if (prop && ret == EDID_LENGTH) {1737 memcpy(data->edid_data, prop, EDID_LENGTH);1738 data->has_edid = true;1739 }1740 1741 data->diu_reg = of_iomap(np, 0);1742 if (!data->diu_reg) {1743 dev_err(&pdev->dev, "cannot map DIU registers\n");1744 ret = -EFAULT;1745 goto error;1746 }1747 1748 /* Get the IRQ of the DIU */1749 data->irq = irq_of_parse_and_map(np, 0);1750 1751 if (!data->irq) {1752 dev_err(&pdev->dev, "could not get DIU IRQ\n");1753 ret = -EINVAL;1754 goto error;1755 }1756 data->monitor_port = monitor_port;1757 1758 /* Initialize the dummy Area Descriptor */1759 data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi));1760 data->dummy_ad.pix_fmt = 0x88882317;1761 data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);1762 data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) | 2);1763 data->dummy_ad.offset_xyi = 0;1764 data->dummy_ad.offset_xyd = 0;1765 data->dummy_ad.next_ad = 0;1766 data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);1767 1768 /*1769 * Let DIU continue to display splash screen if it was pre-initialized1770 * by the bootloader; otherwise, clear the display.1771 */1772 if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)1773 out_be32(&data->diu_reg->desc[0], 0);1774 1775 out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);1776 out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);1777 1778 /*1779 * Older versions of U-Boot leave interrupts enabled, so disable1780 * all of them and clear the status register.1781 */1782 out_be32(&data->diu_reg->int_mask, 0xffffffff);1783 in_be32(&data->diu_reg->int_status);1784 1785 ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",1786 data->diu_reg);1787 if (ret) {1788 dev_err(&pdev->dev, "could not claim irq\n");1789 goto error;1790 }1791 1792 for (i = 0; i < NUM_AOIS; i++) {1793 ret = install_fb(&data->fsl_diu_info[i]);1794 if (ret) {1795 dev_err(&pdev->dev, "could not register fb %d\n", i);1796 free_irq(data->irq, data->diu_reg);1797 goto error;1798 }1799 }1800 1801 sysfs_attr_init(&data->dev_attr.attr);1802 data->dev_attr.attr.name = "monitor";1803 data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;1804 data->dev_attr.show = show_monitor;1805 data->dev_attr.store = store_monitor;1806 ret = device_create_file(&pdev->dev, &data->dev_attr);1807 if (ret) {1808 dev_err(&pdev->dev, "could not create sysfs file %s\n",1809 data->dev_attr.attr.name);1810 }1811 1812 dev_set_drvdata(&pdev->dev, data);1813 return 0;1814 1815error:1816 for (i = 0; i < NUM_AOIS; i++)1817 uninstall_fb(&data->fsl_diu_info[i]);1818 1819 iounmap(data->diu_reg);1820 1821 return ret;1822}1823 1824static void fsl_diu_remove(struct platform_device *pdev)1825{1826 struct fsl_diu_data *data;1827 int i;1828 1829 data = dev_get_drvdata(&pdev->dev);1830 disable_lcdc(&data->fsl_diu_info[0]);1831 1832 free_irq(data->irq, data->diu_reg);1833 1834 for (i = 0; i < NUM_AOIS; i++)1835 uninstall_fb(&data->fsl_diu_info[i]);1836 1837 iounmap(data->diu_reg);1838}1839 1840#ifndef MODULE1841static int __init fsl_diu_setup(char *options)1842{1843 char *opt;1844 unsigned long val;1845 1846 if (!options || !*options)1847 return 0;1848 1849 while ((opt = strsep(&options, ",")) != NULL) {1850 if (!*opt)1851 continue;1852 if (!strncmp(opt, "monitor=", 8)) {1853 monitor_port = fsl_diu_name_to_port(opt + 8);1854 } else if (!strncmp(opt, "bpp=", 4)) {1855 if (!kstrtoul(opt + 4, 10, &val))1856 default_bpp = val;1857 } else1858 fb_mode = opt;1859 }1860 1861 return 0;1862}1863#endif1864 1865static const struct of_device_id fsl_diu_match[] = {1866#ifdef CONFIG_PPC_MPC512x1867 {1868 .compatible = "fsl,mpc5121-diu",1869 },1870#endif1871 {1872 .compatible = "fsl,diu",1873 },1874 {}1875};1876MODULE_DEVICE_TABLE(of, fsl_diu_match);1877 1878static struct platform_driver fsl_diu_driver = {1879 .driver = {1880 .name = "fsl-diu-fb",1881 .of_match_table = fsl_diu_match,1882 },1883 .probe = fsl_diu_probe,1884 .remove = fsl_diu_remove,1885 .suspend = fsl_diu_suspend,1886 .resume = fsl_diu_resume,1887};1888 1889static int __init fsl_diu_init(void)1890{1891#ifdef CONFIG_NOT_COHERENT_CACHE1892 struct device_node *np;1893 const u32 *prop;1894#endif1895 int ret;1896#ifndef MODULE1897 char *option;1898 1899 /*1900 * For kernel boot options (in 'video=xxxfb:<options>' format)1901 */1902 if (fb_get_options("fslfb", &option))1903 return -ENODEV;1904 fsl_diu_setup(option);1905#else1906 monitor_port = fsl_diu_name_to_port(monitor_string);1907#endif1908 1909 /*1910 * Must to verify set_pixel_clock. If not implement on platform,1911 * then that means that there is no platform support for the DIU.1912 */1913 if (!diu_ops.set_pixel_clock)1914 return -ENODEV;1915 1916 pr_info("Freescale Display Interface Unit (DIU) framebuffer driver\n");1917 1918#ifdef CONFIG_NOT_COHERENT_CACHE1919 np = of_get_cpu_node(0, NULL);1920 if (!np) {1921 pr_err("fsl-diu-fb: can't find 'cpu' device node\n");1922 return -ENODEV;1923 }1924 1925 prop = of_get_property(np, "d-cache-size", NULL);1926 if (prop == NULL) {1927 pr_err("fsl-diu-fb: missing 'd-cache-size' property' "1928 "in 'cpu' node\n");1929 of_node_put(np);1930 return -ENODEV;1931 }1932 1933 /*1934 * Freescale PLRU requires 13/8 times the cache size to do a proper1935 * displacement flush1936 */1937 coherence_data_size = be32_to_cpup(prop) * 13;1938 coherence_data_size /= 8;1939 1940 pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",1941 coherence_data_size);1942 1943 prop = of_get_property(np, "d-cache-line-size", NULL);1944 if (prop == NULL) {1945 pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "1946 "in 'cpu' node\n");1947 of_node_put(np);1948 return -ENODEV;1949 }1950 d_cache_line_size = be32_to_cpup(prop);1951 1952 pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",1953 d_cache_line_size);1954 1955 of_node_put(np);1956 coherence_data = vmalloc(coherence_data_size);1957 if (!coherence_data)1958 return -ENOMEM;1959#endif1960 1961 ret = platform_driver_register(&fsl_diu_driver);1962 if (ret) {1963 pr_err("fsl-diu-fb: failed to register platform driver\n");1964#if defined(CONFIG_NOT_COHERENT_CACHE)1965 vfree(coherence_data);1966#endif1967 }1968 return ret;1969}1970 1971static void __exit fsl_diu_exit(void)1972{1973 platform_driver_unregister(&fsl_diu_driver);1974#if defined(CONFIG_NOT_COHERENT_CACHE)1975 vfree(coherence_data);1976#endif1977}1978 1979module_init(fsl_diu_init);1980module_exit(fsl_diu_exit);1981 1982MODULE_AUTHOR("York Sun <yorksun@freescale.com>");1983MODULE_DESCRIPTION("Freescale DIU framebuffer driver");1984MODULE_LICENSE("GPL");1985 1986module_param_named(mode, fb_mode, charp, 0);1987MODULE_PARM_DESC(mode,1988 "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");1989module_param_named(bpp, default_bpp, ulong, 0);1990MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified in 'mode'");1991module_param_named(monitor, monitor_string, charp, 0);1992MODULE_PARM_DESC(monitor, "Specify the monitor port "1993 "(\"dvi\", \"lvds\", or \"dlvds\") if supported by the platform");1994 1995