18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Geode GX display controller. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Arcom Control Systems Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Portions from AMD's original 2.4 driver: 88c2ecf20Sopenharmony_ci * Copyright (C) 2004 Advanced Micro Devices, Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 118c2ecf20Sopenharmony_ci#include <linux/fb.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <asm/io.h> 148c2ecf20Sopenharmony_ci#include <asm/div64.h> 158c2ecf20Sopenharmony_ci#include <asm/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/cs5535.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "gxfb.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciunsigned int gx_frame_buffer_size(void) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci unsigned int val; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (!cs5535_has_vsa2()) { 258c2ecf20Sopenharmony_ci uint32_t hi, lo; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci /* The number of pages is (PMAX - PMIN)+1 */ 288c2ecf20Sopenharmony_ci rdmsr(MSR_GLIU_P2D_RO0, lo, hi); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* PMAX */ 318c2ecf20Sopenharmony_ci val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20); 328c2ecf20Sopenharmony_ci /* PMIN */ 338c2ecf20Sopenharmony_ci val -= (lo & 0x000fffff); 348c2ecf20Sopenharmony_ci val += 1; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* The page size is 4k */ 378c2ecf20Sopenharmony_ci return (val << 12); 388c2ecf20Sopenharmony_ci } 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* FB size can be obtained from the VSA II */ 418c2ecf20Sopenharmony_ci /* Virtual register class = 0x02 */ 428c2ecf20Sopenharmony_ci /* VG_MEM_SIZE(512Kb units) = 0x00 */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci outw(VSA_VR_UNLOCK, VSA_VRC_INDEX); 458c2ecf20Sopenharmony_ci outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFFl; 488c2ecf20Sopenharmony_ci return (val << 19); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciint gx_line_delta(int xres, int bpp) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci /* Must be a multiple of 8 bytes. */ 548c2ecf20Sopenharmony_ci return (xres * (bpp >> 3) + 7) & ~0x7; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_civoid gx_set_mode(struct fb_info *info) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct gxfb_par *par = info->par; 608c2ecf20Sopenharmony_ci u32 gcfg, dcfg; 618c2ecf20Sopenharmony_ci int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal; 628c2ecf20Sopenharmony_ci int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* Unlock the display controller registers. */ 658c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci gcfg = read_dc(par, DC_GENERAL_CFG); 688c2ecf20Sopenharmony_ci dcfg = read_dc(par, DC_DISPLAY_CFG); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Disable the timing generator. */ 718c2ecf20Sopenharmony_ci dcfg &= ~DC_DISPLAY_CFG_TGEN; 728c2ecf20Sopenharmony_ci write_dc(par, DC_DISPLAY_CFG, dcfg); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Wait for pending memory requests before disabling the FIFO load. */ 758c2ecf20Sopenharmony_ci udelay(100); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* Disable FIFO load and compression. */ 788c2ecf20Sopenharmony_ci gcfg &= ~(DC_GENERAL_CFG_DFLE | DC_GENERAL_CFG_CMPE | 798c2ecf20Sopenharmony_ci DC_GENERAL_CFG_DECE); 808c2ecf20Sopenharmony_ci write_dc(par, DC_GENERAL_CFG, gcfg); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Setup DCLK and its divisor. */ 838c2ecf20Sopenharmony_ci gx_set_dclk_frequency(info); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* 868c2ecf20Sopenharmony_ci * Setup new mode. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Clear all unused feature bits. */ 908c2ecf20Sopenharmony_ci gcfg &= DC_GENERAL_CFG_YUVM | DC_GENERAL_CFG_VDSE; 918c2ecf20Sopenharmony_ci dcfg = 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Set FIFO priority (default 6/5) and enable. */ 948c2ecf20Sopenharmony_ci /* FIXME: increase fifo priority for 1280x1024 and higher modes? */ 958c2ecf20Sopenharmony_ci gcfg |= (6 << DC_GENERAL_CFG_DFHPEL_SHIFT) | 968c2ecf20Sopenharmony_ci (5 << DC_GENERAL_CFG_DFHPSL_SHIFT) | DC_GENERAL_CFG_DFLE; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Framebuffer start offset. */ 998c2ecf20Sopenharmony_ci write_dc(par, DC_FB_ST_OFFSET, 0); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* Line delta and line buffer length. */ 1028c2ecf20Sopenharmony_ci write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3); 1038c2ecf20Sopenharmony_ci write_dc(par, DC_LINE_SIZE, 1048c2ecf20Sopenharmony_ci ((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Enable graphics and video data and unmask address lines. */ 1088c2ecf20Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_GDEN | DC_DISPLAY_CFG_VDEN | 1098c2ecf20Sopenharmony_ci DC_DISPLAY_CFG_A20M | DC_DISPLAY_CFG_A18M; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Set pixel format. */ 1128c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 1138c2ecf20Sopenharmony_ci case 8: 1148c2ecf20Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP; 1158c2ecf20Sopenharmony_ci break; 1168c2ecf20Sopenharmony_ci case 16: 1178c2ecf20Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case 32: 1208c2ecf20Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP; 1218c2ecf20Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_PALB; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Enable timing generator. */ 1268c2ecf20Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_TGEN; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Horizontal and vertical timings. */ 1298c2ecf20Sopenharmony_ci hactive = info->var.xres; 1308c2ecf20Sopenharmony_ci hblankstart = hactive; 1318c2ecf20Sopenharmony_ci hsyncstart = hblankstart + info->var.right_margin; 1328c2ecf20Sopenharmony_ci hsyncend = hsyncstart + info->var.hsync_len; 1338c2ecf20Sopenharmony_ci hblankend = hsyncend + info->var.left_margin; 1348c2ecf20Sopenharmony_ci htotal = hblankend; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci vactive = info->var.yres; 1378c2ecf20Sopenharmony_ci vblankstart = vactive; 1388c2ecf20Sopenharmony_ci vsyncstart = vblankstart + info->var.lower_margin; 1398c2ecf20Sopenharmony_ci vsyncend = vsyncstart + info->var.vsync_len; 1408c2ecf20Sopenharmony_ci vblankend = vsyncend + info->var.upper_margin; 1418c2ecf20Sopenharmony_ci vtotal = vblankend; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1) | 1448c2ecf20Sopenharmony_ci ((htotal - 1) << 16)); 1458c2ecf20Sopenharmony_ci write_dc(par, DC_H_BLANK_TIMING, (hblankstart - 1) | 1468c2ecf20Sopenharmony_ci ((hblankend - 1) << 16)); 1478c2ecf20Sopenharmony_ci write_dc(par, DC_H_SYNC_TIMING, (hsyncstart - 1) | 1488c2ecf20Sopenharmony_ci ((hsyncend - 1) << 16)); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1) | 1518c2ecf20Sopenharmony_ci ((vtotal - 1) << 16)); 1528c2ecf20Sopenharmony_ci write_dc(par, DC_V_BLANK_TIMING, (vblankstart - 1) | 1538c2ecf20Sopenharmony_ci ((vblankend - 1) << 16)); 1548c2ecf20Sopenharmony_ci write_dc(par, DC_V_SYNC_TIMING, (vsyncstart - 1) | 1558c2ecf20Sopenharmony_ci ((vsyncend - 1) << 16)); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Write final register values. */ 1588c2ecf20Sopenharmony_ci write_dc(par, DC_DISPLAY_CFG, dcfg); 1598c2ecf20Sopenharmony_ci write_dc(par, DC_GENERAL_CFG, gcfg); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci gx_configure_display(info); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Relock display controller registers */ 1648c2ecf20Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_civoid gx_set_hw_palette_reg(struct fb_info *info, unsigned regno, 1688c2ecf20Sopenharmony_ci unsigned red, unsigned green, unsigned blue) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct gxfb_par *par = info->par; 1718c2ecf20Sopenharmony_ci int val; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Hardware palette is in RGB 8-8-8 format. */ 1748c2ecf20Sopenharmony_ci val = (red << 8) & 0xff0000; 1758c2ecf20Sopenharmony_ci val |= (green) & 0x00ff00; 1768c2ecf20Sopenharmony_ci val |= (blue >> 8) & 0x0000ff; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci write_dc(par, DC_PAL_ADDRESS, regno); 1798c2ecf20Sopenharmony_ci write_dc(par, DC_PAL_DATA, val); 1808c2ecf20Sopenharmony_ci} 181