162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/video/ep93xx-fb.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Framebuffer support for the EP93xx series. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2007 Bluewater Systems Ltd 862306a36Sopenharmony_ci * Author: Ryan Mallon 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb 1362306a36Sopenharmony_ci * drivers. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/clk.h> 2162306a36Sopenharmony_ci#include <linux/fb.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/platform_data/video-ep93xx.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Vertical Frame Timing Registers */ 2762306a36Sopenharmony_ci#define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ 2862306a36Sopenharmony_ci#define EP93XXFB_VSYNC 0x0004 /* SW locked */ 2962306a36Sopenharmony_ci#define EP93XXFB_VACTIVE 0x0008 /* SW locked */ 3062306a36Sopenharmony_ci#define EP93XXFB_VBLANK 0x0228 /* SW locked */ 3162306a36Sopenharmony_ci#define EP93XXFB_VCLK 0x000c /* SW locked */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Horizontal Frame Timing Registers */ 3462306a36Sopenharmony_ci#define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ 3562306a36Sopenharmony_ci#define EP93XXFB_HSYNC 0x0014 /* SW locked */ 3662306a36Sopenharmony_ci#define EP93XXFB_HACTIVE 0x0018 /* SW locked */ 3762306a36Sopenharmony_ci#define EP93XXFB_HBLANK 0x022c /* SW locked */ 3862306a36Sopenharmony_ci#define EP93XXFB_HCLK 0x001c /* SW locked */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* Frame Buffer Memory Configuration Registers */ 4162306a36Sopenharmony_ci#define EP93XXFB_SCREEN_PAGE 0x0028 4262306a36Sopenharmony_ci#define EP93XXFB_SCREEN_HPAGE 0x002c 4362306a36Sopenharmony_ci#define EP93XXFB_SCREEN_LINES 0x0030 4462306a36Sopenharmony_ci#define EP93XXFB_LINE_LENGTH 0x0034 4562306a36Sopenharmony_ci#define EP93XXFB_VLINE_STEP 0x0038 4662306a36Sopenharmony_ci#define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ 4762306a36Sopenharmony_ci#define EP93XXFB_EOL_OFFSET 0x0230 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Other Video Registers */ 5062306a36Sopenharmony_ci#define EP93XXFB_BRIGHTNESS 0x0020 5162306a36Sopenharmony_ci#define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ 5262306a36Sopenharmony_ci#define EP93XXFB_SWLOCK 0x007c /* SW locked */ 5362306a36Sopenharmony_ci#define EP93XXFB_AC_RATE 0x0214 5462306a36Sopenharmony_ci#define EP93XXFB_FIFO_LEVEL 0x0234 5562306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE 0x0054 5662306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) 5762306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) 5862306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) 5962306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) 6062306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) 6162306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) 6262306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) 6362306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) 6462306a36Sopenharmony_ci#define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) 6562306a36Sopenharmony_ci#define EP93XXFB_PARL_IF_OUT 0x0058 6662306a36Sopenharmony_ci#define EP93XXFB_PARL_IF_IN 0x005c 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Blink Control Registers */ 6962306a36Sopenharmony_ci#define EP93XXFB_BLINK_RATE 0x0040 7062306a36Sopenharmony_ci#define EP93XXFB_BLINK_MASK 0x0044 7162306a36Sopenharmony_ci#define EP93XXFB_BLINK_PATTRN 0x0048 7262306a36Sopenharmony_ci#define EP93XXFB_PATTRN_MASK 0x004c 7362306a36Sopenharmony_ci#define EP93XXFB_BKGRND_OFFSET 0x0050 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* Hardware Cursor Registers */ 7662306a36Sopenharmony_ci#define EP93XXFB_CURSOR_ADR_START 0x0060 7762306a36Sopenharmony_ci#define EP93XXFB_CURSOR_ADR_RESET 0x0064 7862306a36Sopenharmony_ci#define EP93XXFB_CURSOR_SIZE 0x0068 7962306a36Sopenharmony_ci#define EP93XXFB_CURSOR_COLOR1 0x006c 8062306a36Sopenharmony_ci#define EP93XXFB_CURSOR_COLOR2 0x0070 8162306a36Sopenharmony_ci#define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c 8262306a36Sopenharmony_ci#define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 8362306a36Sopenharmony_ci#define EP93XXFB_CURSOR_XY_LOC 0x0074 8462306a36Sopenharmony_ci#define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 8562306a36Sopenharmony_ci#define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* LUT Registers */ 8862306a36Sopenharmony_ci#define EP93XXFB_GRY_SCL_LUTR 0x0080 8962306a36Sopenharmony_ci#define EP93XXFB_GRY_SCL_LUTG 0x0280 9062306a36Sopenharmony_ci#define EP93XXFB_GRY_SCL_LUTB 0x0300 9162306a36Sopenharmony_ci#define EP93XXFB_LUT_SW_CONTROL 0x0218 9262306a36Sopenharmony_ci#define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) 9362306a36Sopenharmony_ci#define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) 9462306a36Sopenharmony_ci#define EP93XXFB_COLOR_LUT 0x0400 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* Video Signature Registers */ 9762306a36Sopenharmony_ci#define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 9862306a36Sopenharmony_ci#define EP93XXFB_VID_SIG_CTRL 0x0204 9962306a36Sopenharmony_ci#define EP93XXFB_VSIG 0x0208 10062306a36Sopenharmony_ci#define EP93XXFB_HSIG 0x020c 10162306a36Sopenharmony_ci#define EP93XXFB_SIG_CLR_STR 0x0210 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* Minimum / Maximum resolutions supported */ 10462306a36Sopenharmony_ci#define EP93XXFB_MIN_XRES 64 10562306a36Sopenharmony_ci#define EP93XXFB_MIN_YRES 64 10662306a36Sopenharmony_ci#define EP93XXFB_MAX_XRES 1024 10762306a36Sopenharmony_ci#define EP93XXFB_MAX_YRES 768 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistruct ep93xx_fbi { 11062306a36Sopenharmony_ci struct ep93xxfb_mach_info *mach_info; 11162306a36Sopenharmony_ci struct clk *clk; 11262306a36Sopenharmony_ci struct resource *res; 11362306a36Sopenharmony_ci void __iomem *mmio_base; 11462306a36Sopenharmony_ci unsigned int pseudo_palette[256]; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int check_screenpage_bug = 1; 11862306a36Sopenharmony_cimodule_param(check_screenpage_bug, int, 0644); 11962306a36Sopenharmony_ciMODULE_PARM_DESC(check_screenpage_bug, 12062306a36Sopenharmony_ci "Check for bit 27 screen page bug. Default = 1"); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, 12362306a36Sopenharmony_ci unsigned int off) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return __raw_readl(fbi->mmio_base + off); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, 12962306a36Sopenharmony_ci unsigned int val, unsigned int off) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci __raw_writel(val, fbi->mmio_base + off); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Write to one of the locked raster registers. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_cistatic inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, 13862306a36Sopenharmony_ci unsigned int val, unsigned int reg) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * We don't need a lock or delay here since the raster register 14262306a36Sopenharmony_ci * block will remain unlocked until the next access. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); 14562306a36Sopenharmony_ci ep93xxfb_writel(fbi, val, reg); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void ep93xxfb_set_video_attribs(struct fb_info *info) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 15162306a36Sopenharmony_ci unsigned int attribs; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci attribs = EP93XXFB_ENABLE; 15462306a36Sopenharmony_ci attribs |= fbi->mach_info->flags; 15562306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int ep93xxfb_set_pixelmode(struct fb_info *info) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 16162306a36Sopenharmony_ci unsigned int val; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci info->var.transp.offset = 0; 16462306a36Sopenharmony_ci info->var.transp.length = 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 16762306a36Sopenharmony_ci case 8: 16862306a36Sopenharmony_ci val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | 16962306a36Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_18B; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci info->var.red.offset = 0; 17262306a36Sopenharmony_ci info->var.red.length = 8; 17362306a36Sopenharmony_ci info->var.green.offset = 0; 17462306a36Sopenharmony_ci info->var.green.length = 8; 17562306a36Sopenharmony_ci info->var.blue.offset = 0; 17662306a36Sopenharmony_ci info->var.blue.length = 8; 17762306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci case 16: 18162306a36Sopenharmony_ci val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | 18262306a36Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_18B; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci info->var.red.offset = 11; 18562306a36Sopenharmony_ci info->var.red.length = 5; 18662306a36Sopenharmony_ci info->var.green.offset = 5; 18762306a36Sopenharmony_ci info->var.green.length = 6; 18862306a36Sopenharmony_ci info->var.blue.offset = 0; 18962306a36Sopenharmony_ci info->var.blue.length = 5; 19062306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci case 24: 19462306a36Sopenharmony_ci val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | 19562306a36Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_24B; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci info->var.red.offset = 16; 19862306a36Sopenharmony_ci info->var.red.length = 8; 19962306a36Sopenharmony_ci info->var.green.offset = 8; 20062306a36Sopenharmony_ci info->var.green.length = 8; 20162306a36Sopenharmony_ci info->var.blue.offset = 0; 20262306a36Sopenharmony_ci info->var.blue.length = 8; 20362306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci case 32: 20762306a36Sopenharmony_ci val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | 20862306a36Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_24B; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci info->var.red.offset = 16; 21162306a36Sopenharmony_ci info->var.red.length = 8; 21262306a36Sopenharmony_ci info->var.green.offset = 8; 21362306a36Sopenharmony_ci info->var.green.length = 8; 21462306a36Sopenharmony_ci info->var.blue.offset = 0; 21562306a36Sopenharmony_ci info->var.blue.length = 8; 21662306a36Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci default: 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void ep93xxfb_set_timing(struct fb_info *info) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 23062306a36Sopenharmony_ci unsigned int vlines_total, hclks_total, start, stop; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci vlines_total = info->var.yres + info->var.upper_margin + 23362306a36Sopenharmony_ci info->var.lower_margin + info->var.vsync_len - 1; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci hclks_total = info->var.xres + info->var.left_margin + 23662306a36Sopenharmony_ci info->var.right_margin + info->var.hsync_len - 1; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); 23962306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci start = vlines_total; 24262306a36Sopenharmony_ci stop = vlines_total - info->var.vsync_len; 24362306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci start = vlines_total - info->var.vsync_len - info->var.upper_margin; 24662306a36Sopenharmony_ci stop = info->var.lower_margin - 1; 24762306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); 24862306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci start = vlines_total; 25162306a36Sopenharmony_ci stop = vlines_total + 1; 25262306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci start = hclks_total; 25562306a36Sopenharmony_ci stop = hclks_total - info->var.hsync_len; 25662306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci start = hclks_total - info->var.hsync_len - info->var.left_margin; 25962306a36Sopenharmony_ci stop = info->var.right_margin - 1; 26062306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); 26162306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci start = hclks_total; 26462306a36Sopenharmony_ci stop = hclks_total; 26562306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic int ep93xxfb_set_par(struct fb_info *info) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ep93xxfb_set_timing(info); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * 27962306a36Sopenharmony_ci info->var.bits_per_pixel / 8; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); 28262306a36Sopenharmony_ci ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); 28362306a36Sopenharmony_ci ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) 28462306a36Sopenharmony_ci / 32) - 1, EP93XXFB_LINE_LENGTH); 28562306a36Sopenharmony_ci ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); 28662306a36Sopenharmony_ci ep93xxfb_set_video_attribs(info); 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int ep93xxfb_check_var(struct fb_var_screeninfo *var, 29162306a36Sopenharmony_ci struct fb_info *info) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci int err; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci err = ep93xxfb_set_pixelmode(info); 29662306a36Sopenharmony_ci if (err) 29762306a36Sopenharmony_ci return err; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); 30062306a36Sopenharmony_ci var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); 30162306a36Sopenharmony_ci var->xres_virtual = max(var->xres_virtual, var->xres); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); 30462306a36Sopenharmony_ci var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); 30562306a36Sopenharmony_ci var->yres_virtual = max(var->yres_virtual, var->yres); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (offset < info->fix.smem_len) { 31562306a36Sopenharmony_ci return dma_mmap_wc(info->device, vma, info->screen_base, 31662306a36Sopenharmony_ci info->fix.smem_start, info->fix.smem_len); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int ep93xxfb_blank(int blank_mode, struct fb_info *info) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 32562306a36Sopenharmony_ci unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (blank_mode) { 32862306a36Sopenharmony_ci if (fbi->mach_info->blank) 32962306a36Sopenharmony_ci fbi->mach_info->blank(blank_mode, info); 33062306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, 33162306a36Sopenharmony_ci EP93XXFB_ATTRIBS); 33262306a36Sopenharmony_ci clk_disable(fbi->clk); 33362306a36Sopenharmony_ci } else { 33462306a36Sopenharmony_ci clk_enable(fbi->clk); 33562306a36Sopenharmony_ci ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, 33662306a36Sopenharmony_ci EP93XXFB_ATTRIBS); 33762306a36Sopenharmony_ci if (fbi->mach_info->blank) 33862306a36Sopenharmony_ci fbi->mach_info->blank(blank_mode, info); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic inline int ep93xxfb_convert_color(int val, int width) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return ((val << width) + 0x7fff - val) >> 16; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, 35062306a36Sopenharmony_ci unsigned int green, unsigned int blue, 35162306a36Sopenharmony_ci unsigned int transp, struct fb_info *info) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 35462306a36Sopenharmony_ci unsigned int *pal = info->pseudo_palette; 35562306a36Sopenharmony_ci unsigned int ctrl, i, rgb, lut_current, lut_stat; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (info->fix.visual) { 35862306a36Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 35962306a36Sopenharmony_ci if (regno > 255) 36062306a36Sopenharmony_ci return 1; 36162306a36Sopenharmony_ci rgb = ((red & 0xff00) << 8) | (green & 0xff00) | 36262306a36Sopenharmony_ci ((blue & 0xff00) >> 8); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci pal[regno] = rgb; 36562306a36Sopenharmony_ci ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); 36662306a36Sopenharmony_ci ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); 36762306a36Sopenharmony_ci lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); 36862306a36Sopenharmony_ci lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (lut_stat == lut_current) { 37162306a36Sopenharmony_ci for (i = 0; i < 256; i++) { 37262306a36Sopenharmony_ci ep93xxfb_writel(fbi, pal[i], 37362306a36Sopenharmony_ci EP93XXFB_COLOR_LUT + (i << 2)); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ep93xxfb_writel(fbi, 37762306a36Sopenharmony_ci ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, 37862306a36Sopenharmony_ci EP93XXFB_LUT_SW_CONTROL); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 38362306a36Sopenharmony_ci if (regno > 16) 38462306a36Sopenharmony_ci return 1; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci red = ep93xxfb_convert_color(red, info->var.red.length); 38762306a36Sopenharmony_ci green = ep93xxfb_convert_color(green, info->var.green.length); 38862306a36Sopenharmony_ci blue = ep93xxfb_convert_color(blue, info->var.blue.length); 38962306a36Sopenharmony_ci transp = ep93xxfb_convert_color(transp, 39062306a36Sopenharmony_ci info->var.transp.length); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci pal[regno] = (red << info->var.red.offset) | 39362306a36Sopenharmony_ci (green << info->var.green.offset) | 39462306a36Sopenharmony_ci (blue << info->var.blue.offset) | 39562306a36Sopenharmony_ci (transp << info->var.transp.offset); 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci default: 39962306a36Sopenharmony_ci return 1; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic const struct fb_ops ep93xxfb_ops = { 40662306a36Sopenharmony_ci .owner = THIS_MODULE, 40762306a36Sopenharmony_ci .fb_check_var = ep93xxfb_check_var, 40862306a36Sopenharmony_ci .fb_set_par = ep93xxfb_set_par, 40962306a36Sopenharmony_ci .fb_blank = ep93xxfb_blank, 41062306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 41162306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 41262306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 41362306a36Sopenharmony_ci .fb_setcolreg = ep93xxfb_setcolreg, 41462306a36Sopenharmony_ci .fb_mmap = ep93xxfb_mmap, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int ep93xxfb_alloc_videomem(struct fb_info *info) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci char __iomem *virt_addr; 42062306a36Sopenharmony_ci dma_addr_t phys_addr; 42162306a36Sopenharmony_ci unsigned int fb_size; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Maximum 16bpp -> used memory is maximum x*y*2 bytes */ 42462306a36Sopenharmony_ci fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci virt_addr = dma_alloc_wc(info->device, fb_size, &phys_addr, GFP_KERNEL); 42762306a36Sopenharmony_ci if (!virt_addr) 42862306a36Sopenharmony_ci return -ENOMEM; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * There is a bug in the ep93xx framebuffer which causes problems 43262306a36Sopenharmony_ci * if bit 27 of the physical address is set. 43362306a36Sopenharmony_ci * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 43462306a36Sopenharmony_ci * There does not seem to be any official errata for this, but I 43562306a36Sopenharmony_ci * have confirmed the problem exists on my hardware (ep9315) at 43662306a36Sopenharmony_ci * least. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci if (check_screenpage_bug && phys_addr & (1 << 27)) { 43962306a36Sopenharmony_ci fb_err(info, "ep93xx framebuffer bug. phys addr (0x%x) " 44062306a36Sopenharmony_ci "has bit 27 set: cannot init framebuffer\n", 44162306a36Sopenharmony_ci phys_addr); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci dma_free_coherent(info->device, fb_size, virt_addr, phys_addr); 44462306a36Sopenharmony_ci return -ENOMEM; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci info->fix.smem_start = phys_addr; 44862306a36Sopenharmony_ci info->fix.smem_len = fb_size; 44962306a36Sopenharmony_ci info->screen_base = virt_addr; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void ep93xxfb_dealloc_videomem(struct fb_info *info) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci if (info->screen_base) 45762306a36Sopenharmony_ci dma_free_coherent(info->device, info->fix.smem_len, 45862306a36Sopenharmony_ci info->screen_base, info->fix.smem_start); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int ep93xxfb_probe(struct platform_device *pdev) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct ep93xxfb_mach_info *mach_info = dev_get_platdata(&pdev->dev); 46462306a36Sopenharmony_ci struct fb_info *info; 46562306a36Sopenharmony_ci struct ep93xx_fbi *fbi; 46662306a36Sopenharmony_ci struct resource *res; 46762306a36Sopenharmony_ci char *video_mode; 46862306a36Sopenharmony_ci int err; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!mach_info) 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); 47462306a36Sopenharmony_ci if (!info) 47562306a36Sopenharmony_ci return -ENOMEM; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci platform_set_drvdata(pdev, info); 47862306a36Sopenharmony_ci fbi = info->par; 47962306a36Sopenharmony_ci fbi->mach_info = mach_info; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci err = fb_alloc_cmap(&info->cmap, 256, 0); 48262306a36Sopenharmony_ci if (err) 48362306a36Sopenharmony_ci goto failed_cmap; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = ep93xxfb_alloc_videomem(info); 48662306a36Sopenharmony_ci if (err) 48762306a36Sopenharmony_ci goto failed_videomem; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 49062306a36Sopenharmony_ci if (!res) { 49162306a36Sopenharmony_ci err = -ENXIO; 49262306a36Sopenharmony_ci goto failed_resource; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * FIXME - We don't do a request_mem_region here because we are 49762306a36Sopenharmony_ci * sharing the register space with the backlight driver (see 49862306a36Sopenharmony_ci * drivers/video/backlight/ep93xx_bl.c) and doing so will cause 49962306a36Sopenharmony_ci * the second loaded driver to return -EBUSY. 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * NOTE: No locking is required; the backlight does not touch 50262306a36Sopenharmony_ci * any of the framebuffer registers. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci fbi->res = res; 50562306a36Sopenharmony_ci fbi->mmio_base = devm_ioremap(&pdev->dev, res->start, 50662306a36Sopenharmony_ci resource_size(res)); 50762306a36Sopenharmony_ci if (!fbi->mmio_base) { 50862306a36Sopenharmony_ci err = -ENXIO; 50962306a36Sopenharmony_ci goto failed_resource; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci strcpy(info->fix.id, pdev->name); 51362306a36Sopenharmony_ci info->fbops = &ep93xxfb_ops; 51462306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 51562306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 51662306a36Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 51762306a36Sopenharmony_ci info->var.vmode = FB_VMODE_NONINTERLACED; 51862306a36Sopenharmony_ci info->node = -1; 51962306a36Sopenharmony_ci info->state = FBINFO_STATE_RUNNING; 52062306a36Sopenharmony_ci info->pseudo_palette = &fbi->pseudo_palette; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci fb_get_options("ep93xx-fb", &video_mode); 52362306a36Sopenharmony_ci err = fb_find_mode(&info->var, info, video_mode, 52462306a36Sopenharmony_ci NULL, 0, NULL, 16); 52562306a36Sopenharmony_ci if (err == 0) { 52662306a36Sopenharmony_ci fb_err(info, "No suitable video mode found\n"); 52762306a36Sopenharmony_ci err = -EINVAL; 52862306a36Sopenharmony_ci goto failed_resource; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (mach_info->setup) { 53262306a36Sopenharmony_ci err = mach_info->setup(pdev); 53362306a36Sopenharmony_ci if (err) 53462306a36Sopenharmony_ci goto failed_resource; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci err = ep93xxfb_check_var(&info->var, info); 53862306a36Sopenharmony_ci if (err) 53962306a36Sopenharmony_ci goto failed_check; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci fbi->clk = devm_clk_get(&pdev->dev, NULL); 54262306a36Sopenharmony_ci if (IS_ERR(fbi->clk)) { 54362306a36Sopenharmony_ci err = PTR_ERR(fbi->clk); 54462306a36Sopenharmony_ci fbi->clk = NULL; 54562306a36Sopenharmony_ci goto failed_check; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ep93xxfb_set_par(info); 54962306a36Sopenharmony_ci err = clk_prepare_enable(fbi->clk); 55062306a36Sopenharmony_ci if (err) 55162306a36Sopenharmony_ci goto failed_check; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci err = register_framebuffer(info); 55462306a36Sopenharmony_ci if (err) 55562306a36Sopenharmony_ci goto failed_framebuffer; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci fb_info(info, "registered. Mode = %dx%d-%d\n", 55862306a36Sopenharmony_ci info->var.xres, info->var.yres, info->var.bits_per_pixel); 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cifailed_framebuffer: 56262306a36Sopenharmony_ci clk_disable_unprepare(fbi->clk); 56362306a36Sopenharmony_cifailed_check: 56462306a36Sopenharmony_ci if (fbi->mach_info->teardown) 56562306a36Sopenharmony_ci fbi->mach_info->teardown(pdev); 56662306a36Sopenharmony_cifailed_resource: 56762306a36Sopenharmony_ci ep93xxfb_dealloc_videomem(info); 56862306a36Sopenharmony_cifailed_videomem: 56962306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 57062306a36Sopenharmony_cifailed_cmap: 57162306a36Sopenharmony_ci kfree(info); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return err; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic void ep93xxfb_remove(struct platform_device *pdev) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 57962306a36Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci unregister_framebuffer(info); 58262306a36Sopenharmony_ci clk_disable_unprepare(fbi->clk); 58362306a36Sopenharmony_ci ep93xxfb_dealloc_videomem(info); 58462306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (fbi->mach_info->teardown) 58762306a36Sopenharmony_ci fbi->mach_info->teardown(pdev); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci kfree(info); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic struct platform_driver ep93xxfb_driver = { 59362306a36Sopenharmony_ci .probe = ep93xxfb_probe, 59462306a36Sopenharmony_ci .remove_new = ep93xxfb_remove, 59562306a36Sopenharmony_ci .driver = { 59662306a36Sopenharmony_ci .name = "ep93xx-fb", 59762306a36Sopenharmony_ci }, 59862306a36Sopenharmony_ci}; 59962306a36Sopenharmony_cimodule_platform_driver(ep93xxfb_driver); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ciMODULE_DESCRIPTION("EP93XX Framebuffer Driver"); 60262306a36Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-fb"); 60362306a36Sopenharmony_ciMODULE_AUTHOR("Ryan Mallon, " 60462306a36Sopenharmony_ci "H Hartley Sweeten <hsweeten@visionengravers.com"); 60562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 606