18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Frame buffer driver for the Carmine GPU. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The driver configures the GPU as follows 68c2ecf20Sopenharmony_ci * - FB0 is display 0 with unique memory area 78c2ecf20Sopenharmony_ci * - FB1 is display 1 with unique memory area 88c2ecf20Sopenharmony_ci * - both display use 32 bit colors 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/fb.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "carminefb.h" 198c2ecf20Sopenharmony_ci#include "carminefb_regs.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN) 228c2ecf20Sopenharmony_ci#error "The endianness of the target host has not been defined." 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * The initial video mode can be supplied via two different ways: 278c2ecf20Sopenharmony_ci * - as a string that is passed to fb_find_mode() (module option fb_mode_str) 288c2ecf20Sopenharmony_ci * - as an integer that picks the video mode from carmine_modedb[] (module 298c2ecf20Sopenharmony_ci * option fb_mode) 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * If nothing is used than the initial video mode will be the 328c2ecf20Sopenharmony_ci * CARMINEFB_DEFAULT_VIDEO_MODE member of the carmine_modedb[]. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#define CARMINEFB_DEFAULT_VIDEO_MODE 1 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic unsigned int fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; 378c2ecf20Sopenharmony_cimodule_param(fb_mode, uint, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_mode, "Initial video mode as integer."); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic char *fb_mode_str; 418c2ecf20Sopenharmony_cimodule_param(fb_mode_str, charp, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_mode_str, "Initial video mode in characters."); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Carminefb displays: 468c2ecf20Sopenharmony_ci * 0b000 None 478c2ecf20Sopenharmony_ci * 0b001 Display 0 488c2ecf20Sopenharmony_ci * 0b010 Display 1 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic int fb_displays = CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1; 518c2ecf20Sopenharmony_cimodule_param(fb_displays, int, 0444); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fb_displays, "Bit mode, which displays are used"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct carmine_hw { 558c2ecf20Sopenharmony_ci void __iomem *v_regs; 568c2ecf20Sopenharmony_ci void __iomem *screen_mem; 578c2ecf20Sopenharmony_ci struct fb_info *fb[MAX_DISPLAY]; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct carmine_resolution { 618c2ecf20Sopenharmony_ci u32 htp; 628c2ecf20Sopenharmony_ci u32 hsp; 638c2ecf20Sopenharmony_ci u32 hsw; 648c2ecf20Sopenharmony_ci u32 hdp; 658c2ecf20Sopenharmony_ci u32 vtr; 668c2ecf20Sopenharmony_ci u32 vsp; 678c2ecf20Sopenharmony_ci u32 vsw; 688c2ecf20Sopenharmony_ci u32 vdp; 698c2ecf20Sopenharmony_ci u32 disp_mode; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct carmine_fb { 738c2ecf20Sopenharmony_ci void __iomem *display_reg; 748c2ecf20Sopenharmony_ci void __iomem *screen_base; 758c2ecf20Sopenharmony_ci u32 smem_offset; 768c2ecf20Sopenharmony_ci u32 cur_mode; 778c2ecf20Sopenharmony_ci u32 new_mode; 788c2ecf20Sopenharmony_ci struct carmine_resolution *res; 798c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo carminefb_fix = { 838c2ecf20Sopenharmony_ci .id = "Carmine", 848c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 858c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 868c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const struct fb_videomode carmine_modedb[] = { 908c2ecf20Sopenharmony_ci { 918c2ecf20Sopenharmony_ci .name = "640x480", 928c2ecf20Sopenharmony_ci .xres = 640, 938c2ecf20Sopenharmony_ci .yres = 480, 948c2ecf20Sopenharmony_ci }, { 958c2ecf20Sopenharmony_ci .name = "800x600", 968c2ecf20Sopenharmony_ci .xres = 800, 978c2ecf20Sopenharmony_ci .yres = 600, 988c2ecf20Sopenharmony_ci }, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct carmine_resolution car_modes[] = { 1028c2ecf20Sopenharmony_ci { 1038c2ecf20Sopenharmony_ci /* 640x480 */ 1048c2ecf20Sopenharmony_ci .htp = 800, 1058c2ecf20Sopenharmony_ci .hsp = 672, 1068c2ecf20Sopenharmony_ci .hsw = 96, 1078c2ecf20Sopenharmony_ci .hdp = 640, 1088c2ecf20Sopenharmony_ci .vtr = 525, 1098c2ecf20Sopenharmony_ci .vsp = 490, 1108c2ecf20Sopenharmony_ci .vsw = 2, 1118c2ecf20Sopenharmony_ci .vdp = 480, 1128c2ecf20Sopenharmony_ci .disp_mode = 0x1400, 1138c2ecf20Sopenharmony_ci }, 1148c2ecf20Sopenharmony_ci { 1158c2ecf20Sopenharmony_ci /* 800x600 */ 1168c2ecf20Sopenharmony_ci .htp = 1060, 1178c2ecf20Sopenharmony_ci .hsp = 864, 1188c2ecf20Sopenharmony_ci .hsw = 72, 1198c2ecf20Sopenharmony_ci .hdp = 800, 1208c2ecf20Sopenharmony_ci .vtr = 628, 1218c2ecf20Sopenharmony_ci .vsp = 601, 1228c2ecf20Sopenharmony_ci .vsw = 2, 1238c2ecf20Sopenharmony_ci .vdp = 600, 1248c2ecf20Sopenharmony_ci .disp_mode = 0x0d00, 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int carmine_find_mode(const struct fb_var_screeninfo *var) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(car_modes); i++) 1338c2ecf20Sopenharmony_ci if (car_modes[i].hdp == var->xres && 1348c2ecf20Sopenharmony_ci car_modes[i].vdp == var->yres) 1358c2ecf20Sopenharmony_ci return i; 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void c_set_disp_reg(const struct carmine_fb *par, 1408c2ecf20Sopenharmony_ci u32 offset, u32 val) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci writel(val, par->display_reg + offset); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic u32 c_get_disp_reg(const struct carmine_fb *par, 1468c2ecf20Sopenharmony_ci u32 offset) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return readl(par->display_reg + offset); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void c_set_hw_reg(const struct carmine_hw *hw, 1528c2ecf20Sopenharmony_ci u32 offset, u32 val) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci writel(val, hw->v_regs + offset); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic u32 c_get_hw_reg(const struct carmine_hw *hw, 1588c2ecf20Sopenharmony_ci u32 offset) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci return readl(hw->v_regs + offset); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int carmine_setcolreg(unsigned regno, unsigned red, unsigned green, 1648c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, struct fb_info *info) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci if (regno >= 16) 1678c2ecf20Sopenharmony_ci return 1; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci red >>= 8; 1708c2ecf20Sopenharmony_ci green >>= 8; 1718c2ecf20Sopenharmony_ci blue >>= 8; 1728c2ecf20Sopenharmony_ci transp >>= 8; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci ((__be32 *)info->pseudo_palette)[regno] = cpu_to_be32(transp << 24 | 1758c2ecf20Sopenharmony_ci red << 0 | green << 8 | blue << 16); 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int carmine_check_var(struct fb_var_screeninfo *var, 1808c2ecf20Sopenharmony_ci struct fb_info *info) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = carmine_find_mode(var); 1858c2ecf20Sopenharmony_ci if (ret < 0) 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (var->grayscale || var->rotate || var->nonstd) 1898c2ecf20Sopenharmony_ci return -EINVAL; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 1928c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 1978c2ecf20Sopenharmony_ci var->transp.offset = 24; 1988c2ecf20Sopenharmony_ci var->red.offset = 0; 1998c2ecf20Sopenharmony_ci var->green.offset = 8; 2008c2ecf20Sopenharmony_ci var->blue.offset = 16; 2018c2ecf20Sopenharmony_ci#else 2028c2ecf20Sopenharmony_ci var->transp.offset = 24; 2038c2ecf20Sopenharmony_ci var->red.offset = 16; 2048c2ecf20Sopenharmony_ci var->green.offset = 8; 2058c2ecf20Sopenharmony_ci var->blue.offset = 0; 2068c2ecf20Sopenharmony_ci#endif 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci var->red.length = 8; 2098c2ecf20Sopenharmony_ci var->green.length = 8; 2108c2ecf20Sopenharmony_ci var->blue.length = 8; 2118c2ecf20Sopenharmony_ci var->transp.length = 8; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci var->red.msb_right = 0; 2148c2ecf20Sopenharmony_ci var->green.msb_right = 0; 2158c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 2168c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void carmine_init_display_param(struct carmine_fb *par) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u32 width; 2238c2ecf20Sopenharmony_ci u32 height; 2248c2ecf20Sopenharmony_ci u32 param; 2258c2ecf20Sopenharmony_ci u32 window_size; 2268c2ecf20Sopenharmony_ci u32 soffset = par->smem_offset; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_C_TRANS, 0); 2298c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_MLMR_TRANS, 0); 2308c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_CURSOR_MODE, 2318c2ecf20Sopenharmony_ci CARMINE_CURSOR0_PRIORITY_MASK | 2328c2ecf20Sopenharmony_ci CARMINE_CURSOR1_PRIORITY_MASK | 2338c2ecf20Sopenharmony_ci CARMINE_CURSOR_CUTZ_MASK); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Set default cursor position */ 2368c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_CUR1_POS, 0 << 16 | 0); 2378c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_CUR2_POS, 0 << 16 | 0); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* Set default display mode */ 2408c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_EXT_MODE, CARMINE_WINDOW_MODE | 2418c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2428c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_EXT_MODE, 2438c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2448c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_EXT_MODE, CARMINE_EXTEND_MODE | 2458c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2468c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_EXT_MODE, CARMINE_EXTEND_MODE | 2478c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2488c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_EXT_MODE, CARMINE_EXTEND_MODE | 2498c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2508c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_EXT_MODE, CARMINE_EXTEND_MODE | 2518c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2528c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_EXT_MODE, CARMINE_EXTEND_MODE | 2538c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2548c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_EXT_MODE, CARMINE_EXTEND_MODE | 2558c2ecf20Sopenharmony_ci CARMINE_EXT_CMODE_DIRECT24_RGBA); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Set default frame size to layer mode register */ 2588c2ecf20Sopenharmony_ci width = par->res->hdp * 4 / CARMINE_DISP_WIDTH_UNIT; 2598c2ecf20Sopenharmony_ci width = width << CARMINE_DISP_WIDTH_SHIFT; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci height = par->res->vdp - 1; 2628c2ecf20Sopenharmony_ci param = width | height; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_MODE_W_H, param); 2658c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIDTH, width); 2668c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_MODE_W_H, param); 2678c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_MODE_W_H, param); 2688c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_MODE_W_H, param); 2698c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_MODE_W_H, param); 2708c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_MODE_W_H, param); 2718c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_MODE_W_H, param); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Set default pos and size */ 2748c2ecf20Sopenharmony_ci window_size = (par->res->vdp - 1) << CARMINE_DISP_WIN_H_SHIFT; 2758c2ecf20Sopenharmony_ci window_size |= par->res->hdp; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_POS, 0); 2788c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_WIN_SIZE, window_size); 2798c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_POS, 0); 2808c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_WIN_SIZE, window_size); 2818c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_POS, 0); 2828c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_WIN_SIZE, window_size); 2838c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_POS, 0); 2848c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_WIN_SIZE, window_size); 2858c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_POS, 0); 2868c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_WIN_SIZE, window_size); 2878c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_POS, 0); 2888c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_WIN_SIZE, window_size); 2898c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_POS, 0); 2908c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_WIN_SIZE, window_size); 2918c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_POS, 0); 2928c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_WIN_SIZE, window_size); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Set default origin address */ 2958c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_ORG_ADR, soffset); 2968c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_ORG_ADR, soffset); 2978c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_ORG_ADR1, soffset); 2988c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_ORG_ADR1, soffset); 2998c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_ORG_ADR1, soffset); 3008c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_ORG_ADR1, soffset); 3018c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_ORG_ADR1, soffset); 3028c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_ORG_ADR1, soffset); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Set default display address */ 3058c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_ADR, soffset); 3068c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_ADR1, soffset); 3078c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_ADR1, soffset); 3088c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_ADR1, soffset); 3098c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_ADR1, soffset); 3108c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_ADR0, soffset); 3118c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_ADR0, soffset); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Set default display position */ 3148c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_DISP_POS, 0); 3158c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_DISP_POS, 0); 3168c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_DISP_POS, 0); 3178c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_DISP_POS, 0); 3188c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_DISP_POS, 0); 3198c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_DISP_POS, 0); 3208c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_DISP_POS, 0); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* Set default blend mode */ 3238c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L0, 0); 3248c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L1, 0); 3258c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L2, 0); 3268c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L3, 0); 3278c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L4, 0); 3288c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L5, 0); 3298c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L6, 0); 3308c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_BLEND_MODE_L7, 0); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* default transparency mode */ 3338c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0_TRANS, 0); 3348c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L1_TRANS, 0); 3358c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2_TRANS, 0); 3368c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3_TRANS, 0); 3378c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4_TRANS, 0); 3388c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5_TRANS, 0); 3398c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6_TRANS, 0); 3408c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7_TRANS, 0); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Set default read skip parameter */ 3438c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0RM, 0); 3448c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2RM, 0); 3458c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3RM, 0); 3468c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4RM, 0); 3478c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5RM, 0); 3488c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6RM, 0); 3498c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7RM, 0); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0PX, 0); 3528c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2PX, 0); 3538c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3PX, 0); 3548c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4PX, 0); 3558c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5PX, 0); 3568c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6PX, 0); 3578c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7PX, 0); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L0PY, 0); 3608c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L2PY, 0); 3618c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L3PY, 0); 3628c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L4PY, 0); 3638c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L5PY, 0); 3648c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L6PY, 0); 3658c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_L7PY, 0); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic void set_display_parameters(struct carmine_fb *par) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci u32 mode; 3718c2ecf20Sopenharmony_ci u32 hdp, vdp, htp, hsp, hsw, vtr, vsp, vsw; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* 3748c2ecf20Sopenharmony_ci * display timing. Parameters are decreased by one because hardware 3758c2ecf20Sopenharmony_ci * spec is 0 to (n - 1) 3768c2ecf20Sopenharmony_ci * */ 3778c2ecf20Sopenharmony_ci hdp = par->res->hdp - 1; 3788c2ecf20Sopenharmony_ci vdp = par->res->vdp - 1; 3798c2ecf20Sopenharmony_ci htp = par->res->htp - 1; 3808c2ecf20Sopenharmony_ci hsp = par->res->hsp - 1; 3818c2ecf20Sopenharmony_ci hsw = par->res->hsw - 1; 3828c2ecf20Sopenharmony_ci vtr = par->res->vtr - 1; 3838c2ecf20Sopenharmony_ci vsp = par->res->vsp - 1; 3848c2ecf20Sopenharmony_ci vsw = par->res->vsw - 1; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_H_TOTAL, 3878c2ecf20Sopenharmony_ci htp << CARMINE_DISP_HTP_SHIFT); 3888c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_H_PERIOD, 3898c2ecf20Sopenharmony_ci (hdp << CARMINE_DISP_HDB_SHIFT) | hdp); 3908c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_V_H_W_H_POS, 3918c2ecf20Sopenharmony_ci (vsw << CARMINE_DISP_VSW_SHIFT) | 3928c2ecf20Sopenharmony_ci (hsw << CARMINE_DISP_HSW_SHIFT) | 3938c2ecf20Sopenharmony_ci (hsp)); 3948c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_V_TOTAL, 3958c2ecf20Sopenharmony_ci vtr << CARMINE_DISP_VTR_SHIFT); 3968c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_V_PERIOD_POS, 3978c2ecf20Sopenharmony_ci (vdp << CARMINE_DISP_VDP_SHIFT) | vsp); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* clock */ 4008c2ecf20Sopenharmony_ci mode = c_get_disp_reg(par, CARMINE_DISP_REG_DCM1); 4018c2ecf20Sopenharmony_ci mode = (mode & ~CARMINE_DISP_DCM_MASK) | 4028c2ecf20Sopenharmony_ci (par->res->disp_mode & CARMINE_DISP_DCM_MASK); 4038c2ecf20Sopenharmony_ci /* enable video output and layer 0 */ 4048c2ecf20Sopenharmony_ci mode |= CARMINE_DEN | CARMINE_L0E; 4058c2ecf20Sopenharmony_ci c_set_disp_reg(par, CARMINE_DISP_REG_DCM1, mode); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int carmine_set_par(struct fb_info *info) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct carmine_fb *par = info->par; 4118c2ecf20Sopenharmony_ci int ret; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = carmine_find_mode(&info->var); 4148c2ecf20Sopenharmony_ci if (ret < 0) 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci par->new_mode = ret; 4188c2ecf20Sopenharmony_ci if (par->cur_mode != par->new_mode) { 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci par->cur_mode = par->new_mode; 4218c2ecf20Sopenharmony_ci par->res = &car_modes[par->new_mode]; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci carmine_init_display_param(par); 4248c2ecf20Sopenharmony_ci set_display_parameters(par); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int init_hardware(struct carmine_hw *hw) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci u32 flags; 4348c2ecf20Sopenharmony_ci u32 loops; 4358c2ecf20Sopenharmony_ci u32 ret; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Initialize Carmine */ 4388c2ecf20Sopenharmony_ci /* Sets internal clock */ 4398c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 4408c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_CLOCK_ENABLE); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Video signal output is turned off */ 4438c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); 4448c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Software reset */ 4478c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 1); 4488c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_SOFTWARE_RESET, 0); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* I/O mode settings */ 4518c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_IO_CONT1 << 16 | 4528c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_IO_CONT0; 4538c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_IOCONT1_IOCONT0, 4548c2ecf20Sopenharmony_ci flags); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* DRAM initial sequence */ 4578c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_MODE << 16 | CARMINE_DFLT_IP_DCTL_ADD; 4588c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, 4598c2ecf20Sopenharmony_ci flags); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_SET_TIME1 << 16 | 4628c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_EMODE; 4638c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_SETTIME1_EMODE, 4648c2ecf20Sopenharmony_ci flags); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_REFRESH << 16 | 4678c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_SET_TIME2; 4688c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_REFRESH_SETTIME2, 4698c2ecf20Sopenharmony_ci flags); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_RESERVE2 << 16 | 4728c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_FIFO_DEPTH; 4738c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV2_RSV1, flags); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_DDRIF2 << 16 | CARMINE_DFLT_IP_DCTL_DDRIF1; 4768c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_DDRIF2_DDRIF1, 4778c2ecf20Sopenharmony_ci flags); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | 4808c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_STATES; 4818c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, 4828c2ecf20Sopenharmony_ci flags); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Executes DLL reset */ 4858c2ecf20Sopenharmony_ci if (CARMINE_DCTL_DLL_RESET) { 4868c2ecf20Sopenharmony_ci for (loops = 0; loops < CARMINE_DCTL_INIT_WAIT_LIMIT; loops++) { 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = c_get_hw_reg(hw, CARMINE_DCTL_REG + 4898c2ecf20Sopenharmony_ci CARMINE_DCTL_REG_RSV0_STATES); 4908c2ecf20Sopenharmony_ci ret &= CARMINE_DCTL_REG_STATES_MASK; 4918c2ecf20Sopenharmony_ci if (!ret) 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci mdelay(CARMINE_DCTL_INIT_WAIT_INTERVAL); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (loops >= CARMINE_DCTL_INIT_WAIT_LIMIT) { 4988c2ecf20Sopenharmony_ci printk(KERN_ERR "DRAM init failed\n"); 4998c2ecf20Sopenharmony_ci return -EIO; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_MODE_AFT_RST << 16 | 5048c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_ADD; 5058c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_MODE_ADD, flags); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci flags = CARMINE_DFLT_IP_DCTL_RESERVE0 << 16 | 5088c2ecf20Sopenharmony_ci CARMINE_DFLT_IP_DCTL_STATES_AFT_RST; 5098c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DCTL_REG + CARMINE_DCTL_REG_RSV0_STATES, 5108c2ecf20Sopenharmony_ci flags); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* Initialize the write back register */ 5138c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_WB_REG + CARMINE_WB_REG_WBM, 5148c2ecf20Sopenharmony_ci CARMINE_WB_REG_WBM_DEFAULT); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Initialize the Kottos registers */ 5178c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRINTM, 0); 5188c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_VRERRM, 0); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* Set DC offsets */ 5218c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PX, 0); 5228c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_PY, 0); 5238c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LX, 0); 5248c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_LY, 0); 5258c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TX, 0); 5268c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_GRAPH_REG + CARMINE_GRAPH_REG_DC_OFFSET_TY, 0); 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic const struct fb_ops carminefb_ops = { 5318c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5328c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 5338c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 5348c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci .fb_check_var = carmine_check_var, 5378c2ecf20Sopenharmony_ci .fb_set_par = carmine_set_par, 5388c2ecf20Sopenharmony_ci .fb_setcolreg = carmine_setcolreg, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic int alloc_carmine_fb(void __iomem *regs, void __iomem *smem_base, 5428c2ecf20Sopenharmony_ci int smem_offset, struct device *device, 5438c2ecf20Sopenharmony_ci struct fb_info **rinfo) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci int ret; 5468c2ecf20Sopenharmony_ci struct fb_info *info; 5478c2ecf20Sopenharmony_ci struct carmine_fb *par; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof *par, device); 5508c2ecf20Sopenharmony_ci if (!info) 5518c2ecf20Sopenharmony_ci return -ENOMEM; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci par = info->par; 5548c2ecf20Sopenharmony_ci par->display_reg = regs; 5558c2ecf20Sopenharmony_ci par->smem_offset = smem_offset; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci info->screen_base = smem_base + smem_offset; 5588c2ecf20Sopenharmony_ci info->screen_size = CARMINE_DISPLAY_MEM; 5598c2ecf20Sopenharmony_ci info->fbops = &carminefb_ops; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci info->fix = carminefb_fix; 5628c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 5638c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci ret = fb_alloc_cmap(&info->cmap, 256, 1); 5668c2ecf20Sopenharmony_ci if (ret < 0) 5678c2ecf20Sopenharmony_ci goto err_free_fb; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (fb_mode >= ARRAY_SIZE(carmine_modedb)) 5708c2ecf20Sopenharmony_ci fb_mode = CARMINEFB_DEFAULT_VIDEO_MODE; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci par->cur_mode = par->new_mode = ~0; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ret = fb_find_mode(&info->var, info, fb_mode_str, carmine_modedb, 5758c2ecf20Sopenharmony_ci ARRAY_SIZE(carmine_modedb), 5768c2ecf20Sopenharmony_ci &carmine_modedb[fb_mode], 32); 5778c2ecf20Sopenharmony_ci if (!ret || ret == 4) { 5788c2ecf20Sopenharmony_ci ret = -EINVAL; 5798c2ecf20Sopenharmony_ci goto err_dealloc_cmap; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci fb_videomode_to_modelist(carmine_modedb, ARRAY_SIZE(carmine_modedb), 5838c2ecf20Sopenharmony_ci &info->modelist); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 5868c2ecf20Sopenharmony_ci if (ret < 0) 5878c2ecf20Sopenharmony_ci goto err_dealloc_cmap; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci *rinfo = info; 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cierr_dealloc_cmap: 5958c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5968c2ecf20Sopenharmony_cierr_free_fb: 5978c2ecf20Sopenharmony_ci framebuffer_release(info); 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void cleanup_fb_device(struct fb_info *info) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci if (info) { 6048c2ecf20Sopenharmony_ci unregister_framebuffer(info); 6058c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 6068c2ecf20Sopenharmony_ci framebuffer_release(info); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int carminefb_probe(struct pci_dev *dev, const struct pci_device_id *ent) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct carmine_hw *hw; 6138c2ecf20Sopenharmony_ci struct device *device = &dev->dev; 6148c2ecf20Sopenharmony_ci struct fb_info *info; 6158c2ecf20Sopenharmony_ci int ret; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ret = pci_enable_device(dev); 6188c2ecf20Sopenharmony_ci if (ret) 6198c2ecf20Sopenharmony_ci return ret; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci ret = -ENOMEM; 6228c2ecf20Sopenharmony_ci hw = kzalloc(sizeof *hw, GFP_KERNEL); 6238c2ecf20Sopenharmony_ci if (!hw) 6248c2ecf20Sopenharmony_ci goto err_enable_pci; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci carminefb_fix.mmio_start = pci_resource_start(dev, CARMINE_CONFIG_BAR); 6278c2ecf20Sopenharmony_ci carminefb_fix.mmio_len = pci_resource_len(dev, CARMINE_CONFIG_BAR); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (!request_mem_region(carminefb_fix.mmio_start, 6308c2ecf20Sopenharmony_ci carminefb_fix.mmio_len, 6318c2ecf20Sopenharmony_ci "carminefb regbase")) { 6328c2ecf20Sopenharmony_ci printk(KERN_ERR "carminefb: Can't reserve regbase.\n"); 6338c2ecf20Sopenharmony_ci ret = -EBUSY; 6348c2ecf20Sopenharmony_ci goto err_free_hw; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci hw->v_regs = ioremap(carminefb_fix.mmio_start, 6378c2ecf20Sopenharmony_ci carminefb_fix.mmio_len); 6388c2ecf20Sopenharmony_ci if (!hw->v_regs) { 6398c2ecf20Sopenharmony_ci printk(KERN_ERR "carminefb: Can't remap %s register.\n", 6408c2ecf20Sopenharmony_ci carminefb_fix.id); 6418c2ecf20Sopenharmony_ci goto err_free_reg_mmio; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci carminefb_fix.smem_start = pci_resource_start(dev, CARMINE_MEMORY_BAR); 6458c2ecf20Sopenharmony_ci carminefb_fix.smem_len = pci_resource_len(dev, CARMINE_MEMORY_BAR); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* The memory area tends to be very large (256 MiB). Remap only what 6488c2ecf20Sopenharmony_ci * is required for that largest resolution to avoid remaps at run 6498c2ecf20Sopenharmony_ci * time 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci if (carminefb_fix.smem_len > CARMINE_TOTAL_DIPLAY_MEM) 6528c2ecf20Sopenharmony_ci carminefb_fix.smem_len = CARMINE_TOTAL_DIPLAY_MEM; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci else if (carminefb_fix.smem_len < CARMINE_TOTAL_DIPLAY_MEM) { 6558c2ecf20Sopenharmony_ci printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " 6568c2ecf20Sopenharmony_ci "are required.", carminefb_fix.smem_len, 6578c2ecf20Sopenharmony_ci CARMINE_TOTAL_DIPLAY_MEM); 6588c2ecf20Sopenharmony_ci goto err_unmap_vregs; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (!request_mem_region(carminefb_fix.smem_start, 6628c2ecf20Sopenharmony_ci carminefb_fix.smem_len, "carminefb smem")) { 6638c2ecf20Sopenharmony_ci printk(KERN_ERR "carminefb: Can't reserve smem.\n"); 6648c2ecf20Sopenharmony_ci goto err_unmap_vregs; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci hw->screen_mem = ioremap(carminefb_fix.smem_start, 6688c2ecf20Sopenharmony_ci carminefb_fix.smem_len); 6698c2ecf20Sopenharmony_ci if (!hw->screen_mem) { 6708c2ecf20Sopenharmony_ci printk(KERN_ERR "carmine: Can't ioremap smem area.\n"); 6718c2ecf20Sopenharmony_ci goto err_reg_smem; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci ret = init_hardware(hw); 6758c2ecf20Sopenharmony_ci if (ret) 6768c2ecf20Sopenharmony_ci goto err_unmap_screen; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci info = NULL; 6798c2ecf20Sopenharmony_ci if (fb_displays & CARMINE_USE_DISPLAY0) { 6808c2ecf20Sopenharmony_ci ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP0_REG, 6818c2ecf20Sopenharmony_ci hw->screen_mem, CARMINE_DISPLAY_MEM * 0, 6828c2ecf20Sopenharmony_ci device, &info); 6838c2ecf20Sopenharmony_ci if (ret) 6848c2ecf20Sopenharmony_ci goto err_deinit_hw; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci hw->fb[0] = info; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci info = NULL; 6908c2ecf20Sopenharmony_ci if (fb_displays & CARMINE_USE_DISPLAY1) { 6918c2ecf20Sopenharmony_ci ret = alloc_carmine_fb(hw->v_regs + CARMINE_DISP1_REG, 6928c2ecf20Sopenharmony_ci hw->screen_mem, CARMINE_DISPLAY_MEM * 1, 6938c2ecf20Sopenharmony_ci device, &info); 6948c2ecf20Sopenharmony_ci if (ret) 6958c2ecf20Sopenharmony_ci goto err_cleanup_fb0; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci hw->fb[1] = info; 6998c2ecf20Sopenharmony_ci info = NULL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci pci_set_drvdata(dev, hw); 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cierr_cleanup_fb0: 7058c2ecf20Sopenharmony_ci cleanup_fb_device(hw->fb[0]); 7068c2ecf20Sopenharmony_cierr_deinit_hw: 7078c2ecf20Sopenharmony_ci /* disable clock, etc */ 7088c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); 7098c2ecf20Sopenharmony_cierr_unmap_screen: 7108c2ecf20Sopenharmony_ci iounmap(hw->screen_mem); 7118c2ecf20Sopenharmony_cierr_reg_smem: 7128c2ecf20Sopenharmony_ci release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len); 7138c2ecf20Sopenharmony_cierr_unmap_vregs: 7148c2ecf20Sopenharmony_ci iounmap(hw->v_regs); 7158c2ecf20Sopenharmony_cierr_free_reg_mmio: 7168c2ecf20Sopenharmony_ci release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); 7178c2ecf20Sopenharmony_cierr_free_hw: 7188c2ecf20Sopenharmony_ci kfree(hw); 7198c2ecf20Sopenharmony_cierr_enable_pci: 7208c2ecf20Sopenharmony_ci pci_disable_device(dev); 7218c2ecf20Sopenharmony_ci return ret; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic void carminefb_remove(struct pci_dev *dev) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct carmine_hw *hw = pci_get_drvdata(dev); 7278c2ecf20Sopenharmony_ci struct fb_fix_screeninfo fix; 7288c2ecf20Sopenharmony_ci int i; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* in case we use only fb1 and not fb1 */ 7318c2ecf20Sopenharmony_ci if (hw->fb[0]) 7328c2ecf20Sopenharmony_ci fix = hw->fb[0]->fix; 7338c2ecf20Sopenharmony_ci else 7348c2ecf20Sopenharmony_ci fix = hw->fb[1]->fix; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* deactivate display(s) and switch clocks */ 7378c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP0_REG + CARMINE_DISP_REG_DCM1, 0); 7388c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_DISP1_REG + CARMINE_DISP_REG_DCM1, 0); 7398c2ecf20Sopenharmony_ci c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, 0); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci for (i = 0; i < MAX_DISPLAY; i++) 7428c2ecf20Sopenharmony_ci cleanup_fb_device(hw->fb[i]); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci iounmap(hw->screen_mem); 7458c2ecf20Sopenharmony_ci release_mem_region(fix.smem_start, fix.smem_len); 7468c2ecf20Sopenharmony_ci iounmap(hw->v_regs); 7478c2ecf20Sopenharmony_ci release_mem_region(fix.mmio_start, fix.mmio_len); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci pci_disable_device(dev); 7508c2ecf20Sopenharmony_ci kfree(hw); 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci#define PCI_VENDOR_ID_FUJITU_LIMITED 0x10cf 7548c2ecf20Sopenharmony_cistatic struct pci_device_id carmine_devices[] = { 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci PCI_DEVICE(PCI_VENDOR_ID_FUJITU_LIMITED, 0x202b)}, 7578c2ecf20Sopenharmony_ci {0, 0, 0, 0, 0, 0, 0} 7588c2ecf20Sopenharmony_ci}; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, carmine_devices); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic struct pci_driver carmine_pci_driver = { 7638c2ecf20Sopenharmony_ci .name = "carminefb", 7648c2ecf20Sopenharmony_ci .id_table = carmine_devices, 7658c2ecf20Sopenharmony_ci .probe = carminefb_probe, 7668c2ecf20Sopenharmony_ci .remove = carminefb_remove, 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic int __init carminefb_init(void) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci if (!(fb_displays & 7728c2ecf20Sopenharmony_ci (CARMINE_USE_DISPLAY0 | CARMINE_USE_DISPLAY1))) { 7738c2ecf20Sopenharmony_ci printk(KERN_ERR "If you disable both displays than you don't " 7748c2ecf20Sopenharmony_ci "need the driver at all\n"); 7758c2ecf20Sopenharmony_ci return -EINVAL; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci return pci_register_driver(&carmine_pci_driver); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_cimodule_init(carminefb_init); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void __exit carminefb_cleanup(void) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci pci_unregister_driver(&carmine_pci_driver); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_cimodule_exit(carminefb_cleanup); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sebastian Siewior <bigeasy@linutronix.de>"); 7888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Fujitsu Carmine based devices"); 7898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 790