18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/video/ep93xx-fb.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Framebuffer support for the EP93xx series. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2007 Bluewater Systems Ltd 88c2ecf20Sopenharmony_ci * Author: Ryan Mallon 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb 138c2ecf20Sopenharmony_ci * drivers. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci#include <linux/fb.h> 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/platform_data/video-ep93xx.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Vertical Frame Timing Registers */ 278c2ecf20Sopenharmony_ci#define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ 288c2ecf20Sopenharmony_ci#define EP93XXFB_VSYNC 0x0004 /* SW locked */ 298c2ecf20Sopenharmony_ci#define EP93XXFB_VACTIVE 0x0008 /* SW locked */ 308c2ecf20Sopenharmony_ci#define EP93XXFB_VBLANK 0x0228 /* SW locked */ 318c2ecf20Sopenharmony_ci#define EP93XXFB_VCLK 0x000c /* SW locked */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* Horizontal Frame Timing Registers */ 348c2ecf20Sopenharmony_ci#define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ 358c2ecf20Sopenharmony_ci#define EP93XXFB_HSYNC 0x0014 /* SW locked */ 368c2ecf20Sopenharmony_ci#define EP93XXFB_HACTIVE 0x0018 /* SW locked */ 378c2ecf20Sopenharmony_ci#define EP93XXFB_HBLANK 0x022c /* SW locked */ 388c2ecf20Sopenharmony_ci#define EP93XXFB_HCLK 0x001c /* SW locked */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Frame Buffer Memory Configuration Registers */ 418c2ecf20Sopenharmony_ci#define EP93XXFB_SCREEN_PAGE 0x0028 428c2ecf20Sopenharmony_ci#define EP93XXFB_SCREEN_HPAGE 0x002c 438c2ecf20Sopenharmony_ci#define EP93XXFB_SCREEN_LINES 0x0030 448c2ecf20Sopenharmony_ci#define EP93XXFB_LINE_LENGTH 0x0034 458c2ecf20Sopenharmony_ci#define EP93XXFB_VLINE_STEP 0x0038 468c2ecf20Sopenharmony_ci#define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ 478c2ecf20Sopenharmony_ci#define EP93XXFB_EOL_OFFSET 0x0230 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Other Video Registers */ 508c2ecf20Sopenharmony_ci#define EP93XXFB_BRIGHTNESS 0x0020 518c2ecf20Sopenharmony_ci#define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ 528c2ecf20Sopenharmony_ci#define EP93XXFB_SWLOCK 0x007c /* SW locked */ 538c2ecf20Sopenharmony_ci#define EP93XXFB_AC_RATE 0x0214 548c2ecf20Sopenharmony_ci#define EP93XXFB_FIFO_LEVEL 0x0234 558c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE 0x0054 568c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) 578c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) 588c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) 598c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) 608c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) 618c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) 628c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) 638c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) 648c2ecf20Sopenharmony_ci#define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) 658c2ecf20Sopenharmony_ci#define EP93XXFB_PARL_IF_OUT 0x0058 668c2ecf20Sopenharmony_ci#define EP93XXFB_PARL_IF_IN 0x005c 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Blink Control Registers */ 698c2ecf20Sopenharmony_ci#define EP93XXFB_BLINK_RATE 0x0040 708c2ecf20Sopenharmony_ci#define EP93XXFB_BLINK_MASK 0x0044 718c2ecf20Sopenharmony_ci#define EP93XXFB_BLINK_PATTRN 0x0048 728c2ecf20Sopenharmony_ci#define EP93XXFB_PATTRN_MASK 0x004c 738c2ecf20Sopenharmony_ci#define EP93XXFB_BKGRND_OFFSET 0x0050 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Hardware Cursor Registers */ 768c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_ADR_START 0x0060 778c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_ADR_RESET 0x0064 788c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_SIZE 0x0068 798c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_COLOR1 0x006c 808c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_COLOR2 0x0070 818c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c 828c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 838c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_XY_LOC 0x0074 848c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 858c2ecf20Sopenharmony_ci#define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* LUT Registers */ 888c2ecf20Sopenharmony_ci#define EP93XXFB_GRY_SCL_LUTR 0x0080 898c2ecf20Sopenharmony_ci#define EP93XXFB_GRY_SCL_LUTG 0x0280 908c2ecf20Sopenharmony_ci#define EP93XXFB_GRY_SCL_LUTB 0x0300 918c2ecf20Sopenharmony_ci#define EP93XXFB_LUT_SW_CONTROL 0x0218 928c2ecf20Sopenharmony_ci#define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) 938c2ecf20Sopenharmony_ci#define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) 948c2ecf20Sopenharmony_ci#define EP93XXFB_COLOR_LUT 0x0400 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Video Signature Registers */ 978c2ecf20Sopenharmony_ci#define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 988c2ecf20Sopenharmony_ci#define EP93XXFB_VID_SIG_CTRL 0x0204 998c2ecf20Sopenharmony_ci#define EP93XXFB_VSIG 0x0208 1008c2ecf20Sopenharmony_ci#define EP93XXFB_HSIG 0x020c 1018c2ecf20Sopenharmony_ci#define EP93XXFB_SIG_CLR_STR 0x0210 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Minimum / Maximum resolutions supported */ 1048c2ecf20Sopenharmony_ci#define EP93XXFB_MIN_XRES 64 1058c2ecf20Sopenharmony_ci#define EP93XXFB_MIN_YRES 64 1068c2ecf20Sopenharmony_ci#define EP93XXFB_MAX_XRES 1024 1078c2ecf20Sopenharmony_ci#define EP93XXFB_MAX_YRES 768 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct ep93xx_fbi { 1108c2ecf20Sopenharmony_ci struct ep93xxfb_mach_info *mach_info; 1118c2ecf20Sopenharmony_ci struct clk *clk; 1128c2ecf20Sopenharmony_ci struct resource *res; 1138c2ecf20Sopenharmony_ci void __iomem *mmio_base; 1148c2ecf20Sopenharmony_ci unsigned int pseudo_palette[256]; 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int check_screenpage_bug = 1; 1188c2ecf20Sopenharmony_cimodule_param(check_screenpage_bug, int, 0644); 1198c2ecf20Sopenharmony_ciMODULE_PARM_DESC(check_screenpage_bug, 1208c2ecf20Sopenharmony_ci "Check for bit 27 screen page bug. Default = 1"); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, 1238c2ecf20Sopenharmony_ci unsigned int off) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci return __raw_readl(fbi->mmio_base + off); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, 1298c2ecf20Sopenharmony_ci unsigned int val, unsigned int off) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci __raw_writel(val, fbi->mmio_base + off); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Write to one of the locked raster registers. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_cistatic inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, 1388c2ecf20Sopenharmony_ci unsigned int val, unsigned int reg) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * We don't need a lock or delay here since the raster register 1428c2ecf20Sopenharmony_ci * block will remain unlocked until the next access. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); 1458c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, val, reg); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void ep93xxfb_set_video_attribs(struct fb_info *info) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 1518c2ecf20Sopenharmony_ci unsigned int attribs; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci attribs = EP93XXFB_ENABLE; 1548c2ecf20Sopenharmony_ci attribs |= fbi->mach_info->flags; 1558c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int ep93xxfb_set_pixelmode(struct fb_info *info) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 1618c2ecf20Sopenharmony_ci unsigned int val; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci info->var.transp.offset = 0; 1648c2ecf20Sopenharmony_ci info->var.transp.length = 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 1678c2ecf20Sopenharmony_ci case 8: 1688c2ecf20Sopenharmony_ci val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | 1698c2ecf20Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_18B; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci info->var.red.offset = 0; 1728c2ecf20Sopenharmony_ci info->var.red.length = 8; 1738c2ecf20Sopenharmony_ci info->var.green.offset = 0; 1748c2ecf20Sopenharmony_ci info->var.green.length = 8; 1758c2ecf20Sopenharmony_ci info->var.blue.offset = 0; 1768c2ecf20Sopenharmony_ci info->var.blue.length = 8; 1778c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_PSEUDOCOLOR; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci case 16: 1818c2ecf20Sopenharmony_ci val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | 1828c2ecf20Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_18B; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci info->var.red.offset = 11; 1858c2ecf20Sopenharmony_ci info->var.red.length = 5; 1868c2ecf20Sopenharmony_ci info->var.green.offset = 5; 1878c2ecf20Sopenharmony_ci info->var.green.length = 6; 1888c2ecf20Sopenharmony_ci info->var.blue.offset = 0; 1898c2ecf20Sopenharmony_ci info->var.blue.length = 5; 1908c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci case 24: 1948c2ecf20Sopenharmony_ci val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | 1958c2ecf20Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_24B; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci info->var.red.offset = 16; 1988c2ecf20Sopenharmony_ci info->var.red.length = 8; 1998c2ecf20Sopenharmony_ci info->var.green.offset = 8; 2008c2ecf20Sopenharmony_ci info->var.green.length = 8; 2018c2ecf20Sopenharmony_ci info->var.blue.offset = 0; 2028c2ecf20Sopenharmony_ci info->var.blue.length = 8; 2038c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci case 32: 2078c2ecf20Sopenharmony_ci val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | 2088c2ecf20Sopenharmony_ci EP93XXFB_PIXELMODE_SHIFT_1P_24B; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci info->var.red.offset = 16; 2118c2ecf20Sopenharmony_ci info->var.red.length = 8; 2128c2ecf20Sopenharmony_ci info->var.green.offset = 8; 2138c2ecf20Sopenharmony_ci info->var.green.length = 8; 2148c2ecf20Sopenharmony_ci info->var.blue.offset = 0; 2158c2ecf20Sopenharmony_ci info->var.blue.length = 8; 2168c2ecf20Sopenharmony_ci info->fix.visual = FB_VISUAL_TRUECOLOR; 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci default: 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); 2248c2ecf20Sopenharmony_ci return 0; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic void ep93xxfb_set_timing(struct fb_info *info) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 2308c2ecf20Sopenharmony_ci unsigned int vlines_total, hclks_total, start, stop; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci vlines_total = info->var.yres + info->var.upper_margin + 2338c2ecf20Sopenharmony_ci info->var.lower_margin + info->var.vsync_len - 1; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci hclks_total = info->var.xres + info->var.left_margin + 2368c2ecf20Sopenharmony_ci info->var.right_margin + info->var.hsync_len - 1; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); 2398c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci start = vlines_total; 2428c2ecf20Sopenharmony_ci stop = vlines_total - info->var.vsync_len; 2438c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci start = vlines_total - info->var.vsync_len - info->var.upper_margin; 2468c2ecf20Sopenharmony_ci stop = info->var.lower_margin - 1; 2478c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); 2488c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci start = vlines_total; 2518c2ecf20Sopenharmony_ci stop = vlines_total + 1; 2528c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci start = hclks_total; 2558c2ecf20Sopenharmony_ci stop = hclks_total - info->var.hsync_len; 2568c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci start = hclks_total - info->var.hsync_len - info->var.left_margin; 2598c2ecf20Sopenharmony_ci stop = info->var.right_margin - 1; 2608c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); 2618c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci start = hclks_total; 2648c2ecf20Sopenharmony_ci stop = hclks_total; 2658c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int ep93xxfb_set_par(struct fb_info *info) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ep93xxfb_set_timing(info); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * 2798c2ecf20Sopenharmony_ci info->var.bits_per_pixel / 8; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); 2828c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); 2838c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) 2848c2ecf20Sopenharmony_ci / 32) - 1, EP93XXFB_LINE_LENGTH); 2858c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); 2868c2ecf20Sopenharmony_ci ep93xxfb_set_video_attribs(info); 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int ep93xxfb_check_var(struct fb_var_screeninfo *var, 2918c2ecf20Sopenharmony_ci struct fb_info *info) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci int err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci err = ep93xxfb_set_pixelmode(info); 2968c2ecf20Sopenharmony_ci if (err) 2978c2ecf20Sopenharmony_ci return err; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); 3008c2ecf20Sopenharmony_ci var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); 3018c2ecf20Sopenharmony_ci var->xres_virtual = max(var->xres_virtual, var->xres); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); 3048c2ecf20Sopenharmony_ci var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); 3058c2ecf20Sopenharmony_ci var->yres_virtual = max(var->yres_virtual, var->yres); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (offset < info->fix.smem_len) { 3158c2ecf20Sopenharmony_ci return dma_mmap_wc(info->dev, vma, info->screen_base, 3168c2ecf20Sopenharmony_ci info->fix.smem_start, info->fix.smem_len); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int ep93xxfb_blank(int blank_mode, struct fb_info *info) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 3258c2ecf20Sopenharmony_ci unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (blank_mode) { 3288c2ecf20Sopenharmony_ci if (fbi->mach_info->blank) 3298c2ecf20Sopenharmony_ci fbi->mach_info->blank(blank_mode, info); 3308c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, 3318c2ecf20Sopenharmony_ci EP93XXFB_ATTRIBS); 3328c2ecf20Sopenharmony_ci clk_disable(fbi->clk); 3338c2ecf20Sopenharmony_ci } else { 3348c2ecf20Sopenharmony_ci clk_enable(fbi->clk); 3358c2ecf20Sopenharmony_ci ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, 3368c2ecf20Sopenharmony_ci EP93XXFB_ATTRIBS); 3378c2ecf20Sopenharmony_ci if (fbi->mach_info->blank) 3388c2ecf20Sopenharmony_ci fbi->mach_info->blank(blank_mode, info); 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic inline int ep93xxfb_convert_color(int val, int width) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci return ((val << width) + 0x7fff - val) >> 16; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, 3508c2ecf20Sopenharmony_ci unsigned int green, unsigned int blue, 3518c2ecf20Sopenharmony_ci unsigned int transp, struct fb_info *info) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 3548c2ecf20Sopenharmony_ci unsigned int *pal = info->pseudo_palette; 3558c2ecf20Sopenharmony_ci unsigned int ctrl, i, rgb, lut_current, lut_stat; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci switch (info->fix.visual) { 3588c2ecf20Sopenharmony_ci case FB_VISUAL_PSEUDOCOLOR: 3598c2ecf20Sopenharmony_ci if (regno > 255) 3608c2ecf20Sopenharmony_ci return 1; 3618c2ecf20Sopenharmony_ci rgb = ((red & 0xff00) << 8) | (green & 0xff00) | 3628c2ecf20Sopenharmony_ci ((blue & 0xff00) >> 8); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci pal[regno] = rgb; 3658c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); 3668c2ecf20Sopenharmony_ci ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); 3678c2ecf20Sopenharmony_ci lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); 3688c2ecf20Sopenharmony_ci lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (lut_stat == lut_current) { 3718c2ecf20Sopenharmony_ci for (i = 0; i < 256; i++) { 3728c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, pal[i], 3738c2ecf20Sopenharmony_ci EP93XXFB_COLOR_LUT + (i << 2)); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ep93xxfb_writel(fbi, 3778c2ecf20Sopenharmony_ci ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, 3788c2ecf20Sopenharmony_ci EP93XXFB_LUT_SW_CONTROL); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci case FB_VISUAL_TRUECOLOR: 3838c2ecf20Sopenharmony_ci if (regno > 16) 3848c2ecf20Sopenharmony_ci return 1; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci red = ep93xxfb_convert_color(red, info->var.red.length); 3878c2ecf20Sopenharmony_ci green = ep93xxfb_convert_color(green, info->var.green.length); 3888c2ecf20Sopenharmony_ci blue = ep93xxfb_convert_color(blue, info->var.blue.length); 3898c2ecf20Sopenharmony_ci transp = ep93xxfb_convert_color(transp, 3908c2ecf20Sopenharmony_ci info->var.transp.length); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci pal[regno] = (red << info->var.red.offset) | 3938c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 3948c2ecf20Sopenharmony_ci (blue << info->var.blue.offset) | 3958c2ecf20Sopenharmony_ci (transp << info->var.transp.offset); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci default: 3998c2ecf20Sopenharmony_ci return 1; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic const struct fb_ops ep93xxfb_ops = { 4068c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4078c2ecf20Sopenharmony_ci .fb_check_var = ep93xxfb_check_var, 4088c2ecf20Sopenharmony_ci .fb_set_par = ep93xxfb_set_par, 4098c2ecf20Sopenharmony_ci .fb_blank = ep93xxfb_blank, 4108c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 4118c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 4128c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 4138c2ecf20Sopenharmony_ci .fb_setcolreg = ep93xxfb_setcolreg, 4148c2ecf20Sopenharmony_ci .fb_mmap = ep93xxfb_mmap, 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int ep93xxfb_alloc_videomem(struct fb_info *info) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci char __iomem *virt_addr; 4208c2ecf20Sopenharmony_ci dma_addr_t phys_addr; 4218c2ecf20Sopenharmony_ci unsigned int fb_size; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Maximum 16bpp -> used memory is maximum x*y*2 bytes */ 4248c2ecf20Sopenharmony_ci fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci virt_addr = dma_alloc_wc(info->dev, fb_size, &phys_addr, GFP_KERNEL); 4278c2ecf20Sopenharmony_ci if (!virt_addr) 4288c2ecf20Sopenharmony_ci return -ENOMEM; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * There is a bug in the ep93xx framebuffer which causes problems 4328c2ecf20Sopenharmony_ci * if bit 27 of the physical address is set. 4338c2ecf20Sopenharmony_ci * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 4348c2ecf20Sopenharmony_ci * There does not seem to be any official errata for this, but I 4358c2ecf20Sopenharmony_ci * have confirmed the problem exists on my hardware (ep9315) at 4368c2ecf20Sopenharmony_ci * least. 4378c2ecf20Sopenharmony_ci */ 4388c2ecf20Sopenharmony_ci if (check_screenpage_bug && phys_addr & (1 << 27)) { 4398c2ecf20Sopenharmony_ci dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " 4408c2ecf20Sopenharmony_ci "has bit 27 set: cannot init framebuffer\n", 4418c2ecf20Sopenharmony_ci phys_addr); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); 4448c2ecf20Sopenharmony_ci return -ENOMEM; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci info->fix.smem_start = phys_addr; 4488c2ecf20Sopenharmony_ci info->fix.smem_len = fb_size; 4498c2ecf20Sopenharmony_ci info->screen_base = virt_addr; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void ep93xxfb_dealloc_videomem(struct fb_info *info) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci if (info->screen_base) 4578c2ecf20Sopenharmony_ci dma_free_coherent(info->dev, info->fix.smem_len, 4588c2ecf20Sopenharmony_ci info->screen_base, info->fix.smem_start); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int ep93xxfb_probe(struct platform_device *pdev) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct ep93xxfb_mach_info *mach_info = dev_get_platdata(&pdev->dev); 4648c2ecf20Sopenharmony_ci struct fb_info *info; 4658c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi; 4668c2ecf20Sopenharmony_ci struct resource *res; 4678c2ecf20Sopenharmony_ci char *video_mode; 4688c2ecf20Sopenharmony_ci int err; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!mach_info) 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); 4748c2ecf20Sopenharmony_ci if (!info) 4758c2ecf20Sopenharmony_ci return -ENOMEM; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, info); 4788c2ecf20Sopenharmony_ci fbi = info->par; 4798c2ecf20Sopenharmony_ci fbi->mach_info = mach_info; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci err = fb_alloc_cmap(&info->cmap, 256, 0); 4828c2ecf20Sopenharmony_ci if (err) 4838c2ecf20Sopenharmony_ci goto failed_cmap; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci err = ep93xxfb_alloc_videomem(info); 4868c2ecf20Sopenharmony_ci if (err) 4878c2ecf20Sopenharmony_ci goto failed_videomem; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4908c2ecf20Sopenharmony_ci if (!res) { 4918c2ecf20Sopenharmony_ci err = -ENXIO; 4928c2ecf20Sopenharmony_ci goto failed_resource; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci /* 4968c2ecf20Sopenharmony_ci * FIXME - We don't do a request_mem_region here because we are 4978c2ecf20Sopenharmony_ci * sharing the register space with the backlight driver (see 4988c2ecf20Sopenharmony_ci * drivers/video/backlight/ep93xx_bl.c) and doing so will cause 4998c2ecf20Sopenharmony_ci * the second loaded driver to return -EBUSY. 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * NOTE: No locking is required; the backlight does not touch 5028c2ecf20Sopenharmony_ci * any of the framebuffer registers. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci fbi->res = res; 5058c2ecf20Sopenharmony_ci fbi->mmio_base = devm_ioremap(&pdev->dev, res->start, 5068c2ecf20Sopenharmony_ci resource_size(res)); 5078c2ecf20Sopenharmony_ci if (!fbi->mmio_base) { 5088c2ecf20Sopenharmony_ci err = -ENXIO; 5098c2ecf20Sopenharmony_ci goto failed_resource; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci strcpy(info->fix.id, pdev->name); 5138c2ecf20Sopenharmony_ci info->fbops = &ep93xxfb_ops; 5148c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 5158c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 5168c2ecf20Sopenharmony_ci info->var.activate = FB_ACTIVATE_NOW; 5178c2ecf20Sopenharmony_ci info->var.vmode = FB_VMODE_NONINTERLACED; 5188c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 5198c2ecf20Sopenharmony_ci info->node = -1; 5208c2ecf20Sopenharmony_ci info->state = FBINFO_STATE_RUNNING; 5218c2ecf20Sopenharmony_ci info->pseudo_palette = &fbi->pseudo_palette; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci fb_get_options("ep93xx-fb", &video_mode); 5248c2ecf20Sopenharmony_ci err = fb_find_mode(&info->var, info, video_mode, 5258c2ecf20Sopenharmony_ci NULL, 0, NULL, 16); 5268c2ecf20Sopenharmony_ci if (err == 0) { 5278c2ecf20Sopenharmony_ci dev_err(info->dev, "No suitable video mode found\n"); 5288c2ecf20Sopenharmony_ci err = -EINVAL; 5298c2ecf20Sopenharmony_ci goto failed_resource; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (mach_info->setup) { 5338c2ecf20Sopenharmony_ci err = mach_info->setup(pdev); 5348c2ecf20Sopenharmony_ci if (err) 5358c2ecf20Sopenharmony_ci goto failed_resource; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci err = ep93xxfb_check_var(&info->var, info); 5398c2ecf20Sopenharmony_ci if (err) 5408c2ecf20Sopenharmony_ci goto failed_check; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci fbi->clk = devm_clk_get(&pdev->dev, NULL); 5438c2ecf20Sopenharmony_ci if (IS_ERR(fbi->clk)) { 5448c2ecf20Sopenharmony_ci err = PTR_ERR(fbi->clk); 5458c2ecf20Sopenharmony_ci fbi->clk = NULL; 5468c2ecf20Sopenharmony_ci goto failed_check; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci ep93xxfb_set_par(info); 5508c2ecf20Sopenharmony_ci clk_enable(fbi->clk); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci err = register_framebuffer(info); 5538c2ecf20Sopenharmony_ci if (err) 5548c2ecf20Sopenharmony_ci goto failed_check; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci dev_info(info->dev, "registered. Mode = %dx%d-%d\n", 5578c2ecf20Sopenharmony_ci info->var.xres, info->var.yres, info->var.bits_per_pixel); 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cifailed_check: 5618c2ecf20Sopenharmony_ci if (fbi->mach_info->teardown) 5628c2ecf20Sopenharmony_ci fbi->mach_info->teardown(pdev); 5638c2ecf20Sopenharmony_cifailed_resource: 5648c2ecf20Sopenharmony_ci ep93xxfb_dealloc_videomem(info); 5658c2ecf20Sopenharmony_cifailed_videomem: 5668c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5678c2ecf20Sopenharmony_cifailed_cmap: 5688c2ecf20Sopenharmony_ci kfree(info); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci return err; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int ep93xxfb_remove(struct platform_device *pdev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(pdev); 5768c2ecf20Sopenharmony_ci struct ep93xx_fbi *fbi = info->par; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci unregister_framebuffer(info); 5798c2ecf20Sopenharmony_ci clk_disable(fbi->clk); 5808c2ecf20Sopenharmony_ci ep93xxfb_dealloc_videomem(info); 5818c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (fbi->mach_info->teardown) 5848c2ecf20Sopenharmony_ci fbi->mach_info->teardown(pdev); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci kfree(info); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic struct platform_driver ep93xxfb_driver = { 5928c2ecf20Sopenharmony_ci .probe = ep93xxfb_probe, 5938c2ecf20Sopenharmony_ci .remove = ep93xxfb_remove, 5948c2ecf20Sopenharmony_ci .driver = { 5958c2ecf20Sopenharmony_ci .name = "ep93xx-fb", 5968c2ecf20Sopenharmony_ci }, 5978c2ecf20Sopenharmony_ci}; 5988c2ecf20Sopenharmony_cimodule_platform_driver(ep93xxfb_driver); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EP93XX Framebuffer Driver"); 6018c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-fb"); 6028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ryan Mallon, " 6038c2ecf20Sopenharmony_ci "H Hartley Sweeten <hsweeten@visionengravers.com"); 6048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 605