162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Geode GX display controller. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 Arcom Control Systems Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Portions from AMD's original 2.4 driver: 862306a36Sopenharmony_ci * Copyright (C) 2004 Advanced Micro Devices, Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/spinlock.h> 1162306a36Sopenharmony_ci#include <linux/fb.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <asm/io.h> 1462306a36Sopenharmony_ci#include <asm/div64.h> 1562306a36Sopenharmony_ci#include <asm/delay.h> 1662306a36Sopenharmony_ci#include <linux/cs5535.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "gxfb.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciunsigned int gx_frame_buffer_size(void) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci unsigned int val; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (!cs5535_has_vsa2()) { 2562306a36Sopenharmony_ci uint32_t hi, lo; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* The number of pages is (PMAX - PMIN)+1 */ 2862306a36Sopenharmony_ci rdmsr(MSR_GLIU_P2D_RO0, lo, hi); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* PMAX */ 3162306a36Sopenharmony_ci val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20); 3262306a36Sopenharmony_ci /* PMIN */ 3362306a36Sopenharmony_ci val -= (lo & 0x000fffff); 3462306a36Sopenharmony_ci val += 1; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci /* The page size is 4k */ 3762306a36Sopenharmony_ci return (val << 12); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* FB size can be obtained from the VSA II */ 4162306a36Sopenharmony_ci /* Virtual register class = 0x02 */ 4262306a36Sopenharmony_ci /* VG_MEM_SIZE(512Kb units) = 0x00 */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci outw(VSA_VR_UNLOCK, VSA_VRC_INDEX); 4562306a36Sopenharmony_ci outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFFl; 4862306a36Sopenharmony_ci return (val << 19); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciint gx_line_delta(int xres, int bpp) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci /* Must be a multiple of 8 bytes. */ 5462306a36Sopenharmony_ci return (xres * (bpp >> 3) + 7) & ~0x7; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_civoid gx_set_mode(struct fb_info *info) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct gxfb_par *par = info->par; 6062306a36Sopenharmony_ci u32 gcfg, dcfg; 6162306a36Sopenharmony_ci int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal; 6262306a36Sopenharmony_ci int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Unlock the display controller registers. */ 6562306a36Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci gcfg = read_dc(par, DC_GENERAL_CFG); 6862306a36Sopenharmony_ci dcfg = read_dc(par, DC_DISPLAY_CFG); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Disable the timing generator. */ 7162306a36Sopenharmony_ci dcfg &= ~DC_DISPLAY_CFG_TGEN; 7262306a36Sopenharmony_ci write_dc(par, DC_DISPLAY_CFG, dcfg); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Wait for pending memory requests before disabling the FIFO load. */ 7562306a36Sopenharmony_ci udelay(100); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Disable FIFO load and compression. */ 7862306a36Sopenharmony_ci gcfg &= ~(DC_GENERAL_CFG_DFLE | DC_GENERAL_CFG_CMPE | 7962306a36Sopenharmony_ci DC_GENERAL_CFG_DECE); 8062306a36Sopenharmony_ci write_dc(par, DC_GENERAL_CFG, gcfg); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Setup DCLK and its divisor. */ 8362306a36Sopenharmony_ci gx_set_dclk_frequency(info); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Setup new mode. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Clear all unused feature bits. */ 9062306a36Sopenharmony_ci gcfg &= DC_GENERAL_CFG_YUVM | DC_GENERAL_CFG_VDSE; 9162306a36Sopenharmony_ci dcfg = 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Set FIFO priority (default 6/5) and enable. */ 9462306a36Sopenharmony_ci /* FIXME: increase fifo priority for 1280x1024 and higher modes? */ 9562306a36Sopenharmony_ci gcfg |= (6 << DC_GENERAL_CFG_DFHPEL_SHIFT) | 9662306a36Sopenharmony_ci (5 << DC_GENERAL_CFG_DFHPSL_SHIFT) | DC_GENERAL_CFG_DFLE; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Framebuffer start offset. */ 9962306a36Sopenharmony_ci write_dc(par, DC_FB_ST_OFFSET, 0); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Line delta and line buffer length. */ 10262306a36Sopenharmony_ci write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3); 10362306a36Sopenharmony_ci write_dc(par, DC_LINE_SIZE, 10462306a36Sopenharmony_ci ((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Enable graphics and video data and unmask address lines. */ 10862306a36Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_GDEN | DC_DISPLAY_CFG_VDEN | 10962306a36Sopenharmony_ci DC_DISPLAY_CFG_A20M | DC_DISPLAY_CFG_A18M; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* Set pixel format. */ 11262306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 11362306a36Sopenharmony_ci case 8: 11462306a36Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP; 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci case 16: 11762306a36Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci case 32: 12062306a36Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP; 12162306a36Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_PALB; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Enable timing generator. */ 12662306a36Sopenharmony_ci dcfg |= DC_DISPLAY_CFG_TGEN; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Horizontal and vertical timings. */ 12962306a36Sopenharmony_ci hactive = info->var.xres; 13062306a36Sopenharmony_ci hblankstart = hactive; 13162306a36Sopenharmony_ci hsyncstart = hblankstart + info->var.right_margin; 13262306a36Sopenharmony_ci hsyncend = hsyncstart + info->var.hsync_len; 13362306a36Sopenharmony_ci hblankend = hsyncend + info->var.left_margin; 13462306a36Sopenharmony_ci htotal = hblankend; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci vactive = info->var.yres; 13762306a36Sopenharmony_ci vblankstart = vactive; 13862306a36Sopenharmony_ci vsyncstart = vblankstart + info->var.lower_margin; 13962306a36Sopenharmony_ci vsyncend = vsyncstart + info->var.vsync_len; 14062306a36Sopenharmony_ci vblankend = vsyncend + info->var.upper_margin; 14162306a36Sopenharmony_ci vtotal = vblankend; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1) | 14462306a36Sopenharmony_ci ((htotal - 1) << 16)); 14562306a36Sopenharmony_ci write_dc(par, DC_H_BLANK_TIMING, (hblankstart - 1) | 14662306a36Sopenharmony_ci ((hblankend - 1) << 16)); 14762306a36Sopenharmony_ci write_dc(par, DC_H_SYNC_TIMING, (hsyncstart - 1) | 14862306a36Sopenharmony_ci ((hsyncend - 1) << 16)); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1) | 15162306a36Sopenharmony_ci ((vtotal - 1) << 16)); 15262306a36Sopenharmony_ci write_dc(par, DC_V_BLANK_TIMING, (vblankstart - 1) | 15362306a36Sopenharmony_ci ((vblankend - 1) << 16)); 15462306a36Sopenharmony_ci write_dc(par, DC_V_SYNC_TIMING, (vsyncstart - 1) | 15562306a36Sopenharmony_ci ((vsyncend - 1) << 16)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Write final register values. */ 15862306a36Sopenharmony_ci write_dc(par, DC_DISPLAY_CFG, dcfg); 15962306a36Sopenharmony_ci write_dc(par, DC_GENERAL_CFG, gcfg); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci gx_configure_display(info); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* Relock display controller registers */ 16462306a36Sopenharmony_ci write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid gx_set_hw_palette_reg(struct fb_info *info, unsigned regno, 16862306a36Sopenharmony_ci unsigned red, unsigned green, unsigned blue) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct gxfb_par *par = info->par; 17162306a36Sopenharmony_ci int val; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Hardware palette is in RGB 8-8-8 format. */ 17462306a36Sopenharmony_ci val = (red << 8) & 0xff0000; 17562306a36Sopenharmony_ci val |= (green) & 0x00ff00; 17662306a36Sopenharmony_ci val |= (blue >> 8) & 0x0000ff; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci write_dc(par, DC_PAL_ADDRESS, regno); 17962306a36Sopenharmony_ci write_dc(par, DC_PAL_DATA, val); 18062306a36Sopenharmony_ci} 181