Commit 81dee67e authored by Sudip Mukherjee's avatar Sudip Mukherjee Committed by Greg Kroah-Hartman

staging: sm750fb: add sm750 to staging

sm750 of Silicon Motion is pci-e display controller device and has
features like dual display and 2D acceleration. This patch adds the
driver to staging.
Signed-off-by: default avatarSudip Mukherjee <sudip@vectorindia.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c3d6047d
......@@ -58,6 +58,8 @@ source "drivers/staging/iio/Kconfig"
source "drivers/staging/sm7xxfb/Kconfig"
source "drivers/staging/sm750fb/Kconfig"
source "drivers/staging/xgifb/Kconfig"
source "drivers/staging/emxx_udc/Kconfig"
......
......@@ -23,6 +23,7 @@ obj-$(CONFIG_VT6656) += vt6656/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_FB_SM7XX) += sm7xxfb/
obj-$(CONFIG_FB_SM7XX) += sm750fb/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_USB_EMXX) += emxx_udc/
obj-$(CONFIG_FT1000) += ft1000/
......
config FB_SM750
tristate "Silicon Motion SM750 framebuffer support"
depends on FB && PCI
help
Frame buffer driver for the Silicon Motion SM750 chip
with 2D accelearion and dual head support.
This driver is also available as a module. The module will be
called sm750fb. If you want to compile it as a module, say M
here and read <file:Documentation/kbuild/modules.txt>.
obj-$(CONFIG_FB_SM750) += sm750fb.o
sm750fb-objs := sm750.o sm750_hw.o sm750_accel.o sm750_cursor.o ddk750_chip.o ddk750_power.o ddk750_mode.o
sm750fb-objs += ddk750_display.o ddk750_help.o ddk750_swi2c.o ddk750_sii164.o ddk750_dvi.o ddk750_hwi2c.o
TODO:
- lots of clechpatch cleanup
- use kernel coding style
- refine the code and remove unused code
- check on hardware effects of removal of USE_HW_I2C and USE_DVICHIP (these two
are supposed to be sample code which is given here if someone wants to
use those functionalities)
- move it to drivers/video/fbdev
- modify the code for drm framework
Please send any patches to
Greg Kroah-Hartman <greg@kroah.com>
Sudip Mukherjee <sudipm.mukherjee@gmail.com>
Teddy Wang <teddy.wang@siliconmotion.com>
Sudip Mukherjee <sudip@vectorindia.org>
#ifndef DDK750_H__
#define DDK750_H__
/*******************************************************************
*
* Copyright (c) 2007 by Silicon Motion, Inc. (SMI)
*
* All rights are reserved. Reproduction or in part is prohibited
* without the written consent of the copyright owner.
*
* RegSC.h --- SM718 SDK
* This file contains the definitions for the System Configuration registers.
*
*******************************************************************/
#include "ddk750_reg.h"
#include "ddk750_mode.h"
#include "ddk750_chip.h"
#include "ddk750_display.h"
#include "ddk750_power.h"
#include "ddk750_help.h"
#ifdef USE_HW_I2C
#include "ddk750_hwi2c.h"
#endif
#include "ddk750_swi2c.h"
#endif
#include "ddk750_help.h"
#include "ddk750_reg.h"
#include "ddk750_chip.h"
#include "ddk750_power.h"
typedef struct _pllcalparam{
unsigned char power;/* d : 0~ 6*/
unsigned char pod;
unsigned char od;
unsigned char value;/* value of 2 power d (2^d) */
}
pllcalparam;
logical_chip_type_t getChipType()
{
unsigned short physicalID;
char physicalRev;
logical_chip_type_t chip;
physicalID = devId750;//either 0x718 or 0x750
physicalRev = revId750;
if (physicalID == 0x718)
{
chip = SM718;
}
else if (physicalID == 0x750)
{
chip = SM750;
/* SM750 and SM750LE are different in their revision ID only. */
if (physicalRev == SM750LE_REVISION_ID){
chip = SM750LE;
}
}
else
{
chip = SM_UNKNOWN;
}
return chip;
}
inline unsigned int twoToPowerOfx(unsigned long x)
{
unsigned long i;
unsigned long result = 1;
for (i=1; i<=x; i++)
result *= 2;
return result;
}
inline unsigned int calcPLL(pll_value_t *pPLL)
{
return (pPLL->inputFreq * pPLL->M / pPLL->N / twoToPowerOfx(pPLL->OD) / twoToPowerOfx(pPLL->POD));
}
unsigned int getPllValue(clock_type_t clockType, pll_value_t *pPLL)
{
unsigned int ulPllReg = 0;
pPLL->inputFreq = DEFAULT_INPUT_CLOCK;
pPLL->clockType = clockType;
switch (clockType)
{
case MXCLK_PLL:
ulPllReg = PEEK32(MXCLK_PLL_CTRL);
break;
case PRIMARY_PLL:
ulPllReg = PEEK32(PANEL_PLL_CTRL);
break;
case SECONDARY_PLL:
ulPllReg = PEEK32(CRT_PLL_CTRL);
break;
case VGA0_PLL:
ulPllReg = PEEK32(VGA_PLL0_CTRL);
break;
case VGA1_PLL:
ulPllReg = PEEK32(VGA_PLL1_CTRL);
break;
}
pPLL->M = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, M);
pPLL->N = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, N);
pPLL->OD = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, OD);
pPLL->POD = FIELD_GET(ulPllReg, PANEL_PLL_CTRL, POD);
return calcPLL(pPLL);
}
unsigned int getChipClock()
{
pll_value_t pll;
#if 1
if(getChipType() == SM750LE)
return MHz(130);
#endif
return getPllValue(MXCLK_PLL, &pll);
}
/*
* This function set up the main chip clock.
*
* Input: Frequency to be set.
*/
void setChipClock(unsigned int frequency)
{
pll_value_t pll;
unsigned int ulActualMxClk;
#if 1
/* Cheok_0509: For SM750LE, the chip clock is fixed. Nothing to set. */
if (getChipType() == SM750LE)
return;
#endif
if (frequency != 0)
{
/*
* Set up PLL, a structure to hold the value to be set in clocks.
*/
pll.inputFreq = DEFAULT_INPUT_CLOCK; /* Defined in CLOCK.H */
pll.clockType = MXCLK_PLL;
/*
* Call calcPllValue() to fill up the other fields for PLL structure.
* Sometime, the chip cannot set up the exact clock required by User.
* Return value from calcPllValue() gives the actual possible clock.
*/
ulActualMxClk = calcPllValue(frequency, &pll);
/* Master Clock Control: MXCLK_PLL */
POKE32(MXCLK_PLL_CTRL, formatPllReg(&pll));
}
}
void setMemoryClock(unsigned int frequency)
{
unsigned int ulReg, divisor;
#if 1
/* Cheok_0509: For SM750LE, the memory clock is fixed. Nothing to set. */
if (getChipType() == SM750LE)
return;
#endif
if (frequency != 0)
{
/* Set the frequency to the maximum frequency that the DDR Memory can take
which is 336MHz. */
if (frequency > MHz(336))
frequency = MHz(336);
/* Calculate the divisor */
divisor = (unsigned int) roundedDiv(getChipClock(), frequency);
/* Set the corresponding divisor in the register. */
ulReg = PEEK32(CURRENT_GATE);
switch(divisor)
{
default:
case 1:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_1);
break;
case 2:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_2);
break;
case 3:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_3);
break;
case 4:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, M2XCLK, DIV_4);
break;
}
setCurrentGate(ulReg);
}
}
/*
* This function set up the master clock (MCLK).
*
* Input: Frequency to be set.
*
* NOTE:
* The maximum frequency the engine can run is 168MHz.
*/
void setMasterClock(unsigned int frequency)
{
unsigned int ulReg, divisor;
#if 1
/* Cheok_0509: For SM750LE, the memory clock is fixed. Nothing to set. */
if (getChipType() == SM750LE)
return;
#endif
if (frequency != 0)
{
/* Set the frequency to the maximum frequency that the SM750 engine can
run, which is about 190 MHz. */
if (frequency > MHz(190))
frequency = MHz(190);
/* Calculate the divisor */
divisor = (unsigned int) roundedDiv(getChipClock(), frequency);
/* Set the corresponding divisor in the register. */
ulReg = PEEK32(CURRENT_GATE);
switch(divisor)
{
default:
case 3:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_3);
break;
case 4:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_4);
break;
case 6:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_6);
break;
case 8:
ulReg = FIELD_SET(ulReg, CURRENT_GATE, MCLK, DIV_8);
break;
}
setCurrentGate(ulReg);
}
}
unsigned int ddk750_getVMSize()
{
unsigned int reg;
unsigned int data;
/* sm750le only use 64 mb memory*/
if(getChipType() == SM750LE)
return MB(64);
/* for 750,always use power mode0*/
reg = PEEK32(MODE0_GATE);
reg = FIELD_SET(reg,MODE0_GATE,GPIO,ON);
POKE32(MODE0_GATE,reg);
/* get frame buffer size from GPIO */
reg = FIELD_GET(PEEK32(MISC_CTRL),MISC_CTRL,LOCALMEM_SIZE);
switch(reg){
case MISC_CTRL_LOCALMEM_SIZE_8M: data = MB(8); break; /* 8 Mega byte */
case MISC_CTRL_LOCALMEM_SIZE_16M: data = MB(16); break; /* 16 Mega byte */
case MISC_CTRL_LOCALMEM_SIZE_32M: data = MB(32); break; /* 32 Mega byte */
case MISC_CTRL_LOCALMEM_SIZE_64M: data = MB(64); break; /* 64 Mega byte */
default: data = 0;break;
}
return data;
}
int ddk750_initHw(initchip_param_t * pInitParam)
{
unsigned int ulReg;
#if 0
//move the code to map regiter function.
if(getChipType() == SM718){
/* turn on big endian bit*/
ulReg = PEEK32(0x74);
/* now consider register definition in a big endian pattern*/
POKE32(0x74,ulReg|0x80000000);
}
#endif
if (pInitParam->powerMode != 0 )
pInitParam->powerMode = 0;
setPowerMode(pInitParam->powerMode);
/* Enable display power gate & LOCALMEM power gate*/
ulReg = PEEK32(CURRENT_GATE);
ulReg = FIELD_SET(ulReg, CURRENT_GATE, DISPLAY, ON);
ulReg = FIELD_SET(ulReg,CURRENT_GATE,LOCALMEM,ON);
setCurrentGate(ulReg);
if(getChipType() != SM750LE){
/* set panel pll and graphic mode via mmio_88 */
ulReg = PEEK32(VGA_CONFIGURATION);
ulReg = FIELD_SET(ulReg,VGA_CONFIGURATION,PLL,PANEL);
ulReg = FIELD_SET(ulReg,VGA_CONFIGURATION,MODE,GRAPHIC);
POKE32(VGA_CONFIGURATION,ulReg);
}else{
#if defined(__i386__) || defined( __x86_64__)
/* set graphic mode via IO method */
outb_p(0x88,0x3d4);
outb_p(0x06,0x3d5);
#endif
}
/* Set the Main Chip Clock */
setChipClock(MHz((unsigned int)pInitParam->chipClock));
/* Set up memory clock. */
setMemoryClock(MHz(pInitParam->memClock));
/* Set up master clock */
setMasterClock(MHz(pInitParam->masterClock));
/* Reset the memory controller. If the memory controller is not reset in SM750,
the system might hang when sw accesses the memory.
The memory should be resetted after changing the MXCLK.
*/
if (pInitParam->resetMemory == 1)
{
ulReg = PEEK32(MISC_CTRL);
ulReg = FIELD_SET(ulReg, MISC_CTRL, LOCALMEM_RESET, RESET);
POKE32(MISC_CTRL, ulReg);
ulReg = FIELD_SET(ulReg, MISC_CTRL, LOCALMEM_RESET, NORMAL);
POKE32(MISC_CTRL, ulReg);
}
if (pInitParam->setAllEngOff == 1)
{
enable2DEngine(0);
/* Disable Overlay, if a former application left it on */
ulReg = PEEK32(VIDEO_DISPLAY_CTRL);
ulReg = FIELD_SET(ulReg, VIDEO_DISPLAY_CTRL, PLANE, DISABLE);
POKE32(VIDEO_DISPLAY_CTRL, ulReg);
/* Disable video alpha, if a former application left it on */
ulReg = PEEK32(VIDEO_ALPHA_DISPLAY_CTRL);
ulReg = FIELD_SET(ulReg, VIDEO_ALPHA_DISPLAY_CTRL, PLANE, DISABLE);
POKE32(VIDEO_ALPHA_DISPLAY_CTRL, ulReg);
/* Disable alpha plane, if a former application left it on */
ulReg = PEEK32(ALPHA_DISPLAY_CTRL);
ulReg = FIELD_SET(ulReg, ALPHA_DISPLAY_CTRL, PLANE, DISABLE);
POKE32(ALPHA_DISPLAY_CTRL, ulReg);
#if 0
/* Disable LCD hardware cursor, if a former application left it on */
ulReg = PEEK32(PANEL_HWC_ADDRESS);
ulReg = FIELD_SET(ulReg, PANEL_HWC_ADDRESS, ENABLE, DISABLE);
POKE32(PANEL_HWC_ADDRESS, ulReg);
/* Disable CRT hardware cursor, if a former application left it on */
ulReg = PEEK32(CRT_HWC_ADDRESS);
ulReg = FIELD_SET(ulReg, CRT_HWC_ADDRESS, ENABLE, DISABLE);
POKE32(CRT_HWC_ADDRESS, ulReg);
/* Disable ZV Port 0, if a former application left it on */
ulReg = PEEK32(ZV0_CAPTURE_CTRL);
ulReg = FIELD_SET(ulReg, ZV0_CAPTURE_CTRL, CAP, DISABLE);
POKE32(ZV0_CAPTURE_CTRL, ulReg);
/* Disable ZV Port 1, if a former application left it on */
ulReg = PEEK32(ZV1_CAPTURE_CTRL);
ulReg = FIELD_SET(ulReg, ZV1_CAPTURE_CTRL, CAP, DISABLE);
POKE32(ZV1_CAPTURE_CTRL, ulReg);
/* Disable ZV Port Power, if a former application left it on */
enableZVPort(0);
/* Disable DMA Channel, if a former application left it on */
ulReg = PEEK32(DMA_ABORT_INTERRUPT);
ulReg = FIELD_SET(ulReg, DMA_ABORT_INTERRUPT, ABORT_1, ABORT);
POKE32(DMA_ABORT_INTERRUPT, ulReg);
/* Disable i2c */
enableI2C(0);
#endif
/* Disable DMA Channel, if a former application left it on */
ulReg = PEEK32(DMA_ABORT_INTERRUPT);
ulReg = FIELD_SET(ulReg, DMA_ABORT_INTERRUPT, ABORT_1, ABORT);
POKE32(DMA_ABORT_INTERRUPT, ulReg);
/* Disable DMA Power, if a former application left it on */
enableDMA(0);
}
/* We can add more initialization as needed. */
return 0;
}
#if 0
unsigned int absDiff(unsigned int a, unsigned int b)
{
if ( a > b )
return(a - b);
else
return(b - a);
}
#endif
/*
monk liu @ 4/6/2011:
re-write the calculatePLL function of ddk750.
the original version function does not use some mathematics tricks and shortcut
when it doing the calculation of the best N,M,D combination
I think this version gives a little upgrade in speed
750 pll clock formular:
Request Clock = (Input Clock * M )/(N * X)
Input Clock = 14318181 hz
X = 2 power D
D ={0,1,2,3,4,5,6}
M = {1,...,255}
N = {2,...,15}
*/
unsigned int calcPllValue(unsigned int request_orig,pll_value_t *pll)
{
/* used for primary and secondary channel pixel clock pll */
static pllcalparam xparm_PIXEL[] = {
/* 2^0 = 1*/ {0,0,0,1},
/* 2^ 1 =2*/ {1,0,1,2},
/* 2^ 2 = 4*/ {2,0,2,4},
{3,0,3,8},
{4,1,3,16},
{5,2,3,32},
/* 2^6 = 64 */ {6,3,3,64},
};
/* used for MXCLK (chip clock) */
static pllcalparam xparm_MXCLK[] = {
/* 2^0 = 1*/ {0,0,0,1},
/* 2^ 1 =2*/ {1,0,1,2},
/* 2^ 2 = 4*/ {2,0,2,4},
{3,0,3,8},
};
/* as sm750 register definition, N located in 2,15 and M located in 1,255 */
int N,M,X,d;
int xcnt;
int miniDiff;
unsigned int RN,quo,rem,fl_quo;
unsigned int input,request;
unsigned int tmpClock,ret;
pllcalparam * xparm;
#if 1
if (getChipType() == SM750LE)
{
/* SM750LE don't have prgrammable PLL and M/N values to work on.
Just return the requested clock. */
return request_orig;
}
#endif
ret = 0;
miniDiff = ~0;
request = request_orig / 1000;
input = pll->inputFreq / 1000;
/* for MXCLK register , no POD provided, so need be treated differently */
if(pll->clockType != MXCLK_PLL){
xparm = &xparm_PIXEL[0];
xcnt = sizeof(xparm_PIXEL)/sizeof(xparm_PIXEL[0]);
}else{
xparm = &xparm_MXCLK[0];
xcnt = sizeof(xparm_MXCLK)/sizeof(xparm_MXCLK[0]);
}
for(N = 15;N>1;N--)
{
/* RN will not exceed maximum long if @request <= 285 MHZ (for 32bit cpu) */
RN = N * request;
quo = RN / input;
rem = RN % input;/* rem always small than 14318181 */
fl_quo = (rem * 10000 /input);
for(d = xcnt - 1;d >= 0;d--){
X = xparm[d].value;
M = quo*X;
M += fl_quo * X / 10000;
/* round step */
M += (fl_quo*X % 10000)>5000?1:0;
if(M < 256 && M > 0)
{
unsigned int diff;
tmpClock = pll->inputFreq *M / N / X;
diff = absDiff(tmpClock,request_orig);
if(diff < miniDiff)
{
pll->M = M;
pll->N = N;
pll->OD = xparm[d].od;
pll->POD = xparm[d].pod;
miniDiff = diff;
ret = tmpClock;
}
}
}
}
//printk("Finally: pll->n[%lu],m[%lu],od[%lu],pod[%lu]\n",pll->N,pll->M,pll->OD,pll->POD);
return ret;
}
unsigned int calcPllValue2(
unsigned int ulRequestClk, /* Required pixel clock in Hz unit */
pll_value_t *pPLL /* Structure to hold the value to be set in PLL */
)
{
unsigned int M, N, OD, POD = 0, diff, pllClk, odPower, podPower;
unsigned int bestDiff = 0xffffffff; /* biggest 32 bit unsigned number */
unsigned int ret;
/* Init PLL structure to know states */
pPLL->M = 0;
pPLL->N = 0;
pPLL->OD = 0;
pPLL->POD = 0;
/* Sanity check: None at the moment */
/* Convert everything in Khz range in order to avoid calculation overflow */
pPLL->inputFreq /= 1000;
ulRequestClk /= 1000;
#ifndef VALIDATION_CHIP
/* The maximum of post divider is 8. */
for (POD=0; POD<=3; POD++)
#endif
{
#ifndef VALIDATION_CHIP
/* MXCLK_PLL does not have post divider. */
if ((POD > 0) && (pPLL->clockType == MXCLK_PLL))
break;
#endif
/* Work out 2 to the power of POD */
podPower = twoToPowerOfx(POD);
/* OD has only 2 bits [15:14] and its value must between 0 to 3 */
for (OD=0; OD<=3; OD++)
{
/* Work out 2 to the power of OD */
odPower = twoToPowerOfx(OD);
#ifdef VALIDATION_CHIP
if (odPower > 4)
podPower = 4;
else
podPower = odPower;
#endif
/* N has 4 bits [11:8] and its value must between 2 and 15.
The N == 1 will behave differently --> Result is not correct. */
for (N=2; N<=15; N++)
{
/* The formula for PLL is ulRequestClk = inputFreq * M / N / (2^OD)
In the following steps, we try to work out a best M value given the others are known.
To avoid decimal calculation, we use 1000 as multiplier for up to 3 decimal places of accuracy.
*/
M = ulRequestClk * N * odPower * 1000 / pPLL->inputFreq;
M = roundedDiv(M, 1000);
/* M field has only 8 bits, reject value bigger than 8 bits */
if (M < 256)
{
/* Calculate the actual clock for a given M & N */
pllClk = pPLL->inputFreq * M / N / odPower / podPower;
/* How much are we different from the requirement */
diff = absDiff(pllClk, ulRequestClk);
if (diff < bestDiff)
{
bestDiff = diff;
/* Store M and N values */
pPLL->M = M;
pPLL->N = N;
pPLL->OD = OD;
#ifdef VALIDATION_CHIP
if (OD > 2)
POD = 2;
else
POD = OD;
#endif
pPLL->POD = POD;
}
}
}
}
}
/* Restore input frequency from Khz to hz unit */
// pPLL->inputFreq *= 1000;
ulRequestClk *= 1000;
pPLL->inputFreq = DEFAULT_INPUT_CLOCK; /* Default reference clock */
/* Output debug information */
//DDKDEBUGPRINT((DISPLAY_LEVEL, "calcPllValue: Requested Frequency = %d\n", ulRequestClk));
//DDKDEBUGPRINT((DISPLAY_LEVEL, "calcPllValue: Input CLK = %dHz, M=%d, N=%d, OD=%d, POD=%d\n", pPLL->inputFreq, pPLL->M, pPLL->N, pPLL->OD, pPLL->POD));
/* Return actual frequency that the PLL can set */
ret = calcPLL(pPLL);
return ret;
}
unsigned int formatPllReg(pll_value_t *pPLL)
{
unsigned int ulPllReg = 0;
/* Note that all PLL's have the same format. Here, we just use Panel PLL parameter
to work out the bit fields in the register.
On returning a 32 bit number, the value can be applied to any PLL in the calling function.
*/
ulPllReg =
FIELD_SET( 0, PANEL_PLL_CTRL, BYPASS, OFF)
| FIELD_SET( 0, PANEL_PLL_CTRL, POWER, ON)
| FIELD_SET( 0, PANEL_PLL_CTRL, INPUT, OSC)
#ifndef VALIDATION_CHIP
| FIELD_VALUE(0, PANEL_PLL_CTRL, POD, pPLL->POD)
#endif
| FIELD_VALUE(0, PANEL_PLL_CTRL, OD, pPLL->OD)
| FIELD_VALUE(0, PANEL_PLL_CTRL, N, pPLL->N)
| FIELD_VALUE(0, PANEL_PLL_CTRL, M, pPLL->M);
return(ulPllReg);
}
#ifndef DDK750_CHIP_H__
#define DDK750_CHIP_H__
#define DEFAULT_INPUT_CLOCK 14318181 /* Default reference clock */
#define SM750LE_REVISION_ID (char)0xfe
/* This is all the chips recognized by this library */
typedef enum _logical_chip_type_t
{
SM_UNKNOWN,
SM718,
SM750,
SM750LE,
}
logical_chip_type_t;
typedef enum _clock_type_t
{
MXCLK_PLL,
PRIMARY_PLL,
SECONDARY_PLL,
VGA0_PLL,
VGA1_PLL,
}
clock_type_t;
typedef struct _pll_value_t
{
clock_type_t clockType;
unsigned long inputFreq; /* Input clock frequency to the PLL */
/* Use this when clockType = PANEL_PLL */
unsigned long M;
unsigned long N;
unsigned long OD;
unsigned long POD;
}
pll_value_t;
/* input struct to initChipParam() function */
typedef struct _initchip_param_t
{
unsigned short powerMode; /* Use power mode 0 or 1 */
unsigned short chipClock; /* Speed of main chip clock in MHz unit
0 = keep the current clock setting
Others = the new main chip clock
*/
unsigned short memClock; /* Speed of memory clock in MHz unit
0 = keep the current clock setting
Others = the new memory clock
*/
unsigned short masterClock; /* Speed of master clock in MHz unit
0 = keep the current clock setting
Others = the new master clock
*/
unsigned short setAllEngOff; /* 0 = leave all engine state untouched.
1 = make sure they are off: 2D, Overlay,
video alpha, alpha, hardware cursors
*/
unsigned char resetMemory; /* 0 = Do not reset the memory controller
1 = Reset the memory controller
*/
/* More initialization parameter can be added if needed */
}
initchip_param_t;
logical_chip_type_t getChipType(void);
unsigned int calcPllValue(unsigned int request,pll_value_t *pll);
unsigned int calcPllValue2(unsigned int,pll_value_t *);
unsigned int formatPllReg(pll_value_t *pPLL);
void ddk750_set_mmio(volatile unsigned char *,unsigned short,char);
unsigned int ddk750_getVMSize(void);
int ddk750_initHw(initchip_param_t *);
unsigned int getPllValue(clock_type_t clockType, pll_value_t *pPLL);
unsigned int getChipClock(void);
void setChipClock(unsigned int);
void setMemoryClock(unsigned int frequency);
void setMasterClock(unsigned int frequency);
#endif
#include "ddk750_reg.h"
#include "ddk750_help.h"
#include "ddk750_display.h"
#include "ddk750_power.h"
#include "ddk750_dvi.h"
#define primaryWaitVerticalSync(delay) waitNextVerticalSync(0,delay)
static void setDisplayControl(int ctrl,int dispState)
{
/* state != 0 means turn on both timing & plane en_bit */
unsigned long ulDisplayCtrlReg, ulReservedBits;
int cnt;
cnt = 0;
/* Set the primary display control */
if (!ctrl)
{
ulDisplayCtrlReg = PEEK32(PANEL_DISPLAY_CTRL);
/* Turn on/off the Panel display control */
if (dispState)
{
/* Timing should be enabled first before enabling the plane
* because changing at the same time does not guarantee that
* the plane will also enabled or disabled.
*/
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
PANEL_DISPLAY_CTRL, TIMING, ENABLE);
POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
PANEL_DISPLAY_CTRL, PLANE, ENABLE);
/* Added some masks to mask out the reserved bits.
* Sometimes, the reserved bits are set/reset randomly when
* writing to the PRIMARY_DISPLAY_CTRL, therefore, the register
* reserved bits are needed to be masked out.
*/
ulReservedBits = FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_1_MASK, ENABLE) |
FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_2_MASK, ENABLE) |
FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_3_MASK, ENABLE);
/* Somehow the register value on the plane is not set
* until a few delay. Need to write
* and read it a couple times
*/
do
{
cnt++;
POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
} while((PEEK32(PANEL_DISPLAY_CTRL) & ~ulReservedBits) !=
(ulDisplayCtrlReg & ~ulReservedBits));
printk("Set Panel Plane enbit:after tried %d times\n",cnt);
}
else
{
/* When turning off, there is no rule on the programming
* sequence since whenever the clock is off, then it does not
* matter whether the plane is enabled or disabled.
* Note: Modifying the plane bit will take effect on the
* next vertical sync. Need to find out if it is necessary to
* wait for 1 vsync before modifying the timing enable bit.
* */
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
PANEL_DISPLAY_CTRL, PLANE, DISABLE);
POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
PANEL_DISPLAY_CTRL, TIMING, DISABLE);
POKE32(PANEL_DISPLAY_CTRL, ulDisplayCtrlReg);
}
}
/* Set the secondary display control */
else
{
ulDisplayCtrlReg = PEEK32(CRT_DISPLAY_CTRL);
if (dispState)
{
/* Timing should be enabled first before enabling the plane because changing at the
same time does not guarantee that the plane will also enabled or disabled.
*/
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
CRT_DISPLAY_CTRL, TIMING, ENABLE);
POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
CRT_DISPLAY_CTRL, PLANE, ENABLE);
/* Added some masks to mask out the reserved bits.
* Sometimes, the reserved bits are set/reset randomly when
* writing to the PRIMARY_DISPLAY_CTRL, therefore, the register
* reserved bits are needed to be masked out.
*/
ulReservedBits = FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_1_MASK, ENABLE) |
FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_2_MASK, ENABLE) |
FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_3_MASK, ENABLE) |
FIELD_SET(0, CRT_DISPLAY_CTRL, RESERVED_4_MASK, ENABLE);
do
{
cnt++;
POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
} while((PEEK32(CRT_DISPLAY_CTRL) & ~ulReservedBits) !=
(ulDisplayCtrlReg & ~ulReservedBits));
printk("Set Crt Plane enbit:after tried %d times\n",cnt);
}
else
{
/* When turning off, there is no rule on the programming
* sequence since whenever the clock is off, then it does not
* matter whether the plane is enabled or disabled.
* Note: Modifying the plane bit will take effect on the next
* vertical sync. Need to find out if it is necessary to
* wait for 1 vsync before modifying the timing enable bit.
*/
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
CRT_DISPLAY_CTRL, PLANE, DISABLE);
POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
ulDisplayCtrlReg = FIELD_SET(ulDisplayCtrlReg,
CRT_DISPLAY_CTRL, TIMING, DISABLE);
POKE32(CRT_DISPLAY_CTRL, ulDisplayCtrlReg);
}
}
}
static void waitNextVerticalSync(int ctrl,int delay)
{
unsigned int status;
if(!ctrl){
/* primary controller */
/* Do not wait when the Primary PLL is off or display control is already off.
This will prevent the software to wait forever. */
if ((FIELD_GET(PEEK32(PANEL_PLL_CTRL), PANEL_PLL_CTRL, POWER) ==
PANEL_PLL_CTRL_POWER_OFF) ||
(FIELD_GET(PEEK32(PANEL_DISPLAY_CTRL), PANEL_DISPLAY_CTRL, TIMING) ==
PANEL_DISPLAY_CTRL_TIMING_DISABLE))
{
return;
}
while (delay-- > 0)
{
/* Wait for end of vsync. */
do
{
status = FIELD_GET(PEEK32(SYSTEM_CTRL),
SYSTEM_CTRL,
PANEL_VSYNC);
}
while (status == SYSTEM_CTRL_PANEL_VSYNC_ACTIVE);
/* Wait for start of vsync. */
do
{
status = FIELD_GET(PEEK32(SYSTEM_CTRL),
SYSTEM_CTRL,
PANEL_VSYNC);
}
while (status == SYSTEM_CTRL_PANEL_VSYNC_INACTIVE);
}
}else{
/* Do not wait when the Primary PLL is off or display control is already off.
This will prevent the software to wait forever. */
if ((FIELD_GET(PEEK32(CRT_PLL_CTRL), CRT_PLL_CTRL, POWER) ==
CRT_PLL_CTRL_POWER_OFF) ||
(FIELD_GET(PEEK32(CRT_DISPLAY_CTRL), CRT_DISPLAY_CTRL, TIMING) ==
CRT_DISPLAY_CTRL_TIMING_DISABLE))
{
return;
}
while (delay-- > 0)
{
/* Wait for end of vsync. */
do
{
status = FIELD_GET(PEEK32(SYSTEM_CTRL),
SYSTEM_CTRL,
CRT_VSYNC);
}
while (status == SYSTEM_CTRL_CRT_VSYNC_ACTIVE);
/* Wait for start of vsync. */
do
{
status = FIELD_GET(PEEK32(SYSTEM_CTRL),
SYSTEM_CTRL,
CRT_VSYNC);
}
while (status == SYSTEM_CTRL_CRT_VSYNC_INACTIVE);
}
}
}
static void swPanelPowerSequence_sm750le(int disp,int delay)
{
unsigned int reg;
reg = PEEK32(DISPLAY_CONTROL_750LE);
if(disp)
reg |= 0xf;
else
reg &= ~0xf;
POKE32(DISPLAY_CONTROL_750LE,reg);
}
static void swPanelPowerSequence(int disp,int delay)
{
unsigned int reg;
/* disp should be 1 to open sequence */
reg = PEEK32(PANEL_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,FPEN,disp);
POKE32(PANEL_DISPLAY_CTRL,reg);
primaryWaitVerticalSync(delay);
reg = PEEK32(PANEL_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,DATA,disp);
POKE32(PANEL_DISPLAY_CTRL,reg);
primaryWaitVerticalSync(delay);
reg = PEEK32(PANEL_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,VBIASEN,disp);
POKE32(PANEL_DISPLAY_CTRL,reg);
primaryWaitVerticalSync(delay);
reg = PEEK32(PANEL_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,FPEN,disp);
POKE32(PANEL_DISPLAY_CTRL,reg);
primaryWaitVerticalSync(delay);
}
void ddk750_setLogicalDispOut(disp_output_t output)
{
unsigned int reg;
if(output & PNL_2_USAGE){
/* set panel path controller select */
reg = PEEK32(PANEL_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,PANEL_DISPLAY_CTRL,SELECT,(output & PNL_2_MASK)>>PNL_2_OFFSET);
POKE32(PANEL_DISPLAY_CTRL,reg);
}
if(output & CRT_2_USAGE){
/* set crt path controller select */
reg = PEEK32(CRT_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,CRT_DISPLAY_CTRL,SELECT,(output & CRT_2_MASK)>>CRT_2_OFFSET);
/*se blank off */
reg = FIELD_SET(reg,CRT_DISPLAY_CTRL,BLANK,OFF);
POKE32(CRT_DISPLAY_CTRL,reg);
}
if(output & PRI_TP_USAGE){
/* set primary timing and plane en_bit */
setDisplayControl(0,(output&PRI_TP_MASK)>>PRI_TP_OFFSET);
}
if(output & SEC_TP_USAGE){
/* set secondary timing and plane en_bit*/
setDisplayControl(1,(output&SEC_TP_MASK)>>SEC_TP_OFFSET);
}
if(output & PNL_SEQ_USAGE){
/* set panel sequence */
swPanelPowerSequence((output&PNL_SEQ_MASK)>>PNL_SEQ_OFFSET,4);
}
if(output & DAC_USAGE)
setDAC((output & DAC_MASK)>>DAC_OFFSET);
if(output & DPMS_USAGE)
ddk750_setDPMS((output & DPMS_MASK) >> DPMS_OFFSET);
}
int ddk750_initDVIDisp()
{
/* Initialize DVI. If the dviInit fail and the VendorID or the DeviceID are
not zeroed, then set the failure flag. If it is zeroe, it might mean
that the system is in Dual CRT Monitor configuration. */
/* De-skew enabled with default 111b value.
This will fix some artifacts problem in some mode on board 2.2.
Somehow this fix does not affect board 2.1.
*/
if ((dviInit(1, /* Select Rising Edge */
1, /* Select 24-bit bus */
0, /* Select Single Edge clock */
1, /* Enable HSync as is */
1, /* Enable VSync as is */
1, /* Enable De-skew */
7, /* Set the de-skew setting to maximum setup */
1, /* Enable continuous Sync */
1, /* Enable PLL Filter */
4 /* Use the recommended value for PLL Filter value */
) != 0) && (dviGetVendorID() != 0x0000) && (dviGetDeviceID() != 0x0000))
{
return (-1);
}
/* TODO: Initialize other display component */
/* Success */
return 0;
}
#ifndef DDK750_DISPLAY_H__
#define DDK750_DISPLAY_H__
/* panel path select
80000[29:28]
*/
#define PNL_2_OFFSET 0
#define PNL_2_MASK (3 << PNL_2_OFFSET)
#define PNL_2_USAGE (PNL_2_MASK << 16)
#define PNL_2_PRI ((0 << PNL_2_OFFSET)|PNL_2_USAGE)
#define PNL_2_SEC ((2 << PNL_2_OFFSET)|PNL_2_USAGE)
/* primary timing & plane enable bit
1: 80000[8] & 80000[2] on
0: both off
*/
#define PRI_TP_OFFSET 4
#define PRI_TP_MASK (1 << PRI_TP_OFFSET)
#define PRI_TP_USAGE (PRI_TP_MASK << 16)
#define PRI_TP_ON ((0x1 << PRI_TP_OFFSET)|PRI_TP_USAGE)
#define PRI_TP_OFF ((0x0 << PRI_TP_OFFSET)|PRI_TP_USAGE)
/* panel sequency status
80000[27:24]
*/
#define PNL_SEQ_OFFSET 6
#define PNL_SEQ_MASK (1 << PNL_SEQ_OFFSET)
#define PNL_SEQ_USAGE (PNL_SEQ_MASK << 16)
#define PNL_SEQ_ON ((1 << PNL_SEQ_OFFSET)|PNL_SEQ_USAGE)
#define PNL_SEQ_OFF ((0 << PNL_SEQ_OFFSET)|PNL_SEQ_USAGE)
/* dual digital output
80000[19]
*/
#define DUAL_TFT_OFFSET 8
#define DUAL_TFT_MASK (1 << DUAL_TFT_OFFSET)
#define DUAL_TFT_USAGE (DUAL_TFT_MASK << 16)
#define DUAL_TFT_ON ((1 << DUAL_TFT_OFFSET)|DUAL_TFT_USAGE)
#define DUAL_TFT_OFF ((0 << DUAL_TFT_OFFSET)|DUAL_TFT_USAGE)
/* secondary timing & plane enable bit
1:80200[8] & 80200[2] on
0: both off
*/
#define SEC_TP_OFFSET 5
#define SEC_TP_MASK (1<< SEC_TP_OFFSET)
#define SEC_TP_USAGE (SEC_TP_MASK << 16)
#define SEC_TP_ON ((0x1 << SEC_TP_OFFSET)|SEC_TP_USAGE)
#define SEC_TP_OFF ((0x0 << SEC_TP_OFFSET)|SEC_TP_USAGE)
/* crt path select
80200[19:18]
*/
#define CRT_2_OFFSET 2
#define CRT_2_MASK (3 << CRT_2_OFFSET)
#define CRT_2_USAGE (CRT_2_MASK << 16)
#define CRT_2_PRI ((0x0 << CRT_2_OFFSET)|CRT_2_USAGE)
#define CRT_2_SEC ((0x2 << CRT_2_OFFSET)|CRT_2_USAGE)
/* DAC affect both DVI and DSUB
4[20]
*/
#define DAC_OFFSET 7
#define DAC_MASK (1 << DAC_OFFSET)
#define DAC_USAGE (DAC_MASK << 16)
#define DAC_ON ((0x0<< DAC_OFFSET)|DAC_USAGE)
#define DAC_OFF ((0x1 << DAC_OFFSET)|DAC_USAGE)
/* DPMS only affect D-SUB head
0[31:30]
*/
#define DPMS_OFFSET 9
#define DPMS_MASK (3 << DPMS_OFFSET)
#define DPMS_USAGE (DPMS_MASK << 16)
#define DPMS_OFF ((3 << DPMS_OFFSET)|DPMS_USAGE)
#define DPMS_ON ((0 << DPMS_OFFSET)|DPMS_USAGE)
/*
LCD1 means panel path TFT1 & panel path DVI (so enable DAC)
CRT means crt path DSUB
*/
#if 0
typedef enum _disp_output_t
{
NO_DISPLAY = DPMS_OFF,
LCD1_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|DPMS_OFF|DAC_ON,
LCD1_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|DPMS_OFF|DAC_ON,
LCD2_PRI = CRT_2_PRI|PRI_TP_ON|DUAL_TFT_ON|DPMS_OFF,
LCD2_SEC = CRT_2_SEC|SEC_TP_ON|DUAL_TFT_ON|DPMS_OFF,
DSUB_PRI = CRT_2_PRI|PRI_TP_ON|DAC_ON,
DSUB_SEC = CRT_2_SEC|SEC_TP_ON|DAC_ON,
LCD1_DSUB_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|
CRT_2_PRI|SEC_TP_OFF|DAC_ON,
LCD1_DSUB_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|
CRT_2_SEC|PRI_TP_OFF|DAC_ON,
/* LCD1 show primary and DSUB show secondary */
LCD1_DSUB_DUAL = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|
CRT_2_SEC|SEC_TP_ON|DAC_ON,
/* LCD1 show secondary and DSUB show primary */
LCD1_DSUB_DUAL_SWAP = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|
CRT_2_PRI|PRI_TP_ON|DAC_ON,
LCD1_LCD2_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|
CRT_2_PRI|SEC_TP_OFF|DPMS_OFF|DUAL_TFT_ON,
LCD1_LCD2_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|
CRT_2_SEC|PRI_TP_OFF|DPMS_OFF|DUAL_TFT_ON,
LCD1_LCD2_DSUB_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|DAC_ON|
CRT_2_PRI|SEC_TP_OFF|DPMS_ON|DUAL_TFT_ON,
LCD1_LCD2_DSUB_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|DAC_ON|
CRT_2_SEC|PRI_TP_OFF|DPMS_ON|DUAL_TFT_ON,
}
disp_output_t;
#else
typedef enum _disp_output_t{
do_LCD1_PRI = PNL_2_PRI|PRI_TP_ON|PNL_SEQ_ON|DAC_ON,
do_LCD1_SEC = PNL_2_SEC|SEC_TP_ON|PNL_SEQ_ON|DAC_ON,
#if 0
do_LCD2_PRI = CRT_2_PRI|PRI_TP_ON,
do_LCD2_SEC = CRT_2_SEC|SEC_TP_ON,
#else
do_LCD2_PRI = CRT_2_PRI|PRI_TP_ON|DUAL_TFT_ON,
do_LCD2_SEC = CRT_2_SEC|SEC_TP_ON|DUAL_TFT_ON,
#endif
/*
do_DSUB_PRI = CRT_2_PRI|PRI_TP_ON|DPMS_ON|DAC_ON,
do_DSUB_SEC = CRT_2_SEC|SEC_TP_ON|DPMS_ON|DAC_ON,
*/
#if 0
do_CRT_PRI = CRT_2_PRI|PRI_TP_ON,
do_CRT_SEC = CRT_2_SEC|SEC_TP_ON,
#else
do_CRT_PRI = CRT_2_PRI|PRI_TP_ON|DPMS_ON|DAC_ON,
do_CRT_SEC = CRT_2_SEC|SEC_TP_ON|DPMS_ON|DAC_ON,
#endif
}
disp_output_t;
#endif
void ddk750_setLogicalDispOut(disp_output_t);
int ddk750_initDVIDisp(void);
#endif
#define USE_DVICHIP
#ifdef USE_DVICHIP
#include "ddk750_help.h"
#include "ddk750_reg.h"
#include "ddk750_dvi.h"
#include "ddk750_sii164.h"
/* This global variable contains all the supported driver and its corresponding
function API. Please set the function pointer to NULL whenever the function
is not supported. */
static dvi_ctrl_device_t g_dcftSupportedDviController[] =
{
#ifdef DVI_CTRL_SII164
{
.pfnInit = sii164InitChip,
.pfnGetVendorId = sii164GetVendorID,
.pfnGetDeviceId = sii164GetDeviceID,
#ifdef SII164_FULL_FUNCTIONS
.pfnResetChip = sii164ResetChip,
.pfnGetChipString = sii164GetChipString,
.pfnSetPower = sii164SetPower,
.pfnEnableHotPlugDetection = sii164EnableHotPlugDetection,
.pfnIsConnected = sii164IsConnected,
.pfnCheckInterrupt = sii164CheckInterrupt,
.pfnClearInterrupt = sii164ClearInterrupt,
#endif
},
#endif
};
int dviInit(
unsigned char edgeSelect,
unsigned char busSelect,
unsigned char dualEdgeClkSelect,
unsigned char hsyncEnable,
unsigned char vsyncEnable,
unsigned char deskewEnable,
unsigned char deskewSetting,
unsigned char continuousSyncEnable,
unsigned char pllFilterEnable,
unsigned char pllFilterValue
)
{
dvi_ctrl_device_t *pCurrentDviCtrl;
pCurrentDviCtrl = g_dcftSupportedDviController;
if(pCurrentDviCtrl->pfnInit != NULL)
{
return pCurrentDviCtrl->pfnInit(edgeSelect, busSelect, dualEdgeClkSelect, hsyncEnable,
vsyncEnable, deskewEnable, deskewSetting, continuousSyncEnable,
pllFilterEnable, pllFilterValue);
}
return -1;//error
}
/*
* dviGetVendorID
* This function gets the vendor ID of the DVI controller chip.
*
* Output:
* Vendor ID
*/
unsigned short dviGetVendorID()
{
dvi_ctrl_device_t *pCurrentDviCtrl;
//pCurrentDviCtrl = getDviCtrl();
pCurrentDviCtrl = g_dcftSupportedDviController;
if (pCurrentDviCtrl != (dvi_ctrl_device_t *)0)
return pCurrentDviCtrl->pfnGetVendorId();
return 0x0000;
}
/*
* dviGetDeviceID
* This function gets the device ID of the DVI controller chip.
*
* Output:
* Device ID
*/
unsigned short dviGetDeviceID()
{
dvi_ctrl_device_t *pCurrentDviCtrl;
// pCurrentDviCtrl = getDviCtrl();
pCurrentDviCtrl = g_dcftSupportedDviController;
if (pCurrentDviCtrl != (dvi_ctrl_device_t *)0)
return pCurrentDviCtrl->pfnGetDeviceId();
return 0x0000;
}
#endif
#ifndef DDK750_DVI_H__
#define DDK750_DVI_H__
/* dvi chip stuffs structros */
typedef long (*PFN_DVICTRL_INIT)(
unsigned char edgeSelect,
unsigned char busSelect,
unsigned char dualEdgeClkSelect,
unsigned char hsyncEnable,
unsigned char vsyncEnable,
unsigned char deskewEnable,
unsigned char deskewSetting,
unsigned char continuousSyncEnable,
unsigned char pllFilterEnable,
unsigned char pllFilterValue);
typedef void (*PFN_DVICTRL_RESETCHIP)(void);
typedef char* (*PFN_DVICTRL_GETCHIPSTRING)(void);
typedef unsigned short (*PFN_DVICTRL_GETVENDORID)(void);
typedef unsigned short (*PFN_DVICTRL_GETDEVICEID)(void);
typedef void (*PFN_DVICTRL_SETPOWER)(unsigned char powerUp);
typedef void (*PFN_DVICTRL_HOTPLUGDETECTION)(unsigned char enableHotPlug);
typedef unsigned char (*PFN_DVICTRL_ISCONNECTED)(void);
typedef unsigned char (*PFN_DVICTRL_CHECKINTERRUPT)(void);
typedef void (*PFN_DVICTRL_CLEARINTERRUPT)(void);
/* Structure to hold all the function pointer to the DVI Controller. */
typedef struct _dvi_ctrl_device_t
{
PFN_DVICTRL_INIT pfnInit;
PFN_DVICTRL_RESETCHIP pfnResetChip;
PFN_DVICTRL_GETCHIPSTRING pfnGetChipString;
PFN_DVICTRL_GETVENDORID pfnGetVendorId;
PFN_DVICTRL_GETDEVICEID pfnGetDeviceId;
PFN_DVICTRL_SETPOWER pfnSetPower;
PFN_DVICTRL_HOTPLUGDETECTION pfnEnableHotPlugDetection;
PFN_DVICTRL_ISCONNECTED pfnIsConnected;
PFN_DVICTRL_CHECKINTERRUPT pfnCheckInterrupt;
PFN_DVICTRL_CLEARINTERRUPT pfnClearInterrupt;
} dvi_ctrl_device_t;
#define DVI_CTRL_SII164
/* dvi functions prototype */
int dviInit(
unsigned char edgeSelect,
unsigned char busSelect,
unsigned char dualEdgeClkSelect,
unsigned char hsyncEnable,
unsigned char vsyncEnable,
unsigned char deskewEnable,
unsigned char deskewSetting,
unsigned char continuousSyncEnable,
unsigned char pllFilterEnable,
unsigned char pllFilterValue
);
unsigned short dviGetVendorID(void);
unsigned short dviGetDeviceID(void);
#endif
//#include "ddk750_reg.h"
//#include "ddk750_chip.h"
#include "ddk750_help.h"
volatile unsigned char __iomem * mmio750 = NULL;
char revId750 = 0;
unsigned short devId750 = 0;
/* after driver mapped io registers, use this function first */
void ddk750_set_mmio(volatile unsigned char * addr,unsigned short devId,char revId)
{
mmio750 = addr;
devId750 = devId;
revId750 = revId;
if(revId == 0xfe)
printk("found sm750le\n");
}
#ifndef DDK750_HELP_H__
#define DDK750_HELP_H__
#include "ddk750_chip.h"
#ifndef USE_INTERNAL_REGISTER_ACCESS
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "sm750_help.h"
#if 0
/* if 718 big endian turned on,be aware that don't use this driver for general use,only for ppc big-endian */
#warning "big endian on target cpu and enable nature big endian support of 718 capability !"
#define PEEK32(addr) __raw_readl((void __iomem *)(mmio750)+(addr))
#define POKE32(addr,data) __raw_writel((data),(void __iomem*)(mmio750)+(addr))
#else /* software control endianess */
#define PEEK32(addr) readl((addr)+mmio750)
#define POKE32(addr,data) writel((data),(addr)+mmio750)
#endif
extern volatile unsigned char __iomem * mmio750;
extern char revId750;
extern unsigned short devId750;
#else
/* implement if you want use it*/
#endif
#endif
#define USE_HW_I2C
#ifdef USE_HW_I2C
#include "ddk750_help.h"
#include "ddk750_reg.h"
#include "ddk750_hwi2c.h"
#include "ddk750_power.h"
#define MAX_HWI2C_FIFO 16
#define HWI2C_WAIT_TIMEOUT 0xF0000
int hwI2CInit(
unsigned char busSpeedMode
)
{
unsigned int value;
/* Enable GPIO 30 & 31 as IIC clock & data */
value = PEEK32(GPIO_MUX);
value = FIELD_SET(value, GPIO_MUX, 30, I2C) |
FIELD_SET(0, GPIO_MUX, 31, I2C);
POKE32(GPIO_MUX, value);
/* Enable Hardware I2C power.
TODO: Check if we need to enable GPIO power?
*/
enableI2C(1);
/* Enable the I2C Controller and set the bus speed mode */
value = PEEK32(I2C_CTRL);
if (busSpeedMode == 0)
value = FIELD_SET(value, I2C_CTRL, MODE, STANDARD);
else
value = FIELD_SET(value, I2C_CTRL, MODE, FAST);
value = FIELD_SET(value, I2C_CTRL, EN, ENABLE);
POKE32(I2C_CTRL, value);
return 0;
}
void hwI2CClose(void)
{
unsigned int value;
/* Disable I2C controller */
value = PEEK32(I2C_CTRL);
value = FIELD_SET(value, I2C_CTRL, EN, DISABLE);
POKE32(I2C_CTRL, value);
/* Disable I2C Power */
enableI2C(0);
/* Set GPIO 30 & 31 back as GPIO pins */
value = PEEK32(GPIO_MUX);
value = FIELD_SET(value, GPIO_MUX, 30, GPIO);
value = FIELD_SET(value, GPIO_MUX, 31, GPIO);
POKE32(GPIO_MUX, value);
}
long hwI2CWaitTXDone(void)
{
unsigned int timeout;
/* Wait until the transfer is completed. */
timeout = HWI2C_WAIT_TIMEOUT;
while ((FIELD_GET(PEEK32(I2C_STATUS), I2C_STATUS, TX) != I2C_STATUS_TX_COMPLETED) &&
(timeout != 0))
timeout--;
if (timeout == 0)
return (-1);
return 0;
}
/*
* This function writes data to the i2c slave device registers.
*
* Parameters:
* deviceAddress - i2c Slave device address
* length - Total number of bytes to be written to the device
* pBuffer - The buffer that contains the data to be written to the
* i2c device.
*
* Return Value:
* Total number of bytes those are actually written.
*/
unsigned int hwI2CWriteData(
unsigned char deviceAddress,
unsigned int length,
unsigned char *pBuffer
)
{
unsigned char count, i;
unsigned int totalBytes = 0;
/* Set the Device Address */
POKE32(I2C_SLAVE_ADDRESS, deviceAddress & ~0x01);
/* Write data.
* Note:
* Only 16 byte can be accessed per i2c start instruction.
*/
do
{
/* Reset I2C by writing 0 to I2C_RESET register to clear the previous status. */
POKE32(I2C_RESET, 0);
/* Set the number of bytes to be written */
if (length < MAX_HWI2C_FIFO)
count = length - 1;
else
count = MAX_HWI2C_FIFO - 1;
POKE32(I2C_BYTE_COUNT, count);
/* Move the data to the I2C data register */
for (i = 0; i <= count; i++)
POKE32(I2C_DATA0 + i, *pBuffer++);
/* Start the I2C */
POKE32(I2C_CTRL, FIELD_SET(PEEK32(I2C_CTRL), I2C_CTRL, CTRL, START));
/* Wait until the transfer is completed. */
if (hwI2CWaitTXDone() != 0)
break;
/* Substract length */
length -= (count + 1);
/* Total byte written */
totalBytes += (count + 1);
} while (length > 0);
return totalBytes;
}
/*
* This function reads data from the slave device and stores them
* in the given buffer
*
* Parameters:
* deviceAddress - i2c Slave device address
* length - Total number of bytes to be read
* pBuffer - Pointer to a buffer to be filled with the data read
* from the slave device. It has to be the same size as the
* length to make sure that it can keep all the data read.
*
* Return Value:
* Total number of actual bytes read from the slave device
*/
unsigned int hwI2CReadData(
unsigned char deviceAddress,
unsigned int length,
unsigned char *pBuffer
)
{
unsigned char count, i;
unsigned int totalBytes = 0;
/* Set the Device Address */
POKE32(I2C_SLAVE_ADDRESS, deviceAddress | 0x01);
/* Read data and save them to the buffer.
* Note:
* Only 16 byte can be accessed per i2c start instruction.
*/
do
{
/* Reset I2C by writing 0 to I2C_RESET register to clear all the status. */
POKE32(I2C_RESET, 0);
/* Set the number of bytes to be read */
if (length <= MAX_HWI2C_FIFO)
count = length - 1;
else
count = MAX_HWI2C_FIFO - 1;
POKE32(I2C_BYTE_COUNT, count);
/* Start the I2C */
POKE32(I2C_CTRL, FIELD_SET(PEEK32(I2C_CTRL), I2C_CTRL, CTRL, START));
/* Wait until transaction done. */
if (hwI2CWaitTXDone() != 0)
break;
/* Save the data to the given buffer */
for (i = 0; i <= count; i++)
*pBuffer++ = PEEK32(I2C_DATA0 + i);
/* Substract length by 16 */
length -= (count + 1);
/* Number of bytes read. */
totalBytes += (count + 1);
} while (length > 0);
return totalBytes;
}
/*
* This function reads the slave device's register
*
* Parameters:
* deviceAddress - i2c Slave device address which register
* to be read from
* registerIndex - Slave device's register to be read
*
* Return Value:
* Register value
*/
unsigned char hwI2CReadReg(
unsigned char deviceAddress,
unsigned char registerIndex
)
{
unsigned char value = (0xFF);
if (hwI2CWriteData(deviceAddress, 1, &registerIndex) == 1)
hwI2CReadData(deviceAddress, 1, &value);
return value;
}
/*
* This function writes a value to the slave device's register
*
* Parameters:
* deviceAddress - i2c Slave device address which register
* to be written
* registerIndex - Slave device's register to be written
* data - Data to be written to the register
*
* Result:
* 0 - Success
* -1 - Fail
*/
int hwI2CWriteReg(
unsigned char deviceAddress,
unsigned char registerIndex,
unsigned char data
)
{
unsigned char value[2];
value[0] = registerIndex;
value[1] = data;
if (hwI2CWriteData(deviceAddress, 2, value) == 2)
return 0;
return (-1);
}
#endif
#ifndef DDK750_HWI2C_H__
#define DDK750_HWI2C_H__
/* hwi2c functions */
int hwI2CInit(unsigned char busSpeedMode);
void hwI2CClose(void);
unsigned char hwI2CReadReg(unsigned char deviceAddress,unsigned char registerIndex);
int hwI2CWriteReg(unsigned char deviceAddress,unsigned char registerIndex,unsigned char data);
#endif
#include "ddk750_help.h"
#include "ddk750_reg.h"
#include "ddk750_mode.h"
#include "ddk750_chip.h"
/*
SM750LE only:
This function takes care extra registers and bit fields required to set
up a mode in SM750LE
Explanation about Display Control register:
HW only supports 7 predefined pixel clocks, and clock select is
in bit 29:27 of Display Control register.
*/
static unsigned long displayControlAdjust_SM750LE(mode_parameter_t *pModeParam, unsigned long dispControl)
{
unsigned long x, y;
x = pModeParam->horizontal_display_end;
y = pModeParam->vertical_display_end;
/* SM750LE has to set up the top-left and bottom-right
registers as well.
Note that normal SM750/SM718 only use those two register for
auto-centering mode.
*/
POKE32(CRT_AUTO_CENTERING_TL,
FIELD_VALUE(0, CRT_AUTO_CENTERING_TL, TOP, 0)
| FIELD_VALUE(0, CRT_AUTO_CENTERING_TL, LEFT, 0));
POKE32(CRT_AUTO_CENTERING_BR,
FIELD_VALUE(0, CRT_AUTO_CENTERING_BR, BOTTOM, y-1)
| FIELD_VALUE(0, CRT_AUTO_CENTERING_BR, RIGHT, x-1));
/* Assume common fields in dispControl have been properly set before
calling this function.
This function only sets the extra fields in dispControl.
*/
/* Clear bit 29:27 of display control register */
dispControl &= FIELD_CLEAR(CRT_DISPLAY_CTRL, CLK);
/* Set bit 29:27 of display control register for the right clock */
/* Note that SM750LE only need to supported 7 resoluitons. */
if ( x == 800 && y == 600 )
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL41);
else if (x == 1024 && y == 768)
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL65);
else if (x == 1152 && y == 864)
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL80);
else if (x == 1280 && y == 768)
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL80);
else if (x == 1280 && y == 720)
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL74);
else if (x == 1280 && y == 960)
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL108);
else if (x == 1280 && y == 1024)
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL108);
else /* default to VGA clock */
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLK, PLL25);
/* Set bit 25:24 of display controller */
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CRTSELECT, CRT);
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, RGBBIT, 24BIT);
/* Set bit 14 of display controller */
dispControl = FIELD_SET(dispControl, CRT_DISPLAY_CTRL, CLOCK_PHASE, ACTIVE_LOW);
POKE32(CRT_DISPLAY_CTRL, dispControl);
return dispControl;
}
/* only timing related registers will be programed */
static int programModeRegisters(mode_parameter_t * pModeParam,pll_value_t * pll)
{
int ret = 0;
int cnt = 0;
unsigned int ulTmpValue,ulReg;
if(pll->clockType == SECONDARY_PLL)
{
/* programe secondary pixel clock */
POKE32(CRT_PLL_CTRL,formatPllReg(pll));
POKE32(CRT_HORIZONTAL_TOTAL,
FIELD_VALUE(0, CRT_HORIZONTAL_TOTAL, TOTAL, pModeParam->horizontal_total - 1)
| FIELD_VALUE(0, CRT_HORIZONTAL_TOTAL, DISPLAY_END, pModeParam->horizontal_display_end - 1));
POKE32(CRT_HORIZONTAL_SYNC,
FIELD_VALUE(0, CRT_HORIZONTAL_SYNC, WIDTH, pModeParam->horizontal_sync_width)
| FIELD_VALUE(0, CRT_HORIZONTAL_SYNC, START, pModeParam->horizontal_sync_start - 1));
POKE32(CRT_VERTICAL_TOTAL,
FIELD_VALUE(0, CRT_VERTICAL_TOTAL, TOTAL, pModeParam->vertical_total - 1)
| FIELD_VALUE(0, CRT_VERTICAL_TOTAL, DISPLAY_END, pModeParam->vertical_display_end - 1));
POKE32(CRT_VERTICAL_SYNC,
FIELD_VALUE(0, CRT_VERTICAL_SYNC, HEIGHT, pModeParam->vertical_sync_height)
| FIELD_VALUE(0, CRT_VERTICAL_SYNC, START, pModeParam->vertical_sync_start - 1));
ulTmpValue = FIELD_VALUE(0,CRT_DISPLAY_CTRL,VSYNC_PHASE,pModeParam->vertical_sync_polarity)|
FIELD_VALUE(0,CRT_DISPLAY_CTRL,HSYNC_PHASE,pModeParam->horizontal_sync_polarity)|
FIELD_SET(0,CRT_DISPLAY_CTRL,TIMING,ENABLE)|
FIELD_SET(0,CRT_DISPLAY_CTRL,PLANE,ENABLE);
if(getChipType() == SM750LE){
displayControlAdjust_SM750LE(pModeParam,ulTmpValue);
}else{
ulReg = PEEK32(CRT_DISPLAY_CTRL)
& FIELD_CLEAR(CRT_DISPLAY_CTRL,VSYNC_PHASE)
& FIELD_CLEAR(CRT_DISPLAY_CTRL,HSYNC_PHASE)
& FIELD_CLEAR(CRT_DISPLAY_CTRL,TIMING)
& FIELD_CLEAR(CRT_DISPLAY_CTRL,PLANE);
POKE32(CRT_DISPLAY_CTRL,ulTmpValue|ulReg);
}
}
else if(pll->clockType == PRIMARY_PLL)
{
unsigned int ulReservedBits;
POKE32(PANEL_PLL_CTRL,formatPllReg(pll));
POKE32(PANEL_HORIZONTAL_TOTAL,
FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, TOTAL, pModeParam->horizontal_total - 1)
| FIELD_VALUE(0, PANEL_HORIZONTAL_TOTAL, DISPLAY_END, pModeParam->horizontal_display_end - 1));
POKE32(PANEL_HORIZONTAL_SYNC,
FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, WIDTH, pModeParam->horizontal_sync_width)
| FIELD_VALUE(0, PANEL_HORIZONTAL_SYNC, START, pModeParam->horizontal_sync_start - 1));
POKE32(PANEL_VERTICAL_TOTAL,
FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, TOTAL, pModeParam->vertical_total - 1)
| FIELD_VALUE(0, PANEL_VERTICAL_TOTAL, DISPLAY_END, pModeParam->vertical_display_end - 1));
POKE32(PANEL_VERTICAL_SYNC,
FIELD_VALUE(0, PANEL_VERTICAL_SYNC, HEIGHT, pModeParam->vertical_sync_height)
| FIELD_VALUE(0, PANEL_VERTICAL_SYNC, START, pModeParam->vertical_sync_start - 1));
ulTmpValue = FIELD_VALUE(0,PANEL_DISPLAY_CTRL,VSYNC_PHASE,pModeParam->vertical_sync_polarity)|
FIELD_VALUE(0,PANEL_DISPLAY_CTRL,HSYNC_PHASE,pModeParam->horizontal_sync_polarity)|
FIELD_VALUE(0,PANEL_DISPLAY_CTRL,CLOCK_PHASE,pModeParam->clock_phase_polarity)|
FIELD_SET(0,PANEL_DISPLAY_CTRL,TIMING,ENABLE)|
FIELD_SET(0,PANEL_DISPLAY_CTRL,PLANE,ENABLE);
ulReservedBits = FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_1_MASK, ENABLE) |
FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_2_MASK, ENABLE) |
FIELD_SET(0, PANEL_DISPLAY_CTRL, RESERVED_3_MASK, ENABLE)|
FIELD_SET(0,PANEL_DISPLAY_CTRL,VSYNC,ACTIVE_LOW);
ulReg = (PEEK32(PANEL_DISPLAY_CTRL) & ~ulReservedBits)
& FIELD_CLEAR(PANEL_DISPLAY_CTRL, CLOCK_PHASE)
& FIELD_CLEAR(PANEL_DISPLAY_CTRL, VSYNC_PHASE)
& FIELD_CLEAR(PANEL_DISPLAY_CTRL, HSYNC_PHASE)
& FIELD_CLEAR(PANEL_DISPLAY_CTRL, TIMING)
& FIELD_CLEAR(PANEL_DISPLAY_CTRL, PLANE);
/* May a hardware bug or just my test chip (not confirmed).
* PANEL_DISPLAY_CTRL register seems requiring few writes
* before a value can be succesfully written in.
* Added some masks to mask out the reserved bits.
* Note: This problem happens by design. The hardware will wait for the
* next vertical sync to turn on/off the plane.
*/
POKE32(PANEL_DISPLAY_CTRL,ulTmpValue|ulReg);
#if 1
while((PEEK32(PANEL_DISPLAY_CTRL) & ~ulReservedBits) != (ulTmpValue|ulReg))
{
cnt++;
if(cnt > 1000)
break;
POKE32(PANEL_DISPLAY_CTRL,ulTmpValue|ulReg);
}
#endif
}
else{
ret = -1;
}
return ret;
}
int ddk750_setModeTiming(mode_parameter_t * parm,clock_type_t clock)
{
pll_value_t pll;
unsigned int uiActualPixelClk;
pll.inputFreq = DEFAULT_INPUT_CLOCK;
pll.clockType = clock;
uiActualPixelClk = calcPllValue(parm->pixel_clock,&pll);
if(getChipType() == SM750LE){
/* set graphic mode via IO method */
outb_p(0x88,0x3d4);
outb_p(0x06,0x3d5);
}
programModeRegisters(parm,&pll);
return 0;
}
#ifndef DDK750_MODE_H__
#define DDK750_MODE_H__
#include "ddk750_chip.h"
typedef enum _spolarity_t
{
POS = 0, /* positive */
NEG, /* negative */
}
spolarity_t;
typedef struct _mode_parameter_t
{
/* Horizontal timing. */
unsigned long horizontal_total;
unsigned long horizontal_display_end;
unsigned long horizontal_sync_start;
unsigned long horizontal_sync_width;
spolarity_t horizontal_sync_polarity;
/* Vertical timing. */
unsigned long vertical_total;
unsigned long vertical_display_end;
unsigned long vertical_sync_start;
unsigned long vertical_sync_height;
spolarity_t vertical_sync_polarity;
/* Refresh timing. */
unsigned long pixel_clock;
unsigned long horizontal_frequency;
unsigned long vertical_frequency;
/* Clock Phase. This clock phase only applies to Panel. */
spolarity_t clock_phase_polarity;
}
mode_parameter_t;
int ddk750_setModeTiming(mode_parameter_t *,clock_type_t);
#endif
#include "ddk750_help.h"
#include "ddk750_reg.h"
#include "ddk750_power.h"
void ddk750_setDPMS(DPMS_t state)
{
unsigned int value;
if(getChipType() == SM750LE){
value = PEEK32(CRT_DISPLAY_CTRL);
POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(value,CRT_DISPLAY_CTRL,DPMS,state));
}else{
value = PEEK32(SYSTEM_CTRL);
value= FIELD_VALUE(value,SYSTEM_CTRL,DPMS,state);
POKE32(SYSTEM_CTRL, value);
}
}
unsigned int getPowerMode()
{
if(getChipType() == SM750LE)
return 0;
return (FIELD_GET(PEEK32(POWER_MODE_CTRL), POWER_MODE_CTRL, MODE));
}
/*
* SM50x can operate in one of three modes: 0, 1 or Sleep.
* On hardware reset, power mode 0 is default.
*/
void setPowerMode(unsigned int powerMode)
{
unsigned int control_value = 0;
control_value = PEEK32(POWER_MODE_CTRL);
if(getChipType() == SM750LE)
return;
switch (powerMode)
{
case POWER_MODE_CTRL_MODE_MODE0:
control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, MODE0);
break;
case POWER_MODE_CTRL_MODE_MODE1:
control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, MODE1);
break;
case POWER_MODE_CTRL_MODE_SLEEP:
control_value = FIELD_SET(control_value, POWER_MODE_CTRL, MODE, SLEEP);
break;
default:
break;
}
/* Set up other fields in Power Control Register */
if (powerMode == POWER_MODE_CTRL_MODE_SLEEP)
{
control_value =
#ifdef VALIDATION_CHIP
FIELD_SET( control_value, POWER_MODE_CTRL, 336CLK, OFF) |
#endif
FIELD_SET( control_value, POWER_MODE_CTRL, OSC_INPUT, OFF);
}
else
{
control_value =
#ifdef VALIDATION_CHIP
FIELD_SET( control_value, POWER_MODE_CTRL, 336CLK, ON) |
#endif
FIELD_SET( control_value, POWER_MODE_CTRL, OSC_INPUT, ON);
}
/* Program new power mode. */
POKE32(POWER_MODE_CTRL, control_value);
}
void setCurrentGate(unsigned int gate)
{
unsigned int gate_reg;
unsigned int mode;
/* Get current power mode. */
mode = getPowerMode();
switch (mode)
{
case POWER_MODE_CTRL_MODE_MODE0:
gate_reg = MODE0_GATE;
break;
case POWER_MODE_CTRL_MODE_MODE1:
gate_reg = MODE1_GATE;
break;
default:
gate_reg = MODE0_GATE;
break;
}
POKE32(gate_reg, gate);
}
/*
* This function enable/disable the 2D engine.
*/
void enable2DEngine(unsigned int enable)
{
uint32_t gate;
gate = PEEK32(CURRENT_GATE);
if (enable)
{
gate = FIELD_SET(gate, CURRENT_GATE, DE, ON);
gate = FIELD_SET(gate, CURRENT_GATE, CSC, ON);
}
else
{
gate = FIELD_SET(gate, CURRENT_GATE, DE, OFF);
gate = FIELD_SET(gate, CURRENT_GATE, CSC, OFF);
}
setCurrentGate(gate);
}
/*
* This function enable/disable the ZV Port.
*/
void enableZVPort(unsigned int enable)
{
uint32_t gate;
/* Enable ZV Port Gate */
gate = PEEK32(CURRENT_GATE);
if (enable)
{
gate = FIELD_SET(gate, CURRENT_GATE, ZVPORT, ON);
#if 1
/* Using Software I2C */
gate = FIELD_SET(gate, CURRENT_GATE, GPIO, ON);
#else
/* Using Hardware I2C */
gate = FIELD_SET(gate, CURRENT_GATE, I2C, ON);
#endif
}
else
{
/* Disable ZV Port Gate. There is no way to know whether the GPIO pins are being used
or not. Therefore, do not disable the GPIO gate. */
gate = FIELD_SET(gate, CURRENT_GATE, ZVPORT, OFF);
}
setCurrentGate(gate);
}
void enableSSP(unsigned int enable)
{
uint32_t gate;
/* Enable SSP Gate */
gate = PEEK32(CURRENT_GATE);
if (enable)
gate = FIELD_SET(gate, CURRENT_GATE, SSP, ON);
else
gate = FIELD_SET(gate, CURRENT_GATE, SSP, OFF);
setCurrentGate(gate);
}
void enableDMA(unsigned int enable)
{
uint32_t gate;
/* Enable DMA Gate */
gate = PEEK32(CURRENT_GATE);
if (enable)
gate = FIELD_SET(gate, CURRENT_GATE, DMA, ON);
else
gate = FIELD_SET(gate, CURRENT_GATE, DMA, OFF);
setCurrentGate(gate);
}
/*
* This function enable/disable the GPIO Engine
*/
void enableGPIO(unsigned int enable)
{
uint32_t gate;
/* Enable GPIO Gate */
gate = PEEK32(CURRENT_GATE);
if (enable)
gate = FIELD_SET(gate, CURRENT_GATE, GPIO, ON);
else
gate = FIELD_SET(gate, CURRENT_GATE, GPIO, OFF);
setCurrentGate(gate);
}
/*
* This function enable/disable the PWM Engine
*/
void enablePWM(unsigned int enable)
{
uint32_t gate;
/* Enable PWM Gate */
gate = PEEK32(CURRENT_GATE);
if (enable)
gate = FIELD_SET(gate, CURRENT_GATE, PWM, ON);
else
gate = FIELD_SET(gate, CURRENT_GATE, PWM, OFF);
setCurrentGate(gate);
}
/*
* This function enable/disable the I2C Engine
*/
void enableI2C(unsigned int enable)
{
uint32_t gate;
/* Enable I2C Gate */
gate = PEEK32(CURRENT_GATE);
if (enable)
gate = FIELD_SET(gate, CURRENT_GATE, I2C, ON);
else
gate = FIELD_SET(gate, CURRENT_GATE, I2C, OFF);
setCurrentGate(gate);
}
#ifndef DDK750_POWER_H__
#define DDK750_POWER_H__
typedef enum _DPMS_t
{
crtDPMS_ON = 0x0,
crtDPMS_STANDBY = 0x1,
crtDPMS_SUSPEND = 0x2,
crtDPMS_OFF = 0x3,
}
DPMS_t;
#define setDAC(off) \
{ \
POKE32(MISC_CTRL,FIELD_VALUE(PEEK32(MISC_CTRL), \
MISC_CTRL, \
DAC_POWER, \
off)); \
}
void ddk750_setDPMS(DPMS_t);
unsigned int getPowerMode(void);
/*
* This function sets the current power mode
*/
void setPowerMode(unsigned int powerMode);
/*
* This function sets current gate
*/
void setCurrentGate(unsigned int gate);
/*
* This function enable/disable the 2D engine.
*/
void enable2DEngine(unsigned int enable);
/*
* This function enable/disable the ZV Port
*/
void enableZVPort(unsigned int enable);
/*
* This function enable/disable the DMA Engine
*/
void enableDMA(unsigned int enable);
/*
* This function enable/disable the GPIO Engine
*/
void enableGPIO(unsigned int enable);
/*
* This function enable/disable the PWM Engine
*/
void enablePWM(unsigned int enable);
/*
* This function enable/disable the I2C Engine
*/
void enableI2C(unsigned int enable);
/*
* This function enable/disable the SSP.
*/
void enableSSP(unsigned int enable);
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
#define USE_DVICHIP
#ifdef USE_DVICHIP
#include "ddk750_sii164.h"
#include "ddk750_hwi2c.h"
/* I2C Address of each SII164 chip */
#define SII164_I2C_ADDRESS 0x70
/* Define this definition to use hardware i2c. */
#define USE_HW_I2C
#ifdef USE_HW_I2C
#define i2cWriteReg hwI2CWriteReg
#define i2cReadReg hwI2CReadReg
#else
#define i2cWriteReg swI2CWriteReg
#define i2cReadReg swI2CReadReg
#endif
/* SII164 Vendor and Device ID */
#define SII164_VENDOR_ID 0x0001
#define SII164_DEVICE_ID 0x0006
#ifdef SII164_FULL_FUNCTIONS
/* Name of the DVI Controller chip */
static char *gDviCtrlChipName = "Silicon Image SiI 164";
#endif
/*
* sii164GetVendorID
* This function gets the vendor ID of the DVI controller chip.
*
* Output:
* Vendor ID
*/
unsigned short sii164GetVendorID()
{
unsigned short vendorID;
vendorID = ((unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_VENDOR_ID_HIGH) << 8) |
(unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_VENDOR_ID_LOW);
return vendorID;
}
/*
* sii164GetDeviceID
* This function gets the device ID of the DVI controller chip.
*
* Output:
* Device ID
*/
unsigned short sii164GetDeviceID()
{
unsigned short deviceID;
deviceID = ((unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_DEVICE_ID_HIGH) << 8) |
(unsigned short) i2cReadReg(SII164_I2C_ADDRESS, SII164_DEVICE_ID_LOW);
return deviceID;
}
/* DVI.C will handle all SiI164 chip stuffs and try it best to make code minimal and useful */
/*
* sii164InitChip
* This function initialize and detect the DVI controller chip.
*
* Input:
* edgeSelect - Edge Select:
* 0 = Input data is falling edge latched (falling edge
* latched first in dual edge mode)
* 1 = Input data is rising edge latched (rising edge
* latched first in dual edge mode)
* busSelect - Input Bus Select:
* 0 = Input data bus is 12-bits wide
* 1 = Input data bus is 24-bits wide
* dualEdgeClkSelect - Dual Edge Clock Select
* 0 = Input data is single edge latched
* 1 = Input data is dual edge latched
* hsyncEnable - Horizontal Sync Enable:
* 0 = HSYNC input is transmitted as fixed LOW
* 1 = HSYNC input is transmitted as is
* vsyncEnable - Vertical Sync Enable:
* 0 = VSYNC input is transmitted as fixed LOW
* 1 = VSYNC input is transmitted as is
* deskewEnable - De-skewing Enable:
* 0 = De-skew disabled
* 1 = De-skew enabled
* deskewSetting - De-skewing Setting (increment of 260psec)
* 0 = 1 step --> minimum setup / maximum hold
* 1 = 2 step
* 2 = 3 step
* 3 = 4 step
* 4 = 5 step
* 5 = 6 step
* 6 = 7 step
* 7 = 8 step --> maximum setup / minimum hold
* continuousSyncEnable- SYNC Continuous:
* 0 = Disable
* 1 = Enable
* pllFilterEnable - PLL Filter Enable
* 0 = Disable PLL Filter
* 1 = Enable PLL Filter
* pllFilterValue - PLL Filter characteristics:
* 0~7 (recommended value is 4)
*
* Output:
* 0 - Success
* -1 - Fail.
*/
long sii164InitChip(
unsigned char edgeSelect,
unsigned char busSelect,
unsigned char dualEdgeClkSelect,
unsigned char hsyncEnable,
unsigned char vsyncEnable,
unsigned char deskewEnable,
unsigned char deskewSetting,
unsigned char continuousSyncEnable,
unsigned char pllFilterEnable,
unsigned char pllFilterValue
)
{
//unsigned char ucRegIndex, ucRegValue;
//unsigned char ucDeviceAddress,
unsigned char config;
//unsigned long delayCount;
/* Initialize the i2c bus */
#ifdef USE_HW_I2C
/* Use fast mode. */
hwI2CInit(1);
#else
swI2CInit(DEFAULT_I2C_SCL, DEFAULT_I2C_SDA);
#endif
/* Check if SII164 Chip exists */
if ((sii164GetVendorID() == SII164_VENDOR_ID) && (sii164GetDeviceID() == SII164_DEVICE_ID))
{
#ifdef DDKDEBUG
//sii164PrintRegisterValues();
#endif
/*
* Initialize SII164 controller chip.
*/
/* Select the edge */
if (edgeSelect == 0)
config = SII164_CONFIGURATION_LATCH_FALLING;
else
config = SII164_CONFIGURATION_LATCH_RISING;
/* Select bus wide */
if (busSelect == 0)
config |= SII164_CONFIGURATION_BUS_12BITS;
else
config |= SII164_CONFIGURATION_BUS_24BITS;
/* Select Dual/Single Edge Clock */
if (dualEdgeClkSelect == 0)
config |= SII164_CONFIGURATION_CLOCK_SINGLE;
else
config |= SII164_CONFIGURATION_CLOCK_DUAL;
/* Select HSync Enable */
if (hsyncEnable == 0)
config |= SII164_CONFIGURATION_HSYNC_FORCE_LOW;
else
config |= SII164_CONFIGURATION_HSYNC_AS_IS;
/* Select VSync Enable */
if (vsyncEnable == 0)
config |= SII164_CONFIGURATION_VSYNC_FORCE_LOW;
else
config |= SII164_CONFIGURATION_VSYNC_AS_IS;
i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
/* De-skew enabled with default 111b value.
This will fix some artifacts problem in some mode on board 2.2.
Somehow this fix does not affect board 2.1.
*/
if (deskewEnable == 0)
config = SII164_DESKEW_DISABLE;
else
config = SII164_DESKEW_ENABLE;
switch (deskewSetting)
{
case 0:
config |= SII164_DESKEW_1_STEP;
break;
case 1:
config |= SII164_DESKEW_2_STEP;
break;
case 2:
config |= SII164_DESKEW_3_STEP;
break;
case 3:
config |= SII164_DESKEW_4_STEP;
break;
case 4:
config |= SII164_DESKEW_5_STEP;
break;
case 5:
config |= SII164_DESKEW_6_STEP;
break;
case 6:
config |= SII164_DESKEW_7_STEP;
break;
case 7:
config |= SII164_DESKEW_8_STEP;
break;
}
i2cWriteReg(SII164_I2C_ADDRESS, SII164_DESKEW, config);
/* Enable/Disable Continuous Sync. */
if (continuousSyncEnable == 0)
config = SII164_PLL_FILTER_SYNC_CONTINUOUS_DISABLE;
else
config = SII164_PLL_FILTER_SYNC_CONTINUOUS_ENABLE;
/* Enable/Disable PLL Filter */
if (pllFilterEnable == 0)
config |= SII164_PLL_FILTER_DISABLE;
else
config |= SII164_PLL_FILTER_ENABLE;
/* Set the PLL Filter value */
config |= ((pllFilterValue & 0x07) << 1);
i2cWriteReg(SII164_I2C_ADDRESS, SII164_PLL, config);
/* Recover from Power Down and enable output. */
config = i2cReadReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION);
config |= SII164_CONFIGURATION_POWER_NORMAL;
i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
#ifdef DDKDEBUG
//sii164PrintRegisterValues();
#endif
return 0;
}
/* Return -1 if initialization fails. */
return (-1);
}
/* below sii164 function is not neccessary */
#ifdef SII164_FULL_FUNCTIONS
/*
* sii164ResetChip
* This function resets the DVI Controller Chip.
*/
void sii164ResetChip()
{
/* Power down */
sii164SetPower(0);
sii164SetPower(1);
}
/*
* sii164GetChipString
* This function returns a char string name of the current DVI Controller chip.
* It's convenient for application need to display the chip name.
*/
char *sii164GetChipString()
{
return gDviCtrlChipName;
}
/*
* sii164SetPower
* This function sets the power configuration of the DVI Controller Chip.
*
* Input:
* powerUp - Flag to set the power down or up
*/
void sii164SetPower(
unsigned char powerUp
)
{
unsigned char config;
config = i2cReadReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION);
if (powerUp == 1)
{
/* Power up the chip */
config &= ~SII164_CONFIGURATION_POWER_MASK;
config |= SII164_CONFIGURATION_POWER_NORMAL;
i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
}
else
{
/* Power down the chip */
config &= ~SII164_CONFIGURATION_POWER_MASK;
config |= SII164_CONFIGURATION_POWER_DOWN;
i2cWriteReg(SII164_I2C_ADDRESS, SII164_CONFIGURATION, config);
}
}
/*
* sii164SelectHotPlugDetectionMode
* This function selects the mode of the hot plug detection.
*/
static void sii164SelectHotPlugDetectionMode(
sii164_hot_plug_mode_t hotPlugMode
)
{
unsigned char detectReg;
detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT) & ~SII164_DETECT_MONITOR_SENSE_OUTPUT_FLAG;
switch (hotPlugMode)
{
case SII164_HOTPLUG_DISABLE:
detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_HIGH;
break;
case SII164_HOTPLUG_USE_MDI:
detectReg &= ~SII164_DETECT_INTERRUPT_MASK;
detectReg |= SII164_DETECT_INTERRUPT_BY_HTPLG_PIN;
detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_MDI;
break;
case SII164_HOTPLUG_USE_RSEN:
detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_RSEN;
break;
case SII164_HOTPLUG_USE_HTPLG:
detectReg |= SII164_DETECT_MONITOR_SENSE_OUTPUT_HTPLG;
break;
}
i2cWriteReg(SII164_I2C_ADDRESS, SII164_DETECT, detectReg);
}
/*
* sii164EnableHotPlugDetection
* This function enables the Hot Plug detection.
*
* enableHotPlug - Enable (=1) / disable (=0) Hot Plug detection
*/
void sii164EnableHotPlugDetection(
unsigned char enableHotPlug
)
{
unsigned char detectReg;
detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT);
/* Depending on each DVI controller, need to enable the hot plug based on each
individual chip design. */
if (enableHotPlug != 0)
sii164SelectHotPlugDetectionMode(SII164_HOTPLUG_USE_MDI);
else
sii164SelectHotPlugDetectionMode(SII164_HOTPLUG_DISABLE);
}
/*
* sii164IsConnected
* Check if the DVI Monitor is connected.
*
* Output:
* 0 - Not Connected
* 1 - Connected
*/
unsigned char sii164IsConnected()
{
unsigned char hotPlugValue;
hotPlugValue = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT) & SII164_DETECT_HOT_PLUG_STATUS_MASK;
if (hotPlugValue == SII164_DETECT_HOT_PLUG_STATUS_ON)
return 1;
else
return 0;
}
/*
* sii164CheckInterrupt
* Checks if interrupt has occured.
*
* Output:
* 0 - No interrupt
* 1 - Interrupt occurs
*/
unsigned char sii164CheckInterrupt()
{
unsigned char detectReg;
detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT) & SII164_DETECT_MONITOR_STATE_MASK;
if (detectReg == SII164_DETECT_MONITOR_STATE_CHANGE)
return 1;
else
return 0;
}
/*
* sii164ClearInterrupt
* Clear the hot plug interrupt.
*/
void sii164ClearInterrupt()
{
unsigned char detectReg;
/* Clear the MDI interrupt */
detectReg = i2cReadReg(SII164_I2C_ADDRESS, SII164_DETECT);
i2cWriteReg(SII164_I2C_ADDRESS, SII164_DETECT, detectReg | SII164_DETECT_MONITOR_STATE_CLEAR);
}
#endif
#endif
#ifndef DDK750_SII164_H__
#define DDK750_SII164_H__
#define USE_DVICHIP
/* Hot Plug detection mode structure */
typedef enum _sii164_hot_plug_mode_t
{
SII164_HOTPLUG_DISABLE = 0, /* Disable Hot Plug output bit (always high). */
SII164_HOTPLUG_USE_MDI, /* Use Monitor Detect Interrupt bit. */
SII164_HOTPLUG_USE_RSEN, /* Use Receiver Sense detect bit. */
SII164_HOTPLUG_USE_HTPLG /* Use Hot Plug detect bit. */
} sii164_hot_plug_mode_t;
/* Silicon Image SiI164 chip prototype */
long sii164InitChip(
unsigned char edgeSelect,
unsigned char busSelect,
unsigned char dualEdgeClkSelect,
unsigned char hsyncEnable,
unsigned char vsyncEnable,
unsigned char deskewEnable,
unsigned char deskewSetting,
unsigned char continuousSyncEnable,
unsigned char pllFilterEnable,
unsigned char pllFilterValue
);
unsigned short sii164GetVendorID(void);
unsigned short sii164GetDeviceID(void);
#ifdef SII164_FULL_FUNCTIONS
void sii164ResetChip(void);
char *sii164GetChipString(void);
void sii164SetPower(unsigned char powerUp);
void sii164EnableHotPlugDetection(unsigned char enableHotPlug);
unsigned char sii164IsConnected(void);
unsigned char sii164CheckInterrupt(void);
void sii164ClearInterrupt(void);
#endif
/* below register definination is used for Silicon Image SiI164 DVI controller chip */
/*
* Vendor ID registers
*/
#define SII164_VENDOR_ID_LOW 0x00
#define SII164_VENDOR_ID_HIGH 0x01
/*
* Device ID registers
*/
#define SII164_DEVICE_ID_LOW 0x02
#define SII164_DEVICE_ID_HIGH 0x03
/*
* Device Revision
*/
#define SII164_DEVICE_REVISION 0x04
/*
* Frequency Limitation registers
*/
#define SII164_FREQUENCY_LIMIT_LOW 0x06
#define SII164_FREQUENCY_LIMIT_HIGH 0x07
/*
* Power Down and Input Signal Configuration registers
*/
#define SII164_CONFIGURATION 0x08
/* Power down (PD) */
#define SII164_CONFIGURATION_POWER_DOWN 0x00
#define SII164_CONFIGURATION_POWER_NORMAL 0x01
#define SII164_CONFIGURATION_POWER_MASK 0x01
/* Input Edge Latch Select (EDGE) */
#define SII164_CONFIGURATION_LATCH_FALLING 0x00
#define SII164_CONFIGURATION_LATCH_RISING 0x02
/* Bus Select (BSEL) */
#define SII164_CONFIGURATION_BUS_12BITS 0x00
#define SII164_CONFIGURATION_BUS_24BITS 0x04
/* Dual Edge Clock Select (DSEL) */
#define SII164_CONFIGURATION_CLOCK_SINGLE 0x00
#define SII164_CONFIGURATION_CLOCK_DUAL 0x08
/* Horizontal Sync Enable (HEN) */
#define SII164_CONFIGURATION_HSYNC_FORCE_LOW 0x00
#define SII164_CONFIGURATION_HSYNC_AS_IS 0x10
/* Vertical Sync Enable (VEN) */
#define SII164_CONFIGURATION_VSYNC_FORCE_LOW 0x00
#define SII164_CONFIGURATION_VSYNC_AS_IS 0x20
/*
* Detection registers
*/
#define SII164_DETECT 0x09
/* Monitor Detect Interrupt (MDI) */
#define SII164_DETECT_MONITOR_STATE_CHANGE 0x00
#define SII164_DETECT_MONITOR_STATE_NO_CHANGE 0x01
#define SII164_DETECT_MONITOR_STATE_CLEAR 0x01
#define SII164_DETECT_MONITOR_STATE_MASK 0x01
/* Hot Plug detect Input (HTPLG) */
#define SII164_DETECT_HOT_PLUG_STATUS_OFF 0x00
#define SII164_DETECT_HOT_PLUG_STATUS_ON 0x02
#define SII164_DETECT_HOT_PLUG_STATUS_MASK 0x02
/* Receiver Sense (RSEN) */
#define SII164_DETECT_RECEIVER_SENSE_NOT_DETECTED 0x00
#define SII164_DETECT_RECEIVER_SENSE_DETECTED 0x04
/* Interrupt Generation Method (TSEL) */
#define SII164_DETECT_INTERRUPT_BY_RSEN_PIN 0x00
#define SII164_DETECT_INTERRUPT_BY_HTPLG_PIN 0x08
#define SII164_DETECT_INTERRUPT_MASK 0x08
/* Monitor Sense Output (MSEN) */
#define SII164_DETECT_MONITOR_SENSE_OUTPUT_HIGH 0x00
#define SII164_DETECT_MONITOR_SENSE_OUTPUT_MDI 0x10
#define SII164_DETECT_MONITOR_SENSE_OUTPUT_RSEN 0x20
#define SII164_DETECT_MONITOR_SENSE_OUTPUT_HTPLG 0x30
#define SII164_DETECT_MONITOR_SENSE_OUTPUT_FLAG 0x30
/*
* Skewing registers
*/
#define SII164_DESKEW 0x0A
/* General Purpose Input (CTL[3:1]) */
#define SII164_DESKEW_GENERAL_PURPOSE_INPUT_MASK 0x0E
/* De-skewing Enable bit (DKEN) */
#define SII164_DESKEW_DISABLE 0x00
#define SII164_DESKEW_ENABLE 0x10
/* De-skewing Setting (DK[3:1])*/
#define SII164_DESKEW_1_STEP 0x00
#define SII164_DESKEW_2_STEP 0x20
#define SII164_DESKEW_3_STEP 0x40
#define SII164_DESKEW_4_STEP 0x60
#define SII164_DESKEW_5_STEP 0x80
#define SII164_DESKEW_6_STEP 0xA0
#define SII164_DESKEW_7_STEP 0xC0
#define SII164_DESKEW_8_STEP 0xE0
/*
* User Configuration Data registers (CFG 7:0)
*/
#define SII164_USER_CONFIGURATION 0x0B
/*
* PLL registers
*/
#define SII164_PLL 0x0C
/* PLL Filter Value (PLLF) */
#define SII164_PLL_FILTER_VALUE_MASK 0x0E
/* PLL Filter Enable (PFEN) */
#define SII164_PLL_FILTER_DISABLE 0x00
#define SII164_PLL_FILTER_ENABLE 0x01
/* Sync Continuous (SCNT) */
#define SII164_PLL_FILTER_SYNC_CONTINUOUS_DISABLE 0x00
#define SII164_PLL_FILTER_SYNC_CONTINUOUS_ENABLE 0x80
#endif
/*******************************************************************
*
* Copyright (c) 2007 by Silicon Motion, Inc. (SMI)
*
* All rights are reserved. Reproduction or in part is prohibited
* without the written consent of the copyright owner.
*
* swi2c.c --- SM750/SM718 DDK
* This file contains the source code for I2C using software
* implementation.
*
*******************************************************************/
#include "ddk750_help.h"
#include "ddk750_reg.h"
#include "ddk750_swi2c.h"
#include "ddk750_power.h"
/*******************************************************************
* I2C Software Master Driver:
* ===========================
* Each i2c cycle is split into 4 sections. Each of these section marks
* a point in time where the SCL or SDA may be changed.
*
* 1 Cycle == | Section I. | Section 2. | Section 3. | Section 4. |
* +-------------+-------------+-------------+-------------+
* | SCL set LOW |SCL no change| SCL set HIGH|SCL no change|
*
* ____________ _____________
* SCL == XXXX _____________ ____________ /
*
* I.e. the SCL may only be changed in section 1. and section 3. while
* the SDA may only be changed in section 2. and section 4. The table
* below gives the changes for these 2 lines in the varios sections.
*
* Section changes Table:
* ======================
* blank = no change, L = set bit LOW, H = set bit HIGH
*
* | 1.| 2.| 3.| 4.|
* ---------------+---+---+---+---+
* Tx Start SDA | | H | | L |
* SCL | L | | H | |
* ---------------+---+---+---+---+
* Tx Stop SDA | | L | | H |
* SCL | L | | H | |
* ---------------+---+---+---+---+
* Tx bit H SDA | | H | | |
* SCL | L | | H | |
* ---------------+---+---+---+---+
* Tx bit L SDA | | L | | |
* SCL | L | | H | |
* ---------------+---+---+---+---+
*
******************************************************************/
/* GPIO pins used for this I2C. It ranges from 0 to 63. */
static unsigned char g_i2cClockGPIO = DEFAULT_I2C_SCL;
static unsigned char g_i2cDataGPIO = DEFAULT_I2C_SDA;
/*
* Below is the variable declaration for the GPIO pin register usage
* for the i2c Clock and i2c Data.
*
* Note:
* Notice that the GPIO usage for the i2c clock and i2c Data are
* separated. This is to make this code flexible enough when
* two separate GPIO pins for the clock and data are located
* in two different GPIO register set (worst case).
*/
/* i2c Clock GPIO Register usage */
static unsigned long g_i2cClkGPIOMuxReg = GPIO_MUX;
static unsigned long g_i2cClkGPIODataReg = GPIO_DATA;
static unsigned long g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION;
/* i2c Data GPIO Register usage */
static unsigned long g_i2cDataGPIOMuxReg = GPIO_MUX;
static unsigned long g_i2cDataGPIODataReg = GPIO_DATA;
static unsigned long g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION;
static unsigned char peekIO(unsigned short port,unsigned short index)
{
#if defined(__i386__) || defined( __x86_64__)
outb_p(index,port);
return inb_p(port+1);
#endif
}
/*
* This function puts a delay between command
*/
static void swI2CWait(void)
{
/* find a bug:
* peekIO method works well before suspend/resume
* but after suspend, peekIO(0x3ce,0x61) & 0x10
* always be non-zero,which makes the while loop
* never finish.
* use non-ultimate for loop below is safe
* */
#if 0
/* Change wait algorithm to use PCI bus clock,
it's more reliable than counter loop ..
write 0x61 to 0x3ce and read from 0x3cf
*/
while(peekIO(0x3ce,0x61) & 0x10);
#else
int i, Temp;
for(i=0; i<600; i++)
{
Temp = i;
Temp += i;
}
#endif
}
/*
* This function set/reset the SCL GPIO pin
*
* Parameters:
* value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
*
* Notes:
* When setting SCL to high, just set the GPIO as input where the pull up
* resistor will pull the signal up. Do not use software to pull up the
* signal because the i2c will fail when other device try to drive the
* signal due to SM50x will drive the signal to always high.
*/
void swI2CSCL(unsigned char value)
{
unsigned long ulGPIOData;
unsigned long ulGPIODirection;
ulGPIODirection = PEEK32(g_i2cClkGPIODataDirReg);
if (value) /* High */
{
/* Set direction as input. This will automatically pull the signal up. */
ulGPIODirection &= ~(1 << g_i2cClockGPIO);
POKE32(g_i2cClkGPIODataDirReg, ulGPIODirection);
}
else /* Low */
{
/* Set the signal down */
ulGPIOData = PEEK32(g_i2cClkGPIODataReg);
ulGPIOData &= ~(1 << g_i2cClockGPIO);
POKE32(g_i2cClkGPIODataReg, ulGPIOData);
/* Set direction as output */
ulGPIODirection |= (1 << g_i2cClockGPIO);
POKE32(g_i2cClkGPIODataDirReg, ulGPIODirection);
}
}
/*
* This function set/reset the SDA GPIO pin
*
* Parameters:
* value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
*
* Notes:
* When setting SCL to high, just set the GPIO as input where the pull up
* resistor will pull the signal up. Do not use software to pull up the
* signal because the i2c will fail when other device try to drive the
* signal due to SM50x will drive the signal to always high.
*/
void swI2CSDA(unsigned char value)
{
unsigned long ulGPIOData;
unsigned long ulGPIODirection;
ulGPIODirection = PEEK32(g_i2cDataGPIODataDirReg);
if (value) /* High */
{
/* Set direction as input. This will automatically pull the signal up. */
ulGPIODirection &= ~(1 << g_i2cDataGPIO);
POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection);
}
else /* Low */
{
/* Set the signal down */
ulGPIOData = PEEK32(g_i2cDataGPIODataReg);
ulGPIOData &= ~(1 << g_i2cDataGPIO);
POKE32(g_i2cDataGPIODataReg, ulGPIOData);
/* Set direction as output */
ulGPIODirection |= (1 << g_i2cDataGPIO);
POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection);
}
}
/*
* This function read the data from the SDA GPIO pin
*
* Return Value:
* The SDA data bit sent by the Slave
*/
static unsigned char swI2CReadSDA(void)
{
unsigned long ulGPIODirection;
unsigned long ulGPIOData;
/* Make sure that the direction is input (High) */
ulGPIODirection = PEEK32(g_i2cDataGPIODataDirReg);
if ((ulGPIODirection & (1 << g_i2cDataGPIO)) != (~(1 << g_i2cDataGPIO)))
{
ulGPIODirection &= ~(1 << g_i2cDataGPIO);
POKE32(g_i2cDataGPIODataDirReg, ulGPIODirection);
}
/* Now read the SDA line */
ulGPIOData = PEEK32(g_i2cDataGPIODataReg);
if (ulGPIOData & (1 << g_i2cDataGPIO))
return 1;
else
return 0;
}
#pragma optimize( "", off )
/*
* This function sends ACK signal
*/
static void swI2CAck(void)
{
return; /* Single byte read is ok without it. */
}
/*
* This function sends the start command to the slave device
*/
void swI2CStart(void)
{
/* Start I2C */
swI2CSDA(1);
swI2CSCL(1);
swI2CSDA(0);
}
/*
* This function sends the stop command to the slave device
*/
void swI2CStop(void)
{
/* Stop the I2C */
swI2CSCL(1);
swI2CSDA(0);
swI2CSDA(1);
}
/*
* This function writes one byte to the slave device
*
* Parameters:
* data - Data to be write to the slave device
*
* Return Value:
* 0 - Success
* -1 - Fail to write byte
*/
long swI2CWriteByte(unsigned char data)
{
unsigned char value = data;
int i;
/* Sending the data bit by bit */
for (i=0; i<8; i++)
{
/* Set SCL to low */
swI2CSCL(0);
/* Send data bit */
if ((value & 0x80) != 0)
swI2CSDA(1);
else
swI2CSDA(0);
swI2CWait();
/* Toggle clk line to one */
swI2CSCL(1);
swI2CWait();
/* Shift byte to be sent */
value = value << 1;
}
/* Set the SCL Low and SDA High (prepare to get input) */
swI2CSCL(0);
swI2CSDA(1);
/* Set the SCL High for ack */
swI2CWait();
swI2CSCL(1);
swI2CWait();
/* Read SDA, until SDA==0 */
for(i=0; i<0xff; i++)
{
if (!swI2CReadSDA())
break;
swI2CSCL(0);
swI2CWait();
swI2CSCL(1);
swI2CWait();
}
/* Set the SCL Low and SDA High */
swI2CSCL(0);
swI2CSDA(1);
if (i<0xff)
return 0;
else
return (-1);
}
/*
* This function reads one byte from the slave device
*
* Parameters:
* ack - Flag to indicate either to send the acknowledge
* message to the slave device or not
*
* Return Value:
* One byte data read from the Slave device
*/
unsigned char swI2CReadByte(unsigned char ack)
{
int i;
unsigned char data = 0;
for(i=7; i>=0; i--)
{
/* Set the SCL to Low and SDA to High (Input) */
swI2CSCL(0);
swI2CSDA(1);
swI2CWait();
/* Set the SCL High */
swI2CSCL(1);
swI2CWait();
/* Read data bits from SDA */
data |= (swI2CReadSDA() << i);
}
if (ack)
swI2CAck();
/* Set the SCL Low and SDA High */
swI2CSCL(0);
swI2CSDA(1);
return data;
}
#pragma optimize( "", on )
/*
* This function initializes GPIO port for SW I2C communication.
*
* Parameters:
* i2cClkGPIO - The GPIO pin to be used as i2c SCL
* i2cDataGPIO - The GPIO pin to be used as i2c SDA
*
* Return Value:
* -1 - Fail to initialize the i2c
* 0 - Success
*/
long swI2CInit_SM750LE(
unsigned char i2cClkGPIO,
unsigned char i2cDataGPIO
)
{
int i;
/* Initialize the GPIO pin for the i2c Clock Register */
g_i2cClkGPIODataReg = GPIO_DATA_SM750LE;
g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION_SM750LE;
/* Initialize the Clock GPIO Offset */
g_i2cClockGPIO = i2cClkGPIO;
/* Initialize the GPIO pin for the i2c Data Register */
g_i2cDataGPIODataReg = GPIO_DATA_SM750LE;
g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION_SM750LE;
/* Initialize the Data GPIO Offset */
g_i2cDataGPIO = i2cDataGPIO;
/* Note that SM750LE don't have GPIO MUX and power is always on */
/* Clear the i2c lines. */
for(i=0; i<9; i++)
swI2CStop();
return 0;
}
/*
* This function initializes the i2c attributes and bus
*
* Parameters:
* i2cClkGPIO - The GPIO pin to be used as i2c SCL
* i2cDataGPIO - The GPIO pin to be used as i2c SDA
*
* Return Value:
* -1 - Fail to initialize the i2c
* 0 - Success
*/
long swI2CInit(
unsigned char i2cClkGPIO,
unsigned char i2cDataGPIO
)
{
int i;
/* Return 0 if the GPIO pins to be used is out of range. The range is only from [0..63] */
if ((i2cClkGPIO > 31) || (i2cDataGPIO > 31))
return (-1);
if (getChipType() == SM750LE)
return( swI2CInit_SM750LE(i2cClkGPIO, i2cDataGPIO) );
/* Initialize the GPIO pin for the i2c Clock Register */
g_i2cClkGPIOMuxReg = GPIO_MUX;
g_i2cClkGPIODataReg = GPIO_DATA;
g_i2cClkGPIODataDirReg = GPIO_DATA_DIRECTION;
/* Initialize the Clock GPIO Offset */
g_i2cClockGPIO = i2cClkGPIO;
/* Initialize the GPIO pin for the i2c Data Register */
g_i2cDataGPIOMuxReg = GPIO_MUX;
g_i2cDataGPIODataReg = GPIO_DATA;
g_i2cDataGPIODataDirReg = GPIO_DATA_DIRECTION;
/* Initialize the Data GPIO Offset */
g_i2cDataGPIO = i2cDataGPIO;
/* Enable the GPIO pins for the i2c Clock and Data (GPIO MUX) */
POKE32(g_i2cClkGPIOMuxReg,
PEEK32(g_i2cClkGPIOMuxReg) & ~(1 << g_i2cClockGPIO));
POKE32(g_i2cDataGPIOMuxReg,
PEEK32(g_i2cDataGPIOMuxReg) & ~(1 << g_i2cDataGPIO));
/* Enable GPIO power */
enableGPIO(1);
/* Clear the i2c lines. */
for(i=0; i<9; i++)
swI2CStop();
return 0;
}
/*
* This function reads the slave device's register
*
* Parameters:
* deviceAddress - i2c Slave device address which register
* to be read from
* registerIndex - Slave device's register to be read
*
* Return Value:
* Register value
*/
unsigned char swI2CReadReg(
unsigned char deviceAddress,
unsigned char registerIndex
)
{
unsigned char data;
/* Send the Start signal */
swI2CStart();
/* Send the device address */
swI2CWriteByte(deviceAddress);
/* Send the register index */
swI2CWriteByte(registerIndex);
/* Get the bus again and get the data from the device read address */
swI2CStart();
swI2CWriteByte(deviceAddress + 1);
data = swI2CReadByte(1);
/* Stop swI2C and release the bus */
swI2CStop();
return data;
}
/*
* This function writes a value to the slave device's register
*
* Parameters:
* deviceAddress - i2c Slave device address which register
* to be written
* registerIndex - Slave device's register to be written
* data - Data to be written to the register
*
* Result:
* 0 - Success
* -1 - Fail
*/
long swI2CWriteReg(
unsigned char deviceAddress,
unsigned char registerIndex,
unsigned char data
)
{
long returnValue = 0;
/* Send the Start signal */
swI2CStart();
/* Send the device address and read the data. All should return success
in order for the writing processed to be successful
*/
if ((swI2CWriteByte(deviceAddress) != 0) ||
(swI2CWriteByte(registerIndex) != 0) ||
(swI2CWriteByte(data) != 0))
{
returnValue = -1;
}
/* Stop i2c and release the bus */
swI2CStop();
return returnValue;
}
/*******************************************************************
*
* Copyright (c) 2007 by Silicon Motion, Inc. (SMI)
*
* All rights are reserved. Reproduction or in part is prohibited
* without the written consent of the copyright owner.
*
* swi2c.h --- SM750/SM718 DDK
* This file contains the definitions for i2c using software
* implementation.
*
*******************************************************************/
#ifndef _SWI2C_H_
#define _SWI2C_H_
/* Default i2c CLK and Data GPIO. These are the default i2c pins */
#define DEFAULT_I2C_SCL 30
#define DEFAULT_I2C_SDA 31
/*
* This function initializes the i2c attributes and bus
*
* Parameters:
* i2cClkGPIO - The GPIO pin to be used as i2c SCL
* i2cDataGPIO - The GPIO pin to be used as i2c SDA
*
* Return Value:
* -1 - Fail to initialize the i2c
* 0 - Success
*/
long swI2CInit(
unsigned char i2cClkGPIO,
unsigned char i2cDataGPIO
);
/*
* This function reads the slave device's register
*
* Parameters:
* deviceAddress - i2c Slave device address which register
* to be read from
* registerIndex - Slave device's register to be read
*
* Return Value:
* Register value
*/
unsigned char swI2CReadReg(
unsigned char deviceAddress,
unsigned char registerIndex
);
/*
* This function writes a value to the slave device's register
*
* Parameters:
* deviceAddress - i2c Slave device address which register
* to be written
* registerIndex - Slave device's register to be written
* data - Data to be written to the register
*
* Result:
* 0 - Success
* -1 - Fail
*/
long swI2CWriteReg(
unsigned char deviceAddress,
unsigned char registerIndex,
unsigned char data
);
/*
* These two functions are used to toggle the data on the SCL and SDA I2C lines.
* The used of these two functions are not recommended unless it is necessary.
*/
/*
* This function set/reset the SCL GPIO pin
*
* Parameters:
* value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
*/
void swI2CSCL(unsigned char value);
/*
* This function set/reset the SDA GPIO pin
*
* Parameters:
* value - Bit value to set to the SCL or SDA (0 = low, 1 = high)
*/
void swI2CSDA(unsigned char value);
#endif /* _SWI2C_H_ */
static const struct fb_videomode modedb2[] = {
{
/* 640x400 @ 70 Hz, 31.5 kHz hsync */
NULL, 70, 640, 400, 39721, 40, 24, 39, 9, 96, 2,
0, FB_VMODE_NONINTERLACED
}, {
/* 640x480 @ 60 Hz, 31.5 kHz hsync */
NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
0, FB_VMODE_NONINTERLACED
}, {
/* 800x600 @ 56 Hz, 35.15 kHz hsync */
NULL, 56, 800, 600, 27777, 128, 24, 22, 1, 72, 2,
0, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 87 Hz interlaced, 35.5 kHz hsync */
NULL, 87, 1024, 768, 22271, 56, 24, 33, 8, 160, 8,
0, FB_VMODE_INTERLACED
}, {
/* 640x400 @ 85 Hz, 37.86 kHz hsync */
NULL, 85, 640, 400, 31746, 96, 32, 41, 1, 64, 3,
FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 640x480 @ 72 Hz, 36.5 kHz hsync */
NULL, 72, 640, 480, 31746, 144, 40, 30, 8, 40, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 640x480 @ 75 Hz, 37.50 kHz hsync */
NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 800x600 @ 60 Hz, 37.8 kHz hsync */
NULL, 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 640x480 @ 85 Hz, 43.27 kHz hsync */
NULL, 85, 640, 480, 27777, 80, 56, 25, 1, 56, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1152x864 @ 89 Hz interlaced, 44 kHz hsync */
NULL, 69, 1152, 864, 15384, 96, 16, 110, 1, 216, 10,
0, FB_VMODE_INTERLACED
}, {
/* 800x600 @ 72 Hz, 48.0 kHz hsync */
NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 60 Hz, 48.4 kHz hsync */
NULL, 60, 1024, 768, 15384, 168, 8, 29, 3, 144, 6,
0, FB_VMODE_NONINTERLACED
}, {
/* 640x480 @ 100 Hz, 53.01 kHz hsync */
NULL, 100, 640, 480, 21834, 96, 32, 36, 8, 96, 6,
0, FB_VMODE_NONINTERLACED
}, {
/* 1152x864 @ 60 Hz, 53.5 kHz hsync */
NULL, 60, 1152, 864, 11123, 208, 64, 16, 4, 256, 8,
0, FB_VMODE_NONINTERLACED
}, {
/* 800x600 @ 85 Hz, 55.84 kHz hsync */
NULL, 85, 800, 600, 16460, 160, 64, 36, 16, 64, 5,
0, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 70 Hz, 56.5 kHz hsync */
NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6,
0, FB_VMODE_NONINTERLACED
}, {
/* 1280x960-60 VESA */
NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
}, {
/* 1280x1024-60 VESA */
NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA
}, {
/* 1280x1024 @ 87 Hz interlaced, 51 kHz hsync */
NULL, 87, 1280, 1024, 12500, 56, 16, 128, 1, 216, 12,
0, FB_VMODE_INTERLACED
}, {
/* 800x600 @ 100 Hz, 64.02 kHz hsync */
NULL, 100, 800, 600, 14357, 160, 64, 30, 4, 64, 6,
0, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 76 Hz, 62.5 kHz hsync */
NULL, 76, 1024, 768, 11764, 208, 8, 36, 16, 120, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1152x864 @ 70 Hz, 62.4 kHz hsync */
NULL, 70, 1152, 864, 10869, 106, 56, 20, 1, 160, 10,
0, FB_VMODE_NONINTERLACED
}, {
/* 1280x1024 @ 61 Hz, 64.2 kHz hsync */
NULL, 61, 1280, 1024, 9090, 200, 48, 26, 1, 184, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1400x1050 @ 60Hz, 63.9 kHz hsync */
NULL, 68, 1400, 1050, 9259, 136, 40, 13, 1, 112, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1400x1050 @ 75,107 Hz, 82,392 kHz +hsync +vsync*/
NULL, 75, 1400, 1050, 9271, 120, 56, 13, 0, 112, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1400x1050 @ 60 Hz, ? kHz +hsync +vsync*/
NULL, 60, 1400, 1050, 9259, 128, 40, 12, 0, 112, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 85 Hz, 70.24 kHz hsync */
NULL, 85, 1024, 768, 10111, 192, 32, 34, 14, 160, 6,
0, FB_VMODE_NONINTERLACED
}, {
/* 1152x864 @ 78 Hz, 70.8 kHz hsync */
NULL, 78, 1152, 864, 9090, 228, 88, 32, 0, 84, 12,
0, FB_VMODE_NONINTERLACED
}, {
/* 1280x1024 @ 70 Hz, 74.59 kHz hsync */
NULL, 70, 1280, 1024, 7905, 224, 32, 28, 8, 160, 8,
0, FB_VMODE_NONINTERLACED
}, {
/* 1600x1200 @ 60Hz, 75.00 kHz hsync */
NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1152x864 @ 84 Hz, 76.0 kHz hsync */
NULL, 84, 1152, 864, 7407, 184, 312, 32, 0, 128, 12,
0, FB_VMODE_NONINTERLACED
}, {
/* 1280x1024 @ 74 Hz, 78.85 kHz hsync */
NULL, 74, 1280, 1024, 7407, 256, 32, 34, 3, 144, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1024x768 @ 100Hz, 80.21 kHz hsync */
NULL, 100, 1024, 768, 8658, 192, 32, 21, 3, 192, 10,
0, FB_VMODE_NONINTERLACED
}, {
/* 1280x1024 @ 76 Hz, 81.13 kHz hsync */
NULL, 76, 1280, 1024, 7407, 248, 32, 34, 3, 104, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1600x1200 @ 70 Hz, 87.50 kHz hsync */
NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 1152x864 @ 100 Hz, 89.62 kHz hsync */
NULL, 100, 1152, 864, 7264, 224, 32, 17, 2, 128, 19,
0, FB_VMODE_NONINTERLACED
}, {
/* 1280x1024 @ 85 Hz, 91.15 kHz hsync */
NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1600x1200 @ 75 Hz, 93.75 kHz hsync */
NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1600x1200 @ 85 Hz, 105.77 kHz hsync */
NULL, 85, 1600, 1200, 4545, 272, 16, 37, 4, 192, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1280x1024 @ 100 Hz, 107.16 kHz hsync */
NULL, 100, 1280, 1024, 5502, 256, 32, 26, 7, 128, 15,
0, FB_VMODE_NONINTERLACED
}, {
/* 1800x1440 @ 64Hz, 96.15 kHz hsync */
NULL, 64, 1800, 1440, 4347, 304, 96, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 1800x1440 @ 70Hz, 104.52 kHz hsync */
NULL, 70, 1800, 1440, 4000, 304, 96, 46, 1, 192, 3,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
}, {
/* 512x384 @ 78 Hz, 31.50 kHz hsync */
NULL, 78, 512, 384, 49603, 48, 16, 16, 1, 64, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 512x384 @ 85 Hz, 34.38 kHz hsync */
NULL, 85, 512, 384, 45454, 48, 16, 16, 1, 64, 3,
0, FB_VMODE_NONINTERLACED
}, {
/* 320x200 @ 70 Hz, 31.5 kHz hsync, 8:5 aspect ratio */
NULL, 70, 320, 200, 79440, 16, 16, 20, 4, 48, 1,
0, FB_VMODE_DOUBLE
}, {
/* 320x240 @ 60 Hz, 31.5 kHz hsync, 4:3 aspect ratio */
NULL, 60, 320, 240, 79440, 16, 16, 16, 5, 48, 1,
0, FB_VMODE_DOUBLE
}, {
/* 320x240 @ 72 Hz, 36.5 kHz hsync */
NULL, 72, 320, 240, 63492, 16, 16, 16, 4, 48, 2,
0, FB_VMODE_DOUBLE
}, {
/* 400x300 @ 56 Hz, 35.2 kHz hsync, 4:3 aspect ratio */
NULL, 56, 400, 300, 55555, 64, 16, 10, 1, 32, 1,
0, FB_VMODE_DOUBLE
}, {
/* 400x300 @ 60 Hz, 37.8 kHz hsync */
NULL, 60, 400, 300, 50000, 48, 16, 11, 1, 64, 2,
0, FB_VMODE_DOUBLE
}, {
/* 400x300 @ 72 Hz, 48.0 kHz hsync */
NULL, 72, 400, 300, 40000, 32, 24, 11, 19, 64, 3,
0, FB_VMODE_DOUBLE
}, {
/* 480x300 @ 56 Hz, 35.2 kHz hsync, 8:5 aspect ratio */
NULL, 56, 480, 300, 46176, 80, 16, 10, 1, 40, 1,
0, FB_VMODE_DOUBLE
}, {
/* 480x300 @ 60 Hz, 37.8 kHz hsync */
NULL, 60, 480, 300, 41858, 56, 16, 11, 1, 80, 2,
0, FB_VMODE_DOUBLE
}, {
/* 480x300 @ 63 Hz, 39.6 kHz hsync */
NULL, 63, 480, 300, 40000, 56, 16, 11, 1, 80, 2,
0, FB_VMODE_DOUBLE
}, {
/* 480x300 @ 72 Hz, 48.0 kHz hsync */
NULL, 72, 480, 300, 33386, 40, 24, 11, 19, 80, 3,
0, FB_VMODE_DOUBLE
},
};
static const int nmodedb2 = sizeof(modedb2);
Introduction:
SM750 of Silicon MOtion is pci express display controller device.
The SM750 embedded graphics features include:
- dual display
- 2D acceleration
- 16MB integrated video memory
About the kernel module paramter of driver:
Use 1280,8bpp index color and 60 hz mode:
insmod ./sm750fb.ko g_option="1280x1024-8@60"
Disable MTRR,Disable 2d acceleration,Disable hardware cursor,
and use a 800x600 mode :
insmod ./sm750fb.ko g_option="noaccel:nomtrr:nohwc:800x600"
dual frame buffer for driver with "dual" parameter
insmod ./sm750fb.ko g_option="dual,800x600:1024x768"
it will create fb0 and fb1 (or fb1,fb2 if fb0 already exist) under /dev
and user can use con2fb to link fbX and ttyX
Notes:
1) if you build the driver with built-in method, the paramter
you edited in the grub config file will be also the
same format as above modular method,but additionaly add
"video=sm750fb:"
ahead of parameters,so,it looks like:
video=sm750fb:noaccel,1280x1024@60,otherparam,etc...
it equal to modular method with below command:
insmod ./sm750fb.ko g_option="noaccel:1280x1024@60:otherparm:etc..."
2) if you put 800x600 into the paramter without bpp and
refresh rate, kernel driver will defaulty use 16bpp and 60hz
Important:
if you have vesafb enabled in your config then /dev/fb0 will be created by vesafb
and this driver will use fb1, fb2. In that case, you need to configure your X-server
to use fb1. Another simple althernative is to disable vesafb from your config.
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/errno.h>
#include<linux/string.h>
#include<linux/mm.h>
#include<linux/slab.h>
#include<linux/delay.h>
#include<linux/fb.h>
#include<linux/ioport.h>
#include<linux/init.h>
#include<linux/pci.h>
#include<linux/mm_types.h>
#include<linux/vmalloc.h>
#include<linux/pagemap.h>
#include<linux/screen_info.h>
#include<linux/vmalloc.h>
#include<linux/pagemap.h>
#include <linux/console.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
#include <asm/fb.h>
#include "sm750.h"
#include "sm750_hw.h"
#include "sm750_accel.h"
#include "sm750_cursor.h"
#include "modedb.h"
int smi_indent = 0;
/*
#ifdef __BIG_ENDIAN
ssize_t lynxfb_ops_write(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos);
ssize_t lynxfb_ops_read(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
#endif
*/
typedef void (*PROC_SPEC_SETUP)(struct lynx_share*,char *);
typedef int (*PROC_SPEC_MAP)(struct lynx_share*,struct pci_dev*);
typedef int (*PROC_SPEC_INITHW)(struct lynx_share*,struct pci_dev*);
/* common var for all device */
static int g_hwcursor = 1;
static int g_noaccel = 0;
#ifdef CONFIG_MTRR
static int g_nomtrr = 0;
#endif
static const char * g_fbmode[] = {NULL,NULL};
static const char * g_def_fbmode = "800x600-16@60";
static char * g_settings = NULL;
static int g_dualview = 0;
#ifdef MODULE
static char * g_option = NULL;
#endif
/* if not use spin_lock,system will die if user load driver
* and immediatly unload driver frequently (dual)*/
static inline void myspin_lock(spinlock_t * sl){
struct lynx_share * share;
share = container_of(sl,struct lynx_share,slock);
if(share->dual){
spin_lock(sl);
}
}
static inline void myspin_unlock(spinlock_t * sl){
struct lynx_share * share;
share = container_of(sl,struct lynx_share,slock);
if(share->dual){
spin_unlock(sl);
}
}
static const struct fb_videomode lynx750_ext[] = {
/* 1024x600-60 VESA [1.71:1] */
{NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1024x600-70 VESA */
{NULL, 70, 1024, 600, 17211, 152, 48, 21, 1, 104, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1024x600-75 VESA */
{NULL, 75, 1024, 600, 15822, 160, 56, 23, 1, 104, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1024x600-85 VESA */
{NULL, 85, 1024, 600, 13730, 168, 56, 26, 1, 112, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 720x480 */
{NULL, 60, 720, 480, 37427, 88, 16, 13, 1, 72, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1280x720 [1.78:1] */
{NULL, 60, 1280, 720, 13426, 162, 86, 22, 1, 136, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
/* 1280x768@60 */
{NULL,60,1280,768,12579,192,64,20,3,128,7,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
{NULL,60,1360,768,11804,208,64,23,1,144,3,
FB_SYNC_HOR_HIGH_ACT|FB_VMODE_NONINTERLACED},
/* 1360 x 768 [1.77083:1] */
{NULL, 60, 1360, 768, 11804, 208, 64, 23, 1, 144, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1368 x 768 [1.78:1] */
{NULL, 60, 1368, 768, 11647, 216, 72, 23, 1, 144, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1440 x 900 [16:10] */
{NULL, 60, 1440, 900, 9392, 232, 80, 28, 1, 152, 3,
FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
/* 1440x960 [15:10] */
{NULL, 60, 1440, 960, 8733, 240, 88, 30, 1, 152, 3,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
/* 1920x1080 [16:9] */
{NULL, 60, 1920, 1080, 6734, 148, 88, 41, 1, 44, 3,
FB_SYNC_VERT_HIGH_ACT,FB_VMODE_NONINTERLACED},
};
/* no hardware cursor supported under version 2.6.10, kernel bug */
static int lynxfb_ops_cursor(struct fb_info* info,struct fb_cursor* fbcursor)
{
struct lynxfb_par * par;
struct lynxfb_crtc * crtc;
struct lynx_cursor * cursor;
par = info->par;
crtc = &par->crtc;
cursor = &crtc->cursor;
if(fbcursor->image.width > cursor->maxW ||
fbcursor->image.height > cursor->maxH ||
fbcursor->image.depth > 1){
return -ENXIO;
}
cursor->disable(cursor);
if(fbcursor->set & FB_CUR_SETSIZE){
cursor->setSize(cursor,fbcursor->image.width,fbcursor->image.height);
}
if(fbcursor->set & FB_CUR_SETPOS){
cursor->setPos(cursor,fbcursor->image.dx - info->var.xoffset,
fbcursor->image.dy - info->var.yoffset);
}
if(fbcursor->set & FB_CUR_SETCMAP){
/* get the 16bit color of kernel means */
u16 fg,bg;
fg = ((info->cmap.red[fbcursor->image.fg_color] & 0xf800))|
((info->cmap.green[fbcursor->image.fg_color] & 0xfc00) >> 5)|
((info->cmap.blue[fbcursor->image.fg_color] & 0xf800) >> 11);
bg = ((info->cmap.red[fbcursor->image.bg_color] & 0xf800))|
((info->cmap.green[fbcursor->image.bg_color] & 0xfc00) >> 5)|
((info->cmap.blue[fbcursor->image.bg_color] & 0xf800) >> 11);
cursor->setColor(cursor,fg,bg);
}
if(fbcursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE))
{
cursor->setData(cursor,
fbcursor->rop,
fbcursor->image.data,
fbcursor->mask);
}
if(fbcursor->enable){
cursor->enable(cursor);
}
return 0;
}
static void lynxfb_ops_fillrect(struct fb_info* info,const struct fb_fillrect* region)
{
struct lynxfb_par * par;
struct lynx_share * share;
unsigned int base,pitch,Bpp,rop;
u32 color;
if(info->state != FBINFO_STATE_RUNNING){
return;
}
par = info->par;
share = par->share;
/* each time 2d function begin to work,below three variable always need
* be set, seems we can put them together in some place */
base = par->crtc.oScreen;
pitch = info->fix.line_length;
Bpp = info->var.bits_per_pixel >> 3;
color = (Bpp == 1)?region->color:((u32*)info->pseudo_palette)[region->color];
rop = ( region->rop != ROP_COPY ) ? HW_ROP2_XOR:HW_ROP2_COPY;
myspin_lock(&share->slock);
share->accel.de_fillrect(&share->accel,
base,pitch,Bpp,
region->dx,region->dy,
region->width,region->height,
color,rop);
myspin_unlock(&share->slock);
}
static void lynxfb_ops_copyarea(struct fb_info * info,const struct fb_copyarea * region)
{
struct lynxfb_par * par;
struct lynx_share * share;
unsigned int base,pitch,Bpp;
par = info->par;
share = par->share;
/* each time 2d function begin to work,below three variable always need
* be set, seems we can put them together in some place */
base = par->crtc.oScreen;
pitch = info->fix.line_length;
Bpp = info->var.bits_per_pixel >> 3;
myspin_lock(&share->slock);
share->accel.de_copyarea(&share->accel,
base,pitch,region->sx,region->sy,
base,pitch,Bpp,region->dx,region->dy,
region->width,region->height,HW_ROP2_COPY);
myspin_unlock(&share->slock);
}
static void lynxfb_ops_imageblit(struct fb_info*info,const struct fb_image* image)
{
unsigned int base,pitch,Bpp;
unsigned int fgcol,bgcol;
struct lynxfb_par * par;
struct lynx_share * share;
par = info->par;
share = par->share;
/* each time 2d function begin to work,below three variable always need
* be set, seems we can put them together in some place */
base = par->crtc.oScreen;
pitch = info->fix.line_length;
Bpp = info->var.bits_per_pixel >> 3;
if(image->depth == 1){
if(info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR)
{
fgcol = ((u32*)info->pseudo_palette)[image->fg_color];
bgcol = ((u32*)info->pseudo_palette)[image->bg_color];
}
else
{
fgcol = image->fg_color;
bgcol = image->bg_color;
}
goto _do_work;
}
return;
_do_work:
myspin_lock(&share->slock);
share->accel.de_imageblit(&share->accel,
image->data,image->width>>3,0,
base,pitch,Bpp,
image->dx,image->dy,
image->width,image->height,
fgcol,bgcol,HW_ROP2_COPY);
myspin_unlock(&share->slock);
}
static int lynxfb_ops_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct lynxfb_par * par;
struct lynxfb_crtc * crtc;
int ret;
if(!info)
return -EINVAL;
ret = 0;
par = info->par;
crtc = &par->crtc;
ret = crtc->proc_panDisplay(crtc, var, info);
return ret;
}
#ifdef CONFIG_PM
static int lynxfb_suspend(struct pci_dev * pdev,pm_message_t mesg)
{
struct fb_info * info;
struct lynx_share * share;
int ret;
if(mesg.event == pdev->dev.power.power_state.event)
return 0;
ret = 0;
share = pci_get_drvdata(pdev);
switch (mesg.event) {
case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
pdev->dev.power.power_state = mesg;
return 0;
}
console_lock();
if (mesg.event & PM_EVENT_SLEEP) {
info = share->fbinfo[0];
if(info)
fb_set_suspend(info, 1);/* 1 means do suspend*/
info = share->fbinfo[1];
if(info)
fb_set_suspend(info, 1);/* 1 means do suspend*/
ret = pci_save_state(pdev);
if(ret){
pr_err("error:%d occured in pci_save_state\n",ret);
return ret;
}
/* set chip to sleep mode */
if(share->suspend)
(*share->suspend)(share);
pci_disable_device(pdev);
ret = pci_set_power_state(pdev,pci_choose_state(pdev,mesg));
if(ret){
pr_err("error:%d occured in pci_set_power_state\n",ret);
return ret;
}
}
pdev->dev.power.power_state = mesg;
console_unlock();
return ret;
}
static int lynxfb_ops_set_par(struct fb_info * info)
{
struct lynxfb_par * par;
struct lynx_share * share;
struct lynxfb_crtc * crtc;
struct lynxfb_output * output;
struct fb_var_screeninfo * var;
struct fb_fix_screeninfo * fix;
int ret;
unsigned int line_length;
if(!info)
return -EINVAL;
ret = 0;
par = info->par;
share = par->share;
crtc = &par->crtc;
output = &par->output;
var = &info->var;
fix = &info->fix;
/* fix structur is not so FIX ... */
line_length = var->xres_virtual * var->bits_per_pixel / 8;
line_length = PADDING(crtc->line_pad,line_length);
fix->line_length = line_length;
pr_err("fix->line_length = %d\n",fix->line_length);
/* var->red,green,blue,transp are need to be set by driver
* and these data should be set before setcolreg routine
* */
switch(var->bits_per_pixel){
case 8:
fix->visual = FB_VISUAL_PSEUDOCOLOR;
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 0;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.length = 0;
var->transp.offset = 0;
break;
case 16:
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.length = 0;
var->transp.offset = 0;
fix->visual = FB_VISUAL_TRUECOLOR;
break;
case 24:
case 32:
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0 ;
var->blue.length = 8;
fix->visual = FB_VISUAL_TRUECOLOR;
break;
default:
ret = -EINVAL;
break;
}
var->height = var->width = -1;
var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
if(ret){
pr_err("pixel bpp format not satisfied\n.");
return ret;
}
ret = crtc->proc_setMode(crtc,var,fix);
if(!ret)
ret = output->proc_setMode(output,var,fix);
return ret;
}
static inline unsigned int chan_to_field(unsigned int chan,struct fb_bitfield * bf)
{
chan &= 0xffff;
chan >>= 16 - bf->length;
return chan << bf->offset;
}
static int lynxfb_resume(struct pci_dev* pdev)
{
struct fb_info * info;
struct lynx_share * share;
struct lynxfb_par * par;
struct lynxfb_crtc * crtc;
struct lynx_cursor * cursor;
int ret;
ret = 0;
share = pci_get_drvdata(pdev);
console_lock();
if((ret = pci_set_power_state(pdev, PCI_D0)) != 0){
pr_err("error:%d occured in pci_set_power_state\n",ret);
return ret;
}
if(pdev->dev.power.power_state.event != PM_EVENT_FREEZE){
pci_restore_state(pdev);
if ((ret = pci_enable_device(pdev)) != 0){
pr_err("error:%d occured in pci_enable_device\n",ret);
return ret;
}
pci_set_master(pdev);
}
if(share->resume)
(*share->resume)(share);
hw_sm750_inithw(share,pdev);
info = share->fbinfo[0];
if(info){
par = info->par;
crtc = &par->crtc;
cursor = &crtc->cursor;
memset(cursor->vstart, 0x0, cursor->size);
memset(crtc->vScreen,0x0,crtc->vidmem_size);
lynxfb_ops_set_par(info);
fb_set_suspend(info, 0);
}
info = share->fbinfo[1];
if(info){
par = info->par;
crtc = &par->crtc;
cursor = &crtc->cursor;
memset(cursor->vstart, 0x0, cursor->size);
memset(crtc->vScreen,0x0,crtc->vidmem_size);
lynxfb_ops_set_par(info);
fb_set_suspend(info, 0);
}
console_unlock();
return ret;
}
#endif
static int lynxfb_ops_mmap(struct fb_info * info, struct vm_area_struct * vma)
{
unsigned long off;
unsigned long start;
u32 len;
struct file *file;
file = vma->vm_file;
if (!info)
return -ENODEV;
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
return -EINVAL;
off = vma->vm_pgoff << PAGE_SHIFT;
printk("lynxfb mmap pgoff: %x\n", vma->vm_pgoff);
printk("lynxfb mmap off 1: %x\n", off);
/* frame buffer memory */
start = info->fix.smem_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
printk("lynxfb mmap start 1: %x\n", start);
printk("lynxfb mmap len 1: %x\n", len);
if (off >= len) {
/* memory mapped io */
off -= len;
printk("lynxfb mmap off 2: %x\n", off);
if (info->var.accel_flags) {
printk("lynxfb mmap accel flags true");
return -EINVAL;
}
start = info->fix.mmio_start;
len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
printk("lynxfb mmap start 2: %x\n", start);
printk("lynxfb mmap len 2: %x\n", len);
}
start &= PAGE_MASK;
printk("lynxfb mmap start 3: %x\n", start);
printk("lynxfb mmap vm start: %x\n", vma->vm_start);
printk("lynxfb mmap vm end: %x\n", vma->vm_end);
printk("lynxfb mmap len: %x\n", len);
printk("lynxfb mmap off: %x\n", off);
if ((vma->vm_end - vma->vm_start + off) > len)
{
return -EINVAL;
}
off += start;
printk("lynxfb mmap off 3: %x\n", off);
vma->vm_pgoff = off >> PAGE_SHIFT;
/* This is an IO map - tell maydump to skip this VMA */
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
fb_pgprotect(file, vma, off);
printk("lynxfb mmap off 4: %x\n", off);
printk("lynxfb mmap pgprot: %x\n", vma->vm_page_prot);
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
static int lynxfb_ops_check_var(struct fb_var_screeninfo* var,struct fb_info* info)
{
struct lynxfb_par * par;
struct lynxfb_crtc * crtc;
struct lynxfb_output * output;
struct lynx_share * share;
int ret;
resource_size_t request;
par = info->par;
crtc = &par->crtc;
output = &par->output;
share = par->share;
ret = 0;
pr_debug("check var:%dx%d-%d\n",
var->xres,
var->yres,
var->bits_per_pixel);
switch(var->bits_per_pixel){
case 8:
case 16:
case 24: /* support 24 bpp for only lynx712/722/720 */
case 32:
break;
default:
pr_err("bpp %d not supported\n",var->bits_per_pixel);
ret = -EINVAL;
goto exit;
}
switch(var->bits_per_pixel){
case 8:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 0;
var->green.length = 8;
var->blue.offset = 0;
var->blue.length = 8;
var->transp.length = 0;
var->transp.offset = 0;
break;
case 16:
var->red.offset = 11;
var->red.length = 5;
var->green.offset = 5;
var->green.length = 6;
var->blue.offset = 0;
var->blue.length = 5;
var->transp.length = 0;
var->transp.offset = 0;
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 24:
case 32:
var->red.offset = 16;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 0 ;
var->blue.length = 8;
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
default:
ret = -EINVAL;
break;
}
var->height = var->width = -1;
var->accel_flags = 0;/*FB_ACCELF_TEXT;*/
/* check if current fb's video memory big enought to hold the onscreen */
request = var->xres_virtual * (var->bits_per_pixel >> 3);
/* defaulty crtc->channel go with par->index */
request = PADDING(crtc->line_pad,request);
request = request * var->yres_virtual;
if(crtc->vidmem_size < request){
pr_err("not enough video memory for mode\n");
return -ENOMEM;
}
ret = output->proc_checkMode(output,var);
if(!ret)
ret = crtc->proc_checkMode(crtc,var);
exit:
return ret;
}
static int lynxfb_ops_setcolreg(unsigned regno,unsigned red,
unsigned green,unsigned blue,
unsigned transp,struct fb_info * info)
{
struct lynxfb_par * par;
struct lynxfb_crtc * crtc;
struct fb_var_screeninfo * var;
int ret;
par = info->par;
crtc = &par->crtc;
var = &info->var;
ret = 0;
//pr_debug("regno=%d,red=%d,green=%d,blue=%d\n",regno,red,green,blue);
if(regno > 256){
pr_err("regno = %d\n",regno);
return -EINVAL;
}
if(info->var.grayscale)
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
if(var->bits_per_pixel == 8 && info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
{
red >>= 8;
green >>= 8;
blue >>= 8;
ret = crtc->proc_setColReg(crtc,regno,red,green,blue);
goto exit;
}
if(info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 256 )
{
u32 val;
if(var->bits_per_pixel == 16 ||
var->bits_per_pixel == 32 ||
var->bits_per_pixel == 24)
{
val = chan_to_field(red,&var->red);
val |= chan_to_field(green,&var->green);
val |= chan_to_field(blue,&var->blue);
par->pseudo_palette[regno] = val;
goto exit;
}
}
ret = -EINVAL;
exit:
return ret;
}
static int lynxfb_ops_blank(int blank,struct fb_info* info)
{
struct lynxfb_par * par;
struct lynxfb_output * output;
pr_debug("blank = %d.\n",blank);
par = info->par;
output = &par->output;
return output->proc_setBLANK(output,blank);
}
static int sm750fb_set_drv(struct lynxfb_par * par)
{
int ret;
struct lynx_share * share;
struct sm750_share * spec_share;
struct lynxfb_output * output;
struct lynxfb_crtc * crtc;
ret = 0;
share = par->share;
spec_share = container_of(share,struct sm750_share,share);
output = &par->output;
crtc = &par->crtc;
crtc->vidmem_size = (share->dual)?share->vidmem_size>>1:share->vidmem_size;
/* setup crtc and output member */
spec_share->hwCursor = g_hwcursor;
crtc->proc_setMode = hw_sm750_crtc_setMode;
crtc->proc_checkMode = hw_sm750_crtc_checkMode;
crtc->proc_setColReg = hw_sm750_setColReg;
crtc->proc_panDisplay = hw_sm750_pan_display;
crtc->clear = hw_sm750_crtc_clear;
crtc->line_pad = 16;
//crtc->xpanstep = crtc->ypanstep = crtc->ywrapstep = 0;
crtc->xpanstep = 8;
crtc->ypanstep = 1;
crtc->ywrapstep = 0;
output->proc_setMode = hw_sm750_output_setMode;
output->proc_checkMode = hw_sm750_output_checkMode;
output->proc_setBLANK = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_setBLANK:hw_sm750_setBLANK;
output->clear = hw_sm750_output_clear;
/* chip specific phase */
share->accel.de_wait = (share->revid == SM750LE_REVISION_ID)?hw_sm750le_deWait: hw_sm750_deWait;
switch (spec_share->state.dataflow)
{
case sm750_simul_pri:
output->paths = sm750_pnc;
crtc->channel = sm750_primary;
crtc->oScreen = 0;
crtc->vScreen = share->pvMem;
pr_info("use simul primary mode\n");
break;
case sm750_simul_sec:
output->paths = sm750_pnc;
crtc->channel = sm750_secondary;
crtc->oScreen = 0;
crtc->vScreen = share->pvMem;
break;
case sm750_dual_normal:
if(par->index == 0){
output->paths = sm750_panel;
crtc->channel = sm750_primary;
crtc->oScreen = 0;
crtc->vScreen = share->pvMem;
}else{
output->paths = sm750_crt;
crtc->channel = sm750_secondary;
/* not consider of padding stuffs for oScreen,need fix*/
crtc->oScreen = (share->vidmem_size >> 1);
crtc->vScreen = share->pvMem + crtc->oScreen;
}
break;
case sm750_dual_swap:
if(par->index == 0){
output->paths = sm750_panel;
crtc->channel = sm750_secondary;
crtc->oScreen = 0;
crtc->vScreen = share->pvMem;
}else{
output->paths = sm750_crt;
crtc->channel = sm750_primary;
/* not consider of padding stuffs for oScreen,need fix*/
crtc->oScreen = (share->vidmem_size >> 1);
crtc->vScreen = share->pvMem + crtc->oScreen;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static struct fb_ops lynxfb_ops={
.owner = THIS_MODULE,
.fb_check_var = lynxfb_ops_check_var,
.fb_set_par = lynxfb_ops_set_par,
.fb_setcolreg = lynxfb_ops_setcolreg,
.fb_blank = lynxfb_ops_blank,
/*.fb_mmap = lynxfb_ops_mmap,*/
/* will be hooked by hardware */
.fb_fillrect = cfb_fillrect,
.fb_imageblit = cfb_imageblit,
.fb_copyarea = cfb_copyarea,
/* cursor */
.fb_cursor = lynxfb_ops_cursor,
};
static int lynxfb_set_fbinfo(struct fb_info* info,int index)
{
int i;
struct lynxfb_par * par;
struct lynx_share * share;
struct lynxfb_crtc * crtc;
struct lynxfb_output * output;
struct fb_var_screeninfo * var;
struct fb_fix_screeninfo * fix;
const struct fb_videomode * pdb[] = {
lynx750_ext, NULL,vesa_modes,
};
int cdb[] = {ARRAY_SIZE(lynx750_ext),0,VESA_MODEDB_SIZE};
static const char * mdb_desc[] ={
"driver prepared modes",
"kernel prepared default modedb",
"kernel HELPERS prepared vesa_modes",
};
static const char * fixId[2]=
{
"sm750_fb1","sm750_fb2",
};
int ret,line_length;
ret = 0;
par = (struct lynxfb_par *)info->par;
share = par->share;
crtc = &par->crtc;
output = &par->output;
var = &info->var;
fix = &info->fix;
/* set index */
par->index = index;
output->channel = &crtc->channel;
sm750fb_set_drv(par);
lynxfb_ops.fb_pan_display = lynxfb_ops_pan_display;
/* set current cursor variable and proc pointer,
* must be set after crtc member initialized */
crtc->cursor.offset = crtc->oScreen + crtc->vidmem_size - 1024;
crtc->cursor.mmio = share->pvReg + 0x800f0 + (int)crtc->channel * 0x140;
pr_info("crtc->cursor.mmio = %p\n",crtc->cursor.mmio);
crtc->cursor.maxH = crtc->cursor.maxW = 64;
crtc->cursor.size = crtc->cursor.maxH*crtc->cursor.maxW*2/8;
crtc->cursor.disable = hw_cursor_disable;
crtc->cursor.enable = hw_cursor_enable;
crtc->cursor.setColor = hw_cursor_setColor;
crtc->cursor.setPos = hw_cursor_setPos;
crtc->cursor.setSize = hw_cursor_setSize;
crtc->cursor.setData = hw_cursor_setData;
crtc->cursor.vstart = share->pvMem + crtc->cursor.offset;
crtc->cursor.share = share;
memset(crtc->cursor.vstart, 0, crtc->cursor.size);
if(!g_hwcursor){
lynxfb_ops.fb_cursor = NULL;
crtc->cursor.disable(&crtc->cursor);
}
/* set info->fbops, must be set before fb_find_mode */
if(!share->accel_off){
/* use 2d acceleration */
lynxfb_ops.fb_fillrect = lynxfb_ops_fillrect;
lynxfb_ops.fb_copyarea = lynxfb_ops_copyarea;
lynxfb_ops.fb_imageblit = lynxfb_ops_imageblit;
}
info->fbops = &lynxfb_ops;
if(!g_fbmode[index]){
g_fbmode[index] = g_def_fbmode;
if(index)
g_fbmode[index] = g_fbmode[0];
}
for(i=0;i<3;i++){
ret = fb_find_mode(var,info,g_fbmode[index],
pdb[i],cdb[i],NULL,8);
if(ret == 1){
pr_info("success! use specified mode:%s in %s\n",
g_fbmode[index],
mdb_desc[i]);
break;
}else if(ret == 2){
pr_warn("use specified mode:%s in %s,with an ignored refresh rate\n",
g_fbmode[index],
mdb_desc[i]);
break;
}else if(ret == 3){
pr_warn("wanna use default mode\n");
// break;
}else if(ret == 4){
pr_warn("fall back to any valid mode\n");
}else{
pr_warn("ret = %d,fb_find_mode failed,with %s\n",ret,mdb_desc[i]);
}
}
/* some member of info->var had been set by fb_find_mode */
pr_info("Member of info->var is :\n\
xres=%d\n\
yres=%d\n\
xres_virtual=%d\n\
yres_virtual=%d\n\
xoffset=%d\n\
yoffset=%d\n\
bits_per_pixel=%d\n \
...\n",var->xres,var->yres,var->xres_virtual,var->yres_virtual,
var->xoffset,var->yoffset,var->bits_per_pixel);
/* set par */
par->info = info;
/* set info */
line_length = PADDING(crtc->line_pad,
(var->xres_virtual * var->bits_per_pixel/8));
info->pseudo_palette = &par->pseudo_palette[0];
info->screen_base = crtc->vScreen;
pr_debug("screen_base vaddr = %p\n",info->screen_base);
info->screen_size = line_length * var->yres_virtual;
info->flags = FBINFO_FLAG_DEFAULT|0;
/* set info->fix */
fix->type = FB_TYPE_PACKED_PIXELS;
fix->type_aux = 0;
fix->xpanstep = crtc->xpanstep;
fix->ypanstep = crtc->ypanstep;
fix->ywrapstep = crtc->ywrapstep;
fix->accel = FB_ACCEL_SMI;
strlcpy(fix->id,fixId[index],sizeof(fix->id));
fix->smem_start = crtc->oScreen + share->vidmem_start;
pr_info("fix->smem_start = %lx\n",fix->smem_start);
/* according to mmap experiment from user space application,
* fix->mmio_len should not larger than virtual size
* (xres_virtual x yres_virtual x ByPP)
* Below line maybe buggy when user mmap fb dev node and write
* data into the bound over virtual size
* */
fix->smem_len = crtc->vidmem_size;
pr_info("fix->smem_len = %x\n",fix->smem_len);
info->screen_size = fix->smem_len;
fix->line_length = line_length;
fix->mmio_start = share->vidreg_start;
pr_info("fix->mmio_start = %lx\n",fix->mmio_start);
fix->mmio_len = share->vidreg_size;
pr_info("fix->mmio_len = %x\n",fix->mmio_len);
switch(var->bits_per_pixel)
{
case 8:
fix->visual = FB_VISUAL_PSEUDOCOLOR;
break;
case 16:
case 32:
fix->visual = FB_VISUAL_TRUECOLOR;
break;
}
/* set var */
var->activate = FB_ACTIVATE_NOW;
var->accel_flags = 0;
var->vmode = FB_VMODE_NONINTERLACED;
pr_debug("#1 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
info->cmap.start,info->cmap.len,
info->cmap.red,info->cmap.green,info->cmap.blue,
info->cmap.transp);
if((ret = fb_alloc_cmap(&info->cmap,256,0)) < 0){
pr_err("Could not allcate memory for cmap.\n");
goto exit;
}
pr_debug("#2 show info->cmap : \nstart=%d,len=%d,red=%p,green=%p,blue=%p,transp=%p\n",
info->cmap.start,info->cmap.len,
info->cmap.red,info->cmap.green,info->cmap.blue,
info->cmap.transp);
exit:
lynxfb_ops_check_var(var,info);
// lynxfb_ops_set_par(info);
return ret;
}
/* chip specific g_option configuration routine */
static void sm750fb_setup(struct lynx_share * share,char * src)
{
struct sm750_share * spec_share;
char * opt;
#ifdef CAP_EXPENSION
char * exp_res;
#endif
int swap;
spec_share = container_of(share,struct sm750_share,share);
#ifdef CAP_EXPENSIION
exp_res = NULL;
#endif
swap = 0;
spec_share->state.initParm.chip_clk = 0;
spec_share->state.initParm.mem_clk = 0;
spec_share->state.initParm.master_clk = 0;
spec_share->state.initParm.powerMode = 0;
spec_share->state.initParm.setAllEngOff = 0;
spec_share->state.initParm.resetMemory = 1;
/*defaultly turn g_hwcursor on for both view */
g_hwcursor = 3;
if(!src || !*src){
pr_warn("no specific g_option.\n");
goto NO_PARAM;
}
while((opt = strsep(&src,":")) != NULL && *opt != NULL){
pr_err("opt=%s\n",opt);
pr_err("src=%s\n",src);
if(!strncmp(opt,"swap",strlen("swap")))
swap = 1;
else if(!strncmp(opt,"nocrt",strlen("nocrt")))
spec_share->state.nocrt = 1;
else if(!strncmp(opt,"36bit",strlen("36bit")))
spec_share->state.pnltype = sm750_doubleTFT;
else if(!strncmp(opt,"18bit",strlen("18bit")))
spec_share->state.pnltype = sm750_dualTFT;
else if(!strncmp(opt,"24bit",strlen("24bit")))
spec_share->state.pnltype = sm750_24TFT;
#ifdef CAP_EXPANSION
else if(!strncmp(opt,"exp:",strlen("exp:")))
exp_res = opt + strlen("exp:");
#endif
else if(!strncmp(opt,"nohwc0",strlen("nohwc0")))
g_hwcursor &= ~0x1;
else if(!strncmp(opt,"nohwc1",strlen("nohwc1")))
g_hwcursor &= ~0x2;
else if(!strncmp(opt,"nohwc",strlen("nohwc")))
g_hwcursor = 0;
else
{
if(!g_fbmode[0]){
g_fbmode[0] = opt;
pr_info("find fbmode0 : %s\n",g_fbmode[0]);
}else if(!g_fbmode[1]){
g_fbmode[1] = opt;
pr_info("find fbmode1 : %s\n",g_fbmode[1]);
}else{
pr_warn("How many view you wann set?\n");
}
}
}
#ifdef CAP_EXPANSION
if(getExpRes(exp_res,&spec_share->state.xLCD,&spec_share->state.yLCD))
{
/* seems exp_res is not valid*/
spec_share->state.xLCD = spec_share->state.yLCD = 0;
}
#endif
NO_PARAM:
if(share->revid != SM750LE_REVISION_ID){
if(share->dual)
{
if(swap)
spec_share->state.dataflow = sm750_dual_swap;
else
spec_share->state.dataflow = sm750_dual_normal;
}else{
if(swap)
spec_share->state.dataflow = sm750_simul_sec;
else
spec_share->state.dataflow = sm750_simul_pri;
}
}else{
/* SM750LE only have one crt channel */
spec_share->state.dataflow = sm750_simul_sec;
/* sm750le do not have complex attributes*/
spec_share->state.nocrt = 0;
}
}
static int lynxfb_pci_probe(struct pci_dev * pdev,
const struct pci_device_id * ent)
{
struct fb_info * info[] = {NULL,NULL};
struct lynx_share * share = NULL;
struct sm750_share *spec_share = NULL;
size_t spec_offset = 0;
int fbidx;
/* enable device */
if(pci_enable_device(pdev)){
pr_err("can not enable device.\n");
goto err_enable;
}
/* though offset of share in sm750_share is 0,
* we use this marcro as the same */
spec_offset = offsetof(struct sm750_share,share);
spec_share = kzalloc(sizeof(*spec_share),GFP_KERNEL);
if(!spec_share){
pr_err("Could not allocate memory for share.\n");
goto err_share;
}
/* setting share structure */
share = (struct lynx_share * )(&(spec_share->share));
share->fbinfo[0] = share->fbinfo[1] = NULL;
share->devid = pdev->device;
share->revid = pdev->revision;
pr_info("share->revid = %02x\n",share->revid);
share->pdev = pdev;
#ifdef CONFIG_MTRR
share->mtrr_off = g_nomtrr;
share->mtrr.vram = 0;
share->mtrr.vram_added = 0;
#endif
share->accel_off = g_noaccel;
share->dual = g_dualview;
spin_lock_init(&share->slock);
if(!share->accel_off){
/* hook deInit and 2d routines, notes that below hw_xxx
* routine can work on most of lynx chips
* if some chip need specific function,please hook it in smXXX_set_drv
* routine */
share->accel.de_init = hw_de_init;
share->accel.de_fillrect = hw_fillrect;
share->accel.de_copyarea = hw_copyarea;
share->accel.de_imageblit = hw_imageblit;
pr_info("enable 2d acceleration\n");
}else{
pr_info("disable 2d acceleration\n");
}
/* call chip specific setup routine */
sm750fb_setup(share,g_settings);
/* call chip specific mmap routine */
if(hw_sm750_map(share,pdev)){
pr_err("Memory map failed\n");
goto err_map;
}
#ifdef CONFIG_MTRR
if(!share->mtrr_off){
pr_info("enable mtrr\n");
share->mtrr.vram = mtrr_add(share->vidmem_start,
share->vidmem_size,
MTRR_TYPE_WRCOMB,1);
if(share->mtrr.vram < 0){
/* don't block driver with the failure of MTRR */
pr_err("Unable to setup MTRR.\n");
}else{
share->mtrr.vram_added = 1;
pr_info("MTRR added succesfully\n");
}
}
#endif
memset(share->pvMem,0,share->vidmem_size);
pr_info("sm%3x mmio address = %p\n",share->devid,share->pvReg);
pci_set_drvdata(pdev,share);
/* call chipInit routine */
hw_sm750_inithw(share,pdev);
/* allocate frame buffer info structor according to g_dualview */
fbidx = 0;
ALLOC_FB:
info[fbidx] = framebuffer_alloc(sizeof(struct lynxfb_par),&pdev->dev);
if(!info[fbidx])
{
pr_err("Could not allocate framebuffer #%d.\n",fbidx);
if(fbidx == 0)
goto err_info0_alloc;
else
goto err_info1_alloc;
}
else
{
struct lynxfb_par * par;
pr_info("framebuffer #%d alloc okay\n",fbidx);
share->fbinfo[fbidx] = info[fbidx];
par = info[fbidx]->par;
par->share = share;
/* set fb_info structure */
if(lynxfb_set_fbinfo(info[fbidx],fbidx)){
pr_err("Failed to initial fb_info #%d.\n",fbidx);
if(fbidx == 0)
goto err_info0_set;
else
goto err_info1_set;
}
/* register frame buffer*/
pr_info("Ready to register framebuffer #%d.\n",fbidx);
int errno = register_framebuffer(info[fbidx]);
if (errno < 0) {
pr_err("Failed to register fb_info #%d. err %d\n",fbidx, errno);
if(fbidx == 0)
goto err_register0;
else
goto err_register1;
}
pr_info("Accomplished register framebuffer #%d.\n",fbidx);
}
/* no dual view by far */
fbidx++;
if(share->dual && fbidx < 2)
goto ALLOC_FB;
return 0;
err_register1:
err_info1_set:
framebuffer_release(info[1]);
err_info1_alloc:
unregister_framebuffer(info[0]);
err_register0:
err_info0_set:
framebuffer_release(info[0]);
err_info0_alloc:
err_map:
kfree(spec_share);
err_share:
err_enable:
return -ENODEV;
}
static void __exit lynxfb_pci_remove(struct pci_dev * pdev)
{
struct fb_info * info;
struct lynx_share * share;
void * spec_share;
struct lynxfb_par * par;
int cnt;
cnt = 2;
share = pci_get_drvdata(pdev);
while(cnt-- > 0){
info = share->fbinfo[cnt];
if(!info)
continue;
par = info->par;
unregister_framebuffer(info);
/* clean crtc & output allocations*/
par->crtc.clear(&par->crtc);
par->output.clear(&par->output);
/* release frame buffer*/
framebuffer_release(info);
}
#ifdef CONFIG_MTRR
if(share->mtrr.vram_added)
mtrr_del(share->mtrr.vram,share->vidmem_start,share->vidmem_size);
#endif
// pci_release_regions(pdev);
iounmap(share->pvReg);
iounmap(share->pvMem);
spec_share = container_of(share,struct sm750_share,share);
kfree(g_settings);
kfree(spec_share);
pci_set_drvdata(pdev,NULL);
}
static int __init lynxfb_setup(char * options)
{
int len;
char * opt,*tmp;
if(!options || !*options){
pr_warn("no options.\n");
return 0;
}
pr_info("options:%s\n",options);
len = strlen(options) + 1;
g_settings = kmalloc(len,GFP_KERNEL);
if(!g_settings)
return -ENOMEM;
memset(g_settings,0,len);
tmp = g_settings;
/* Notes:
char * strsep(char **s,const char * ct);
@s: the string to be searched
@ct :the characters to search for
strsep() updates @options to pointer after the first found token
it also returns the pointer ahead the token.
*/
while((opt = strsep(&options,":"))!=NULL)
{
/* options that mean for any lynx chips are configured here */
if(!strncmp(opt,"noaccel",strlen("noaccel")))
g_noaccel = 1;
#ifdef CONFIG_MTRR
else if(!strncmp(opt,"nomtrr",strlen("nomtrr")))
g_nomtrr = 1;
#endif
else if(!strncmp(opt,"dual",strlen("dual")))
g_dualview = 1;
else
{
strcat(tmp,opt);
tmp += strlen(opt);
if(options != NULL)
*tmp++ = ':';
else
*tmp++ = 0;
}
}
/* misc g_settings are transport to chip specific routines */
pr_info("parameter left for chip specific analysis:%s\n",g_settings);
return 0;
}
static struct pci_device_id smi_pci_table[] = {
{ PCI_DEVICE(0x126f, 0x0750), },
{0,}
};
MODULE_DEVICE_TABLE(pci,smi_pci_table);
static struct pci_driver lynxfb_driver = {
.name = "sm750fb",
.id_table = smi_pci_table,
.probe = lynxfb_pci_probe,
.remove = lynxfb_pci_remove,
#ifdef CONFIG_PM
.suspend = lynxfb_suspend,
.resume = lynxfb_resume,
#endif
};
static int __init lynxfb_init(void)
{
char *option ;
int ret;
#ifdef MODULE
option = g_option;
#else
if(fb_get_options("sm750fb",&option))
return -ENODEV;
#endif
lynxfb_setup(option);
ret = pci_register_driver(&lynxfb_driver);
return ret;
}
module_init(lynxfb_init);
static void __exit lynxfb_exit(void)
{
pci_unregister_driver(&lynxfb_driver);
}
module_exit(lynxfb_exit);
module_param(g_option,charp,S_IRUGO);
MODULE_PARM_DESC(g_option,
"\n\t\tCommon options:\n"
"\t\tnoaccel:disable 2d capabilities\n"
"\t\tnomtrr:disable MTRR attribute for video memory\n"
"\t\tdualview:dual frame buffer feature enabled\n"
"\t\tnohwc:disable hardware cursor\n"
"\t\tUsual example:\n"
"\t\tinsmod ./sm750fb.ko g_option=\"noaccel,nohwc,1280x1024-8@60\"\n"
);
MODULE_AUTHOR("monk liu <monk.liu@siliconmotion.com>");
MODULE_AUTHOR("Sudip Mukherjee <sudip@vectorindia.org>");
MODULE_DESCRIPTION("Frame buffer driver for SM750 chipset");
MODULE_LICENSE("GPL v2");
#ifndef LYNXDRV_H_
#define LYNXDRV_H_
#define FB_ACCEL_SMI 0xab
/* please use revision id to distinguish sm750le and sm750*/
#define SPC_SM750 0
//#define SPC_SM750LE 8
#define MB(x) ((x)<<20)
#define MHZ(x) ((x) * 1000000)
/* align should be 2,4,8,16 */
#define PADDING(align,data) (((data)+(align)-1)&(~((align) -1)))
extern int smi_indent;
struct lynx_accel{
/* base virtual address of DPR registers */
volatile unsigned char __iomem * dprBase;
/* base virtual address of de data port */
volatile unsigned char __iomem * dpPortBase;
/* function fointers */
int (*de_init)(struct lynx_accel *);
int (*de_wait)(void);/* see if hardware ready to work */
int (*de_fillrect)(struct lynx_accel *,u32,u32,u32,
u32,u32,u32,u32,u32,u32);
int (*de_copyarea)(struct lynx_accel *,u32,u32,u32,u32,
u32,u32,u32,u32,
u32,u32,u32,u32);
int (*de_imageblit)(struct lynx_accel *,const char *,u32,u32,u32,
u32,u32,u32,u32,u32,u32,u32,u32,u32);
};
/* lynx_share stands for a presentation of two frame buffer
that use one smi adaptor , it is similar to a basic class of C++
*/
struct lynx_share{
/* common members */
u16 devid;
u8 revid;
struct pci_dev * pdev;
struct fb_info * fbinfo[2];
struct lynx_accel accel;
int accel_off;
int dual;
#ifdef CONFIG_MTRR
int mtrr_off;
struct{
int vram;
int vram_added;
}mtrr;
#endif
/* all smi graphic adaptor got below attributes */
resource_size_t vidmem_start;
resource_size_t vidreg_start;
resource_size_t vidmem_size;
resource_size_t vidreg_size;
volatile unsigned char __iomem * pvReg;
unsigned char __iomem * pvMem;
/* locks*/
spinlock_t slock;
/* function pointers */
void (*suspend)(struct lynx_share*);
void (*resume)(struct lynx_share*);
};
struct lynx_cursor{
/* cursor width ,height and size */
int w;
int h;
int size;
/* hardware limitation */
int maxW;
int maxH;
/* base virtual address and offset of cursor image */
char __iomem * vstart;
int offset;
/* mmio addr of hw cursor */
volatile char __iomem * mmio;
/* the lynx_share of this adaptor */
struct lynx_share * share;
/* proc_routines */
void (*enable)(struct lynx_cursor *);
void (*disable)(struct lynx_cursor *);
void (*setSize)(struct lynx_cursor *,int,int);
void (*setPos)(struct lynx_cursor *,int,int);
void (*setColor)(struct lynx_cursor *,u32,u32);
void (*setData)(struct lynx_cursor *,u16,const u8*,const u8*);
};
struct lynxfb_crtc{
unsigned char __iomem * vCursor;//virtual address of cursor
unsigned char __iomem * vScreen;//virtual address of on_screen
int oCursor;//cursor address offset in vidmem
int oScreen;//onscreen address offset in vidmem
int channel;/* which channel this crtc stands for*/
resource_size_t vidmem_size;/* this view's video memory max size */
/* below attributes belong to info->fix, their value depends on specific adaptor*/
u16 line_pad;/* padding information:0,1,2,4,8,16,... */
u16 xpanstep;
u16 ypanstep;
u16 ywrapstep;
void * priv;
int(*proc_setMode)(struct lynxfb_crtc*,
struct fb_var_screeninfo*,
struct fb_fix_screeninfo*);
int(*proc_checkMode)(struct lynxfb_crtc*,struct fb_var_screeninfo*);
int(*proc_setColReg)(struct lynxfb_crtc*,ushort,ushort,ushort,ushort);
void (*clear)(struct lynxfb_crtc*);
/* pan display */
int(*proc_panDisplay)(struct lynxfb_crtc*, struct fb_var_screeninfo*,
struct fb_info*);
/* cursor information */
struct lynx_cursor cursor;
};
struct lynxfb_output{
int dpms;
int paths;
/* which paths(s) this output stands for,for sm750:
paths=1:means output for panel paths
paths=2:means output for crt paths
paths=3:means output for both panel and crt paths
*/
int * channel;
/* which channel these outputs linked with,for sm750:
*channel=0 means primary channel
*channel=1 means secondary channel
output->channel ==> &crtc->channel
*/
void * priv;
int(*proc_setMode)(struct lynxfb_output*,
struct fb_var_screeninfo*,
struct fb_fix_screeninfo*);
int(*proc_checkMode)(struct lynxfb_output*,struct fb_var_screeninfo*);
int(*proc_setBLANK)(struct lynxfb_output*,int);
void (*clear)(struct lynxfb_output*);
};
struct lynxfb_par{
/* either 0 or 1 for dual head adaptor,0 is the older one registered */
int index;
unsigned int pseudo_palette[256];
struct lynxfb_crtc crtc;
struct lynxfb_output output;
struct fb_info * info;
struct lynx_share * share;
};
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#define PS_TO_HZ(ps) \
({ \
unsigned long long hz = 1000*1000*1000*1000ULL; \
do_div(hz,ps); \
(unsigned long)hz;})
static inline unsigned long ps_to_hz(unsigned int psvalue)
{
unsigned long long numerator=1000*1000*1000*1000ULL;
/* 10^12 / picosecond period gives frequency in Hz */
do_div(numerator, psvalue);
return (unsigned long)numerator;
}
#endif
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/errno.h>
#include<linux/string.h>
#include<linux/mm.h>
#include<linux/slab.h>
#include<linux/delay.h>
#include<linux/fb.h>
#include<linux/ioport.h>
#include<linux/init.h>
#include<linux/pci.h>
#include<linux/vmalloc.h>
#include<linux/pagemap.h>
#include <linux/console.h>
#include<linux/platform_device.h>
#include<linux/screen_info.h>
#include "sm750.h"
#include "sm750_accel.h"
#include "sm750_help.h"
static inline void write_dpr(struct lynx_accel * accel,int offset,u32 regValue)
{
writel(regValue,accel->dprBase + offset);
}
static inline u32 read_dpr(struct lynx_accel * accel,int offset)
{
return readl(accel->dprBase + offset);
}
static inline void write_dpPort(struct lynx_accel * accel,u32 data)
{
writel(data,accel->dpPortBase);
}
void hw_de_init(struct lynx_accel * accel)
{
/* setup 2d engine registers */
u32 reg,clr;
write_dpr(accel,DE_MASKS,0xFFFFFFFF);
/* dpr1c */
reg = FIELD_SET(0,DE_STRETCH_FORMAT,PATTERN_XY,NORMAL)|
FIELD_VALUE(0,DE_STRETCH_FORMAT,PATTERN_Y,0)|
FIELD_VALUE(0,DE_STRETCH_FORMAT,PATTERN_X,0)|
FIELD_SET(0,DE_STRETCH_FORMAT,ADDRESSING,XY)|
FIELD_VALUE(0,DE_STRETCH_FORMAT,SOURCE_HEIGHT,3);
clr = FIELD_CLEAR(DE_STRETCH_FORMAT,PATTERN_XY)&
FIELD_CLEAR(DE_STRETCH_FORMAT,PATTERN_Y)&
FIELD_CLEAR(DE_STRETCH_FORMAT,PATTERN_X)&
FIELD_CLEAR(DE_STRETCH_FORMAT,ADDRESSING)&
FIELD_CLEAR(DE_STRETCH_FORMAT,SOURCE_HEIGHT);
/* DE_STRETCH bpp format need be initilized in setMode routine */
write_dpr(accel,DE_STRETCH_FORMAT,(read_dpr(accel,DE_STRETCH_FORMAT) & clr) | reg);
/* disable clipping and transparent */
write_dpr(accel,DE_CLIP_TL,0);//dpr2c
write_dpr(accel,DE_CLIP_BR,0);//dpr30
write_dpr(accel,DE_COLOR_COMPARE_MASK,0);//dpr24
write_dpr(accel,DE_COLOR_COMPARE,0);
reg = FIELD_SET(0,DE_CONTROL,TRANSPARENCY,DISABLE)|
FIELD_SET(0,DE_CONTROL,TRANSPARENCY_MATCH,OPAQUE)|
FIELD_SET(0,DE_CONTROL,TRANSPARENCY_SELECT,SOURCE);
clr = FIELD_CLEAR(DE_CONTROL,TRANSPARENCY)&
FIELD_CLEAR(DE_CONTROL,TRANSPARENCY_MATCH)&
FIELD_CLEAR(DE_CONTROL,TRANSPARENCY_SELECT);
/* dpr0c */
write_dpr(accel,DE_CONTROL,(read_dpr(accel,DE_CONTROL)&clr)|reg);
}
/* set2dformat only be called from setmode functions
* but if you need dual framebuffer driver,need call set2dformat
* every time you use 2d function */
void hw_set2dformat(struct lynx_accel * accel,int fmt)
{
u32 reg;
/* fmt=0,1,2 for 8,16,32,bpp on sm718/750/502 */
reg = read_dpr(accel,DE_STRETCH_FORMAT);
reg = FIELD_VALUE(reg,DE_STRETCH_FORMAT,PIXEL_FORMAT,fmt);
write_dpr(accel,DE_STRETCH_FORMAT,reg);
}
/* seems sm712 RectFill command is broken,so need use BitBlt to
* replace it. */
int hw712_fillrect(struct lynx_accel * accel,
u32 base,u32 pitch,u32 Bpp,
u32 x,u32 y,u32 width,u32 height,
u32 color,u32 rop)
{
u32 deCtrl;
if(accel->de_wait() != 0)
{
/* int time wait and always busy,seems hardware
* got something error */
pr_debug("%s:De engine always bussy\n",__func__);
return -1;
}
/* 24bpp 2d acceleration still not work,we already support 2d on
* both 8/16/32 bpp now, so there is no harm if we disable 2d on
* 24bpp for current stage. */
#if 0
if(Bpp == 3){
width *= 3;
x *= 3;
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0,DE_PITCH,DESTINATION,pitch)|
FIELD_VALUE(0,DE_PITCH,SOURCE,pitch));//dpr10
}
else
#endif
{
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0,DE_PITCH,DESTINATION,pitch/Bpp)|
FIELD_VALUE(0,DE_PITCH,SOURCE,pitch/Bpp));//dpr10
}
write_dpr(accel,DE_FOREGROUND,color);//DPR14
write_dpr(accel,DE_MONO_PATTERN_HIGH,~0);//DPR34
write_dpr(accel,DE_MONO_PATTERN_LOW,~0);//DPR38
write_dpr(accel,DE_WINDOW_SOURCE_BASE,base);//dpr44
write_dpr(accel,DE_WINDOW_DESTINATION_BASE,base);//dpr40
write_dpr(accel,DE_WINDOW_WIDTH,
FIELD_VALUE(0,DE_WINDOW_WIDTH,DESTINATION,pitch/Bpp)|
FIELD_VALUE(0,DE_WINDOW_WIDTH,SOURCE,pitch/Bpp));//dpr3c
write_dpr(accel,DE_DESTINATION,
FIELD_SET(0,DE_DESTINATION,WRAP,DISABLE)|
FIELD_VALUE(0,DE_DESTINATION,X,x)|
FIELD_VALUE(0,DE_DESTINATION,Y,y));//dpr4
write_dpr(accel,DE_DIMENSION,
FIELD_VALUE(0,DE_DIMENSION,X,width)|
FIELD_VALUE(0,DE_DIMENSION,Y_ET,height));//dpr8
deCtrl =
FIELD_SET(0,DE_CONTROL,STATUS,START)|
FIELD_SET(0,DE_CONTROL,COMMAND,BITBLT)|
FIELD_SET(0,DE_CONTROL,ROP2_SOURCE,PATTERN)|
FIELD_SET(0,DE_CONTROL,ROP_SELECT,ROP2)|
FIELD_VALUE(0,DE_CONTROL,ROP,rop);//dpr0xc
#if 0
/* dump registers */
int i;
inf_msg("x,y,w,h = %d,%d,%d,%d\n",x,y,width,height);
for(i=0x04;i<=0x44;i+=4){
inf_msg("dpr%02x = %08x\n",i,read_dpr(accel,i));
}
inf_msg("deCtrl = %08x\n",deCtrl);
#endif
write_dpr(accel,DE_CONTROL,deCtrl);
return 0;
}
int hw_fillrect(struct lynx_accel * accel,
u32 base,u32 pitch,u32 Bpp,
u32 x,u32 y,u32 width,u32 height,
u32 color,u32 rop)
{
u32 deCtrl;
if(accel->de_wait() != 0)
{
/* int time wait and always busy,seems hardware
* got something error */
pr_debug("%s:De engine always bussy\n",__func__);
return -1;
}
write_dpr(accel,DE_WINDOW_DESTINATION_BASE,base);//dpr40
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0,DE_PITCH,DESTINATION,pitch/Bpp)|
FIELD_VALUE(0,DE_PITCH,SOURCE,pitch/Bpp));//dpr10
write_dpr(accel,DE_WINDOW_WIDTH,
FIELD_VALUE(0,DE_WINDOW_WIDTH,DESTINATION,pitch/Bpp)|
FIELD_VALUE(0,DE_WINDOW_WIDTH,SOURCE,pitch/Bpp));//dpr44
write_dpr(accel,DE_FOREGROUND,color);//DPR14
write_dpr(accel,DE_DESTINATION,
FIELD_SET(0,DE_DESTINATION,WRAP,DISABLE)|
FIELD_VALUE(0,DE_DESTINATION,X,x)|
FIELD_VALUE(0,DE_DESTINATION,Y,y));//dpr4
write_dpr(accel,DE_DIMENSION,
FIELD_VALUE(0,DE_DIMENSION,X,width)|
FIELD_VALUE(0,DE_DIMENSION,Y_ET,height));//dpr8
deCtrl =
FIELD_SET(0,DE_CONTROL,STATUS,START)|
FIELD_SET(0,DE_CONTROL,DIRECTION,LEFT_TO_RIGHT)|
FIELD_SET(0,DE_CONTROL,LAST_PIXEL,ON)|
FIELD_SET(0,DE_CONTROL,COMMAND,RECTANGLE_FILL)|
FIELD_SET(0,DE_CONTROL,ROP_SELECT,ROP2)|
FIELD_VALUE(0,DE_CONTROL,ROP,rop);//dpr0xc
write_dpr(accel,DE_CONTROL,deCtrl);
return 0;
}
int hw_copyarea(
struct lynx_accel * accel,
unsigned int sBase, /* Address of source: offset in frame buffer */
unsigned int sPitch, /* Pitch value of source surface in BYTE */
unsigned int sx,
unsigned int sy, /* Starting coordinate of source surface */
unsigned int dBase, /* Address of destination: offset in frame buffer */
unsigned int dPitch, /* Pitch value of destination surface in BYTE */
unsigned int Bpp, /* Color depth of destination surface */
unsigned int dx,
unsigned int dy, /* Starting coordinate of destination surface */
unsigned int width,
unsigned int height, /* width and height of rectangle in pixel value */
unsigned int rop2) /* ROP value */
{
unsigned int nDirection, de_ctrl;
int opSign;
nDirection = LEFT_TO_RIGHT;
/* Direction of ROP2 operation: 1 = Left to Right, (-1) = Right to Left */
opSign = 1;
de_ctrl = 0;
/* If source and destination are the same surface, need to check for overlay cases */
if (sBase == dBase && sPitch == dPitch)
{
/* Determine direction of operation */
if (sy < dy)
{
/* +----------+
|S |
| +----------+
| | | |
| | | |
+---|------+ |
| D|
+----------+ */
nDirection = BOTTOM_TO_TOP;
}
else if (sy > dy)
{
/* +----------+
|D |
| +----------+
| | | |
| | | |
+---|------+ |
| S|
+----------+ */
nDirection = TOP_TO_BOTTOM;
}
else
{
/* sy == dy */
if (sx <= dx)
{
/* +------+---+------+
|S | | D|
| | | |
| | | |
| | | |
+------+---+------+ */
nDirection = RIGHT_TO_LEFT;
}
else
{
/* sx > dx */
/* +------+---+------+
|D | | S|
| | | |
| | | |
| | | |
+------+---+------+ */
nDirection = LEFT_TO_RIGHT;
}
}
}
if ((nDirection == BOTTOM_TO_TOP) || (nDirection == RIGHT_TO_LEFT))
{
sx += width - 1;
sy += height - 1;
dx += width - 1;
dy += height - 1;
opSign = (-1);
}
/* Note:
DE_FOREGROUND are DE_BACKGROUND are don't care.
DE_COLOR_COMPARE and DE_COLOR_COMPARE_MAKS are set by set deSetTransparency().
*/
/* 2D Source Base.
It is an address offset (128 bit aligned) from the beginning of frame buffer.
*/
write_dpr(accel,DE_WINDOW_SOURCE_BASE, sBase);//dpr40
/* 2D Destination Base.
It is an address offset (128 bit aligned) from the beginning of frame buffer.
*/
write_dpr(accel,DE_WINDOW_DESTINATION_BASE, dBase);//dpr44
#if 0
/* Program pitch (distance between the 1st points of two adjacent lines).
Note that input pitch is BYTE value, but the 2D Pitch register uses
pixel values. Need Byte to pixel convertion.
*/
if(Bpp == 3){
sx *= 3;
dx *= 3;
width *= 3;
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0, DE_PITCH, DESTINATION, dPitch) |
FIELD_VALUE(0, DE_PITCH, SOURCE, sPitch));//dpr10
}
else
#endif
{
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0, DE_PITCH, DESTINATION, (dPitch/Bpp)) |
FIELD_VALUE(0, DE_PITCH, SOURCE, (sPitch/Bpp)));//dpr10
}
/* Screen Window width in Pixels.
2D engine uses this value to calculate the linear address in frame buffer for a given point.
*/
write_dpr(accel,DE_WINDOW_WIDTH,
FIELD_VALUE(0, DE_WINDOW_WIDTH, DESTINATION, (dPitch/Bpp)) |
FIELD_VALUE(0, DE_WINDOW_WIDTH, SOURCE, (sPitch/Bpp)));//dpr3c
if (accel->de_wait() != 0){
return -1;
}
{
write_dpr(accel,DE_SOURCE,
FIELD_SET (0, DE_SOURCE, WRAP, DISABLE) |
FIELD_VALUE(0, DE_SOURCE, X_K1, sx) |
FIELD_VALUE(0, DE_SOURCE, Y_K2, sy));//dpr0
write_dpr(accel,DE_DESTINATION,
FIELD_SET (0, DE_DESTINATION, WRAP, DISABLE) |
FIELD_VALUE(0, DE_DESTINATION, X, dx) |
FIELD_VALUE(0, DE_DESTINATION, Y, dy));//dpr04
write_dpr(accel,DE_DIMENSION,
FIELD_VALUE(0, DE_DIMENSION, X, width) |
FIELD_VALUE(0, DE_DIMENSION, Y_ET, height));//dpr08
de_ctrl =
FIELD_VALUE(0, DE_CONTROL, ROP, rop2) |
FIELD_SET(0, DE_CONTROL, ROP_SELECT, ROP2) |
FIELD_SET(0, DE_CONTROL, COMMAND, BITBLT) |
((nDirection == RIGHT_TO_LEFT) ?
FIELD_SET(0, DE_CONTROL, DIRECTION, RIGHT_TO_LEFT)
: FIELD_SET(0, DE_CONTROL, DIRECTION, LEFT_TO_RIGHT)) |
FIELD_SET(0, DE_CONTROL, STATUS, START);
write_dpr(accel,DE_CONTROL,de_ctrl);//dpr0c
}
return 0;
}
static unsigned int deGetTransparency(struct lynx_accel * accel)
{
unsigned int de_ctrl;
de_ctrl = read_dpr(accel,DE_CONTROL);
de_ctrl &=
FIELD_MASK(DE_CONTROL_TRANSPARENCY_MATCH) |
FIELD_MASK(DE_CONTROL_TRANSPARENCY_SELECT)|
FIELD_MASK(DE_CONTROL_TRANSPARENCY);
return de_ctrl;
}
int hw_imageblit(
struct lynx_accel * accel,
unsigned char *pSrcbuf, /* pointer to start of source buffer in system memory */
int srcDelta, /* Pitch value (in bytes) of the source buffer, +ive means top down and -ive mean button up */
unsigned int startBit, /* Mono data can start at any bit in a byte, this value should be 0 to 7 */
unsigned int dBase, /* Address of destination: offset in frame buffer */
unsigned int dPitch, /* Pitch value of destination surface in BYTE */
unsigned int bytePerPixel, /* Color depth of destination surface */
unsigned int dx,
unsigned int dy, /* Starting coordinate of destination surface */
unsigned int width,
unsigned int height, /* width and height of rectange in pixel value */
unsigned int fColor, /* Foreground color (corresponding to a 1 in the monochrome data */
unsigned int bColor, /* Background color (corresponding to a 0 in the monochrome data */
unsigned int rop2) /* ROP value */
{
unsigned int ulBytesPerScan;
unsigned int ul4BytesPerScan;
unsigned int ulBytesRemain;
unsigned int de_ctrl = 0;
unsigned char ajRemain[4];
int i, j;
startBit &= 7; /* Just make sure the start bit is within legal range */
ulBytesPerScan = (width + startBit + 7) / 8;
ul4BytesPerScan = ulBytesPerScan & ~3;
ulBytesRemain = ulBytesPerScan & 3;
if(accel->de_wait() != 0)
{
// inf_msg("*** ImageBlit return -1 ***\n");
return -1;
}
/* 2D Source Base.
Use 0 for HOST Blt.
*/
write_dpr(accel,DE_WINDOW_SOURCE_BASE, 0);
/* 2D Destination Base.
It is an address offset (128 bit aligned) from the beginning of frame buffer.
*/
write_dpr(accel,DE_WINDOW_DESTINATION_BASE, dBase);
#if 0
/* Program pitch (distance between the 1st points of two adjacent lines).
Note that input pitch is BYTE value, but the 2D Pitch register uses
pixel values. Need Byte to pixel convertion.
*/
if(bytePerPixel == 3 ){
dx *= 3;
width *= 3;
startBit *= 3;
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0, DE_PITCH, DESTINATION, dPitch) |
FIELD_VALUE(0, DE_PITCH, SOURCE, dPitch));//dpr10
}
else
#endif
{
write_dpr(accel,DE_PITCH,
FIELD_VALUE(0, DE_PITCH, DESTINATION, dPitch/bytePerPixel) |
FIELD_VALUE(0, DE_PITCH, SOURCE, dPitch/bytePerPixel));//dpr10
}
/* Screen Window width in Pixels.
2D engine uses this value to calculate the linear address in frame buffer for a given point.
*/
write_dpr(accel,DE_WINDOW_WIDTH,
FIELD_VALUE(0, DE_WINDOW_WIDTH, DESTINATION, (dPitch/bytePerPixel)) |
FIELD_VALUE(0, DE_WINDOW_WIDTH, SOURCE, (dPitch/bytePerPixel)));
/* Note: For 2D Source in Host Write, only X_K1_MONO field is needed, and Y_K2 field is not used.
For mono bitmap, use startBit for X_K1. */
write_dpr(accel,DE_SOURCE,
FIELD_SET (0, DE_SOURCE, WRAP, DISABLE) |
FIELD_VALUE(0, DE_SOURCE, X_K1_MONO, startBit));//dpr00
write_dpr(accel,DE_DESTINATION,
FIELD_SET (0, DE_DESTINATION, WRAP, DISABLE) |
FIELD_VALUE(0, DE_DESTINATION, X, dx) |
FIELD_VALUE(0, DE_DESTINATION, Y, dy));//dpr04
write_dpr(accel,DE_DIMENSION,
FIELD_VALUE(0, DE_DIMENSION, X, width) |
FIELD_VALUE(0, DE_DIMENSION, Y_ET, height));//dpr08
write_dpr(accel,DE_FOREGROUND, fColor);
write_dpr(accel,DE_BACKGROUND, bColor);
de_ctrl = FIELD_VALUE(0, DE_CONTROL, ROP, rop2) |
FIELD_SET(0, DE_CONTROL, ROP_SELECT, ROP2) |
FIELD_SET(0, DE_CONTROL, COMMAND, HOST_WRITE) |
FIELD_SET(0, DE_CONTROL, HOST, MONO) |
FIELD_SET(0, DE_CONTROL, STATUS, START);
write_dpr(accel,DE_CONTROL, de_ctrl | deGetTransparency(accel));
/* Write MONO data (line by line) to 2D Engine data port */
for (i=0; i<height; i++)
{
/* For each line, send the data in chunks of 4 bytes */
for (j=0; j<(ul4BytesPerScan/4); j++)
{
write_dpPort(accel, *(unsigned int *)(pSrcbuf + (j * 4)));
}
if (ulBytesRemain)
{
memcpy(ajRemain, pSrcbuf+ul4BytesPerScan, ulBytesRemain);
write_dpPort(accel, *(unsigned int *)ajRemain);
}
pSrcbuf += srcDelta;
}
return 0;
}
#ifndef ACCEL_H__
#define ACCEL_H__
#define HW_ROP2_COPY 0xc
#define HW_ROP2_XOR 0x6
/* notes: below address are the offset value from de_base_address (0x100000)*/
/* for sm718/750/502 de_base is at mmreg_1mb*/
#define DE_BASE_ADDR_TYPE1 0x100000
/* for sm712,de_base is at mmreg_32kb */
#define DE_BASE_ADDR_TYPE2 0x8000
/* for sm722,de_base is at mmreg_0 */
#define DE_BASE_ADDR_TYPE3 0
/* type1 data port address is at mmreg_0x110000*/
#define DE_PORT_ADDR_TYPE1 0x110000
/* for sm712,data port address is at mmreg_0 */
#define DE_PORT_ADDR_TYPE2 0x100000
/* for sm722,data port address is at mmreg_1mb */
#define DE_PORT_ADDR_TYPE3 0x100000
#define DE_SOURCE 0x0
#define DE_SOURCE_WRAP 31:31
#define DE_SOURCE_WRAP_DISABLE 0
#define DE_SOURCE_WRAP_ENABLE 1
#define DE_SOURCE_X_K1 29:16
#define DE_SOURCE_Y_K2 15:0
#define DE_SOURCE_X_K1_MONO 20:16
#define DE_DESTINATION 0x4
#define DE_DESTINATION_WRAP 31:31
#define DE_DESTINATION_WRAP_DISABLE 0
#define DE_DESTINATION_WRAP_ENABLE 1
#define DE_DESTINATION_X 28:16
#define DE_DESTINATION_Y 15:0
#define DE_DIMENSION 0x8
#define DE_DIMENSION_X 28:16
#define DE_DIMENSION_Y_ET 15:0
#define DE_CONTROL 0xC
#define DE_CONTROL_STATUS 31:31
#define DE_CONTROL_STATUS_STOP 0
#define DE_CONTROL_STATUS_START 1
#define DE_CONTROL_PATTERN 30:30
#define DE_CONTROL_PATTERN_MONO 0
#define DE_CONTROL_PATTERN_COLOR 1
#define DE_CONTROL_UPDATE_DESTINATION_X 29:29
#define DE_CONTROL_UPDATE_DESTINATION_X_DISABLE 0
#define DE_CONTROL_UPDATE_DESTINATION_X_ENABLE 1
#define DE_CONTROL_QUICK_START 28:28
#define DE_CONTROL_QUICK_START_DISABLE 0
#define DE_CONTROL_QUICK_START_ENABLE 1
#define DE_CONTROL_DIRECTION 27:27
#define DE_CONTROL_DIRECTION_LEFT_TO_RIGHT 0
#define DE_CONTROL_DIRECTION_RIGHT_TO_LEFT 1
#define DE_CONTROL_MAJOR 26:26
#define DE_CONTROL_MAJOR_X 0
#define DE_CONTROL_MAJOR_Y 1
#define DE_CONTROL_STEP_X 25:25
#define DE_CONTROL_STEP_X_POSITIVE 1
#define DE_CONTROL_STEP_X_NEGATIVE 0
#define DE_CONTROL_STEP_Y 24:24
#define DE_CONTROL_STEP_Y_POSITIVE 1
#define DE_CONTROL_STEP_Y_NEGATIVE 0
#define DE_CONTROL_STRETCH 23:23
#define DE_CONTROL_STRETCH_DISABLE 0
#define DE_CONTROL_STRETCH_ENABLE 1
#define DE_CONTROL_HOST 22:22
#define DE_CONTROL_HOST_COLOR 0
#define DE_CONTROL_HOST_MONO 1
#define DE_CONTROL_LAST_PIXEL 21:21
#define DE_CONTROL_LAST_PIXEL_OFF 0
#define DE_CONTROL_LAST_PIXEL_ON 1
#define DE_CONTROL_COMMAND 20:16
#define DE_CONTROL_COMMAND_BITBLT 0
#define DE_CONTROL_COMMAND_RECTANGLE_FILL 1
#define DE_CONTROL_COMMAND_DE_TILE 2
#define DE_CONTROL_COMMAND_TRAPEZOID_FILL 3
#define DE_CONTROL_COMMAND_ALPHA_BLEND 4
#define DE_CONTROL_COMMAND_RLE_STRIP 5
#define DE_CONTROL_COMMAND_SHORT_STROKE 6
#define DE_CONTROL_COMMAND_LINE_DRAW 7
#define DE_CONTROL_COMMAND_HOST_WRITE 8
#define DE_CONTROL_COMMAND_HOST_READ 9
#define DE_CONTROL_COMMAND_HOST_WRITE_BOTTOM_UP 10
#define DE_CONTROL_COMMAND_ROTATE 11
#define DE_CONTROL_COMMAND_FONT 12
#define DE_CONTROL_COMMAND_TEXTURE_LOAD 15
#define DE_CONTROL_ROP_SELECT 15:15
#define DE_CONTROL_ROP_SELECT_ROP3 0
#define DE_CONTROL_ROP_SELECT_ROP2 1
#define DE_CONTROL_ROP2_SOURCE 14:14
#define DE_CONTROL_ROP2_SOURCE_BITMAP 0
#define DE_CONTROL_ROP2_SOURCE_PATTERN 1
#define DE_CONTROL_MONO_DATA 13:12
#define DE_CONTROL_MONO_DATA_NOT_PACKED 0
#define DE_CONTROL_MONO_DATA_8_PACKED 1
#define DE_CONTROL_MONO_DATA_16_PACKED 2
#define DE_CONTROL_MONO_DATA_32_PACKED 3
#define DE_CONTROL_REPEAT_ROTATE 11:11
#define DE_CONTROL_REPEAT_ROTATE_DISABLE 0
#define DE_CONTROL_REPEAT_ROTATE_ENABLE 1
#define DE_CONTROL_TRANSPARENCY_MATCH 10:10
#define DE_CONTROL_TRANSPARENCY_MATCH_OPAQUE 0
#define DE_CONTROL_TRANSPARENCY_MATCH_TRANSPARENT 1
#define DE_CONTROL_TRANSPARENCY_SELECT 9:9
#define DE_CONTROL_TRANSPARENCY_SELECT_SOURCE 0
#define DE_CONTROL_TRANSPARENCY_SELECT_DESTINATION 1
#define DE_CONTROL_TRANSPARENCY 8:8
#define DE_CONTROL_TRANSPARENCY_DISABLE 0
#define DE_CONTROL_TRANSPARENCY_ENABLE 1
#define DE_CONTROL_ROP 7:0
// Pseudo fields.
#define DE_CONTROL_SHORT_STROKE_DIR 27:24
#define DE_CONTROL_SHORT_STROKE_DIR_225 0
#define DE_CONTROL_SHORT_STROKE_DIR_135 1
#define DE_CONTROL_SHORT_STROKE_DIR_315 2
#define DE_CONTROL_SHORT_STROKE_DIR_45 3
#define DE_CONTROL_SHORT_STROKE_DIR_270 4
#define DE_CONTROL_SHORT_STROKE_DIR_90 5
#define DE_CONTROL_SHORT_STROKE_DIR_180 8
#define DE_CONTROL_SHORT_STROKE_DIR_0 10
#define DE_CONTROL_ROTATION 25:24
#define DE_CONTROL_ROTATION_0 0
#define DE_CONTROL_ROTATION_270 1
#define DE_CONTROL_ROTATION_90 2
#define DE_CONTROL_ROTATION_180 3
#define DE_PITCH 0x000010
#define DE_PITCH_DESTINATION 28:16
#define DE_PITCH_SOURCE 12:0
#define DE_FOREGROUND 0x000014
#define DE_FOREGROUND_COLOR 31:0
#define DE_BACKGROUND 0x000018
#define DE_BACKGROUND_COLOR 31:0
#define DE_STRETCH_FORMAT 0x00001C
#define DE_STRETCH_FORMAT_PATTERN_XY 30:30
#define DE_STRETCH_FORMAT_PATTERN_XY_NORMAL 0
#define DE_STRETCH_FORMAT_PATTERN_XY_OVERWRITE 1
#define DE_STRETCH_FORMAT_PATTERN_Y 29:27
#define DE_STRETCH_FORMAT_PATTERN_X 25:23
#define DE_STRETCH_FORMAT_PIXEL_FORMAT 21:20
#define DE_STRETCH_FORMAT_PIXEL_FORMAT_8 0
#define DE_STRETCH_FORMAT_PIXEL_FORMAT_16 1
#define DE_STRETCH_FORMAT_PIXEL_FORMAT_32 2
#define DE_STRETCH_FORMAT_PIXEL_FORMAT_24 3
#define DE_STRETCH_FORMAT_ADDRESSING 19:16
#define DE_STRETCH_FORMAT_ADDRESSING_XY 0
#define DE_STRETCH_FORMAT_ADDRESSING_LINEAR 15
#define DE_STRETCH_FORMAT_SOURCE_HEIGHT 11:0
#define DE_COLOR_COMPARE 0x000020
#define DE_COLOR_COMPARE_COLOR 23:0
#define DE_COLOR_COMPARE_MASK 0x000024
#define DE_COLOR_COMPARE_MASK_MASKS 23:0
#define DE_MASKS 0x000028
#define DE_MASKS_BYTE_MASK 31:16
#define DE_MASKS_BIT_MASK 15:0
#define DE_CLIP_TL 0x00002C
#define DE_CLIP_TL_TOP 31:16
#define DE_CLIP_TL_STATUS 13:13
#define DE_CLIP_TL_STATUS_DISABLE 0
#define DE_CLIP_TL_STATUS_ENABLE 1
#define DE_CLIP_TL_INHIBIT 12:12
#define DE_CLIP_TL_INHIBIT_OUTSIDE 0
#define DE_CLIP_TL_INHIBIT_INSIDE 1
#define DE_CLIP_TL_LEFT 11:0
#define DE_CLIP_BR 0x000030
#define DE_CLIP_BR_BOTTOM 31:16
#define DE_CLIP_BR_RIGHT 12:0
#define DE_MONO_PATTERN_LOW 0x000034
#define DE_MONO_PATTERN_LOW_PATTERN 31:0
#define DE_MONO_PATTERN_HIGH 0x000038
#define DE_MONO_PATTERN_HIGH_PATTERN 31:0
#define DE_WINDOW_WIDTH 0x00003C
#define DE_WINDOW_WIDTH_DESTINATION 28:16
#define DE_WINDOW_WIDTH_SOURCE 12:0
#define DE_WINDOW_SOURCE_BASE 0x000040
#define DE_WINDOW_SOURCE_BASE_EXT 27:27
#define DE_WINDOW_SOURCE_BASE_EXT_LOCAL 0
#define DE_WINDOW_SOURCE_BASE_EXT_EXTERNAL 1
#define DE_WINDOW_SOURCE_BASE_CS 26:26
#define DE_WINDOW_SOURCE_BASE_CS_0 0
#define DE_WINDOW_SOURCE_BASE_CS_1 1
#define DE_WINDOW_SOURCE_BASE_ADDRESS 25:0
#define DE_WINDOW_DESTINATION_BASE 0x000044
#define DE_WINDOW_DESTINATION_BASE_EXT 27:27
#define DE_WINDOW_DESTINATION_BASE_EXT_LOCAL 0
#define DE_WINDOW_DESTINATION_BASE_EXT_EXTERNAL 1
#define DE_WINDOW_DESTINATION_BASE_CS 26:26
#define DE_WINDOW_DESTINATION_BASE_CS_0 0
#define DE_WINDOW_DESTINATION_BASE_CS_1 1
#define DE_WINDOW_DESTINATION_BASE_ADDRESS 25:0
#define DE_ALPHA 0x000048
#define DE_ALPHA_VALUE 7:0
#define DE_WRAP 0x00004C
#define DE_WRAP_X 31:16
#define DE_WRAP_Y 15:0
#define DE_STATUS 0x000050
#define DE_STATUS_CSC 1:1
#define DE_STATUS_CSC_CLEAR 0
#define DE_STATUS_CSC_NOT_ACTIVE 0
#define DE_STATUS_CSC_ACTIVE 1
#define DE_STATUS_2D 0:0
#define DE_STATUS_2D_CLEAR 0
#define DE_STATUS_2D_NOT_ACTIVE 0
#define DE_STATUS_2D_ACTIVE 1
/* blt direction */
#define TOP_TO_BOTTOM 0
#define LEFT_TO_RIGHT 0
#define BOTTOM_TO_TOP 1
#define RIGHT_TO_LEFT 1
void hw_set2dformat(struct lynx_accel * accel,int fmt);
void hw_de_init(struct lynx_accel * accel);
int hw_fillrect(struct lynx_accel * accel,
u32 base,u32 pitch,u32 Bpp,
u32 x,u32 y,u32 width,u32 height,
u32 color,u32 rop);
int hw_copyarea(
struct lynx_accel * accel,
unsigned int sBase, /* Address of source: offset in frame buffer */
unsigned int sPitch, /* Pitch value of source surface in BYTE */
unsigned int sx,
unsigned int sy, /* Starting coordinate of source surface */
unsigned int dBase, /* Address of destination: offset in frame buffer */
unsigned int dPitch, /* Pitch value of destination surface in BYTE */
unsigned int bpp, /* Color depth of destination surface */
unsigned int dx,
unsigned int dy, /* Starting coordinate of destination surface */
unsigned int width,
unsigned int height, /* width and height of rectangle in pixel value */
unsigned int rop2);
int hw_imageblit(
struct lynx_accel * accel,
unsigned char *pSrcbuf, /* pointer to start of source buffer in system memory */
int srcDelta, /* Pitch value (in bytes) of the source buffer, +ive means top down and -ive mean button up */
unsigned int startBit, /* Mono data can start at any bit in a byte, this value should be 0 to 7 */
unsigned int dBase, /* Address of destination: offset in frame buffer */
unsigned int dPitch, /* Pitch value of destination surface in BYTE */
unsigned int bytePerPixel, /* Color depth of destination surface */
unsigned int dx,
unsigned int dy, /* Starting coordinate of destination surface */
unsigned int width,
unsigned int height, /* width and height of rectange in pixel value */
unsigned int fColor, /* Foreground color (corresponding to a 1 in the monochrome data */
unsigned int bColor, /* Background color (corresponding to a 0 in the monochrome data */
unsigned int rop2);
#endif
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/errno.h>
#include<linux/string.h>
#include<linux/mm.h>
#include<linux/slab.h>
#include<linux/delay.h>
#include<linux/fb.h>
#include<linux/ioport.h>
#include<linux/init.h>
#include<linux/pci.h>
#include<linux/vmalloc.h>
#include<linux/pagemap.h>
#include <linux/console.h>
#include<linux/platform_device.h>
#include<linux/screen_info.h>
#include "sm750.h"
#include "sm750_help.h"
#include "sm750_cursor.h"
#define PEEK32(addr) \
readl(cursor->mmio + (addr))
#define POKE32(addr,data) \
writel((data),cursor->mmio + (addr))
/* cursor control for voyager and 718/750*/
#define HWC_ADDRESS 0x0
#define HWC_ADDRESS_ENABLE 31:31
#define HWC_ADDRESS_ENABLE_DISABLE 0
#define HWC_ADDRESS_ENABLE_ENABLE 1
#define HWC_ADDRESS_EXT 27:27
#define HWC_ADDRESS_EXT_LOCAL 0
#define HWC_ADDRESS_EXT_EXTERNAL 1
#define HWC_ADDRESS_CS 26:26
#define HWC_ADDRESS_CS_0 0
#define HWC_ADDRESS_CS_1 1
#define HWC_ADDRESS_ADDRESS 25:0
#define HWC_LOCATION 0x4
#define HWC_LOCATION_TOP 27:27
#define HWC_LOCATION_TOP_INSIDE 0
#define HWC_LOCATION_TOP_OUTSIDE 1
#define HWC_LOCATION_Y 26:16
#define HWC_LOCATION_LEFT 11:11
#define HWC_LOCATION_LEFT_INSIDE 0
#define HWC_LOCATION_LEFT_OUTSIDE 1
#define HWC_LOCATION_X 10:0
#define HWC_COLOR_12 0x8
#define HWC_COLOR_12_2_RGB565 31:16
#define HWC_COLOR_12_1_RGB565 15:0
#define HWC_COLOR_3 0xC
#define HWC_COLOR_3_RGB565 15:0
/* hw_cursor_xxx works for voyager,718 and 750 */
void hw_cursor_enable(struct lynx_cursor * cursor)
{
u32 reg;
reg = FIELD_VALUE(0,HWC_ADDRESS,ADDRESS,cursor->offset)|
FIELD_SET(0,HWC_ADDRESS,EXT,LOCAL)|
FIELD_SET(0,HWC_ADDRESS,ENABLE,ENABLE);
POKE32(HWC_ADDRESS,reg);
}
void hw_cursor_disable(struct lynx_cursor * cursor)
{
POKE32(HWC_ADDRESS,0);
}
void hw_cursor_setSize(struct lynx_cursor * cursor,
int w,int h)
{
cursor->w = w;
cursor->h = h;
}
void hw_cursor_setPos(struct lynx_cursor * cursor,
int x,int y)
{
u32 reg;
reg = FIELD_VALUE(0,HWC_LOCATION,Y,y)|
FIELD_VALUE(0,HWC_LOCATION,X,x);
POKE32(HWC_LOCATION,reg);
}
void hw_cursor_setColor(struct lynx_cursor * cursor,
u32 fg,u32 bg)
{
POKE32(HWC_COLOR_12,(fg<<16)|(bg&0xffff));
POKE32(HWC_COLOR_3,0xffe0);
}
void hw_cursor_setData(struct lynx_cursor * cursor,
u16 rop,const u8* pcol,const u8* pmsk)
{
int i,j,count,pitch,offset;
u8 color,mask,opr;
u16 data;
u16 * pbuffer,*pstart;
static ulong odd = 0;
/* in byte*/
pitch = cursor->w >> 3;
/* in byte */
count = pitch * cursor->h;
/* in ushort */
offset = cursor->maxW * 2 / 8 / 2;
data = 0;
pstart = (u16 *)cursor->vstart;
pbuffer = pstart;
/*
if(odd &1){
hw_cursor_setData2(cursor,rop,pcol,pmsk);
}
odd++;
if(odd > 0xfffffff0)
odd=0;
*/
for(i=0;i<count;i++)
{
color = *pcol++;
mask = *pmsk++;
data = 0;
/* either method below works well,
* but method 2 shows no lag
* and method 1 seems a bit wrong*/
#if 0
if(rop == ROP_XOR)
opr = mask ^ color;
else
opr = mask & color;
for(j=0;j<8;j++)
{
if(opr & (0x80 >> j))
{ //use fg color,id = 2
data |= 2 << (j*2);
}else{
//use bg color,id = 1
data |= 1 << (j*2);
}
}
#else
for(j=0;j<8;j++){
if(mask & (0x80>>j)){
if(rop == ROP_XOR)
opr = mask ^ color;
else
opr = mask & color;
/* 2 stands for forecolor and 1 for backcolor */
data |= ((opr & (0x80>>j))?2:1)<<(j*2);
}
}
#endif
*pbuffer = data;
/* assume pitch is 1,2,4,8,...*/
#if 0
if(!((i+1)&(pitch-1))) /* below line equal to is line */
#else
if((i+1) % pitch == 0)
#endif
{
/* need a return */
pstart += offset;
pbuffer = pstart;
}else{
pbuffer++;
}
}
}
void hw_cursor_setData2(struct lynx_cursor * cursor,
u16 rop,const u8* pcol,const u8* pmsk)
{
int i,j,count,pitch,offset;
u8 color,mask,opr;
u16 data;
u16 * pbuffer,*pstart;
/* in byte*/
pitch = cursor->w >> 3;
/* in byte */
count = pitch * cursor->h;
/* in ushort */
offset = cursor->maxW * 2 / 8 / 2;
data = 0;
pstart = (u16 *)cursor->vstart;
pbuffer = pstart;
for(i=0;i<count;i++)
{
color = *pcol++;
mask = *pmsk++;
data = 0;
/* either method below works well, but method 2 shows no lag */
#if 0
if(rop == ROP_XOR)
opr = mask ^ color;
else
opr = mask & color;
for(j=0;j<8;j++)
{
if(opr & (0x80 >> j))
{ //use fg color,id = 2
data |= 2 << (j*2);
}else{
//use bg color,id = 1
data |= 1 << (j*2);
}
}
#else
for(j=0;j<8;j++){
if(mask & (1<<j))
data |= ((color & (1<<j))?1:2)<<(j*2);
}
#endif
*pbuffer = data;
/* assume pitch is 1,2,4,8,...*/
if(!(i&(pitch-1)))
//if((i+1) % pitch == 0)
{
/* need a return */
pstart += offset;
pbuffer = pstart;
}else{
pbuffer++;
}
}
return 0;
}
#ifndef LYNX_CURSOR_H__
#define LYNX_CURSOR_H__
/* hw_cursor_xxx works for voyager,718 and 750 */
void hw_cursor_enable(struct lynx_cursor * cursor);
void hw_cursor_disable(struct lynx_cursor * cursor);
void hw_cursor_setSize(struct lynx_cursor * cursor,
int w,int h);
void hw_cursor_setPos(struct lynx_cursor * cursor,
int x,int y);
void hw_cursor_setColor(struct lynx_cursor * cursor,
u32 fg,u32 bg);
void hw_cursor_setData(struct lynx_cursor * cursor,
u16 rop,const u8* data,const u8* mask);
void hw_cursor_setData2(struct lynx_cursor * cursor,
u16 rop,const u8* data,const u8* mask);
#endif
#ifndef LYNX_HELP_H__
#define LYNX_HELP_H__
/*****************************************************************************\
* FIELD MACROS *
\*****************************************************************************/
#define _LSB(f) (0 ? f)
#define _MSB(f) (1 ? f)
#define _COUNT(f) (_MSB(f) - _LSB(f) + 1)
#define RAW_MASK(f) (0xFFFFFFFF >> (32 - _COUNT(f)))
#define GET_MASK(f) (RAW_MASK(f) << _LSB(f))
#define GET_FIELD(d,f) (((d) >> _LSB(f)) & RAW_MASK(f))
#define TEST_FIELD(d,f,v) (GET_FIELD(d,f) == f ## _ ## v)
#define SET_FIELD(d,f,v) (((d) & ~GET_MASK(f)) | \
(((f ## _ ## v) & RAW_MASK(f)) << _LSB(f)))
#define SET_FIELDV(d,f,v) (((d) & ~GET_MASK(f)) | \
(((v) & RAW_MASK(f)) << _LSB(f)))
////////////////////////////////////////////////////////////////////////////////
// //
// Internal macros //
// //
////////////////////////////////////////////////////////////////////////////////
#define _F_START(f) (0 ? f)
#define _F_END(f) (1 ? f)
#define _F_SIZE(f) (1 + _F_END(f) - _F_START(f))
#define _F_MASK(f) (((1 << _F_SIZE(f)) - 1) << _F_START(f))
#define _F_NORMALIZE(v, f) (((v) & _F_MASK(f)) >> _F_START(f))
#define _F_DENORMALIZE(v, f) (((v) << _F_START(f)) & _F_MASK(f))
////////////////////////////////////////////////////////////////////////////////
// //
// Global macros //
// //
////////////////////////////////////////////////////////////////////////////////
#define FIELD_GET(x, reg, field) \
( \
_F_NORMALIZE((x), reg ## _ ## field) \
)
#define FIELD_SET(x, reg, field, value) \
( \
(x & ~_F_MASK(reg ## _ ## field)) \
| _F_DENORMALIZE(reg ## _ ## field ## _ ## value, reg ## _ ## field) \
)
#define FIELD_VALUE(x, reg, field, value) \
( \
(x & ~_F_MASK(reg ## _ ## field)) \
| _F_DENORMALIZE(value, reg ## _ ## field) \
)
#define FIELD_CLEAR(reg, field) \
( \
~ _F_MASK(reg ## _ ## field) \
)
////////////////////////////////////////////////////////////////////////////////
// //
// Field Macros //
// //
////////////////////////////////////////////////////////////////////////////////
#define FIELD_START(field) (0 ? field)
#define FIELD_END(field) (1 ? field)
#define FIELD_SIZE(field) (1 + FIELD_END(field) - FIELD_START(field))
#define FIELD_MASK(field) (((1 << (FIELD_SIZE(field)-1)) | ((1 << (FIELD_SIZE(field)-1)) - 1)) << FIELD_START(field))
#define FIELD_NORMALIZE(reg, field) (((reg) & FIELD_MASK(field)) >> FIELD_START(field))
#define FIELD_DENORMALIZE(field, value) (((value) << FIELD_START(field)) & FIELD_MASK(field))
#define FIELD_INIT(reg, field, value) FIELD_DENORMALIZE(reg ## _ ## field, \
reg ## _ ## field ## _ ## value)
#define FIELD_INIT_VAL(reg, field, value) \
(FIELD_DENORMALIZE(reg ## _ ## field, value))
#define FIELD_VAL_SET(x, r, f, v) x = x & ~FIELD_MASK(r ## _ ## f) \
| FIELD_DENORMALIZE(r ## _ ## f, r ## _ ## f ## _ ## v)
#define RGB(r, g, b) \
( \
(unsigned long) (((r) << 16) | ((g) << 8) | (b)) \
)
#define RGB16(r, g, b) \
( \
(unsigned short) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | (((b) & 0xF8) >> 3)) \
)
static inline unsigned int absDiff(unsigned int a,unsigned int b)
{
if(a<b)
return b-a;
else
return a-b;
}
/* n / d + 1 / 2 = (2n + d) / 2d */
#define roundedDiv(num,denom) ((2 * (num) + (denom)) / (2 * (denom)))
#define MB(x) ((x)<<20)
#define KB(x) ((x)<<10)
#define MHz(x) ((x) * 1000000)
#endif
#include <linux/version.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/errno.h>
#include<linux/string.h>
#include<linux/mm.h>
#include<linux/slab.h>
#include<linux/delay.h>
#include<linux/fb.h>
#include<linux/ioport.h>
#include<linux/init.h>
#include<linux/pci.h>
#include<linux/vmalloc.h>
#include<linux/pagemap.h>
#include <linux/console.h>
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
#include<linux/platform_device.h>
#include<linux/screen_info.h>
#include "sm750.h"
#include "sm750_hw.h"
#include "ddk750.h"
#include "sm750_accel.h"
int hw_sm750_map(struct lynx_share* share,struct pci_dev* pdev)
{
int ret;
struct sm750_share * spec_share;
spec_share = container_of(share,struct sm750_share,share);
ret = 0;
share->vidreg_start = pci_resource_start(pdev,1);
share->vidreg_size = MB(2);
pr_info("mmio phyAddr = %x\n",share->vidreg_start);
/* reserve the vidreg space of smi adaptor
* if you do this, u need to add release region code
* in lynxfb_remove, or memory will not be mapped again
* successfully
* */
if((ret = pci_request_region(pdev,1,"sm750fb")))
{
pr_err("Can not request PCI regions.\n");
goto exit;
}
/* now map mmio and vidmem*/
share->pvReg = ioremap_nocache(share->vidreg_start,share->vidreg_size);
if(!share->pvReg){
pr_err("mmio failed\n");
ret = -EFAULT;
goto exit;
}else{
pr_info("mmio virtual addr = %p\n",share->pvReg);
}
share->accel.dprBase = share->pvReg + DE_BASE_ADDR_TYPE1;
share->accel.dpPortBase = share->pvReg + DE_PORT_ADDR_TYPE1;
ddk750_set_mmio(share->pvReg,share->devid,share->revid);
share->vidmem_start = pci_resource_start(pdev,0);
/* don't use pdev_resource[x].end - resource[x].start to
* calculate the resource size,its only the maximum available
* size but not the actual size,use
* @hw_sm750_getVMSize function can be safe.
* */
share->vidmem_size = hw_sm750_getVMSize(share);
pr_info("video memory phyAddr = %x, size = %d bytes\n",
share->vidmem_start,share->vidmem_size);
/* reserve the vidmem space of smi adaptor */
#if 0
if((ret = pci_request_region(pdev,0,_moduleName_)))
{
pr_err("Can not request PCI regions.\n");
goto exit;
}
#endif
share->pvMem = ioremap(share->vidmem_start,
share->vidmem_size);
if(!share->pvMem){
pr_err("Map video memory failed\n");
ret = -EFAULT;
goto exit;
}else{
pr_info("video memory vaddr = %p\n",share->pvMem);
}
exit:
return ret;
}
int hw_sm750_inithw(struct lynx_share* share,struct pci_dev * pdev)
{
struct sm750_share * spec_share;
struct init_status * parm;
spec_share = container_of(share,struct sm750_share,share);
parm = &spec_share->state.initParm;
if(parm->chip_clk == 0)
parm->chip_clk = (getChipType() == SM750LE)?
DEFAULT_SM750LE_CHIP_CLOCK :
DEFAULT_SM750_CHIP_CLOCK;
if(parm->mem_clk == 0)
parm->mem_clk = parm->chip_clk;
if(parm->master_clk == 0)
parm->master_clk = parm->chip_clk/3;
ddk750_initHw((initchip_param_t *)&spec_share->state.initParm);
/* for sm718,open pci burst */
if(share->devid == 0x718){
POKE32(SYSTEM_CTRL,
FIELD_SET(PEEK32(SYSTEM_CTRL),SYSTEM_CTRL,PCI_BURST,ON));
}
/* sm750 use sii164, it can be setup with default value
* by on power, so initDVIDisp can be skipped */
#if 0
ddk750_initDVIDisp();
#endif
if(getChipType() != SM750LE)
{
/* does user need CRT ?*/
if(spec_share->state.nocrt){
POKE32(MISC_CTRL,
FIELD_SET(PEEK32(MISC_CTRL),
MISC_CTRL,
DAC_POWER,OFF));
/* shut off dpms */
POKE32(SYSTEM_CTRL,
FIELD_SET(PEEK32(SYSTEM_CTRL),
SYSTEM_CTRL,
DPMS,VNHN));
}else{
POKE32(MISC_CTRL,
FIELD_SET(PEEK32(MISC_CTRL),
MISC_CTRL,
DAC_POWER,ON));
/* turn on dpms */
POKE32(SYSTEM_CTRL,
FIELD_SET(PEEK32(SYSTEM_CTRL),
SYSTEM_CTRL,
DPMS,VPHP));
}
switch (spec_share->state.pnltype){
case sm750_doubleTFT:
case sm750_24TFT:
case sm750_dualTFT:
POKE32(PANEL_DISPLAY_CTRL,
FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL),
PANEL_DISPLAY_CTRL,
TFT_DISP,
spec_share->state.pnltype));
break;
}
}else{
/* for 750LE ,no DVI chip initilization makes Monitor no signal */
/* Set up GPIO for software I2C to program DVI chip in the
Xilinx SP605 board, in order to have video signal.
*/
swI2CInit(0,1);
/* Customer may NOT use CH7301 DVI chip, which has to be
initialized differently.
*/
if (swI2CReadReg(0xec, 0x4a) == 0x95)
{
/* The following register values for CH7301 are from
Chrontel app note and our experiment.
*/
pr_info("yes,CH7301 DVI chip found\n");
swI2CWriteReg(0xec, 0x1d, 0x16);
swI2CWriteReg(0xec, 0x21, 0x9);
swI2CWriteReg(0xec, 0x49, 0xC0);
pr_info("okay,CH7301 DVI chip setup done\n");
}
}
/* init 2d engine */
if(!share->accel_off){
hw_sm750_initAccel(share);
// share->accel.de_wait = hw_sm750_deWait;
}
return 0;
}
resource_size_t hw_sm750_getVMSize(struct lynx_share * share)
{
resource_size_t ret;
ret = ddk750_getVMSize();
return ret;
}
int hw_sm750_output_checkMode(struct lynxfb_output* output,struct fb_var_screeninfo* var)
{
return 0;
}
int hw_sm750_output_setMode(struct lynxfb_output* output,
struct fb_var_screeninfo* var,struct fb_fix_screeninfo* fix)
{
int ret;
disp_output_t dispSet;
int channel;
ret = 0;
dispSet = 0;
channel = *output->channel;
if(getChipType() != SM750LE){
if(channel == sm750_primary){
pr_info("primary channel\n");
if(output->paths & sm750_panel)
dispSet |= do_LCD1_PRI;
if(output->paths & sm750_crt)
dispSet |= do_CRT_PRI;
}else{
pr_info("secondary channel\n");
if(output->paths & sm750_panel)
dispSet |= do_LCD1_SEC;
if(output->paths & sm750_crt)
dispSet |= do_CRT_SEC;
}
ddk750_setLogicalDispOut(dispSet);
}else{
/* just open DISPLAY_CONTROL_750LE register bit 3:0*/
u32 reg;
reg = PEEK32(DISPLAY_CONTROL_750LE);
reg |= 0xf;
POKE32(DISPLAY_CONTROL_750LE,reg);
}
pr_info("ddk setlogicdispout done \n");
return ret;
}
void hw_sm750_output_clear(struct lynxfb_output* output)
{
return;
}
int hw_sm750_crtc_checkMode(struct lynxfb_crtc* crtc,struct fb_var_screeninfo* var)
{
struct lynx_share * share;
share = container_of(crtc,struct lynxfb_par,crtc)->share;
switch (var->bits_per_pixel){
case 8:
case 16:
break;
case 32:
if(share->revid == (unsigned char)SM750LE_REVISION_ID){
pr_debug("750le do not support 32bpp\n");
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
/*
set the controller's mode for @crtc charged with @var and @fix parameters
*/
int hw_sm750_crtc_setMode(struct lynxfb_crtc* crtc,
struct fb_var_screeninfo* var,
struct fb_fix_screeninfo* fix)
{
int ret,fmt;
u32 reg;
mode_parameter_t modparm;
clock_type_t clock;
struct lynx_share * share;
struct lynxfb_par * par;
ret = 0;
par = container_of(crtc,struct lynxfb_par,crtc);
share = par->share;
#if 1
if(!share->accel_off){
/* set 2d engine pixel format according to mode bpp */
switch(var->bits_per_pixel){
case 8:
fmt = 0;
break;
case 16:
fmt = 1;
break;
case 32:
default:
fmt = 2;
break;
}
hw_set2dformat(&share->accel,fmt);
}
#endif
/* set timing */
// modparm.pixel_clock = PS_TO_HZ(var->pixclock);
modparm.pixel_clock = ps_to_hz(var->pixclock);
modparm.vertical_sync_polarity = (var->sync & FB_SYNC_HOR_HIGH_ACT) ? POS:NEG;
modparm.horizontal_sync_polarity = (var->sync & FB_SYNC_VERT_HIGH_ACT) ? POS:NEG;
modparm.clock_phase_polarity = (var->sync& FB_SYNC_COMP_HIGH_ACT) ? POS:NEG;
modparm.horizontal_display_end = var->xres;
modparm.horizontal_sync_width = var->hsync_len;
modparm.horizontal_sync_start = var->xres + var->right_margin;
modparm.horizontal_total = var->xres + var->left_margin + var->right_margin + var->hsync_len;
modparm.vertical_display_end = var->yres;
modparm.vertical_sync_height = var->vsync_len;
modparm.vertical_sync_start = var->yres + var->lower_margin;
modparm.vertical_total = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
/* choose pll */
if(crtc->channel != sm750_secondary)
clock = PRIMARY_PLL;
else
clock = SECONDARY_PLL;
pr_debug("Request pixel clock = %lu\n",modparm.pixel_clock);
ret = ddk750_setModeTiming(&modparm,clock);
if(ret){
pr_err("Set mode timing failed\n");
goto exit;
}
if(crtc->channel != sm750_secondary){
/* set pitch, offset ,width,start address ,etc... */
POKE32(PANEL_FB_ADDRESS,
FIELD_SET(0,PANEL_FB_ADDRESS,STATUS,CURRENT)|
FIELD_SET(0,PANEL_FB_ADDRESS,EXT,LOCAL)|
FIELD_VALUE(0,PANEL_FB_ADDRESS,ADDRESS,crtc->oScreen));
reg = var->xres * (var->bits_per_pixel >> 3);
/* crtc->channel is not equal to par->index on numeric,be aware of that */
reg = PADDING(crtc->line_pad,reg);
POKE32(PANEL_FB_WIDTH,
FIELD_VALUE(0,PANEL_FB_WIDTH,WIDTH,reg)|
FIELD_VALUE(0,PANEL_FB_WIDTH,OFFSET,fix->line_length));
POKE32(PANEL_WINDOW_WIDTH,
FIELD_VALUE(0,PANEL_WINDOW_WIDTH,WIDTH,var->xres -1)|
FIELD_VALUE(0,PANEL_WINDOW_WIDTH,X,var->xoffset));
POKE32(PANEL_WINDOW_HEIGHT,
FIELD_VALUE(0,PANEL_WINDOW_HEIGHT,HEIGHT,var->yres_virtual - 1)|
FIELD_VALUE(0,PANEL_WINDOW_HEIGHT,Y,var->yoffset));
POKE32(PANEL_PLANE_TL,0);
POKE32(PANEL_PLANE_BR,
FIELD_VALUE(0,PANEL_PLANE_BR,BOTTOM,var->yres - 1)|
FIELD_VALUE(0,PANEL_PLANE_BR,RIGHT,var->xres - 1));
/* set pixel format */
reg = PEEK32(PANEL_DISPLAY_CTRL);
POKE32(PANEL_DISPLAY_CTRL,
FIELD_VALUE(reg,
PANEL_DISPLAY_CTRL,FORMAT,
(var->bits_per_pixel >> 4)
));
}else{
/* not implemented now */
POKE32(CRT_FB_ADDRESS,crtc->oScreen);
reg = var->xres * (var->bits_per_pixel >> 3);
/* crtc->channel is not equal to par->index on numeric,be aware of that */
reg = PADDING(crtc->line_pad,reg);
POKE32(CRT_FB_WIDTH,
FIELD_VALUE(0,CRT_FB_WIDTH,WIDTH,reg)|
FIELD_VALUE(0,CRT_FB_WIDTH,OFFSET,fix->line_length));
/* SET PIXEL FORMAT */
reg = PEEK32(CRT_DISPLAY_CTRL);
reg = FIELD_VALUE(reg,CRT_DISPLAY_CTRL,FORMAT,var->bits_per_pixel >> 4);
POKE32(CRT_DISPLAY_CTRL,reg);
}
exit:
return ret;
}
void hw_sm750_crtc_clear(struct lynxfb_crtc* crtc)
{
return;
}
int hw_sm750_setColReg(struct lynxfb_crtc* crtc,ushort index,
ushort red,ushort green,ushort blue)
{
static unsigned int add[]={PANEL_PALETTE_RAM,CRT_PALETTE_RAM};
POKE32(add[crtc->channel] + index*4 ,(red<<16)|(green<<8)|blue);
return 0;
}
int hw_sm750le_setBLANK(struct lynxfb_output * output,int blank){
int dpms,crtdb;
switch(blank)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_UNBLANK:
#else
case VESA_NO_BLANKING:
#endif
dpms = CRT_DISPLAY_CTRL_DPMS_0;
crtdb = CRT_DISPLAY_CTRL_BLANK_OFF;
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_NORMAL:
dpms = CRT_DISPLAY_CTRL_DPMS_0;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_VSYNC_SUSPEND:
#else
case VESA_VSYNC_SUSPEND:
#endif
dpms = CRT_DISPLAY_CTRL_DPMS_2;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_HSYNC_SUSPEND:
#else
case VESA_HSYNC_SUSPEND:
#endif
dpms = CRT_DISPLAY_CTRL_DPMS_1;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_POWERDOWN:
#else
case VESA_POWERDOWN:
#endif
dpms = CRT_DISPLAY_CTRL_DPMS_3;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
}
if(output->paths & sm750_crt){
POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL),CRT_DISPLAY_CTRL,DPMS,dpms));
POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL),CRT_DISPLAY_CTRL,BLANK,crtdb));
}
return 0;
}
int hw_sm750_setBLANK(struct lynxfb_output* output,int blank)
{
unsigned int dpms,pps,crtdb;
dpms = pps = crtdb = 0;
switch (blank)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_UNBLANK:
#else
case VESA_NO_BLANKING:
#endif
pr_info("flag = FB_BLANK_UNBLANK \n");
dpms = SYSTEM_CTRL_DPMS_VPHP;
pps = PANEL_DISPLAY_CTRL_DATA_ENABLE;
crtdb = CRT_DISPLAY_CTRL_BLANK_OFF;
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_NORMAL:
pr_info("flag = FB_BLANK_NORMAL \n");
dpms = SYSTEM_CTRL_DPMS_VPHP;
pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_VSYNC_SUSPEND:
#else
case VESA_VSYNC_SUSPEND:
#endif
dpms = SYSTEM_CTRL_DPMS_VNHP;
pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_HSYNC_SUSPEND:
#else
case VESA_HSYNC_SUSPEND:
#endif
dpms = SYSTEM_CTRL_DPMS_VPHN;
pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
case FB_BLANK_POWERDOWN:
#else
case VESA_POWERDOWN:
#endif
dpms = SYSTEM_CTRL_DPMS_VNHN;
pps = PANEL_DISPLAY_CTRL_DATA_DISABLE;
crtdb = CRT_DISPLAY_CTRL_BLANK_ON;
break;
}
if(output->paths & sm750_crt){
POKE32(SYSTEM_CTRL,FIELD_VALUE(PEEK32(SYSTEM_CTRL),SYSTEM_CTRL,DPMS,dpms));
POKE32(CRT_DISPLAY_CTRL,FIELD_VALUE(PEEK32(CRT_DISPLAY_CTRL),CRT_DISPLAY_CTRL,BLANK,crtdb));
}
if(output->paths & sm750_panel){
POKE32(PANEL_DISPLAY_CTRL,FIELD_VALUE(PEEK32(PANEL_DISPLAY_CTRL),PANEL_DISPLAY_CTRL,DATA,pps));
}
return 0;
}
void hw_sm750_initAccel(struct lynx_share * share)
{
u32 reg;
enable2DEngine(1);
if(getChipType() == SM750LE){
reg = PEEK32(DE_STATE1);
reg = FIELD_SET(reg,DE_STATE1,DE_ABORT,ON);
POKE32(DE_STATE1,reg);
reg = PEEK32(DE_STATE1);
reg = FIELD_SET(reg,DE_STATE1,DE_ABORT,OFF);
POKE32(DE_STATE1,reg);
}else{
/* engine reset */
reg = PEEK32(SYSTEM_CTRL);
reg = FIELD_SET(reg,SYSTEM_CTRL,DE_ABORT,ON);
POKE32(SYSTEM_CTRL,reg);
reg = PEEK32(SYSTEM_CTRL);
reg = FIELD_SET(reg,SYSTEM_CTRL,DE_ABORT,OFF);
POKE32(SYSTEM_CTRL,reg);
}
/* call 2d init */
share->accel.de_init(&share->accel);
}
int hw_sm750le_deWait()
{
int i=0x10000000;
while(i--){
unsigned int dwVal = PEEK32(DE_STATE2);
if((FIELD_GET(dwVal,DE_STATE2,DE_STATUS) == DE_STATE2_DE_STATUS_IDLE) &&
(FIELD_GET(dwVal,DE_STATE2,DE_FIFO) == DE_STATE2_DE_FIFO_EMPTY) &&
(FIELD_GET(dwVal,DE_STATE2,DE_MEM_FIFO) == DE_STATE2_DE_MEM_FIFO_EMPTY))
{
return 0;
}
}
/* timeout error */
return -1;
}
int hw_sm750_deWait()
{
int i=0x10000000;
while(i--){
unsigned int dwVal = PEEK32(SYSTEM_CTRL);
if((FIELD_GET(dwVal,SYSTEM_CTRL,DE_STATUS) == SYSTEM_CTRL_DE_STATUS_IDLE) &&
(FIELD_GET(dwVal,SYSTEM_CTRL,DE_FIFO) == SYSTEM_CTRL_DE_FIFO_EMPTY) &&
(FIELD_GET(dwVal,SYSTEM_CTRL,DE_MEM_FIFO) == SYSTEM_CTRL_DE_MEM_FIFO_EMPTY))
{
return 0;
}
}
/* timeout error */
return -1;
}
int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
const struct fb_var_screeninfo *var,
const struct fb_info *info)
{
uint32_t total;
//check params
if ((var->xoffset + var->xres > var->xres_virtual) ||
(var->yoffset + var->yres > var->yres_virtual)) {
return -EINVAL;
}
total = var->yoffset * info->fix.line_length +
((var->xoffset * var->bits_per_pixel) >> 3);
total += crtc->oScreen;
if (crtc->channel == sm750_primary) {
POKE32(PANEL_FB_ADDRESS,
FIELD_VALUE(PEEK32(PANEL_FB_ADDRESS),
PANEL_FB_ADDRESS, ADDRESS, total));
} else {
POKE32(CRT_FB_ADDRESS,
FIELD_VALUE(PEEK32(CRT_FB_ADDRESS),
CRT_FB_ADDRESS, ADDRESS, total));
}
return 0;
}
#ifndef LYNX_HW750_H__
#define LYNX_HW750_H__
#define DEFAULT_SM750_CHIP_CLOCK 290
#define DEFAULT_SM750LE_CHIP_CLOCK 333
#ifndef SM750LE_REVISION_ID
#define SM750LE_REVISION_ID (unsigned char)0xfe
#endif
//#define DEFAULT_MEM_CLOCK (DEFAULT_SM750_CHIP_CLOCK/1)
//#define DEFAULT_MASTER_CLOCK (DEFAULT_SM750_CHIP_CLOCK/3)
enum sm750_pnltype{
sm750_24TFT = 0,/* 24bit tft */
sm750_dualTFT = 2,/* dual 18 bit tft */
sm750_doubleTFT = 1,/* 36 bit double pixel tft */
};
/* vga channel is not concerned */
enum sm750_dataflow{
sm750_simul_pri,/* primary => all head */
sm750_simul_sec,/* secondary => all head */
sm750_dual_normal,/* primary => panel head and secondary => crt */
sm750_dual_swap,/* primary => crt head and secondary => panel */
};
enum sm750_channel{
sm750_primary = 0,
/* enum value equal to the register filed data */
sm750_secondary = 1,
};
enum sm750_path{
sm750_panel = 1,
sm750_crt = 2,
sm750_pnc = 3,/* panel and crt */
};
struct init_status{
ushort powerMode;
/* below three clocks are in unit of MHZ*/
ushort chip_clk;
ushort mem_clk;
ushort master_clk;
ushort setAllEngOff;
ushort resetMemory;
};
struct sm750_state{
struct init_status initParm;
enum sm750_pnltype pnltype;
enum sm750_dataflow dataflow;
int nocrt;
int xLCD;
int yLCD;
};
/* sm750_share stands for a presentation of two frame buffer
that use one sm750 adaptor, it is similiar to the super class of lynx_share
in C++
*/
struct sm750_share{
/* it's better to put lynx_share struct to the first place of sm750_share */
struct lynx_share share;
struct sm750_state state;
int hwCursor;
/* 0: no hardware cursor
1: primary crtc hw cursor enabled,
2: secondary crtc hw cursor enabled
3: both ctrc hw cursor enabled
*/
};
int hw_sm750_map(struct lynx_share* share,struct pci_dev* pdev);
int hw_sm750_inithw(struct lynx_share*,struct pci_dev *);
void hw_sm750_initAccel(struct lynx_share *);
int hw_sm750_deWait(void);
int hw_sm750le_deWait(void);
resource_size_t hw_sm750_getVMSize(struct lynx_share *);
int hw_sm750_output_checkMode(struct lynxfb_output*,struct fb_var_screeninfo*);
int hw_sm750_output_setMode(struct lynxfb_output*,struct fb_var_screeninfo*,struct fb_fix_screeninfo*);
int hw_sm750_crtc_checkMode(struct lynxfb_crtc*,struct fb_var_screeninfo*);
int hw_sm750_crtc_setMode(struct lynxfb_crtc*,struct fb_var_screeninfo*,struct fb_fix_screeninfo*);
int hw_sm750_setColReg(struct lynxfb_crtc*,ushort,ushort,ushort,ushort);
int hw_sm750_setBLANK(struct lynxfb_output*,int);
int hw_sm750le_setBLANK(struct lynxfb_output*,int);
void hw_sm750_crtc_clear(struct lynxfb_crtc*);
void hw_sm750_output_clear(struct lynxfb_output*);
int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
const struct fb_var_screeninfo *var,
const struct fb_info *info);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment