diff options
Diffstat (limited to 'drivers/video')
46 files changed, 7469 insertions, 994 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 27c1fb4b1e0d..55dc6fb6e909 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -186,6 +186,14 @@ config FB_SYS_FOPS depends on FB default n +config FB_WMT_GE_ROPS + tristate + depends on FB + default n + ---help--- + Include functions for accelerated rectangle filling and area + copying using WonderMedia Graphics Engine operations. + config FB_DEFERRED_IO bool depends on FB @@ -635,6 +643,72 @@ config FB_BFIN_LQ035Q1 To compile this driver as a module, choose M here: the module will be called bfin-lq035q1-fb. +config FB_BF537_LQ035 + tristate "SHARP LQ035 TFT LCD (BF537 STAMP)" + depends on FB && (BF534 || BF536 || BF537) && I2C_BLACKFIN_TWI + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select BFIN_GPTIMERS + help + This is the framebuffer device for a SHARP LQ035Q7DB03 TFT LCD + attached to a BF537. + + To compile this driver as a module, choose M here: the + module will be called bf537-lq035. + +config FB_BFIN_7393 + tristate "Blackfin ADV7393 Video encoder" + depends on FB && BLACKFIN + select I2C + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This is the framebuffer device for a ADV7393 video encoder + attached to a Blackfin on the PPI port. + If your Blackfin board has a ADV7393 select Y. + + To compile this driver as a module, choose M here: the + module will be called bfin_adv7393fb. + +choice + prompt "Video mode support" + depends on FB_BFIN_7393 + default NTSC + +config NTSC + bool 'NTSC 720x480' + +config PAL + bool 'PAL 720x576' + +config NTSC_640x480 + bool 'NTSC 640x480 (Experimental)' + +config PAL_640x480 + bool 'PAL 640x480 (Experimental)' + +config NTSC_YCBCR + bool 'NTSC 720x480 YCbCR input' + +config PAL_YCBCR + bool 'PAL 720x576 YCbCR input' + +endchoice + +choice + prompt "Size of ADV7393 frame buffer memory Single/Double Size" + depends on (FB_BFIN_7393) + default ADV7393_1XMEM + +config ADV7393_1XMEM + bool 'Single' + +config ADV7393_2XMEM + bool 'Double' +endchoice + config FB_STI tristate "HP STI frame buffer device support" depends on FB && PARISC @@ -750,24 +824,14 @@ config FB_N411 config FB_HGA tristate "Hercules mono graphics support" depends on FB && X86 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT help Say Y here if you have a Hercules mono graphics card. To compile this driver as a module, choose M here: the module will be called hgafb. - As this card technology is 15 years old, most people will answer N - here. - -config FB_HGA_ACCEL - bool "Hercules mono Acceleration functions (EXPERIMENTAL)" - depends on FB_HGA && EXPERIMENTAL - ---help--- - This will compile the Hercules mono graphics with - acceleration functions. + As this card technology is at least 25 years old, + most people will answer N here. config FB_SGIVW tristate "SGI Visual Workstation framebuffer support" @@ -1722,6 +1786,24 @@ config FB_AU1200 various panels and CRTs by passing in kernel cmd line option au1200fb:panel=<name>. +config FB_VT8500 + bool "VT8500 LCD Driver" + depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500 + select FB_WMT_GE_ROPS + select FB_SYS_IMAGEBLIT + help + This is the framebuffer driver for VIA VT8500 integrated LCD + controller. + +config FB_WM8505 + bool "WM8505 frame buffer support" + depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505 + select FB_WMT_GE_ROPS + select FB_SYS_IMAGEBLIT + help + This is the framebuffer driver for WonderMedia WM8505 + integrated LCD controller. + source "drivers/video/geode/Kconfig" config FB_HIT @@ -1850,6 +1932,16 @@ config FB_PXA_PARAMETERS <file:Documentation/fb/pxafb.txt> describes the available parameters. +config PXA3XX_GCU + tristate "PXA3xx 2D graphics accelerator driver" + depends on FB_PXA + help + Kernelspace driver for the 2D graphics controller unit (GCU) + found on PXA3xx processors. There is a counterpart driver in the + DirectFB suite, see http://www.directfb.org/ + + If you compile this as a module, it will be called pxa3xx_gcu. + config FB_MBX tristate "2700G LCD framebuffer support" depends on FB && ARCH_PXA @@ -2034,6 +2126,20 @@ config FB_SM501 If unsure, say N. +config FB_UDL + tristate "Displaylink USB Framebuffer support" + depends on FB && USB + select FB_MODE_HELPERS + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + ---help--- + This is a kernel framebuffer driver for DisplayLink USB devices. + Supports fbdev clients like xf86-video-fbdev, kdrive, fbi, and + mplayer -vo fbdev. Supports all USB 2.0 era DisplayLink devices. + To compile as a module, choose M here: the module name is udlfb. config FB_PNX4008_DUM tristate "Display Update Module support on Philips PNX4008 board" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 485e8ed1318c..8c8fabdff9d0 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB) += svgalib.o obj-$(CONFIG_FB_MACMODES) += macmodes.o obj-$(CONFIG_FB_DDC) += fb_ddc.o obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o +obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o # Hardware specific drivers go first obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o @@ -100,10 +101,13 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_PXA168) += pxa168fb.o +obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o obj-$(CONFIG_FB_AU1200) += au1200fb.o +obj-$(CONFIG_FB_VT8500) += vt8500lcdfb.o +obj-$(CONFIG_FB_WM8505) += wm8505fb.o obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o @@ -122,6 +126,7 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o obj-$(CONFIG_FB_SM501) += sm501fb.o +obj-$(CONFIG_FB_UDL) += udlfb.o obj-$(CONFIG_FB_XILINX) += xilinxfb.o obj-$(CONFIG_SH_MIPI_DSI) += sh_mipi_dsi.o obj-$(CONFIG_FB_SH_MOBILE_HDMI) += sh_mobile_hdmi.o @@ -141,9 +146,11 @@ obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_EFI) += efifb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o obj-$(CONFIG_FB_OF) += offb.o +obj-$(CONFIG_FB_BF537_LQ035) += bf537-lq035.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o +obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o obj-$(CONFIG_FB_MX3) += mx3fb.o obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 5bf91236c701..5a3ce3ad1ec8 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -2969,10 +2969,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, { struct atyfb_par *par = info->par; struct device_node *dp; - char prop[128]; - phandle node; - int len, i, j, ret; u32 mem, chip_id; + int i, j, ret; /* * Map memory-mapped registers. @@ -3088,23 +3086,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, aty_st_le32(MEM_CNTL, mem, par); } - /* - * If this is the console device, we will set default video - * settings to what the PROM left us with. - */ - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "aliases"); - if (node) { - len = prom_getproperty(node, "screen", prop, sizeof(prop)); - if (len > 0) { - prop[len] = '\0'; - node = prom_finddevice(prop); - } else - node = 0; - } - dp = pci_device_to_OF_node(pdev); - if (node == dp->phandle) { + if (dp == of_console_device) { struct fb_var_screeninfo *var = &default_var; unsigned int N, P, Q, M, T, R; u32 v_total, h_total; @@ -3112,9 +3095,9 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, u8 pll_regs[16]; u8 clock_cntl; - crtc.vxres = prom_getintdefault(node, "width", 1024); - crtc.vyres = prom_getintdefault(node, "height", 768); - var->bits_per_pixel = prom_getintdefault(node, "depth", 8); + crtc.vxres = of_getintprop_default(dp, "width", 1024); + crtc.vyres = of_getintprop_default(dp, "height", 768); + var->bits_per_pixel = of_getintprop_default(dp, "depth", 8); var->xoffset = var->yoffset = 0; crtc.h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); crtc.h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c index a4f4546f0be0..397d15eb1ea8 100644 --- a/drivers/video/backlight/cr_bllcd.c +++ b/drivers/video/backlight/cr_bllcd.c @@ -242,6 +242,7 @@ static int cr_backlight_remove(struct platform_device *pdev) backlight_device_unregister(crp->cr_backlight_device); lcd_device_unregister(crp->cr_lcd_device); pci_dev_put(lpc_dev); + kfree(crp); return 0; } diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c new file mode 100644 index 000000000000..18c507874ff1 --- /dev/null +++ b/drivers/video/bf537-lq035.c @@ -0,0 +1,914 @@ +/* + * Analog Devices Blackfin(BF537 STAMP) + SHARP TFT LCD. + * http://docs.blackfin.uclinux.org/doku.php?id=hw:cards:tft-lcd + * + * Copyright 2006-2010 Analog Devices Inc. + * Licensed under the GPL-2. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/device.h> +#include <linux/backlight.h> +#include <linux/lcd.h> +#include <linux/i2c.h> +#include <linux/spinlock.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include <asm/blackfin.h> +#include <asm/irq.h> +#include <asm/dpmc.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#define NO_BL 1 + +#define MAX_BRIGHENESS 95 +#define MIN_BRIGHENESS 5 +#define NBR_PALETTE 256 + +static const unsigned short ppi_pins[] = { + P_PPI0_CLK, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, 0 +}; + +static unsigned char *fb_buffer; /* RGB Buffer */ +static unsigned long *dma_desc_table; +static int t_conf_done, lq035_open_cnt; +static DEFINE_SPINLOCK(bfin_lq035_lock); + +static int landscape; +module_param(landscape, int, 0); +MODULE_PARM_DESC(landscape, + "LANDSCAPE use 320x240 instead of Native 240x320 Resolution"); + +static int bgr; +module_param(bgr, int, 0); +MODULE_PARM_DESC(bgr, + "BGR use 16-bit BGR-565 instead of RGB-565"); + +static int nocursor = 1; +module_param(nocursor, int, 0644); +MODULE_PARM_DESC(nocursor, "cursor enable/disable"); + +static unsigned long current_brightness; /* backlight */ + +/* AD5280 vcomm */ +static unsigned char vcomm_value = 150; +static struct i2c_client *ad5280_client; + +static void set_vcomm(void) +{ + int nr; + + if (!ad5280_client) + return; + + nr = i2c_smbus_write_byte_data(ad5280_client, 0x00, vcomm_value); + if (nr) + pr_err("i2c_smbus_write_byte_data fail: %d\n", nr); +} + +static int __devinit ad5280_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + ret = i2c_smbus_write_byte_data(client, 0x00, vcomm_value); + if (ret) { + dev_err(&client->dev, "write fail: %d\n", ret); + return ret; + } + + ad5280_client = client; + + return 0; +} + +static int __devexit ad5280_remove(struct i2c_client *client) +{ + ad5280_client = NULL; + return 0; +} + +static const struct i2c_device_id ad5280_id[] = { + {"bf537-lq035-ad5280", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad5280_id); + +static struct i2c_driver ad5280_driver = { + .driver = { + .name = "bf537-lq035-ad5280", + }, + .probe = ad5280_probe, + .remove = __devexit_p(ad5280_remove), + .id_table = ad5280_id, +}; + +#ifdef CONFIG_PNAV10 +#define MOD GPIO_PH13 + +#define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER0_CONFIG +#define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER0_WIDTH +#define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER0_PERIOD +#define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER0_COUNTER +#define TIMDIS_LP TIMDIS0 +#define TIMEN_LP TIMEN0 + +#define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG +#define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH +#define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD +#define TIMDIS_SPS TIMDIS1 +#define TIMEN_SPS TIMEN1 + +#define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER5_CONFIG +#define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER5_WIDTH +#define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER5_PERIOD +#define TIMDIS_SP TIMDIS5 +#define TIMEN_SP TIMEN5 + +#define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER2_CONFIG +#define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER2_WIDTH +#define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER2_PERIOD +#define TIMDIS_PS_CLS TIMDIS2 +#define TIMEN_PS_CLS TIMEN2 + +#define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER3_CONFIG +#define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER3_WIDTH +#define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER3_PERIOD +#define TIMDIS_REV TIMDIS3 +#define TIMEN_REV TIMEN3 +#define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER3_COUNTER + +#define FREQ_PPI_CLK (5*1024*1024) /* PPI_CLK 5MHz */ + +#define TIMERS {P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR5, 0} + +#else + +#define UD GPIO_PF13 /* Up / Down */ +#define MOD GPIO_PF10 +#define LBR GPIO_PF14 /* Left Right */ + +#define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER6_CONFIG +#define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER6_WIDTH +#define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER6_PERIOD +#define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER6_COUNTER +#define TIMDIS_LP TIMDIS6 +#define TIMEN_LP TIMEN6 + +#define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG +#define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH +#define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD +#define TIMDIS_SPS TIMDIS1 +#define TIMEN_SPS TIMEN1 + +#define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER0_CONFIG +#define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER0_WIDTH +#define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER0_PERIOD +#define TIMDIS_SP TIMDIS0 +#define TIMEN_SP TIMEN0 + +#define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER7_CONFIG +#define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER7_WIDTH +#define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER7_PERIOD +#define TIMDIS_PS_CLS TIMDIS7 +#define TIMEN_PS_CLS TIMEN7 + +#define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER5_CONFIG +#define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER5_WIDTH +#define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER5_PERIOD +#define TIMDIS_REV TIMDIS5 +#define TIMEN_REV TIMEN5 +#define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER5_COUNTER + +#define FREQ_PPI_CLK (6*1000*1000) /* PPI_CLK 6MHz */ +#define TIMERS {P_TMR0, P_TMR1, P_TMR5, P_TMR6, P_TMR7, 0} + +#endif + +#define LCD_X_RES 240 /* Horizontal Resolution */ +#define LCD_Y_RES 320 /* Vertical Resolution */ + +#define LCD_BBP 16 /* Bit Per Pixel */ + +/* the LCD and the DMA start counting differently; + * since one starts at 0 and the other starts at 1, + * we have a difference of 1 between START_LINES + * and U_LINES. + */ +#define START_LINES 8 /* lines for field flyback or field blanking signal */ +#define U_LINES 9 /* number of undisplayed blanking lines */ + +#define FRAMES_PER_SEC (60) + +#define DCLKS_PER_FRAME (FREQ_PPI_CLK/FRAMES_PER_SEC) +#define DCLKS_PER_LINE (DCLKS_PER_FRAME/(LCD_Y_RES+U_LINES)) + +#define PPI_CONFIG_VALUE (PORT_DIR|XFR_TYPE|DLEN_16|POLS) +#define PPI_DELAY_VALUE (0) +#define TIMER_CONFIG (PWM_OUT|PERIOD_CNT|TIN_SEL|CLK_SEL) + +#define ACTIVE_VIDEO_MEM_OFFSET (LCD_X_RES*START_LINES*(LCD_BBP/8)) +#define ACTIVE_VIDEO_MEM_SIZE (LCD_Y_RES*LCD_X_RES*(LCD_BBP/8)) +#define TOTAL_VIDEO_MEM_SIZE ((LCD_Y_RES+U_LINES)*LCD_X_RES*(LCD_BBP/8)) +#define TOTAL_DMA_DESC_SIZE (2 * sizeof(u32) * (LCD_Y_RES + U_LINES)) + +static void start_timers(void) /* CHECK with HW */ +{ + unsigned long flags; + + local_irq_save(flags); + + bfin_write_TIMER_ENABLE(TIMEN_REV); + SSYNC(); + + while (bfin_read_TIMER_REV_COUNTER() <= 11) + continue; + bfin_write_TIMER_ENABLE(TIMEN_LP); + SSYNC(); + + while (bfin_read_TIMER_LP_COUNTER() < 3) + continue; + bfin_write_TIMER_ENABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS); + SSYNC(); + t_conf_done = 1; + local_irq_restore(flags); +} + +static void config_timers(void) +{ + /* Stop timers */ + bfin_write_TIMER_DISABLE(TIMDIS_SP|TIMDIS_SPS|TIMDIS_REV| + TIMDIS_LP|TIMDIS_PS_CLS); + SSYNC(); + + /* LP, timer 6 */ + bfin_write_TIMER_LP_CONFIG(TIMER_CONFIG|PULSE_HI); + bfin_write_TIMER_LP_WIDTH(1); + + bfin_write_TIMER_LP_PERIOD(DCLKS_PER_LINE); + SSYNC(); + + /* SPS, timer 1 */ + bfin_write_TIMER_SPS_CONFIG(TIMER_CONFIG|PULSE_HI); + bfin_write_TIMER_SPS_WIDTH(DCLKS_PER_LINE*2); + bfin_write_TIMER_SPS_PERIOD((DCLKS_PER_LINE * (LCD_Y_RES+U_LINES))); + SSYNC(); + + /* SP, timer 0 */ + bfin_write_TIMER_SP_CONFIG(TIMER_CONFIG|PULSE_HI); + bfin_write_TIMER_SP_WIDTH(1); + bfin_write_TIMER_SP_PERIOD(DCLKS_PER_LINE); + SSYNC(); + + /* PS & CLS, timer 7 */ + bfin_write_TIMER_PS_CLS_CONFIG(TIMER_CONFIG); + bfin_write_TIMER_PS_CLS_WIDTH(LCD_X_RES + START_LINES); + bfin_write_TIMER_PS_CLS_PERIOD(DCLKS_PER_LINE); + + SSYNC(); + +#ifdef NO_BL + /* REV, timer 5 */ + bfin_write_TIMER_REV_CONFIG(TIMER_CONFIG|PULSE_HI); + + bfin_write_TIMER_REV_WIDTH(DCLKS_PER_LINE); + bfin_write_TIMER_REV_PERIOD(DCLKS_PER_LINE*2); + + SSYNC(); +#endif +} + +static void config_ppi(void) +{ + bfin_write_PPI_DELAY(PPI_DELAY_VALUE); + bfin_write_PPI_COUNT(LCD_X_RES-1); + /* 0x10 -> PORT_CFG -> 2 or 3 frame syncs */ + bfin_write_PPI_CONTROL((PPI_CONFIG_VALUE|0x10) & (~POLS)); +} + +static int config_dma(void) +{ + u32 i; + + if (landscape) { + + for (i = 0; i < U_LINES; ++i) { + /* blanking lines point to first line of fb_buffer */ + dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2]; + dma_desc_table[2*i+1] = (unsigned long)fb_buffer; + } + + for (i = U_LINES; i < U_LINES + LCD_Y_RES; ++i) { + /* visible lines */ + dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2]; + dma_desc_table[2*i+1] = (unsigned long)fb_buffer + + (LCD_Y_RES+U_LINES-1-i)*2; + } + + /* last descriptor points to first */ + dma_desc_table[2*(LCD_Y_RES+U_LINES-1)] = (unsigned long)&dma_desc_table[0]; + + set_dma_x_count(CH_PPI, LCD_X_RES); + set_dma_x_modify(CH_PPI, LCD_Y_RES * (LCD_BBP / 8)); + set_dma_y_count(CH_PPI, 0); + set_dma_y_modify(CH_PPI, 0); + set_dma_next_desc_addr(CH_PPI, (void *)dma_desc_table[0]); + set_dma_config(CH_PPI, DMAFLOW_LARGE | NDSIZE_4 | WDSIZE_16); + + } else { + + set_dma_config(CH_PPI, set_bfin_dma_config(DIR_READ, + DMA_FLOW_AUTO, + INTR_DISABLE, + DIMENSION_2D, + DATA_SIZE_16, + DMA_NOSYNC_KEEP_DMA_BUF)); + set_dma_x_count(CH_PPI, LCD_X_RES); + set_dma_x_modify(CH_PPI, LCD_BBP / 8); + set_dma_y_count(CH_PPI, LCD_Y_RES+U_LINES); + set_dma_y_modify(CH_PPI, LCD_BBP / 8); + set_dma_start_addr(CH_PPI, (unsigned long) fb_buffer); + } + + return 0; +} + +static int __devinit request_ports(void) +{ + u16 tmr_req[] = TIMERS; + + /* + UD: PF13 + MOD: PF10 + LBR: PF14 + PPI_CLK: PF15 + */ + + if (peripheral_request_list(ppi_pins, KBUILD_MODNAME)) { + pr_err("requesting PPI peripheral failed\n"); + return -EBUSY; + } + + if (peripheral_request_list(tmr_req, KBUILD_MODNAME)) { + peripheral_free_list(ppi_pins); + pr_err("requesting timer peripheral failed\n"); + return -EBUSY; + } + +#if (defined(UD) && defined(LBR)) + if (gpio_request(UD, KBUILD_MODNAME)) { + pr_err("requesting GPIO %d failed\n", UD); + return -EBUSY; + } + + if (gpio_request(LBR, KBUILD_MODNAME)) { + pr_err("requesting GPIO %d failed\n", LBR); + gpio_free(UD); + return -EBUSY; + } + + gpio_direction_output(UD, 0); + gpio_direction_output(LBR, 1); + +#endif + + if (gpio_request(MOD, KBUILD_MODNAME)) { + pr_err("requesting GPIO %d failed\n", MOD); +#if (defined(UD) && defined(LBR)) + gpio_free(LBR); + gpio_free(UD); +#endif + return -EBUSY; + } + + gpio_direction_output(MOD, 1); + + SSYNC(); + return 0; +} + +static void free_ports(void) +{ + u16 tmr_req[] = TIMERS; + + peripheral_free_list(ppi_pins); + peripheral_free_list(tmr_req); + +#if defined(UD) && defined(LBR) + gpio_free(LBR); + gpio_free(UD); +#endif + gpio_free(MOD); +} + +static struct fb_info bfin_lq035_fb; + +static struct fb_var_screeninfo bfin_lq035_fb_defined = { + .bits_per_pixel = LCD_BBP, + .activate = FB_ACTIVATE_TEST, + .xres = LCD_X_RES, /*default portrait mode RGB*/ + .yres = LCD_Y_RES, + .xres_virtual = LCD_X_RES, + .yres_virtual = LCD_Y_RES, + .height = -1, + .width = -1, + .left_margin = 0, + .right_margin = 0, + .upper_margin = 0, + .lower_margin = 0, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .transp = {0, 0, 0}, +}; + +static struct fb_fix_screeninfo bfin_lq035_fb_fix __devinitdata = { + .id = KBUILD_MODNAME, + .smem_len = ACTIVE_VIDEO_MEM_SIZE, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .line_length = LCD_X_RES*(LCD_BBP/8), + .accel = FB_ACCEL_NONE, +}; + + +static int bfin_lq035_fb_open(struct fb_info *info, int user) +{ + unsigned long flags; + + spin_lock_irqsave(&bfin_lq035_lock, flags); + lq035_open_cnt++; + spin_unlock_irqrestore(&bfin_lq035_lock, flags); + + if (lq035_open_cnt <= 1) { + bfin_write_PPI_CONTROL(0); + SSYNC(); + + set_vcomm(); + config_dma(); + config_ppi(); + + /* start dma */ + enable_dma(CH_PPI); + SSYNC(); + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); + SSYNC(); + + if (!t_conf_done) { + config_timers(); + start_timers(); + } + /* gpio_set_value(MOD,1); */ + } + + return 0; +} + +static int bfin_lq035_fb_release(struct fb_info *info, int user) +{ + unsigned long flags; + + spin_lock_irqsave(&bfin_lq035_lock, flags); + lq035_open_cnt--; + spin_unlock_irqrestore(&bfin_lq035_lock, flags); + + + if (lq035_open_cnt <= 0) { + + bfin_write_PPI_CONTROL(0); + SSYNC(); + + disable_dma(CH_PPI); + } + + return 0; +} + + +static int bfin_lq035_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + switch (var->bits_per_pixel) { + case 16:/* DIRECTCOLOUR, 64k */ + var->red.offset = info->var.red.offset; + var->green.offset = info->var.green.offset; + var->blue.offset = info->var.blue.offset; + var->red.length = info->var.red.length; + var->green.length = info->var.green.length; + var->blue.length = info->var.blue.length; + var->transp.offset = 0; + var->transp.length = 0; + var->transp.msb_right = 0; + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + break; + default: + pr_debug("%s: depth not supported: %u BPP\n", __func__, + var->bits_per_pixel); + return -EINVAL; + } + + if (info->var.xres != var->xres || + info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_debug("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_debug("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +/* fb_rotate + * Rotate the display of this angle. This doesn't seems to be used by the core, + * but as our hardware supports it, so why not implementing it... + */ +static void bfin_lq035_fb_rotate(struct fb_info *fbi, int angle) +{ + pr_debug("%s: %p %d", __func__, fbi, angle); +#if (defined(UD) && defined(LBR)) + switch (angle) { + + case 180: + gpio_set_value(LBR, 0); + gpio_set_value(UD, 1); + break; + default: + gpio_set_value(LBR, 1); + gpio_set_value(UD, 0); + break; + } +#endif +} + +static int bfin_lq035_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + if (nocursor) + return 0; + else + return -EINVAL; /* just to force soft_cursor() call */ +} + +static int bfin_lq035_fb_setcolreg(u_int regno, u_int red, u_int green, + u_int blue, u_int transp, + struct fb_info *info) +{ + if (regno >= NBR_PALETTE) + return -EINVAL; + + if (info->var.grayscale) + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + + u32 value; + /* Place color in the pseudopalette */ + if (regno > 16) + return -EINVAL; + + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + + value = (red << info->var.red.offset) | + (green << info->var.green.offset)| + (blue << info->var.blue.offset); + value &= 0xFFFF; + + ((u32 *) (info->pseudo_palette))[regno] = value; + + } + + return 0; +} + +static struct fb_ops bfin_lq035_fb_ops = { + .owner = THIS_MODULE, + .fb_open = bfin_lq035_fb_open, + .fb_release = bfin_lq035_fb_release, + .fb_check_var = bfin_lq035_fb_check_var, + .fb_rotate = bfin_lq035_fb_rotate, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = bfin_lq035_fb_cursor, + .fb_setcolreg = bfin_lq035_fb_setcolreg, +}; + +static int bl_get_brightness(struct backlight_device *bd) +{ + return current_brightness; +} + +static const struct backlight_ops bfin_lq035fb_bl_ops = { + .get_brightness = bl_get_brightness, +}; + +static struct backlight_device *bl_dev; + +static int bfin_lcd_get_power(struct lcd_device *dev) +{ + return 0; +} + +static int bfin_lcd_set_power(struct lcd_device *dev, int power) +{ + return 0; +} + +static int bfin_lcd_get_contrast(struct lcd_device *dev) +{ + return (int)vcomm_value; +} + +static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) +{ + if (contrast > 255) + contrast = 255; + if (contrast < 0) + contrast = 0; + + vcomm_value = (unsigned char)contrast; + set_vcomm(); + return 0; +} + +static int bfin_lcd_check_fb(struct lcd_device *lcd, struct fb_info *fi) +{ + if (!fi || (fi == &bfin_lq035_fb)) + return 1; + return 0; +} + +static struct lcd_ops bfin_lcd_ops = { + .get_power = bfin_lcd_get_power, + .set_power = bfin_lcd_set_power, + .get_contrast = bfin_lcd_get_contrast, + .set_contrast = bfin_lcd_set_contrast, + .check_fb = bfin_lcd_check_fb, +}; + +static struct lcd_device *lcd_dev; + +static int __devinit bfin_lq035_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + dma_addr_t dma_handle; + + if (request_dma(CH_PPI, KBUILD_MODNAME)) { + pr_err("couldn't request PPI DMA\n"); + return -EFAULT; + } + + if (request_ports()) { + pr_err("couldn't request gpio port\n"); + free_dma(CH_PPI); + return -EFAULT; + } + + fb_buffer = dma_alloc_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, + &dma_handle, GFP_KERNEL); + if (fb_buffer == NULL) { + pr_err("couldn't allocate dma buffer\n"); + free_dma(CH_PPI); + free_ports(); + return -ENOMEM; + } + + if (L1_DATA_A_LENGTH) + dma_desc_table = l1_data_sram_zalloc(TOTAL_DMA_DESC_SIZE); + else + dma_desc_table = dma_alloc_coherent(NULL, TOTAL_DMA_DESC_SIZE, + &dma_handle, 0); + + if (dma_desc_table == NULL) { + pr_err("couldn't allocate dma descriptor\n"); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + return -ENOMEM; + } + + bfin_lq035_fb.screen_base = (void *)fb_buffer; + bfin_lq035_fb_fix.smem_start = (int)fb_buffer; + if (landscape) { + bfin_lq035_fb_defined.xres = LCD_Y_RES; + bfin_lq035_fb_defined.yres = LCD_X_RES; + bfin_lq035_fb_defined.xres_virtual = LCD_Y_RES; + bfin_lq035_fb_defined.yres_virtual = LCD_X_RES; + + bfin_lq035_fb_fix.line_length = LCD_Y_RES*(LCD_BBP/8); + } else { + bfin_lq035_fb.screen_base += ACTIVE_VIDEO_MEM_OFFSET; + bfin_lq035_fb_fix.smem_start += ACTIVE_VIDEO_MEM_OFFSET; + } + + bfin_lq035_fb_defined.green.msb_right = 0; + bfin_lq035_fb_defined.red.msb_right = 0; + bfin_lq035_fb_defined.blue.msb_right = 0; + bfin_lq035_fb_defined.green.offset = 5; + bfin_lq035_fb_defined.green.length = 6; + bfin_lq035_fb_defined.red.length = 5; + bfin_lq035_fb_defined.blue.length = 5; + + if (bgr) { + bfin_lq035_fb_defined.red.offset = 0; + bfin_lq035_fb_defined.blue.offset = 11; + } else { + bfin_lq035_fb_defined.red.offset = 11; + bfin_lq035_fb_defined.blue.offset = 0; + } + + bfin_lq035_fb.fbops = &bfin_lq035_fb_ops; + bfin_lq035_fb.var = bfin_lq035_fb_defined; + + bfin_lq035_fb.fix = bfin_lq035_fb_fix; + bfin_lq035_fb.flags = FBINFO_DEFAULT; + + + bfin_lq035_fb.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL); + if (bfin_lq035_fb.pseudo_palette == NULL) { + pr_err("failed to allocate pseudo_palette\n"); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + return -ENOMEM; + } + + if (fb_alloc_cmap(&bfin_lq035_fb.cmap, NBR_PALETTE, 0) < 0) { + pr_err("failed to allocate colormap (%d entries)\n", + NBR_PALETTE); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + kfree(bfin_lq035_fb.pseudo_palette); + return -EFAULT; + } + + if (register_framebuffer(&bfin_lq035_fb) < 0) { + pr_err("unable to register framebuffer\n"); + free_dma(CH_PPI); + free_ports(); + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + fb_buffer = NULL; + kfree(bfin_lq035_fb.pseudo_palette); + fb_dealloc_cmap(&bfin_lq035_fb.cmap); + return -EINVAL; + } + + i2c_add_driver(&ad5280_driver); + + memset(&props, 0, sizeof(props)); + props.max_brightness = MAX_BRIGHENESS; + bl_dev = backlight_device_register("bf537-bl", NULL, NULL, + &bfin_lq035fb_bl_ops, &props); + + lcd_dev = lcd_device_register(KBUILD_MODNAME, &pdev->dev, NULL, + &bfin_lcd_ops); + lcd_dev->props.max_contrast = 255, + + pr_info("initialized"); + + return 0; +} + +static int __devexit bfin_lq035_remove(struct platform_device *pdev) +{ + if (fb_buffer != NULL) + dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); + + if (L1_DATA_A_LENGTH) + l1_data_sram_free(dma_desc_table); + else + dma_free_coherent(NULL, TOTAL_DMA_DESC_SIZE, NULL, 0); + + bfin_write_TIMER_DISABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS| + TIMEN_LP|TIMEN_REV); + t_conf_done = 0; + + free_dma(CH_PPI); + + + kfree(bfin_lq035_fb.pseudo_palette); + fb_dealloc_cmap(&bfin_lq035_fb.cmap); + + + lcd_device_unregister(lcd_dev); + backlight_device_unregister(bl_dev); + + unregister_framebuffer(&bfin_lq035_fb); + i2c_del_driver(&ad5280_driver); + + free_ports(); + + pr_info("unregistered LCD driver\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_lq035_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (lq035_open_cnt > 0) { + bfin_write_PPI_CONTROL(0); + SSYNC(); + disable_dma(CH_PPI); + } + + return 0; +} + +static int bfin_lq035_resume(struct platform_device *pdev) +{ + if (lq035_open_cnt > 0) { + bfin_write_PPI_CONTROL(0); + SSYNC(); + + config_dma(); + config_ppi(); + + enable_dma(CH_PPI); + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); + SSYNC(); + + config_timers(); + start_timers(); + } else { + t_conf_done = 0; + } + + return 0; +} +#else +# define bfin_lq035_suspend NULL +# define bfin_lq035_resume NULL +#endif + +static struct platform_driver bfin_lq035_driver = { + .probe = bfin_lq035_probe, + .remove = __devexit_p(bfin_lq035_remove), + .suspend = bfin_lq035_suspend, + .resume = bfin_lq035_resume, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init bfin_lq035_driver_init(void) +{ + request_module("i2c-bfin-twi"); + return platform_driver_register(&bfin_lq035_driver); +} +module_init(bfin_lq035_driver_init); + +static void __exit bfin_lq035_driver_cleanup(void) +{ + platform_driver_unregister(&bfin_lq035_driver); +} +module_exit(bfin_lq035_driver_cleanup); + +MODULE_DESCRIPTION("SHARP LQ035Q7DB03 TFT LCD Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c new file mode 100644 index 000000000000..8486f541156b --- /dev/null +++ b/drivers/video/bfin_adv7393fb.c @@ -0,0 +1,832 @@ +/* + * Frame buffer driver for ADV7393/2 video encoder + * + * Copyright 2006-2009 Analog Devices Inc. + * Licensed under the GPL-2 or late. + */ + +/* + * TODO: Remove Globals + * TODO: Code Cleanup + */ + +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <asm/blackfin.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <linux/uaccess.h> +#include <linux/gpio.h> +#include <asm/portmux.h> + +#include <linux/dma-mapping.h> +#include <linux/proc_fs.h> +#include <linux/platform_device.h> + +#include <linux/i2c.h> +#include <linux/i2c-dev.h> + +#include "bfin_adv7393fb.h" + +static int mode = VMODE; +static int mem = VMEM; +static int nocursor = 1; + +static const unsigned short ppi_pins[] = { + P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, + P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, + P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, + P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, + P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, + 0 +}; + +/* + * card parameters + */ + +static struct bfin_adv7393_fb_par { + /* structure holding blackfin / adv7393 paramters when + screen is blanked */ + struct { + u8 Mode; /* ntsc/pal/? */ + } vga_state; + atomic_t ref_count; +} bfin_par; + +/* --------------------------------------------------------------------- */ + +static struct fb_var_screeninfo bfin_adv7393_fb_defined = { + .xres = 720, + .yres = 480, + .xres_virtual = 720, + .yres_virtual = 480, + .bits_per_pixel = 16, + .activate = FB_ACTIVATE_TEST, + .height = -1, + .width = -1, + .left_margin = 0, + .right_margin = 0, + .upper_margin = 0, + .lower_margin = 0, + .vmode = FB_VMODE_INTERLACED, + .red = {11, 5, 0}, + .green = {5, 6, 0}, + .blue = {0, 5, 0}, + .transp = {0, 0, 0}, +}; + +static struct fb_fix_screeninfo bfin_adv7393_fb_fix __devinitdata = { + .id = "BFIN ADV7393", + .smem_len = 720 * 480 * 2, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .line_length = 720 * 2, + .accel = FB_ACCEL_NONE +}; + +static struct fb_ops bfin_adv7393_fb_ops = { + .owner = THIS_MODULE, + .fb_open = bfin_adv7393_fb_open, + .fb_release = bfin_adv7393_fb_release, + .fb_check_var = bfin_adv7393_fb_check_var, + .fb_pan_display = bfin_adv7393_fb_pan_display, + .fb_blank = bfin_adv7393_fb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = bfin_adv7393_fb_cursor, + .fb_setcolreg = bfin_adv7393_fb_setcolreg, +}; + +static int dma_desc_list(struct adv7393fb_device *fbdev, u16 arg) +{ + if (arg == BUILD) { /* Build */ + fbdev->vb1 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->vb1 == NULL) + goto error; + + fbdev->av1 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->av1 == NULL) + goto error; + + fbdev->vb2 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->vb2 == NULL) + goto error; + + fbdev->av2 = l1_data_sram_zalloc(sizeof(struct dmasg)); + if (fbdev->av2 == NULL) + goto error; + + /* Build linked DMA descriptor list */ + fbdev->vb1->next_desc_addr = fbdev->av1; + fbdev->av1->next_desc_addr = fbdev->vb2; + fbdev->vb2->next_desc_addr = fbdev->av2; + fbdev->av2->next_desc_addr = fbdev->vb1; + + /* Save list head */ + fbdev->descriptor_list_head = fbdev->av2; + + /* Vertical Blanking Field 1 */ + fbdev->vb1->start_addr = VB_DUMMY_MEMORY_SOURCE; + fbdev->vb1->cfg = DMA_CFG_VAL; + + fbdev->vb1->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + + fbdev->vb1->x_modify = 0; + fbdev->vb1->y_count = fbdev->modes[mode].vb1_lines; + fbdev->vb1->y_modify = 0; + + /* Active Video Field 1 */ + + fbdev->av1->start_addr = (unsigned long)fbdev->fb_mem; + fbdev->av1->cfg = DMA_CFG_VAL; + fbdev->av1->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + fbdev->av1->x_modify = fbdev->modes[mode].bpp / 8; + fbdev->av1->y_count = fbdev->modes[mode].a_lines; + fbdev->av1->y_modify = + (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank + + 1) * (fbdev->modes[mode].bpp / 8); + + /* Vertical Blanking Field 2 */ + + fbdev->vb2->start_addr = VB_DUMMY_MEMORY_SOURCE; + fbdev->vb2->cfg = DMA_CFG_VAL; + fbdev->vb2->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + + fbdev->vb2->x_modify = 0; + fbdev->vb2->y_count = fbdev->modes[mode].vb2_lines; + fbdev->vb2->y_modify = 0; + + /* Active Video Field 2 */ + + fbdev->av2->start_addr = + (unsigned long)fbdev->fb_mem + fbdev->line_len; + + fbdev->av2->cfg = DMA_CFG_VAL; + + fbdev->av2->x_count = + fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; + + fbdev->av2->x_modify = (fbdev->modes[mode].bpp / 8); + fbdev->av2->y_count = fbdev->modes[mode].a_lines; + + fbdev->av2->y_modify = + (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank + + 1) * (fbdev->modes[mode].bpp / 8); + + return 1; + } + +error: + l1_data_sram_free(fbdev->vb1); + l1_data_sram_free(fbdev->av1); + l1_data_sram_free(fbdev->vb2); + l1_data_sram_free(fbdev->av2); + + return 0; +} + +static int bfin_config_dma(struct adv7393fb_device *fbdev) +{ + BUG_ON(!(fbdev->fb_mem)); + + set_dma_x_count(CH_PPI, fbdev->descriptor_list_head->x_count); + set_dma_x_modify(CH_PPI, fbdev->descriptor_list_head->x_modify); + set_dma_y_count(CH_PPI, fbdev->descriptor_list_head->y_count); + set_dma_y_modify(CH_PPI, fbdev->descriptor_list_head->y_modify); + set_dma_start_addr(CH_PPI, fbdev->descriptor_list_head->start_addr); + set_dma_next_desc_addr(CH_PPI, + fbdev->descriptor_list_head->next_desc_addr); + set_dma_config(CH_PPI, fbdev->descriptor_list_head->cfg); + + return 1; +} + +static void bfin_disable_dma(void) +{ + bfin_write_DMA0_CONFIG(bfin_read_DMA0_CONFIG() & ~DMAEN); +} + +static void bfin_config_ppi(struct adv7393fb_device *fbdev) +{ + if (ANOMALY_05000183) { + bfin_write_TIMER2_CONFIG(WDTH_CAP); + bfin_write_TIMER_ENABLE(TIMEN2); + } + + bfin_write_PPI_CONTROL(0x381E); + bfin_write_PPI_FRAME(fbdev->modes[mode].tot_lines); + bfin_write_PPI_COUNT(fbdev->modes[mode].xres + + fbdev->modes[mode].boeft_blank - 1); + bfin_write_PPI_DELAY(fbdev->modes[mode].aoeft_blank - 1); +} + +static void bfin_enable_ppi(void) +{ + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); +} + +static void bfin_disable_ppi(void) +{ + bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN); +} + +static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7393_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int +adv7393_write_block(struct i2c_client *client, + const u8 *data, unsigned int len) +{ + int ret = -1; + u8 reg; + + while (len >= 2) { + reg = *data++; + ret = adv7393_write(client, reg, *data++); + if (ret < 0) + break; + len -= 2; + } + + return ret; +} + +static int adv7393_mode(struct i2c_client *client, u16 mode) +{ + switch (mode) { + case POWER_ON: /* ADV7393 Sleep mode OFF */ + adv7393_write(client, 0x00, 0x1E); + break; + case POWER_DOWN: /* ADV7393 Sleep mode ON */ + adv7393_write(client, 0x00, 0x1F); + break; + case BLANK_OFF: /* Pixel Data Valid */ + adv7393_write(client, 0x82, 0xCB); + break; + case BLANK_ON: /* Pixel Data Invalid */ + adv7393_write(client, 0x82, 0x8B); + break; + default: + return -EINVAL; + break; + } + return 0; +} + +static irqreturn_t ppi_irq_error(int irq, void *dev_id) +{ + + struct adv7393fb_device *fbdev = (struct adv7393fb_device *)dev_id; + + u16 status = bfin_read_PPI_STATUS(); + + pr_debug("%s: PPI Status = 0x%X\n", __func__, status); + + if (status) { + bfin_disable_dma(); /* TODO: Check Sequence */ + bfin_disable_ppi(); + bfin_clear_PPI_STATUS(); + bfin_config_dma(fbdev); + bfin_enable_ppi(); + } + + return IRQ_HANDLED; + +} + +static int proc_output(char *buf) +{ + char *p = buf; + + p += sprintf(p, + "Usage:\n" + "echo 0x[REG][Value] > adv7393\n" + "example: echo 0x1234 >adv7393\n" + "writes 0x34 into Register 0x12\n"); + + return p - buf; +} + +static int +adv7393_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = proc_output(page); + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int +adv7393_write_proc(struct file *file, const char __user * buffer, + unsigned long count, void *data) +{ + struct adv7393fb_device *fbdev = data; + char line[8]; + unsigned int val; + int ret; + + ret = copy_from_user(line, buffer, count); + if (ret) + return -EFAULT; + + val = simple_strtoul(line, NULL, 0); + adv7393_write(fbdev->client, val >> 8, val & 0xff); + + return count; +} + +static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct proc_dir_entry *entry; + int num_modes = ARRAY_SIZE(known_modes); + + struct adv7393fb_device *fbdev = NULL; + + if (mem > 2) { + dev_err(&client->dev, "mem out of allowed range [1;2]\n"); + return -EINVAL; + } + + if (mode > num_modes) { + dev_err(&client->dev, "mode %d: not supported", mode); + return -EFAULT; + } + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) { + dev_err(&client->dev, "failed to allocate device private record"); + return -ENOMEM; + } + + i2c_set_clientdata(client, fbdev); + + fbdev->modes = known_modes; + fbdev->client = client; + + fbdev->fb_len = + mem * fbdev->modes[mode].xres * fbdev->modes[mode].xres * + (fbdev->modes[mode].bpp / 8); + + fbdev->line_len = + fbdev->modes[mode].xres * (fbdev->modes[mode].bpp / 8); + + /* Workaround "PPI Does Not Start Properly In Specific Mode" */ + if (ANOMALY_05000400) { + if (gpio_request(P_IDENT(P_PPI0_FS3), "PPI0_FS3")) { + dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); + ret = -EBUSY; + goto out_8; + } + gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); + } + + if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { + dev_err(&client->dev, "requesting PPI peripheral failed\n"); + ret = -EFAULT; + goto out_8; + } + + fbdev->fb_mem = + dma_alloc_coherent(NULL, fbdev->fb_len, &fbdev->dma_handle, + GFP_KERNEL); + + if (NULL == fbdev->fb_mem) { + dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n", + (u32) fbdev->fb_len); + ret = -ENOMEM; + goto out_7; + } + + fbdev->info.screen_base = (void *)fbdev->fb_mem; + bfin_adv7393_fb_fix.smem_start = (int)fbdev->fb_mem; + + bfin_adv7393_fb_fix.smem_len = fbdev->fb_len; + bfin_adv7393_fb_fix.line_length = fbdev->line_len; + + if (mem > 1) + bfin_adv7393_fb_fix.ypanstep = 1; + + bfin_adv7393_fb_defined.red.length = 5; + bfin_adv7393_fb_defined.green.length = 6; + bfin_adv7393_fb_defined.blue.length = 5; + + bfin_adv7393_fb_defined.xres = fbdev->modes[mode].xres; + bfin_adv7393_fb_defined.yres = fbdev->modes[mode].yres; + bfin_adv7393_fb_defined.xres_virtual = fbdev->modes[mode].xres; + bfin_adv7393_fb_defined.yres_virtual = mem * fbdev->modes[mode].yres; + bfin_adv7393_fb_defined.bits_per_pixel = fbdev->modes[mode].bpp; + + fbdev->info.fbops = &bfin_adv7393_fb_ops; + fbdev->info.var = bfin_adv7393_fb_defined; + fbdev->info.fix = bfin_adv7393_fb_fix; + fbdev->info.par = &bfin_par; + fbdev->info.flags = FBINFO_DEFAULT; + + fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL); + if (!fbdev->info.pseudo_palette) { + dev_err(&client->dev, "failed to allocate pseudo_palette\n"); + ret = -ENOMEM; + goto out_6; + } + + if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { + dev_err(&client->dev, "failed to allocate colormap (%d entries)\n", + BFIN_LCD_NBR_PALETTE_ENTRIES); + ret = -EFAULT; + goto out_5; + } + + if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) { + dev_err(&client->dev, "unable to request PPI DMA\n"); + ret = -EFAULT; + goto out_4; + } + + if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, IRQF_DISABLED, + "PPI ERROR", fbdev) < 0) { + dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); + ret = -EFAULT; + goto out_3; + } + + fbdev->open = 0; + + ret = adv7393_write_block(client, fbdev->modes[mode].adv7393_i2c_initd, + fbdev->modes[mode].adv7393_i2c_initd_len); + + if (ret) { + dev_err(&client->dev, "i2c attach: init error\n"); + goto out_1; + } + + + if (register_framebuffer(&fbdev->info) < 0) { + dev_err(&client->dev, "unable to register framebuffer\n"); + ret = -EFAULT; + goto out_1; + } + + dev_info(&client->dev, "fb%d: %s frame buffer device\n", + fbdev->info.node, fbdev->info.fix.id); + dev_info(&client->dev, "fb memory address : 0x%p\n", fbdev->fb_mem); + + entry = create_proc_entry("driver/adv7393", 0, NULL); + if (!entry) { + dev_err(&client->dev, "unable to create /proc entry\n"); + ret = -EFAULT; + goto out_0; + } + + entry->read_proc = adv7393_read_proc; + entry->write_proc = adv7393_write_proc; + entry->data = fbdev; + + return 0; + + out_0: + unregister_framebuffer(&fbdev->info); + out_1: + free_irq(IRQ_PPI_ERROR, fbdev); + out_3: + free_dma(CH_PPI); + out_4: + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, + fbdev->dma_handle); + out_5: + fb_dealloc_cmap(&fbdev->info.cmap); + out_6: + kfree(fbdev->info.pseudo_palette); + out_7: + peripheral_free_list(ppi_pins); + out_8: + kfree(fbdev); + + return ret; +} + +static int bfin_adv7393_fb_open(struct fb_info *info, int user) +{ + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + fbdev->info.screen_base = (void *)fbdev->fb_mem; + if (!fbdev->info.screen_base) { + dev_err(&fbdev->client->dev, "unable to map device\n"); + return -ENOMEM; + } + + fbdev->open = 1; + dma_desc_list(fbdev, BUILD); + adv7393_mode(fbdev->client, BLANK_OFF); + bfin_config_ppi(fbdev); + bfin_config_dma(fbdev); + bfin_enable_ppi(); + + return 0; +} + +static int bfin_adv7393_fb_release(struct fb_info *info, int user) +{ + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + adv7393_mode(fbdev->client, BLANK_ON); + bfin_disable_dma(); + bfin_disable_ppi(); + dma_desc_list(fbdev, DESTRUCT); + fbdev->open = 0; + return 0; +} + +static int +bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + + switch (var->bits_per_pixel) { + case 16:/* DIRECTCOLOUR, 64k */ + var->red.offset = info->var.red.offset; + var->green.offset = info->var.green.offset; + var->blue.offset = info->var.blue.offset; + var->red.length = info->var.red.length; + var->green.length = info->var.green.length; + var->blue.length = info->var.blue.length; + var->transp.offset = 0; + var->transp.length = 0; + var->transp.msb_right = 0; + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + break; + default: + pr_debug("%s: depth not supported: %u BPP\n", __func__, + var->bits_per_pixel); + return -EINVAL; + } + + if (info->var.xres != var->xres || + info->var.yres != var->yres || + info->var.xres_virtual != var->xres_virtual || + info->var.yres_virtual != var->yres_virtual) { + pr_debug("%s: Resolution not supported: X%u x Y%u\n", + __func__, var->xres, var->yres); + return -EINVAL; + } + + /* + * Memory limit + */ + + if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { + pr_debug("%s: Memory Limit requested yres_virtual = %u\n", + __func__, var->yres_virtual); + return -ENOMEM; + } + + return 0; +} + +static int +bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + int dy; + u32 dmaaddr; + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + if (!var || !info) + return -EINVAL; + + if (var->xoffset - info->var.xoffset) { + /* No support for X panning for now! */ + return -EINVAL; + } + dy = var->yoffset - info->var.yoffset; + + if (dy) { + pr_debug("%s: Panning screen of %d lines\n", __func__, dy); + + dmaaddr = fbdev->av1->start_addr; + dmaaddr += (info->fix.line_length * dy); + /* TODO: Wait for current frame to finished */ + + fbdev->av1->start_addr = (unsigned long)dmaaddr; + fbdev->av2->start_addr = (unsigned long)dmaaddr + fbdev->line_len; + } + + return 0; + +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ +static int bfin_adv7393_fb_blank(int blank, struct fb_info *info) +{ + struct adv7393fb_device *fbdev = to_adv7393fb_device(info); + + switch (blank) { + + case VESA_NO_BLANKING: + /* Turn on panel */ + adv7393_mode(fbdev->client, BLANK_OFF); + break; + + case VESA_VSYNC_SUSPEND: + case VESA_HSYNC_SUSPEND: + case VESA_POWERDOWN: + /* Turn off panel */ + adv7393_mode(fbdev->client, BLANK_ON); + break; + + default: + return -EINVAL; + break; + } + return 0; +} + +int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + if (nocursor) + return 0; + else + return -EINVAL; /* just to force soft_cursor() call */ +} + +static int bfin_adv7393_fb_setcolreg(u_int regno, u_int red, u_int green, + u_int blue, u_int transp, + struct fb_info *info) +{ + if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES) + return -EINVAL; + + if (info->var.grayscale) + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + u32 value; + /* Place color in the pseudopalette */ + if (regno > 16) + return -EINVAL; + + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + + value = (red << info->var.red.offset) | + (green << info->var.green.offset)| + (blue << info->var.blue.offset); + value &= 0xFFFF; + + ((u32 *) (info->pseudo_palette))[regno] = value; + } + + return 0; +} + +static int __devexit bfin_adv7393_fb_remove(struct i2c_client *client) +{ + struct adv7393fb_device *fbdev = i2c_get_clientdata(client); + + adv7393_mode(client, POWER_DOWN); + + if (fbdev->fb_mem) + dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, fbdev->dma_handle); + free_dma(CH_PPI); + free_irq(IRQ_PPI_ERROR, fbdev); + unregister_framebuffer(&fbdev->info); + remove_proc_entry("driver/adv7393", NULL); + fb_dealloc_cmap(&fbdev->info.cmap); + kfree(fbdev->info.pseudo_palette); + + if (ANOMALY_05000400) + gpio_free(P_IDENT(P_PPI0_FS3)); /* FS3 */ + peripheral_free_list(ppi_pins); + kfree(fbdev); + + return 0; +} + +#ifdef CONFIG_PM +static int bfin_adv7393_fb_suspend(struct device *dev) +{ + struct adv7393fb_device *fbdev = dev_get_drvdata(dev); + + if (fbdev->open) { + bfin_disable_dma(); + bfin_disable_ppi(); + dma_desc_list(fbdev, DESTRUCT); + } + adv7393_mode(fbdev->client, POWER_DOWN); + + return 0; +} + +static int bfin_adv7393_fb_resume(struct device *dev) +{ + struct adv7393fb_device *fbdev = dev_get_drvdata(dev); + + adv7393_mode(fbdev->client, POWER_ON); + + if (fbdev->open) { + dma_desc_list(fbdev, BUILD); + bfin_config_ppi(fbdev); + bfin_config_dma(fbdev); + bfin_enable_ppi(); + } + + return 0; +} + +static const struct dev_pm_ops bfin_adv7393_dev_pm_ops = { + .suspend = bfin_adv7393_fb_suspend, + .resume = bfin_adv7393_fb_resume, +}; +#endif + +static const struct i2c_device_id bfin_adv7393_id[] = { + {DRIVER_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bfin_adv7393_id); + +static struct i2c_driver bfin_adv7393_fb_driver = { + .driver = { + .name = DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &bfin_adv7393_dev_pm_ops, +#endif + }, + .probe = bfin_adv7393_fb_probe, + .remove = __devexit_p(bfin_adv7393_fb_remove), + .id_table = bfin_adv7393_id, +}; + +static int __init bfin_adv7393_fb_driver_init(void) +{ +#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) + request_module("i2c-bfin-twi"); +#else + request_module("i2c-gpio"); +#endif + + return i2c_add_driver(&bfin_adv7393_fb_driver); +} +module_init(bfin_adv7393_fb_driver_init); + +static void __exit bfin_adv7393_fb_driver_cleanup(void) +{ + i2c_del_driver(&bfin_adv7393_fb_driver); +} +module_exit(bfin_adv7393_fb_driver_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Frame buffer driver for ADV7393/2 Video Encoder"); + +module_param(mode, int, 0); +MODULE_PARM_DESC(mode, + "Video Mode (0=NTSC,1=PAL,2=NTSC 640x480,3=PAL 640x480,4=NTSC YCbCr input,5=PAL YCbCr input)"); + +module_param(mem, int, 0); +MODULE_PARM_DESC(mem, + "Size of frame buffer memory 1=Single 2=Double Size (allows y-panning / frame stacking)"); + +module_param(nocursor, int, 0644); +MODULE_PARM_DESC(nocursor, "cursor enable/disable"); diff --git a/drivers/video/bfin_adv7393fb.h b/drivers/video/bfin_adv7393fb.h new file mode 100644 index 000000000000..8c7f9e4fc6eb --- /dev/null +++ b/drivers/video/bfin_adv7393fb.h @@ -0,0 +1,321 @@ +/* + * Frame buffer driver for ADV7393/2 video encoder + * + * Copyright 2006-2009 Analog Devices Inc. + * Licensed under the GPL-2 or late. + */ + +#ifndef __BFIN_ADV7393FB_H__ +#define __BFIN_ADV7393FB_H__ + +#define BFIN_LCD_NBR_PALETTE_ENTRIES 256 + +#ifdef CONFIG_NTSC +# define VMODE 0 +#endif +#ifdef CONFIG_PAL +# define VMODE 1 +#endif +#ifdef CONFIG_NTSC_640x480 +# define VMODE 2 +#endif +#ifdef CONFIG_PAL_640x480 +# define VMODE 3 +#endif +#ifdef CONFIG_NTSC_YCBCR +# define VMODE 4 +#endif +#ifdef CONFIG_PAL_YCBCR +# define VMODE 5 +#endif + +#ifndef VMODE +# define VMODE 1 +#endif + +#ifdef CONFIG_ADV7393_2XMEM +# define VMEM 2 +#else +# define VMEM 1 +#endif + +#if defined(CONFIG_BF537) || defined(CONFIG_BF536) || defined(CONFIG_BF534) +# define DMA_CFG_VAL 0x7935 /* Set Sync Bit */ +# define VB_DUMMY_MEMORY_SOURCE L1_DATA_B_START +#else +# define DMA_CFG_VAL 0x7915 +# define VB_DUMMY_MEMORY_SOURCE BOOT_ROM_START +#endif + +enum { + DESTRUCT, + BUILD, +}; + +enum { + POWER_ON, + POWER_DOWN, + BLANK_ON, + BLANK_OFF, +}; + +#define DRIVER_NAME "bfin-adv7393" + +struct adv7393fb_modes { + const s8 name[25]; /* Full name */ + u16 xres; /* Active Horizonzal Pixels */ + u16 yres; /* Active Vertical Pixels */ + u16 bpp; + u16 vmode; + u16 a_lines; /* Active Lines per Field */ + u16 vb1_lines; /* Vertical Blanking Field 1 Lines */ + u16 vb2_lines; /* Vertical Blanking Field 2 Lines */ + u16 tot_lines; /* Total Lines per Frame */ + u16 boeft_blank; /* Before Odd/Even Field Transition No. of Blank Pixels */ + u16 aoeft_blank; /* After Odd/Even Field Transition No. of Blank Pixels */ + const s8 *adv7393_i2c_initd; + u16 adv7393_i2c_initd_len; +}; + +static const u8 init_NTSC_TESTPATTERN[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x10, /* SSAF Luma Filter Enabled, NTSC Mode */ + 0x82, 0xCB, /* Step control on, pixel data valid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x84, 0x40, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ +}; + +static const u8 init_NTSC[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0xC3, 0x26, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC5, 0x12, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC2, 0x4A, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC6, 0x5E, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBD, 0x19, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBF, 0x42, /* Program RGB->YCrCb Color Space convertion matrix */ + 0x8C, 0x1F, /* NTSC Subcarrier Frequency */ + 0x8D, 0x7C, /* NTSC Subcarrier Frequency */ + 0x8E, 0xF0, /* NTSC Subcarrier Frequency */ + 0x8F, 0x21, /* NTSC Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x20, + 0x8A, 0x0d, +}; + +static const u8 init_PAL[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0xC3, 0x26, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC5, 0x12, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC2, 0x4A, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xC6, 0x5E, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBD, 0x19, /* Program RGB->YCrCb Color Space convertion matrix */ + 0xBF, 0x42, /* Program RGB->YCrCb Color Space convertion matrix */ + 0x8C, 0xCB, /* PAL Subcarrier Frequency */ + 0x8D, 0x8A, /* PAL Subcarrier Frequency */ + 0x8E, 0x09, /* PAL Subcarrier Frequency */ + 0x8F, 0x2A, /* PAL Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x20, + 0x8A, 0x0d, +}; + +static const u8 init_NTSC_YCbCr[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0x8C, 0x1F, /* NTSC Subcarrier Frequency */ + 0x8D, 0x7C, /* NTSC Subcarrier Frequency */ + 0x8E, 0xF0, /* NTSC Subcarrier Frequency */ + 0x8F, 0x21, /* NTSC Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x08, + 0x8A, 0x0d, +}; + +static const u8 init_PAL_YCbCr[] = { + 0x00, 0x1E, /* Power up all DACs and PLL */ + 0x8C, 0xCB, /* PAL Subcarrier Frequency */ + 0x8D, 0x8A, /* PAL Subcarrier Frequency */ + 0x8E, 0x09, /* PAL Subcarrier Frequency */ + 0x8F, 0x2A, /* PAL Subcarrier Frequency */ + 0x01, 0x00, /* SD-Only Mode */ + 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */ + 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ + 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */ + 0x86, 0x82, + 0x8B, 0x11, + 0x88, 0x08, + 0x8A, 0x0d, +}; + +static struct adv7393fb_modes known_modes[] = { + /* NTSC 720x480 CRT */ + { + .name = "NTSC 720x480", + .xres = 720, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 240, + .vb1_lines = 22, + .vb2_lines = 23, + .tot_lines = 525, + .boeft_blank = 16, + .aoeft_blank = 122, + .adv7393_i2c_initd = init_NTSC, + .adv7393_i2c_initd_len = sizeof(init_NTSC) + }, + /* PAL 720x480 CRT */ + { + .name = "PAL 720x576", + .xres = 720, + .yres = 576, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 288, + .vb1_lines = 24, + .vb2_lines = 25, + .tot_lines = 625, + .boeft_blank = 12, + .aoeft_blank = 132, + .adv7393_i2c_initd = init_PAL, + .adv7393_i2c_initd_len = sizeof(init_PAL) + }, + /* NTSC 640x480 CRT Experimental */ + { + .name = "NTSC 640x480", + .xres = 640, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 240, + .vb1_lines = 22, + .vb2_lines = 23, + .tot_lines = 525, + .boeft_blank = 16 + 40, + .aoeft_blank = 122 + 40, + .adv7393_i2c_initd = init_NTSC, + .adv7393_i2c_initd_len = sizeof(init_NTSC) + }, + /* PAL 640x480 CRT Experimental */ + { + .name = "PAL 640x480", + .xres = 640, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 288 - 20, + .vb1_lines = 24 + 20, + .vb2_lines = 25 + 20, + .tot_lines = 625, + .boeft_blank = 12 + 40, + .aoeft_blank = 132 + 40, + .adv7393_i2c_initd = init_PAL, + .adv7393_i2c_initd_len = sizeof(init_PAL) + }, + /* NTSC 720x480 YCbCR */ + { + .name = "NTSC 720x480 YCbCR", + .xres = 720, + .yres = 480, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 240, + .vb1_lines = 22, + .vb2_lines = 23, + .tot_lines = 525, + .boeft_blank = 16, + .aoeft_blank = 122, + .adv7393_i2c_initd = init_NTSC_YCbCr, + .adv7393_i2c_initd_len = sizeof(init_NTSC_YCbCr) + }, + /* PAL 720x480 CRT */ + { + .name = "PAL 720x576 YCbCR", + .xres = 720, + .yres = 576, + .bpp = 16, + .vmode = FB_VMODE_INTERLACED, + .a_lines = 288, + .vb1_lines = 24, + .vb2_lines = 25, + .tot_lines = 625, + .boeft_blank = 12, + .aoeft_blank = 132, + .adv7393_i2c_initd = init_PAL_YCbCr, + .adv7393_i2c_initd_len = sizeof(init_PAL_YCbCr) + } +}; + +struct adv7393fb_regs { + +}; + +struct adv7393fb_device { + struct fb_info info; /* FB driver info record */ + + struct i2c_client *client; + + struct dmasg *descriptor_list_head; + struct dmasg *vb1; + struct dmasg *av1; + struct dmasg *vb2; + struct dmasg *av2; + + dma_addr_t dma_handle; + + struct fb_info bfin_adv7393_fb; + + struct adv7393fb_modes *modes; + + struct adv7393fb_regs *regs; /* Registers memory map */ + size_t regs_len; + size_t fb_len; + size_t line_len; + u16 open; + u16 *fb_mem; /* RGB Buffer */ + +}; + +#define to_adv7393fb_device(_info) \ + (_info ? container_of(_info, struct adv7393fb_device, info) : NULL); + +static int bfin_adv7393_fb_open(struct fb_info *info, int user); +static int bfin_adv7393_fb_release(struct fb_info *info, int user); +static int bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); + +static int bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); + +static int bfin_adv7393_fb_blank(int blank, struct fb_info *info); + +static void bfin_config_ppi(struct adv7393fb_device *fbdev); +static int bfin_config_dma(struct adv7393fb_device *fbdev); +static void bfin_disable_dma(void); +static void bfin_enable_ppi(void); +static void bfin_disable_ppi(void); + +static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value); +static inline int adv7393_read(struct i2c_client *client, u8 reg); +static int adv7393_write_block(struct i2c_client *client, const u8 *data, + unsigned int len); + +int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor); +static int bfin_adv7393_fb_setcolreg(u_int, u_int, u_int, u_int, + u_int, struct fb_info *info); + +#endif diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c index 6b19136aa181..caaa27d4a46a 100644 --- a/drivers/video/carminefb.c +++ b/drivers/video/carminefb.c @@ -654,7 +654,7 @@ static int __devinit carminefb_probe(struct pci_dev *dev, printk(KERN_ERR "carminefb: Memory bar is only %d bytes, %d " "are required.", carminefb_fix.smem_len, CARMINE_TOTAL_DIPLAY_MEM); - goto err_free_reg_mmio; + goto err_unmap_vregs; } if (!request_mem_region(carminefb_fix.smem_start, @@ -667,8 +667,6 @@ static int __devinit carminefb_probe(struct pci_dev *dev, carminefb_fix.smem_len); if (!hw->screen_mem) { printk(KERN_ERR "carmine: Can't ioremap smem area.\n"); - release_mem_region(carminefb_fix.smem_start, - carminefb_fix.smem_len); goto err_reg_smem; } @@ -710,7 +708,7 @@ err_deinit_hw: err_unmap_screen: iounmap(hw->screen_mem); err_reg_smem: - release_mem_region(carminefb_fix.mmio_start, carminefb_fix.mmio_len); + release_mem_region(carminefb_fix.smem_start, carminefb_fix.smem_len); err_unmap_vregs: iounmap(hw->v_regs); err_free_reg_mmio: diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index cad7d45c8bac..c265aed09e04 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -1029,10 +1029,6 @@ static int __init fb_probe(struct platform_device *device) goto err_release_pl_mem; } - ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); - if (ret) - goto err_release_pl_mem; - /* Initialize par */ da8xx_fb_info->var.bits_per_pixel = lcd_cfg->bpp; @@ -1060,7 +1056,7 @@ static int __init fb_probe(struct platform_device *device) ret = fb_alloc_cmap(&da8xx_fb_info->cmap, PALETTE_SIZE, 0); if (ret) - goto err_free_irq; + goto err_release_pl_mem; da8xx_fb_info->cmap.len = par->palette_sz; /* initialize var_screeninfo */ @@ -1088,8 +1084,13 @@ static int __init fb_probe(struct platform_device *device) goto err_cpu_freq; } #endif + + ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); + if (ret) + goto irq_freq; return 0; +irq_freq: #ifdef CONFIG_CPU_FREQ err_cpu_freq: unregister_framebuffer(da8xx_fb_info); @@ -1098,9 +1099,6 @@ err_cpu_freq: err_dealloc_cmap: fb_dealloc_cmap(&da8xx_fb_info->cmap); -err_free_irq: - free_irq(par->irq, par); - err_release_pl_mem: dma_free_coherent(NULL, PALETTE_SIZE, par->v_palette_base, par->p_palette_base); diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index affdf3e32cf3..5c3960da755a 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -80,6 +80,7 @@ static const struct fb_cmap default_16_colors = { * @cmap: frame buffer colormap structure * @len: length of @cmap * @transp: boolean, 1 if there is transparency, 0 otherwise + * @flags: flags for kmalloc memory allocation * * Allocates memory for a colormap @cmap. @len is the * number of entries in the palette. diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 0e6aa3d96a42..4ac1201ad6c2 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1458,7 +1458,7 @@ static bool apertures_overlap(struct aperture *gen, struct aperture *hw) if (gen->base == hw->base) return true; /* is the generic aperture base inside the hw base->hw base+size */ - if (gen->base > hw->base && gen->base <= hw->base + hw->size) + if (gen->base > hw->base && gen->base < hw->base + hw->size) return true; return false; } diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 563a98b88e9b..4f57485f8c54 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -973,6 +973,90 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) DPRINTK("========================================\n"); } +/** + * fb_edid_add_monspecs() - add monitor video modes from E-EDID data + * @edid: 128 byte array with an E-EDID block + * @spacs: monitor specs to be extended + */ +void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) +{ + unsigned char *block; + struct fb_videomode *m; + int num = 0, i; + u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; + u8 pos = 4, svd_n = 0; + + if (!edid) + return; + + if (!edid_checksum(edid)) + return; + + if (edid[0] != 0x2 || + edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE) + return; + + DPRINTK(" Short Video Descriptors\n"); + + while (pos < edid[2]) { + u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; + pr_debug("Data block %u of %u bytes\n", type, len); + if (type == 2) + for (i = pos; i < pos + len; i++) { + u8 idx = edid[pos + i] & 0x7f; + svd[svd_n++] = idx; + pr_debug("N%sative mode #%d\n", + edid[pos + i] & 0x80 ? "" : "on-n", idx); + } + pos += len + 1; + } + + block = edid + edid[2]; + + DPRINTK(" Extended Detailed Timings\n"); + + for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE; + i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) + if (PIXEL_CLOCK) + edt[num++] = block - edid; + + /* Yikes, EDID data is totally useless */ + if (!(num + svd_n)) + return; + + m = kzalloc((specs->modedb_len + num + svd_n) * + sizeof(struct fb_videomode), GFP_KERNEL); + + if (!m) + return; + + memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode)); + + for (i = specs->modedb_len; i < specs->modedb_len + num; i++) { + get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]); + if (i == specs->modedb_len) + m[i].flag |= FB_MODE_IS_FIRST; + pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh); + } + + for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) { + int idx = svd[i - specs->modedb_len - num]; + if (!idx || idx > 63) { + pr_warning("Reserved SVD code %d\n", idx); + } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) { + pr_warning("Unimplemented SVD code %d\n", idx); + } else { + memcpy(&m[i], cea_modes + idx, sizeof(m[i])); + pr_debug("Adding SVD #%d: %ux%u@%u\n", idx, + m[i].xres, m[i].yres, m[i].refresh); + } + } + + kfree(specs->modedb); + specs->modedb = m; + specs->modedb_len = specs->modedb_len + num + svd_n; +} + /* * VESA Generalized Timing Formula (GTF) */ @@ -1289,6 +1373,9 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) { specs = NULL; } +void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) +{ +} void fb_destroy_modedb(struct fb_videomode *modedb) { } @@ -1396,6 +1483,7 @@ EXPORT_SYMBOL(fb_firmware_edid); EXPORT_SYMBOL(fb_parse_edid); EXPORT_SYMBOL(fb_edid_to_monspecs); +EXPORT_SYMBOL(fb_edid_add_monspecs); EXPORT_SYMBOL(fb_get_mode); EXPORT_SYMBOL(fb_validate_mode); EXPORT_SYMBOL(fb_destroy_modedb); diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h index e4c4d89b7860..be8ccb47ebe0 100644 --- a/drivers/video/geode/lxfb.h +++ b/drivers/video/geode/lxfb.h @@ -22,6 +22,7 @@ #define DC_HFILT_COUNT 0x100 #define DC_VFILT_COUNT 0x100 #define VP_COEFF_SIZE 0x1000 +#define VP_PAL_COUNT 0x100 #define OUTPUT_CRT 0x01 #define OUTPUT_PANEL 0x02 @@ -48,7 +49,8 @@ struct lxfb_par { uint64_t vp[VP_REG_COUNT]; uint64_t fp[FP_REG_COUNT]; - uint32_t pal[DC_PAL_COUNT]; + uint32_t dc_pal[DC_PAL_COUNT]; + uint32_t vp_pal[VP_PAL_COUNT]; uint32_t hcoeff[DC_HFILT_COUNT * 2]; uint32_t vcoeff[DC_VFILT_COUNT]; uint32_t vp_coeff[VP_COEFF_SIZE / 4]; diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c index 85ec7f64c42a..79e9abc72b83 100644 --- a/drivers/video/geode/lxfb_ops.c +++ b/drivers/video/geode/lxfb_ops.c @@ -610,10 +610,15 @@ static void lx_save_regs(struct lxfb_par *par) memcpy(par->vp, par->vp_regs, sizeof(par->vp)); memcpy(par->fp, par->vp_regs + VP_FP_START, sizeof(par->fp)); - /* save the palette */ + /* save the display controller palette */ write_dc(par, DC_PAL_ADDRESS, 0); - for (i = 0; i < ARRAY_SIZE(par->pal); i++) - par->pal[i] = read_dc(par, DC_PAL_DATA); + for (i = 0; i < ARRAY_SIZE(par->dc_pal); i++) + par->dc_pal[i] = read_dc(par, DC_PAL_DATA); + + /* save the video processor palette */ + write_vp(par, VP_PAR, 0); + for (i = 0; i < ARRAY_SIZE(par->vp_pal); i++) + par->vp_pal[i] = read_vp(par, VP_PDR); /* save the horizontal filter coefficients */ filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL; @@ -706,8 +711,8 @@ static void lx_restore_display_ctlr(struct lxfb_par *par) /* restore the palette */ write_dc(par, DC_PAL_ADDRESS, 0); - for (i = 0; i < ARRAY_SIZE(par->pal); i++) - write_dc(par, DC_PAL_DATA, par->pal[i]); + for (i = 0; i < ARRAY_SIZE(par->dc_pal); i++) + write_dc(par, DC_PAL_DATA, par->dc_pal[i]); /* restore the horizontal filter coefficients */ filt = par->dc[DC_IRQ_FILT_CTL] | DC_IRQ_FILT_CTL_H_FILT_SEL; @@ -751,6 +756,11 @@ static void lx_restore_video_proc(struct lxfb_par *par) } } + /* restore video processor palette */ + write_vp(par, VP_PAR, 0); + for (i = 0; i < ARRAY_SIZE(par->vp_pal); i++) + write_vp(par, VP_PDR, par->vp_pal[i]); + /* restore video coeff ram */ memcpy(par->vp_regs + VP_VCR, par->vp_coeff, sizeof(par->vp_coeff)); } diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c index af8f0f2cc782..4052718eefaa 100644 --- a/drivers/video/hgafb.c +++ b/drivers/video/hgafb.c @@ -454,7 +454,6 @@ static int hgafb_blank(int blank_mode, struct fb_info *info) /* * Accel functions */ -#ifdef CONFIG_FB_HGA_ACCEL static void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { u_int rows, y; @@ -466,7 +465,7 @@ static void hgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) dest = rowaddr(info, y) + (rect->dx >> 3); switch (rect->rop) { case ROP_COPY: - //fb_memset(dest, rect->color, (rect->width >> 3)); + memset_io(dest, rect->color, (rect->width >> 3)); break; case ROP_XOR: fb_writeb(~(fb_readb(dest)), dest); @@ -488,7 +487,7 @@ static void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) for (rows = area->height; rows--; ) { src = rowaddr(info, y1) + (area->sx >> 3); dest = rowaddr(info, y2) + (area->dx >> 3); - //fb_memmove(dest, src, (area->width >> 3)); + memmove(dest, src, (area->width >> 3)); y1++; y2++; } @@ -499,7 +498,7 @@ static void hgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area) for (rows = area->height; rows--;) { src = rowaddr(info, y1) + (area->sx >> 3); dest = rowaddr(info, y2) + (area->dx >> 3); - //fb_memmove(dest, src, (area->width >> 3)); + memmove(dest, src, (area->width >> 3)); y1--; y2--; } @@ -511,20 +510,17 @@ static void hgafb_imageblit(struct fb_info *info, const struct fb_image *image) u8 __iomem *dest; u8 *cdat = (u8 *) image->data; u_int rows, y = image->dy; + u_int x; u8 d; for (rows = image->height; rows--; y++) { - d = *cdat++; - dest = rowaddr(info, y) + (image->dx >> 3); - fb_writeb(d, dest); + for (x = 0; x < image->width; x+= 8) { + d = *cdat++; + dest = rowaddr(info, y) + ((image->dx + x)>> 3); + fb_writeb(d, dest); + } } } -#else /* !CONFIG_FB_HGA_ACCEL */ -#define hgafb_fillrect cfb_fillrect -#define hgafb_copyarea cfb_copyarea -#define hgafb_imageblit cfb_imageblit -#endif /* CONFIG_FB_HGA_ACCEL */ - static struct fb_ops hgafb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c index cd2c728a809b..7db17d0d8a8c 100644 --- a/drivers/video/i810/i810-i2c.c +++ b/drivers/video/i810/i810-i2c.c @@ -45,8 +45,10 @@ static void i810i2c_setscl(void *data, int state) struct i810fb_par *par = chan->par; u8 __iomem *mmio = par->mmio_start_virtual; - i810_writel(mmio, chan->ddc_base, (state ? SCL_VAL_OUT : 0) | SCL_DIR | - SCL_DIR_MASK | SCL_VAL_MASK); + if (state) + i810_writel(mmio, chan->ddc_base, SCL_DIR_MASK | SCL_VAL_MASK); + else + i810_writel(mmio, chan->ddc_base, SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK); i810_readl(mmio, chan->ddc_base); /* flush posted write */ } @@ -56,8 +58,10 @@ static void i810i2c_setsda(void *data, int state) struct i810fb_par *par = chan->par; u8 __iomem *mmio = par->mmio_start_virtual; - i810_writel(mmio, chan->ddc_base, (state ? SDA_VAL_OUT : 0) | SDA_DIR | - SDA_DIR_MASK | SDA_VAL_MASK); + if (state) + i810_writel(mmio, chan->ddc_base, SDA_DIR_MASK | SDA_VAL_MASK); + else + i810_writel(mmio, chan->ddc_base, SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK); i810_readl(mmio, chan->ddc_base); /* flush posted write */ } diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 5c363d026f64..1ab2c2588675 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -53,11 +53,8 @@ #define LCDC_SIZE 0x04 #define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20) -#ifdef CONFIG_ARCH_MX1 -#define SIZE_YMAX(y) ((y) & 0x1ff) -#else -#define SIZE_YMAX(y) ((y) & 0x3ff) -#endif +#define YMAX_MASK (cpu_is_mx1() ? 0x1ff : 0x3ff) +#define SIZE_YMAX(y) ((y) & YMAX_MASK) #define LCDC_VPW 0x08 #define VPW_VPW(x) ((x) & 0x3ff) @@ -623,7 +620,7 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf if (var->right_margin > 255) printk(KERN_ERR "%s: invalid right_margin %d\n", info->fix.id, var->right_margin); - if (var->yres < 1 || var->yres > 511) + if (var->yres < 1 || var->yres > YMAX_MASK) printk(KERN_ERR "%s: invalid yres %d\n", info->fix.id, var->yres); if (var->vsync_len > 100) diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 0a4dbdc1693a..d2bb365f09b3 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -274,10 +274,61 @@ static const struct fb_videomode modedb[] = { /* 800x520i @ 50 Hz, 15.625 kHz hsync (PAL RGB) */ NULL, 50, 800, 520, 58823, 144, 64, 72, 28, 80, 5, 0, FB_VMODE_INTERLACED + }, { + /* 864x480 @ 60 Hz, 35.15 kHz hsync */ + NULL, 60, 864, 480, 27777, 1, 1, 1, 1, 0, 0, + 0, FB_VMODE_NONINTERLACED }, }; #ifdef CONFIG_FB_MODE_HELPERS +const struct fb_videomode cea_modes[64] = { + /* #1: 640x480p@59.94/60Hz */ + [1] = { + NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #3: 720x480p@59.94/60Hz */ + [3] = { + NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #5: 1920x1080i@59.94/60Hz */ + [5] = { + NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED, 0, + }, + /* #7: 720(1440)x480iH@59.94/60Hz */ + [7] = { + NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, FB_VMODE_INTERLACED, 0, + }, + /* #9: 720(1440)x240pH@59.94/60Hz */ + [9] = { + NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #18: 720x576pH@50Hz */ + [18] = { + NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, FB_VMODE_NONINTERLACED, 0, + }, + /* #19: 1280x720p@50Hz */ + [19] = { + NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0, + }, + /* #20: 1920x1080i@50Hz */ + [20] = { + NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED, 0, + }, + /* #32: 1920x1080p@23.98/24Hz */ + [32] = { + NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0, + }, + /* #35: (2880)x480p4x@59.94/60Hz */ + [35] = { + NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0, FB_VMODE_NONINTERLACED, 0, + }, +}; + const struct fb_videomode vesa_modes[] = { /* 0 640x350-85 VESA */ { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, @@ -855,6 +906,7 @@ const struct fb_videomode *fb_find_nearest_mode(const struct fb_videomode *mode, abs(cmode->yres - mode->yres); if (diff > d) { diff = d; + diff_refresh = abs(cmode->refresh - mode->refresh); best = cmode; } else if (diff == d) { d = abs(cmode->refresh - mode->refresh); diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index ca0f6be9d12e..cb013919e9ce 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -1474,8 +1474,7 @@ static int mx3fb_probe(struct platform_device *pdev) goto eremap; } - pr_debug("Remapped %x to %x at %p\n", sdc_reg->start, sdc_reg->end, - mx3fb->reg_base); + pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base); /* IDMAC interface */ dmaengine_get(); diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig index 455c6055325d..083c8fe53e24 100644 --- a/drivers/video/omap/Kconfig +++ b/drivers/video/omap/Kconfig @@ -1,7 +1,7 @@ config FB_OMAP tristate "OMAP frame buffer support (EXPERIMENTAL)" - depends on FB && ARCH_OMAP && (OMAP2_DSS = "n") - + depends on FB && (OMAP2_DSS = "n") + depends on ARCH_OMAP1 || ARCH_OMAP2 || ARCH_OMAP3 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c index 2fd7e5271be9..9441e2eb3dee 100644 --- a/drivers/video/omap2/vram.c +++ b/drivers/video/omap2/vram.c @@ -551,7 +551,7 @@ void __init omap_vram_reserve_sdram_memblock(void) if (!size) return; - size = PAGE_ALIGN(size); + size = ALIGN(size, SZ_2M); if (paddr) { if (paddr & ~PAGE_MASK) { @@ -576,7 +576,7 @@ void __init omap_vram_reserve_sdram_memblock(void) return; } } else { - paddr = memblock_alloc(size, PAGE_SIZE); + paddr = memblock_alloc(size, SZ_2M); } memblock_free(paddr, size); diff --git a/drivers/video/pxa3xx-gcu.c b/drivers/video/pxa3xx-gcu.c new file mode 100644 index 000000000000..b81168df253d --- /dev/null +++ b/drivers/video/pxa3xx-gcu.c @@ -0,0 +1,772 @@ +/* + * pxa3xx-gc.c - Linux kernel module for PXA3xx graphics controllers + * + * This driver needs a DirectFB counterpart in user space, communication + * is handled via mmap()ed memory areas and an ioctl. + * + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (c) 2009 Janine Kropp <nin@directfb.org> + * Copyright (c) 2009 Denis Oliver Kropp <dok@directfb.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * WARNING: This controller is attached to System Bus 2 of the PXA which + * needs its arbiter to be enabled explictly (CKENB & 1<<9). + * There is currently no way to do this from Linux, so you need to teach + * your bootloader for now. + */ + +#include <linux/module.h> +#include <linux/version.h> + +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> +#include <linux/ioctl.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/fs.h> +#include <linux/io.h> + +#include "pxa3xx-gcu.h" + +#define DRV_NAME "pxa3xx-gcu" +#define MISCDEV_MINOR 197 + +#define REG_GCCR 0x00 +#define GCCR_SYNC_CLR (1 << 9) +#define GCCR_BP_RST (1 << 8) +#define GCCR_ABORT (1 << 6) +#define GCCR_STOP (1 << 4) + +#define REG_GCISCR 0x04 +#define REG_GCIECR 0x08 +#define REG_GCRBBR 0x20 +#define REG_GCRBLR 0x24 +#define REG_GCRBHR 0x28 +#define REG_GCRBTR 0x2C +#define REG_GCRBEXHR 0x30 + +#define IE_EOB (1 << 0) +#define IE_EEOB (1 << 5) +#define IE_ALL 0xff + +#define SHARED_SIZE PAGE_ALIGN(sizeof(struct pxa3xx_gcu_shared)) + +/* #define PXA3XX_GCU_DEBUG */ +/* #define PXA3XX_GCU_DEBUG_TIMER */ + +#ifdef PXA3XX_GCU_DEBUG +#define QDUMP(msg) \ + do { \ + QPRINT(priv, KERN_DEBUG, msg); \ + } while (0) +#else +#define QDUMP(msg) do {} while (0) +#endif + +#define QERROR(msg) \ + do { \ + QPRINT(priv, KERN_ERR, msg); \ + } while (0) + +struct pxa3xx_gcu_batch { + struct pxa3xx_gcu_batch *next; + u32 *ptr; + dma_addr_t phys; + unsigned long length; +}; + +struct pxa3xx_gcu_priv { + void __iomem *mmio_base; + struct clk *clk; + struct pxa3xx_gcu_shared *shared; + dma_addr_t shared_phys; + struct resource *resource_mem; + struct miscdevice misc_dev; + struct file_operations misc_fops; + wait_queue_head_t wait_idle; + wait_queue_head_t wait_free; + spinlock_t spinlock; + struct timeval base_time; + + struct pxa3xx_gcu_batch *free; + + struct pxa3xx_gcu_batch *ready; + struct pxa3xx_gcu_batch *ready_last; + struct pxa3xx_gcu_batch *running; +}; + +static inline unsigned long +gc_readl(struct pxa3xx_gcu_priv *priv, unsigned int off) +{ + return __raw_readl(priv->mmio_base + off); +} + +static inline void +gc_writel(struct pxa3xx_gcu_priv *priv, unsigned int off, unsigned long val) +{ + __raw_writel(val, priv->mmio_base + off); +} + +#define QPRINT(priv, level, msg) \ + do { \ + struct timeval tv; \ + struct pxa3xx_gcu_shared *shared = priv->shared; \ + u32 base = gc_readl(priv, REG_GCRBBR); \ + \ + do_gettimeofday(&tv); \ + \ + printk(level "%ld.%03ld.%03ld - %-17s: %-21s (%s, " \ + "STATUS " \ + "0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, " \ + "T %5ld)\n", \ + tv.tv_sec - priv->base_time.tv_sec, \ + tv.tv_usec / 1000, tv.tv_usec % 1000, \ + __func__, msg, \ + shared->hw_running ? "running" : " idle", \ + gc_readl(priv, REG_GCISCR), \ + gc_readl(priv, REG_GCRBBR), \ + gc_readl(priv, REG_GCRBLR), \ + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, \ + (gc_readl(priv, REG_GCRBHR) - base) / 4, \ + (gc_readl(priv, REG_GCRBTR) - base) / 4); \ + } while (0) + +static void +pxa3xx_gcu_reset(struct pxa3xx_gcu_priv *priv) +{ + QDUMP("RESET"); + + /* disable interrupts */ + gc_writel(priv, REG_GCIECR, 0); + + /* reset hardware */ + gc_writel(priv, REG_GCCR, GCCR_ABORT); + gc_writel(priv, REG_GCCR, 0); + + memset(priv->shared, 0, SHARED_SIZE); + priv->shared->buffer_phys = priv->shared_phys; + priv->shared->magic = PXA3XX_GCU_SHARED_MAGIC; + + do_gettimeofday(&priv->base_time); + + /* set up the ring buffer pointers */ + gc_writel(priv, REG_GCRBLR, 0); + gc_writel(priv, REG_GCRBBR, priv->shared_phys); + gc_writel(priv, REG_GCRBTR, priv->shared_phys); + + /* enable all IRQs except EOB */ + gc_writel(priv, REG_GCIECR, IE_ALL & ~IE_EOB); +} + +static void +dump_whole_state(struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_shared *sh = priv->shared; + u32 base = gc_readl(priv, REG_GCRBBR); + + QDUMP("DUMP"); + + printk(KERN_DEBUG "== PXA3XX-GCU DUMP ==\n" + "%s, STATUS 0x%02lx, B 0x%08lx [%ld], E %5ld, H %5ld, T %5ld\n", + sh->hw_running ? "running" : "idle ", + gc_readl(priv, REG_GCISCR), + gc_readl(priv, REG_GCRBBR), + gc_readl(priv, REG_GCRBLR), + (gc_readl(priv, REG_GCRBEXHR) - base) / 4, + (gc_readl(priv, REG_GCRBHR) - base) / 4, + (gc_readl(priv, REG_GCRBTR) - base) / 4); +} + +static void +flush_running(struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *running = priv->running; + struct pxa3xx_gcu_batch *next; + + while (running) { + next = running->next; + running->next = priv->free; + priv->free = running; + running = next; + } + + priv->running = NULL; +} + +static void +run_ready(struct pxa3xx_gcu_priv *priv) +{ + unsigned int num = 0; + struct pxa3xx_gcu_shared *shared = priv->shared; + struct pxa3xx_gcu_batch *ready = priv->ready; + + QDUMP("Start"); + + BUG_ON(!ready); + + shared->buffer[num++] = 0x05000000; + + while (ready) { + shared->buffer[num++] = 0x00000001; + shared->buffer[num++] = ready->phys; + ready = ready->next; + } + + shared->buffer[num++] = 0x05000000; + priv->running = priv->ready; + priv->ready = priv->ready_last = NULL; + gc_writel(priv, REG_GCRBLR, 0); + shared->hw_running = 1; + + /* ring base address */ + gc_writel(priv, REG_GCRBBR, shared->buffer_phys); + + /* ring tail address */ + gc_writel(priv, REG_GCRBTR, shared->buffer_phys + num * 4); + + /* ring length */ + gc_writel(priv, REG_GCRBLR, ((num + 63) & ~63) * 4); +} + +static irqreturn_t +pxa3xx_gcu_handle_irq(int irq, void *ctx) +{ + struct pxa3xx_gcu_priv *priv = ctx; + struct pxa3xx_gcu_shared *shared = priv->shared; + u32 status = gc_readl(priv, REG_GCISCR) & IE_ALL; + + QDUMP("-Interrupt"); + + if (!status) + return IRQ_NONE; + + spin_lock(&priv->spinlock); + shared->num_interrupts++; + + if (status & IE_EEOB) { + QDUMP(" [EEOB]"); + + flush_running(priv); + wake_up_all(&priv->wait_free); + + if (priv->ready) { + run_ready(priv); + } else { + /* There is no more data prepared by the userspace. + * Set hw_running = 0 and wait for the next userspace + * kick-off */ + shared->num_idle++; + shared->hw_running = 0; + + QDUMP(" '-> Idle."); + + /* set ring buffer length to zero */ + gc_writel(priv, REG_GCRBLR, 0); + + wake_up_all(&priv->wait_idle); + } + + shared->num_done++; + } else { + QERROR(" [???]"); + dump_whole_state(priv); + } + + /* Clear the interrupt */ + gc_writel(priv, REG_GCISCR, status); + spin_unlock(&priv->spinlock); + + return IRQ_HANDLED; +} + +static int +pxa3xx_gcu_wait_idle(struct pxa3xx_gcu_priv *priv) +{ + int ret = 0; + + QDUMP("Waiting for idle..."); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_wait_idle++; + + while (priv->shared->hw_running) { + int num = priv->shared->num_interrupts; + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); + + ret = wait_event_interruptible_timeout(priv->wait_idle, + !priv->shared->hw_running, HZ*4); + + if (ret < 0) + break; + + if (ret > 0) + continue; + + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr && + priv->shared->num_interrupts == num) { + QERROR("TIMEOUT"); + ret = -ETIMEDOUT; + break; + } + } + + QDUMP("done"); + + return ret; +} + +static int +pxa3xx_gcu_wait_free(struct pxa3xx_gcu_priv *priv) +{ + int ret = 0; + + QDUMP("Waiting for free..."); + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_wait_free++; + + while (!priv->free) { + u32 rbexhr = gc_readl(priv, REG_GCRBEXHR); + + ret = wait_event_interruptible_timeout(priv->wait_free, + priv->free, HZ*4); + + if (ret < 0) + break; + + if (ret > 0) + continue; + + if (gc_readl(priv, REG_GCRBEXHR) == rbexhr) { + QERROR("TIMEOUT"); + ret = -ETIMEDOUT; + break; + } + } + + QDUMP("done"); + + return ret; +} + +/* Misc device layer */ + +static ssize_t +pxa3xx_gcu_misc_write(struct file *filp, const char *buff, + size_t count, loff_t *offp) +{ + int ret; + unsigned long flags; + struct pxa3xx_gcu_batch *buffer; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + int words = count / 4; + + /* Does not need to be atomic. There's a lock in user space, + * but anyhow, this is just for statistics. */ + priv->shared->num_writes++; + + priv->shared->num_words += words; + + /* Last word reserved for batch buffer end command */ + if (words >= PXA3XX_GCU_BATCH_WORDS) + return -E2BIG; + + /* Wait for a free buffer */ + if (!priv->free) { + ret = pxa3xx_gcu_wait_free(priv); + if (ret < 0) + return ret; + } + + /* + * Get buffer from free list + */ + spin_lock_irqsave(&priv->spinlock, flags); + + buffer = priv->free; + priv->free = buffer->next; + + spin_unlock_irqrestore(&priv->spinlock, flags); + + + /* Copy data from user into buffer */ + ret = copy_from_user(buffer->ptr, buff, words * 4); + if (ret) { + spin_lock_irqsave(&priv->spinlock, flags); + buffer->next = priv->free; + priv->free = buffer; + spin_unlock_irqrestore(&priv->spinlock, flags); + return ret; + } + + buffer->length = words; + + /* Append batch buffer end command */ + buffer->ptr[words] = 0x01000000; + + /* + * Add buffer to ready list + */ + spin_lock_irqsave(&priv->spinlock, flags); + + buffer->next = NULL; + + if (priv->ready) { + BUG_ON(priv->ready_last == NULL); + + priv->ready_last->next = buffer; + } else + priv->ready = buffer; + + priv->ready_last = buffer; + + if (!priv->shared->hw_running) + run_ready(priv); + + spin_unlock_irqrestore(&priv->spinlock, flags); + + return words * 4; +} + + +static long +pxa3xx_gcu_misc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + switch (cmd) { + case PXA3XX_GCU_IOCTL_RESET: + spin_lock_irqsave(&priv->spinlock, flags); + pxa3xx_gcu_reset(priv); + spin_unlock_irqrestore(&priv->spinlock, flags); + return 0; + + case PXA3XX_GCU_IOCTL_WAIT_IDLE: + return pxa3xx_gcu_wait_idle(priv); + } + + return -ENOSYS; +} + +static int +pxa3xx_gcu_misc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned int size = vma->vm_end - vma->vm_start; + struct pxa3xx_gcu_priv *priv = + container_of(filp->f_op, struct pxa3xx_gcu_priv, misc_fops); + + switch (vma->vm_pgoff) { + case 0: + /* hand out the shared data area */ + if (size != SHARED_SIZE) + return -EINVAL; + + return dma_mmap_coherent(NULL, vma, + priv->shared, priv->shared_phys, size); + + case SHARED_SIZE >> PAGE_SHIFT: + /* hand out the MMIO base for direct register access + * from userspace */ + if (size != resource_size(priv->resource_mem)) + return -EINVAL; + + vma->vm_flags |= VM_IO; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return io_remap_pfn_range(vma, vma->vm_start, + priv->resource_mem->start >> PAGE_SHIFT, + size, vma->vm_page_prot); + } + + return -EINVAL; +} + + +#ifdef PXA3XX_GCU_DEBUG_TIMER +static struct timer_list pxa3xx_gcu_debug_timer; + +static void pxa3xx_gcu_debug_timedout(unsigned long ptr) +{ + struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr; + + QERROR("Timer DUMP"); + + /* init the timer structure */ + init_timer(&pxa3xx_gcu_debug_timer); + pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout; + pxa3xx_gcu_debug_timer.data = ptr; + pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */ + + add_timer(&pxa3xx_gcu_debug_timer); +} + +static void pxa3xx_gcu_init_debug_timer(void) +{ + pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer); +} +#else +static inline void pxa3xx_gcu_init_debug_timer(void) {} +#endif + +static int +add_buffer(struct platform_device *dev, + struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *buffer; + + buffer = kzalloc(sizeof(struct pxa3xx_gcu_batch), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer->ptr = dma_alloc_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, + &buffer->phys, GFP_KERNEL); + if (!buffer->ptr) { + kfree(buffer); + return -ENOMEM; + } + + buffer->next = priv->free; + + priv->free = buffer; + + return 0; +} + +static void +free_buffers(struct platform_device *dev, + struct pxa3xx_gcu_priv *priv) +{ + struct pxa3xx_gcu_batch *next, *buffer = priv->free; + + while (buffer) { + next = buffer->next; + + dma_free_coherent(&dev->dev, PXA3XX_GCU_BATCH_WORDS * 4, + buffer->ptr, buffer->phys); + + kfree(buffer); + + buffer = next; + } + + priv->free = NULL; +} + +static int __devinit +pxa3xx_gcu_probe(struct platform_device *dev) +{ + int i, ret, irq; + struct resource *r; + struct pxa3xx_gcu_priv *priv; + + priv = kzalloc(sizeof(struct pxa3xx_gcu_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (i = 0; i < 8; i++) { + ret = add_buffer(dev, priv); + if (ret) { + dev_err(&dev->dev, "failed to allocate DMA memory\n"); + goto err_free_priv; + } + } + + init_waitqueue_head(&priv->wait_idle); + init_waitqueue_head(&priv->wait_free); + spin_lock_init(&priv->spinlock); + + /* we allocate the misc device structure as part of our own allocation, + * so we can get a pointer to our priv structure later on with + * container_of(). This isn't really necessary as we have a fixed minor + * number anyway, but this is to avoid statics. */ + + priv->misc_fops.owner = THIS_MODULE; + priv->misc_fops.write = pxa3xx_gcu_misc_write; + priv->misc_fops.unlocked_ioctl = pxa3xx_gcu_misc_ioctl; + priv->misc_fops.mmap = pxa3xx_gcu_misc_mmap; + + priv->misc_dev.minor = MISCDEV_MINOR, + priv->misc_dev.name = DRV_NAME, + priv->misc_dev.fops = &priv->misc_fops, + + /* register misc device */ + ret = misc_register(&priv->misc_dev); + if (ret < 0) { + dev_err(&dev->dev, "misc_register() for minor %d failed\n", + MISCDEV_MINOR); + goto err_free_priv; + } + + /* handle IO resources */ + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&dev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto err_misc_deregister; + } + + if (!request_mem_region(r->start, resource_size(r), dev->name)) { + dev_err(&dev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto err_misc_deregister; + } + + priv->mmio_base = ioremap_nocache(r->start, resource_size(r)); + if (!priv->mmio_base) { + dev_err(&dev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto err_free_mem_region; + } + + /* allocate dma memory */ + priv->shared = dma_alloc_coherent(&dev->dev, SHARED_SIZE, + &priv->shared_phys, GFP_KERNEL); + + if (!priv->shared) { + dev_err(&dev->dev, "failed to allocate DMA memory\n"); + ret = -ENOMEM; + goto err_free_io; + } + + /* enable the clock */ + priv->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&dev->dev, "failed to get clock\n"); + ret = -ENODEV; + goto err_free_dma; + } + + ret = clk_enable(priv->clk); + if (ret < 0) { + dev_err(&dev->dev, "failed to enable clock\n"); + goto err_put_clk; + } + + /* request the IRQ */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no IRQ defined\n"); + ret = -ENODEV; + goto err_put_clk; + } + + ret = request_irq(irq, pxa3xx_gcu_handle_irq, + IRQF_DISABLED, DRV_NAME, priv); + if (ret) { + dev_err(&dev->dev, "request_irq failed\n"); + ret = -EBUSY; + goto err_put_clk; + } + + platform_set_drvdata(dev, priv); + priv->resource_mem = r; + pxa3xx_gcu_reset(priv); + pxa3xx_gcu_init_debug_timer(); + + dev_info(&dev->dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n", + (void *) r->start, (void *) priv->shared_phys, + SHARED_SIZE, irq); + return 0; + +err_put_clk: + clk_disable(priv->clk); + clk_put(priv->clk); + +err_free_dma: + dma_free_coherent(&dev->dev, SHARED_SIZE, + priv->shared, priv->shared_phys); + +err_free_io: + iounmap(priv->mmio_base); + +err_free_mem_region: + release_mem_region(r->start, resource_size(r)); + +err_misc_deregister: + misc_deregister(&priv->misc_dev); + +err_free_priv: + platform_set_drvdata(dev, NULL); + free_buffers(dev, priv); + kfree(priv); + return ret; +} + +static int __devexit +pxa3xx_gcu_remove(struct platform_device *dev) +{ + struct pxa3xx_gcu_priv *priv = platform_get_drvdata(dev); + struct resource *r = priv->resource_mem; + + pxa3xx_gcu_wait_idle(priv); + + misc_deregister(&priv->misc_dev); + dma_free_coherent(&dev->dev, SHARED_SIZE, + priv->shared, priv->shared_phys); + iounmap(priv->mmio_base); + release_mem_region(r->start, resource_size(r)); + platform_set_drvdata(dev, NULL); + clk_disable(priv->clk); + free_buffers(dev, priv); + kfree(priv); + + return 0; +} + +static struct platform_driver pxa3xx_gcu_driver = { + .probe = pxa3xx_gcu_probe, + .remove = __devexit_p(pxa3xx_gcu_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + }, +}; + +static int __init +pxa3xx_gcu_init(void) +{ + return platform_driver_register(&pxa3xx_gcu_driver); +} + +static void __exit +pxa3xx_gcu_exit(void) +{ + platform_driver_unregister(&pxa3xx_gcu_driver); +} + +module_init(pxa3xx_gcu_init); +module_exit(pxa3xx_gcu_exit); + +MODULE_DESCRIPTION("PXA3xx graphics controller unit driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(MISCDEV_MINOR); +MODULE_AUTHOR("Janine Kropp <nin@directfb.org>, " + "Denis Oliver Kropp <dok@directfb.org>, " + "Daniel Mack <daniel@caiaq.de>"); diff --git a/drivers/video/pxa3xx-gcu.h b/drivers/video/pxa3xx-gcu.h new file mode 100644 index 000000000000..0428ed03dc49 --- /dev/null +++ b/drivers/video/pxa3xx-gcu.h @@ -0,0 +1,38 @@ +#ifndef __PXA3XX_GCU_H__ +#define __PXA3XX_GCU_H__ + +#include <linux/types.h> + +/* Number of 32bit words in display list (ring buffer). */ +#define PXA3XX_GCU_BUFFER_WORDS ((256 * 1024 - 256) / 4) + +/* To be increased when breaking the ABI */ +#define PXA3XX_GCU_SHARED_MAGIC 0x30000001 + +#define PXA3XX_GCU_BATCH_WORDS 8192 + +struct pxa3xx_gcu_shared { + u32 buffer[PXA3XX_GCU_BUFFER_WORDS]; + + bool hw_running; + + unsigned long buffer_phys; + + unsigned int num_words; + unsigned int num_writes; + unsigned int num_done; + unsigned int num_interrupts; + unsigned int num_wait_idle; + unsigned int num_wait_free; + unsigned int num_idle; + + u32 magic; +}; + +/* Initialization and synchronization. + * Hardware is started upon write(). */ +#define PXA3XX_GCU_IOCTL_RESET _IO('G', 0) +#define PXA3XX_GCU_IOCTL_WAIT_IDLE _IO('G', 2) + +#endif /* __PXA3XX_GCU_H__ */ + diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c index a6247fc081ab..28b1c6c3d8ac 100644 --- a/drivers/video/s1d13xxxfb.c +++ b/drivers/video/s1d13xxxfb.c @@ -410,28 +410,6 @@ s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ************************************************************/ /** - * bltbit_wait_bitset - waits for change in register value - * @info : framebuffer structure - * @bit : value expected in register - * @timeout : ... - * - * waits until value changes INTO bit - */ -static u8 -bltbit_wait_bitset(struct fb_info *info, u8 bit, int timeout) -{ - while (!(s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit)) { - udelay(10); - if (!--timeout) { - dbg_blit("wait_bitset timeout\n"); - break; - } - } - - return timeout; -} - -/** * bltbit_wait_bitclear - waits for change in register value * @info : frambuffer structure * @bit : value currently in register @@ -454,34 +432,6 @@ bltbit_wait_bitclear(struct fb_info *info, u8 bit, int timeout) return timeout; } -/** - * bltbit_fifo_status - checks the current status of the fifo - * @info : framebuffer structure - * - * returns number of free words in buffer - */ -static u8 -bltbit_fifo_status(struct fb_info *info) -{ - u8 status; - - status = s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0); - - /* its empty so room for 16 words */ - if (status & BBLT_FIFO_EMPTY) - return 16; - - /* its full so we dont want to add */ - if (status & BBLT_FIFO_FULL) - return 0; - - /* its atleast half full but we can add one atleast */ - if (status & BBLT_FIFO_NOT_FULL) - return 1; - - return 0; -} - /* * s1d13xxxfb_bitblt_copyarea - accelerated copyarea function * @info : framebuffer structure diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f9aca9d13d1b..83ce9a04d872 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -23,6 +23,7 @@ #include <linux/io.h> #include <linux/uaccess.h> #include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <mach/map.h> #include <plat/regs-fb-v4.h> @@ -1013,8 +1014,30 @@ static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, return ret; } +static int s3c_fb_open(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + + pm_runtime_get_sync(sfb->dev); + + return 0; +} + +static int s3c_fb_release(struct fb_info *info, int user) +{ + struct s3c_fb_win *win = info->par; + struct s3c_fb *sfb = win->parent; + + pm_runtime_put_sync(sfb->dev); + + return 0; +} + static struct fb_ops s3c_fb_ops = { .owner = THIS_MODULE, + .fb_open = s3c_fb_open, + .fb_release = s3c_fb_release, .fb_check_var = s3c_fb_check_var, .fb_set_par = s3c_fb_set_par, .fb_blank = s3c_fb_blank, @@ -1322,6 +1345,8 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) clk_enable(sfb->bus_clk); + pm_runtime_enable(sfb->dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "failed to find registers\n"); @@ -1360,6 +1385,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); + platform_set_drvdata(pdev, sfb); + pm_runtime_get_sync(sfb->dev); + /* setup gpio and output polarity controls */ pd->setup_gpio(); @@ -1400,6 +1428,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, sfb); + pm_runtime_put_sync(sfb->dev); return 0; @@ -1434,6 +1463,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) struct s3c_fb *sfb = platform_get_drvdata(pdev); int win; + pm_runtime_get_sync(sfb->dev); + for (win = 0; win < S3C_FB_MAX_WIN; win++) if (sfb->windows[win]) s3c_fb_release_win(sfb, sfb->windows[win]); @@ -1450,12 +1481,74 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) kfree(sfb); + pm_runtime_put_sync(sfb->dev); + pm_runtime_disable(sfb->dev); + return 0; } #ifdef CONFIG_PM -static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) +static int s3c_fb_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_win *win; + int win_no; + + for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { + win = sfb->windows[win_no]; + if (!win) + continue; + + /* use the blank function to push into power-down */ + s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); + } + + clk_disable(sfb->bus_clk); + return 0; +} + +static int s3c_fb_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct s3c_fb *sfb = platform_get_drvdata(pdev); + struct s3c_fb_platdata *pd = sfb->pdata; + struct s3c_fb_win *win; + int win_no; + + clk_enable(sfb->bus_clk); + + /* setup registers */ + writel(pd->vidcon1, sfb->regs + VIDCON1); + + /* zero all windows before we do anything */ + for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) + s3c_fb_clear_win(sfb, win_no); + + for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { + void __iomem *regs = sfb->regs + sfb->variant.keycon; + + regs += (win_no * 8); + writel(0xffffff, regs + WKEYCON0); + writel(0xffffff, regs + WKEYCON1); + } + + /* restore framebuffers */ + for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { + win = sfb->windows[win_no]; + if (!win) + continue; + + dev_dbg(&pdev->dev, "resuming window %d\n", win_no); + s3c_fb_set_par(win->fbinfo); + } + + return 0; +} + +int s3c_fb_runtime_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct s3c_fb *sfb = platform_get_drvdata(pdev); struct s3c_fb_win *win; int win_no; @@ -1473,8 +1566,9 @@ static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int s3c_fb_resume(struct platform_device *pdev) +int s3c_fb_runtime_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct s3c_fb *sfb = platform_get_drvdata(pdev); struct s3c_fb_platdata *pd = sfb->pdata; struct s3c_fb_win *win; @@ -1509,9 +1603,12 @@ static int s3c_fb_resume(struct platform_device *pdev) return 0; } + #else #define s3c_fb_suspend NULL #define s3c_fb_resume NULL +#define s3c_fb_runtime_suspend NULL +#define s3c_fb_runtime_resume NULL #endif @@ -1710,15 +1807,21 @@ static struct platform_device_id s3c_fb_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); +static const struct dev_pm_ops s3cfb_pm_ops = { + .suspend = s3c_fb_suspend, + .resume = s3c_fb_resume, + .runtime_suspend = s3c_fb_runtime_suspend, + .runtime_resume = s3c_fb_runtime_resume, +}; + static struct platform_driver s3c_fb_driver = { .probe = s3c_fb_probe, .remove = __devexit_p(s3c_fb_remove), - .suspend = s3c_fb_suspend, - .resume = s3c_fb_resume, .id_table = s3c_fb_driver_ids, .driver = { .name = "s3c-fb", .owner = THIS_MODULE, + .pm = &s3cfb_pm_ops, }, }; diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 3f3d431033ca..24640c8458ab 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> @@ -21,18 +22,40 @@ #include <video/sh_mipi_dsi.h> #include <video/sh_mobile_lcdc.h> -#define CMTSRTCTR 0x80d0 -#define CMTSRTREQ 0x8070 - +#define SYSCTRL 0x0000 +#define SYSCONF 0x0004 +#define TIMSET 0x0008 +#define RESREQSET0 0x0018 +#define RESREQSET1 0x001c +#define HSTTOVSET 0x0020 +#define LPRTOVSET 0x0024 +#define TATOVSET 0x0028 +#define PRTOVSET 0x002c +#define DSICTRL 0x0030 #define DSIINTE 0x0060 +#define PHYCTRL 0x0070 + +/* relative to linkbase */ +#define DTCTR 0x0000 +#define VMCTR1 0x0020 +#define VMCTR2 0x0024 +#define VMLEN1 0x0028 +#define CMTSRTREQ 0x0070 +#define CMTSRTCTR 0x00d0 /* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ #define MAX_SH_MIPI_DSI 2 struct sh_mipi { void __iomem *base; + void __iomem *linkbase; struct clk *dsit_clk; struct clk *dsip_clk; + struct device *dev; + + void *next_board_data; + void (*next_display_on)(void *board_data, struct fb_info *info); + void (*next_display_off)(void *board_data); }; static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; @@ -55,10 +78,10 @@ static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, int cnt = 100; /* transmit a short packet to LCD panel */ - iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */ - iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */ + iowrite32(1 | data, mipi->linkbase + CMTSRTCTR); + iowrite32(1, mipi->linkbase + CMTSRTREQ); - while ((ioread32(mipi->base + 0x8070) & 1) && --cnt) + while ((ioread32(mipi->linkbase + CMTSRTREQ) & 1) && --cnt) udelay(1); return cnt ? 0 : -ETIMEDOUT; @@ -90,7 +113,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) * enable LCDC data tx, transition to LPS after completion of each HS * packet */ - iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */ + iowrite32(0x00000002 | enable, mipi->linkbase + DTCTR); } static void sh_mipi_shutdown(struct platform_device *pdev) @@ -104,14 +127,22 @@ static void mipi_display_on(void *arg, struct fb_info *info) { struct sh_mipi *mipi = arg; + pm_runtime_get_sync(mipi->dev); sh_mipi_dsi_enable(mipi, true); + + if (mipi->next_display_on) + mipi->next_display_on(mipi->next_board_data, info); } static void mipi_display_off(void *arg) { struct sh_mipi *mipi = arg; + if (mipi->next_display_off) + mipi->next_display_off(mipi->next_board_data); + sh_mipi_dsi_enable(mipi, false); + pm_runtime_put(mipi->dev); } static int __init sh_mipi_setup(struct sh_mipi *mipi, @@ -119,8 +150,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, { void __iomem *base = mipi->base; struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan; - u32 pctype, datatype, pixfmt; - u32 linelength; + u32 pctype, datatype, pixfmt, linelength, vmctr2 = 0x00e00000; bool yuv; /* @@ -223,10 +253,10 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, return -EINVAL; /* reset DSI link */ - iowrite32(0x00000001, base); /* SYSCTRL */ + iowrite32(0x00000001, base + SYSCTRL); /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ udelay(50); - iowrite32(0x00000000, base); /* SYSCTRL */ + iowrite32(0x00000000, base + SYSCTRL); /* setup DSI link */ @@ -238,7 +268,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, * ECC check enable * additionally enable first two lanes */ - iowrite32(0x00003703, base + 0x04); /* SYSCONF */ + iowrite32(0x00003703, base + SYSCONF); /* * T_wakeup = 0x7000 * T_hs-trail = 3 @@ -246,28 +276,28 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, * T_clk-trail = 3 * T_clk-prepare = 2 */ - iowrite32(0x70003332, base + 0x08); /* TIMSET */ + iowrite32(0x70003332, base + TIMSET); /* no responses requested */ - iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */ + iowrite32(0x00000000, base + RESREQSET0); /* request response to packets of type 0x28 */ - iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */ + iowrite32(0x00000100, base + RESREQSET1); /* High-speed transmission timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */ + iowrite32(0x0fffffff, base + HSTTOVSET); /* LP reception timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */ + iowrite32(0x0fffffff, base + LPRTOVSET); /* Turn-around timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */ + iowrite32(0x0fffffff, base + TATOVSET); /* Peripheral reset timeout, default 0xffffffff */ - iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */ + iowrite32(0x0fffffff, base + PRTOVSET); /* Enable timeout counters */ - iowrite32(0x00000f00, base + 0x30); /* DSICTRL */ + iowrite32(0x00000f00, base + DSICTRL); /* Interrupts not used, disable all */ iowrite32(0, base + DSIINTE); /* DSI-Tx bias on */ - iowrite32(0x00000001, base + 0x70); /* PHYCTRL */ + iowrite32(0x00000001, base + PHYCTRL); udelay(200); /* Deassert resets, power on, set multiplier */ - iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */ + iowrite32(0x03070b01, base + PHYCTRL); /* setup l-bridge */ @@ -275,20 +305,28 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, * Enable transmission of all packets, * transmit LPS after each HS packet completion */ - iowrite32(0x00000006, base + 0x8000); /* DTCTR */ + iowrite32(0x00000006, mipi->linkbase + DTCTR); /* VSYNC width = 2 (<< 17) */ - iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */ + iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) | + (pdata->clksrc << 16) | (pctype << 12) | datatype, + mipi->linkbase + VMCTR1); + /* * Non-burst mode with sync pulses: VSE and HSE are output, * HSA period allowed, no commands in LP */ - iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ + if (pdata->flags & SH_MIPI_DSI_HSABM) + vmctr2 |= 0x20; + if (pdata->flags & SH_MIPI_DSI_HSPBM) + vmctr2 |= 0x10; + iowrite32(vmctr2, mipi->linkbase + VMCTR2); + /* * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see * sh_mobile_lcdc_info.ch[0].lcd_cfg[0].xres), HSALEN = 1 - default - * (unused, since VMCTR2[HSABM] = 0) + * (unused if VMCTR2[HSABM] = 0) */ - iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ + iowrite32(1 | (linelength << 16), mipi->linkbase + VMLEN1); msleep(5); @@ -321,11 +359,12 @@ static int __init sh_mipi_probe(struct platform_device *pdev) struct sh_mipi *mipi; struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); unsigned long rate, f_current; int idx = pdev->id, ret; char dsip_clk[] = "dsi.p_clk"; - if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) + if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) return -ENODEV; mutex_lock(&array_lock); @@ -356,6 +395,20 @@ static int __init sh_mipi_probe(struct platform_device *pdev) goto emap; } + if (!request_mem_region(res2->start, resource_size(res2), pdev->name)) { + dev_err(&pdev->dev, "MIPI register region 2 already claimed\n"); + ret = -EBUSY; + goto ereqreg2; + } + + mipi->linkbase = ioremap(res2->start, resource_size(res2)); + if (!mipi->linkbase) { + ret = -ENOMEM; + goto emap2; + } + + mipi->dev = &pdev->dev; + mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); if (IS_ERR(mipi->dsit_clk)) { ret = PTR_ERR(mipi->dsit_clk); @@ -405,6 +458,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev) mipi_dsi[idx] = mipi; + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + ret = sh_mipi_setup(mipi, pdata); if (ret < 0) goto emipisetup; @@ -412,15 +468,22 @@ static int __init sh_mipi_probe(struct platform_device *pdev) mutex_unlock(&array_lock); platform_set_drvdata(pdev, mipi); + /* Save original LCDC callbacks */ + mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data; + mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on; + mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off; + /* Set up LCDC callbacks */ pdata->lcd_chan->board_cfg.board_data = mipi; pdata->lcd_chan->board_cfg.display_on = mipi_display_on; pdata->lcd_chan->board_cfg.display_off = mipi_display_off; + pdata->lcd_chan->board_cfg.owner = THIS_MODULE; return 0; emipisetup: mipi_dsi[idx] = NULL; + pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsip_clk); eclkpon: clk_disable(mipi->dsit_clk); @@ -431,6 +494,10 @@ eclkpget: esettrate: clk_put(mipi->dsit_clk); eclktget: + iounmap(mipi->linkbase); +emap2: + release_mem_region(res2->start, resource_size(res2)); +ereqreg2: iounmap(mipi->base); emap: release_mem_region(res->start, resource_size(res)); @@ -447,6 +514,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) { struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); struct sh_mipi *mipi = platform_get_drvdata(pdev); int i, ret; @@ -467,14 +535,19 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) if (ret < 0) return ret; + pdata->lcd_chan->board_cfg.owner = NULL; pdata->lcd_chan->board_cfg.display_on = NULL; pdata->lcd_chan->board_cfg.display_off = NULL; pdata->lcd_chan->board_cfg.board_data = NULL; + pm_runtime_disable(&pdev->dev); clk_disable(mipi->dsip_clk); clk_disable(mipi->dsit_clk); clk_put(mipi->dsit_clk); clk_put(mipi->dsip_clk); + iounmap(mipi->linkbase); + if (res2) + release_mem_region(res2->start, resource_size(res2)); iounmap(mipi->base); if (res) release_mem_region(res->start, resource_size(res)); diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index d7df10315d8d..8c59cc8c5a9c 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c @@ -209,7 +209,11 @@ enum hotplug_state { struct sh_hdmi { void __iomem *base; enum hotplug_state hp_state; /* hot-plug status */ - bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */ + u8 preprogrammed_vic; /* use a pre-programmed VIC or + the external mode */ + u8 edid_block_addr; + u8 edid_segment_nr; + u8 edid_blocks; struct clk *hdmi_clk; struct device *dev; struct fb_info *info; @@ -342,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ - if (!hdmi->preprogrammed_mode) + if (!hdmi->preprogrammed_vic) hdmi_write(hdmi, sync | 1 | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); } @@ -466,7 +470,18 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) */ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) { - if (hdmi->var.yres > 480) { + if (hdmi->var.pixclock < 10000) { + /* for 1080p8bit 148MHz */ + hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); + hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); + hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); + hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); + hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); + hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); + hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); + hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); + } else if (hdmi->var.pixclock < 30000) { /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ /* * [1:0] Speed_A @@ -565,13 +580,11 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); /* - * VIC = 1280 x 720p: ignored if external config is used - * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode + * VIC should be ignored if external config is used, so, we could just use 0, + * but play safe and use a valid value in any case just in case */ - if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920) - vic = 16; - else if (hdmi->var.yres == 480 && hdmi->var.xres == 720) - vic = 2; + if (hdmi->preprogrammed_vic) + vic = hdmi->preprogrammed_vic; else vic = 4; hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); @@ -685,11 +698,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) } static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, - const struct fb_videomode *mode) + const struct fb_videomode *mode, + unsigned long *hdmi_rate, unsigned long *parent_rate) { - long target = PICOS2KHZ(mode->pixclock) * 1000, - rate = clk_round_rate(hdmi->hdmi_clk, target); - unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; + unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; + + *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target); + if ((long)*hdmi_rate < 0) + *hdmi_rate = clk_get_rate(hdmi->hdmi_clk); + + rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX; + if (rate_error && pdata->clk_optimize_parent) + rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate); + else if (clk_get_parent(hdmi->hdmi_clk)) + *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk)); dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", mode->left_margin, mode->xres, @@ -697,14 +720,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, mode->upper_margin, mode->yres, mode->lower_margin, mode->vsync_len); - dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, - rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, - mode->refresh); + dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target, + rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, + mode->refresh, *parent_rate); return rate_error; } -static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) +static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, + unsigned long *parent_rate) { struct fb_var_screeninfo tmpvar; struct fb_var_screeninfo *var = &tmpvar; @@ -735,7 +759,38 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) printk(KERN_CONT "\n"); #endif - fb_edid_to_monspecs(edid, &hdmi->monspec); + if (!hdmi->edid_blocks) { + fb_edid_to_monspecs(edid, &hdmi->monspec); + hdmi->edid_blocks = edid[126] + 1; + + dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n", + hdmi->monspec.modedb_len, hdmi->edid_blocks - 1); + } else { + dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n", + edid[0], edid[2]); + fb_edid_add_monspecs(edid, &hdmi->monspec); + } + + if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 + + (hdmi->edid_block_addr >> 7) + 1) { + /* More blocks to read */ + if (hdmi->edid_block_addr) { + hdmi->edid_block_addr = 0; + hdmi->edid_segment_nr++; + } else { + hdmi->edid_block_addr = 0x80; + } + /* Set EDID word address */ + hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); + /* Enable EDID interrupt */ + hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + /* Set EDID segment pointer - starts reading EDID */ + hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); + return -EAGAIN; + } + + /* All E-EDID blocks ready */ + dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len); fb_get_options("sh_mobile_lcdc", &forced); if (forced && *forced) { @@ -754,11 +809,14 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) for (i = 0, mode = hdmi->monspec.modedb; f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; i++, mode++) { - unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); + unsigned long rate_error; /* No interest in unmatching modes */ if (f_width != mode->xres || f_height != mode->yres) continue; + + rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); + if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) /* * Exact match if either the refresh rate matches or it @@ -787,6 +845,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) found_rate_error = rate_error; } + hdmi->var.width = hdmi->monspec.max_x * 10; + hdmi->var.height = hdmi->monspec.max_y * 10; + /* * TODO 1: if no ->info is present, postpone running the config until * after ->info first gets registered. @@ -802,7 +863,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) if (modelist) { found = &modelist->mode; - found_rate_error = sh_hdmi_rate_error(hdmi, found); + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); } } @@ -810,16 +871,27 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) if (!found) return -ENXIO; - dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", - modelist ? "default" : "EDID", found->xres, found->yres, - found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); - - if ((found->xres == 720 && found->yres == 480) || - (found->xres == 1280 && found->yres == 720) || - (found->xres == 1920 && found->yres == 1080)) - hdmi->preprogrammed_mode = true; + if (found->xres == 640 && found->yres == 480 && found->refresh == 60) + hdmi->preprogrammed_vic = 1; + else if (found->xres == 720 && found->yres == 480 && found->refresh == 60) + hdmi->preprogrammed_vic = 2; + else if (found->xres == 720 && found->yres == 576 && found->refresh == 50) + hdmi->preprogrammed_vic = 17; + else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60) + hdmi->preprogrammed_vic = 4; + else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24) + hdmi->preprogrammed_vic = 32; + else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50) + hdmi->preprogrammed_vic = 31; + else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60) + hdmi->preprogrammed_vic = 16; else - hdmi->preprogrammed_mode = false; + hdmi->preprogrammed_vic = 0; + + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", + modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", + found->xres, found->yres, found->refresh, + PICOS2KHZ(found->pixclock) * 1000, found_rate_error); fb_videomode_to_var(&hdmi->var, found); sh_hdmi_external_video_param(hdmi); @@ -868,32 +940,34 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) /* Check, if hot plug & MSENS pin status are both high */ if ((msens & 0xC0) == 0xC0) { /* Display plug in */ + hdmi->edid_segment_nr = 0; + hdmi->edid_block_addr = 0; + hdmi->edid_blocks = 0; hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; /* Set EDID word address */ hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); - /* Set EDID segment pointer */ - hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); /* Enable EDID interrupt */ hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); + /* Set EDID segment pointer - starts reading EDID */ + hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); } else if (!(status1 & 0x80)) { /* Display unplug, beware multiple interrupts */ - if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) + if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) { + hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; schedule_delayed_work(&hdmi->edid_work, 0); - - hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; + } /* display_off will switch back to mode_a */ } } else if (status1 & 2) { /* EDID error interrupt: retry */ /* Set EDID word address */ - hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); + hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); /* Set EDID segment pointer */ - hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); + hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); } else if (status1 & 4) { /* Disable EDID interrupt */ hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); - hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); } @@ -960,8 +1034,12 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", mode1.xres, mode1.yres, mode2.xres, mode2.yres); - if (fb_mode_is_equal(&mode1, &mode2)) + if (fb_mode_is_equal(&mode1, &mode2)) { + /* It can be a different monitor with an equal video-mode */ + old_var->width = new_var->width; + old_var->height = new_var->height; return false; + } dev_dbg(info->dev, "Switching %u -> %u lines\n", mode1.yres, mode2.yres); @@ -972,39 +1050,37 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) /** * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock - * @hdmi: driver context - * @pixclock: pixel clock period in picoseconds - * return: configured positive rate if successful - * 0 if couldn't set the rate, but managed to enable the clock - * negative error, if couldn't enable the clock + * @hdmi: driver context + * @hdmi_rate: HDMI clock frequency in Hz + * @parent_rate: if != 0 - set parent clock rate for optimal precision + * return: configured positive rate if successful + * 0 if couldn't set the rate, but managed to enable the + * clock, negative error, if couldn't enable the clock */ -static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) +static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, + unsigned long parent_rate) { - long rate; int ret; - rate = PICOS2KHZ(pixclock) * 1000; - rate = clk_round_rate(hdmi->hdmi_clk, rate); - if (rate > 0) { - ret = clk_set_rate(hdmi->hdmi_clk, rate); + if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { + ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); if (ret < 0) { - dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); - rate = 0; + dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); + hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); } else { - dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); + dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); } - } else { - rate = 0; - dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); } - ret = clk_enable(hdmi->hdmi_clk); + ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); if (ret < 0) { - dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); - return ret; + dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); + hdmi_rate = 0; + } else { + dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); } - return rate; + return hdmi_rate; } /* Hotplug interrupt occurred, read EDID */ @@ -1023,17 +1099,20 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) mutex_lock(&hdmi->mutex); - if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { + if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { + unsigned long parent_rate = 0, hdmi_rate; + /* A device has been plugged in */ pm_runtime_get_sync(hdmi->dev); - ret = sh_hdmi_read_edid(hdmi); + ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); if (ret < 0) goto out; + hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; + /* Reconfigure the clock */ - clk_disable(hdmi->hdmi_clk); - ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); + ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); if (ret < 0) goto out; @@ -1057,8 +1136,11 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) * on, if we run a resume here, the logo disappears */ if (lock_fb_info(hdmi->info)) { - sh_hdmi_display_on(hdmi, hdmi->info); - unlock_fb_info(hdmi->info); + struct fb_info *info = hdmi->info; + info->var.width = hdmi->var.width; + info->var.height = hdmi->var.height; + sh_hdmi_display_on(hdmi, info); + unlock_fb_info(info); } } else { /* New monitor or have to wake up */ @@ -1085,7 +1167,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) } out: - if (ret < 0) + if (ret < 0 && ret != -EAGAIN) hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; mutex_unlock(&hdmi->mutex); @@ -1166,13 +1248,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) goto egetclk; } - /* Some arbitrary relaxed pixclock just to get things started */ - rate = sh_hdmi_clk_configure(hdmi, 37037); + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ + rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); + if (rate > 0) + rate = sh_hdmi_clk_configure(hdmi, rate, 0); + if (rate < 0) { ret = rate; goto erate; } + ret = clk_enable(hdmi->hdmi_clk); + if (ret < 0) { + dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); + goto erate; + } + dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { @@ -1190,10 +1281,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, hdmi); - /* Product and revision IDs are 0 in sh-mobile version */ - dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", - hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); - /* Set up LCDC callbacks */ board_cfg = &pdata->lcd_chan->board_cfg; board_cfg->owner = THIS_MODULE; @@ -1206,6 +1293,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); + /* Product and revision IDs are 0 in sh-mobile version */ + dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", + hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); + ret = request_irq(irq, sh_hdmi_hotplug, 0, dev_name(&pdev->dev), hdmi); if (ret < 0) { diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index b02d97a879d6..bd4840a8a6b7 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -54,8 +54,8 @@ static int lcdc_shared_regs[] = { }; #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) -#define DEFAULT_XRES 1280 -#define DEFAULT_YRES 1024 +#define MAX_XRES 1920 +#define MAX_YRES 1080 static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x400, @@ -139,6 +139,7 @@ struct sh_mobile_lcdc_priv { struct notifier_block notifier; unsigned long saved_shared_regs[NR_SHARED_REGS]; int started; + int forced_bpp; /* 2 channel LCDC must share bpp setting */ }; static bool banked(int reg_nr) @@ -461,13 +462,18 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) struct sh_mobile_lcdc_chan *ch; struct sh_mobile_lcdc_board_cfg *board_cfg; unsigned long tmp; + int bpp = 0; int k, m; int ret = 0; /* enable clocks before accessing the hardware */ - for (k = 0; k < ARRAY_SIZE(priv->ch); k++) - if (priv->ch[k].enabled) + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { + if (priv->ch[k].enabled) { sh_mobile_lcdc_clk_on(priv); + if (!bpp) + bpp = priv->ch[k].info->var.bits_per_pixel; + } + } /* reset */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); @@ -535,7 +541,17 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) } /* word and long word swap */ - lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); + switch (bpp) { + case 16: + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); + break; + case 24: + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7); + break; + case 32: + lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4); + break; + } for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; @@ -546,7 +562,16 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) /* set bpp format in PKF[4:0] */ tmp = lcdc_read_chan(ch, LDDFR); tmp &= ~0x0001001f; - tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; + switch (ch->info->var.bits_per_pixel) { + case 16: + tmp |= 0x03; + break; + case 24: + tmp |= 0x0b; + break; + case 32: + break; + } lcdc_write_chan(ch, LDDFR, tmp); /* point out our frame buffer */ @@ -913,25 +938,30 @@ static int sh_mobile_open(struct fb_info *info, int user) static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; + struct sh_mobile_lcdc_priv *p = ch->lcdc; - if (var->xres < 160 || var->xres > 1920 || - var->yres < 120 || var->yres > 1080 || - var->left_margin < 32 || var->left_margin > 320 || - var->right_margin < 12 || var->right_margin > 240 || - var->upper_margin < 12 || var->upper_margin > 120 || - var->lower_margin < 1 || var->lower_margin > 64 || - var->hsync_len < 32 || var->hsync_len > 240 || - var->vsync_len < 2 || var->vsync_len > 64 || - var->pixclock < 6000 || var->pixclock > 40000 || + if (var->xres > MAX_XRES || var->yres > MAX_YRES || var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { - dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n", - var->xres, var->yres, - var->left_margin, var->right_margin, - var->upper_margin, var->lower_margin, - var->hsync_len, var->vsync_len, - var->pixclock); + dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n", + var->left_margin, var->xres, var->right_margin, var->hsync_len, + var->upper_margin, var->yres, var->lower_margin, var->vsync_len, + PICOS2KHZ(var->pixclock)); return -EINVAL; } + + /* only accept the forced_bpp for dual channel configurations */ + if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: /* PKF[4:0] = 00011 - RGB 565 */ + case 24: /* PKF[4:0] = 01011 - RGB 888 */ + case 32: /* PKF[4:0] = 00000 - RGBA 888 */ + break; + default: + return -EINVAL; + } + return 0; } @@ -964,19 +994,27 @@ static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) var->transp.length = 0; break; - case 32: /* PKF[4:0] = 00000 - RGB 888 - * sh7722 pdf says 00RRGGBB but reality is GGBB00RR - * this may be because LDDDSR has word swap enabled.. - */ - var->red.offset = 0; + case 24: /* PKF[4:0] = 01011 - RGB 888 */ + var->red.offset = 16; var->red.length = 8; - var->green.offset = 24; + var->green.offset = 8; var->green.length = 8; - var->blue.offset = 16; + var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; break; + + case 32: /* PKF[4:0] = 00000 - RGBA 888 */ + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; + break; default: return -EINVAL; } @@ -1180,6 +1218,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) goto err1; } + /* for dual channel LCDC (MAIN + SUB) force shared bpp setting */ + if (j == 2) + priv->forced_bpp = pdata->ch[0].bpp; + priv->base = ioremap_nocache(res->start, resource_size(res)); if (!priv->base) goto err1; @@ -1226,7 +1268,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) } if (!mode) - max_size = DEFAULT_XRES * DEFAULT_YRES; + max_size = MAX_XRES * MAX_YRES; else if (max_cfg) dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n", max_cfg->xres, max_cfg->yres); @@ -1238,12 +1280,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) mode = &default_720p; num_cfg = 1; } else { - num_cfg = ch->cfg.num_cfg; + num_cfg = cfg->num_cfg; } fb_videomode_to_modelist(mode, num_cfg, &info->modelist); fb_videomode_to_var(var, mode); + var->width = cfg->lcd_size_cfg.width; + var->height = cfg->lcd_size_cfg.height; /* Default Y virtual resolution is 2x panel size */ var->yres_virtual = var->yres * 2; var->activate = FB_ACTIVATE_NOW; diff --git a/drivers/video/sis/init.c b/drivers/video/sis/init.c index 31137adc8fba..66de832361cc 100644 --- a/drivers/video/sis/init.c +++ b/drivers/video/sis/init.c @@ -56,10 +56,6 @@ * Used by permission. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include "init.h" #ifdef CONFIG_FB_SIS_300 @@ -880,59 +876,59 @@ SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDispl /*********************************************/ void -SiS_SetReg(SISIOADDRESS port, unsigned short index, unsigned short data) +SiS_SetReg(SISIOADDRESS port, u8 index, u8 data) { - outb((u8)index, port); - outb((u8)data, port + 1); + outb(index, port); + outb(data, port + 1); } void -SiS_SetRegByte(SISIOADDRESS port, unsigned short data) +SiS_SetRegByte(SISIOADDRESS port, u8 data) { - outb((u8)data, port); + outb(data, port); } void -SiS_SetRegShort(SISIOADDRESS port, unsigned short data) +SiS_SetRegShort(SISIOADDRESS port, u16 data) { - outw((u16)data, port); + outw(data, port); } void -SiS_SetRegLong(SISIOADDRESS port, unsigned int data) +SiS_SetRegLong(SISIOADDRESS port, u32 data) { - outl((u32)data, port); + outl(data, port); } -unsigned char -SiS_GetReg(SISIOADDRESS port, unsigned short index) +u8 +SiS_GetReg(SISIOADDRESS port, u8 index) { - outb((u8)index, port); + outb(index, port); return inb(port + 1); } -unsigned char +u8 SiS_GetRegByte(SISIOADDRESS port) { return inb(port); } -unsigned short +u16 SiS_GetRegShort(SISIOADDRESS port) { return inw(port); } -unsigned int +u32 SiS_GetRegLong(SISIOADDRESS port) { return inl(port); } void -SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, unsigned short DataOR) +SiS_SetRegANDOR(SISIOADDRESS Port, u8 Index, u8 DataAND, u8 DataOR) { - unsigned short temp; + u8 temp; temp = SiS_GetReg(Port, Index); temp = (temp & (DataAND)) | DataOR; @@ -940,9 +936,9 @@ SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, } void -SiS_SetRegAND(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND) +SiS_SetRegAND(SISIOADDRESS Port, u8 Index, u8 DataAND) { - unsigned short temp; + u8 temp; temp = SiS_GetReg(Port, Index); temp &= DataAND; @@ -950,9 +946,9 @@ SiS_SetRegAND(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND) } void -SiS_SetRegOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataOR) +SiS_SetRegOR(SISIOADDRESS Port, u8 Index, u8 DataOR) { - unsigned short temp; + u8 temp; temp = SiS_GetReg(Port, Index); temp |= DataOR; diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h index ee8ed3c203da..aff73842d877 100644 --- a/drivers/video/sis/init.h +++ b/drivers/video/sis/init.h @@ -1516,19 +1516,6 @@ unsigned short SiS_GetModeID_TV(int VGAEngine, unsigned int VBFlags, int HDispla unsigned short SiS_GetModeID_VGA2(int VGAEngine, unsigned int VBFlags, int HDisplay, int VDisplay, int Depth, unsigned int VBFlags2); -void SiS_SetReg(SISIOADDRESS port, unsigned short index, unsigned short data); -void SiS_SetRegByte(SISIOADDRESS port, unsigned short data); -void SiS_SetRegShort(SISIOADDRESS port, unsigned short data); -void SiS_SetRegLong(SISIOADDRESS port, unsigned int data); -unsigned char SiS_GetReg(SISIOADDRESS port, unsigned short index); -unsigned char SiS_GetRegByte(SISIOADDRESS port); -unsigned short SiS_GetRegShort(SISIOADDRESS port); -unsigned int SiS_GetRegLong(SISIOADDRESS port); -void SiS_SetRegANDOR(SISIOADDRESS Port, unsigned short Index, unsigned short DataAND, - unsigned short DataOR); -void SiS_SetRegAND(SISIOADDRESS Port,unsigned short Index, unsigned short DataAND); -void SiS_SetRegOR(SISIOADDRESS Port,unsigned short Index, unsigned short DataOR); - void SiS_DisplayOn(struct SiS_Private *SiS_Pr); void SiS_DisplayOff(struct SiS_Private *SiS_Pr); void SiSRegInit(struct SiS_Private *SiS_Pr, SISIOADDRESS BaseAddr); diff --git a/drivers/video/sis/init301.c b/drivers/video/sis/init301.c index 9fa66fd4052a..a89e3cafd5ad 100644 --- a/drivers/video/sis/init301.c +++ b/drivers/video/sis/init301.c @@ -57,10 +57,6 @@ * */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #if 1 #define SET_EMI /* 302LV/ELV: Set EMI values */ #endif @@ -5856,7 +5852,7 @@ SiS_SetGroup1_LVDS(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned s temp = tempax & 0x00FF; SiS_SetReg(SiS_Pr->SiS_Part1Port,0x43,temp); temp = ((tempax & 0xFF00) >> 8) << 3; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x44,~0x0F8,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port, 0x44, 0x07, temp); tempax = SiS_Pr->SiS_VDE; /* BDxWadrst1 = BDxWadrst0 + BDxWadroff * VDE */ if(SiS_Pr->SiS_LCDResInfo == Panel_320x240_1 || @@ -5870,7 +5866,7 @@ SiS_SetGroup1_LVDS(struct SiS_Private *SiS_Pr, unsigned short ModeNo, unsigned s temp = ((tempeax & 0xFF0000) >> 16) | 0x10; SiS_SetReg(SiS_Pr->SiS_Part1Port,0x40,temp); temp = ((tempeax & 0x01000000) >> 24) << 7; - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x3C,~0x080,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port, 0x3C, 0x7F, temp); SiS_SetReg(SiS_Pr->SiS_Part1Port,0x2F,0x03); SiS_SetReg(SiS_Pr->SiS_Part1Port,0x03,0x50); diff --git a/drivers/video/sis/init301.h b/drivers/video/sis/init301.h index e1fd31d0fddf..2112d6d7feda 100644 --- a/drivers/video/sis/init301.h +++ b/drivers/video/sis/init301.h @@ -428,17 +428,6 @@ static void SiS_OEM661Setting(struct SiS_Private *SiS_Pr, static void SiS_FinalizeLCD(struct SiS_Private *, unsigned short, unsigned short); #endif -extern void SiS_SetReg(SISIOADDRESS, unsigned short, unsigned short); -extern void SiS_SetRegByte(SISIOADDRESS, unsigned short); -extern void SiS_SetRegShort(SISIOADDRESS, unsigned short); -extern void SiS_SetRegLong(SISIOADDRESS, unsigned int); -extern unsigned char SiS_GetReg(SISIOADDRESS, unsigned short); -extern unsigned char SiS_GetRegByte(SISIOADDRESS); -extern unsigned short SiS_GetRegShort(SISIOADDRESS); -extern unsigned int SiS_GetRegLong(SISIOADDRESS); -extern void SiS_SetRegANDOR(SISIOADDRESS, unsigned short, unsigned short, unsigned short); -extern void SiS_SetRegOR(SISIOADDRESS, unsigned short, unsigned short); -extern void SiS_SetRegAND(SISIOADDRESS, unsigned short, unsigned short); extern void SiS_DisplayOff(struct SiS_Private *SiS_Pr); extern void SiS_DisplayOn(struct SiS_Private *SiS_Pr); extern bool SiS_SearchModeID(struct SiS_Private *, unsigned short *, unsigned short *); diff --git a/drivers/video/sis/sis.h b/drivers/video/sis/sis.h index 80d89d37c414..eac7a01925f3 100644 --- a/drivers/video/sis/sis.h +++ b/drivers/video/sis/sis.h @@ -307,58 +307,19 @@ #define VB2_LCDOVER1600BRIDGE (VB2_307T | VB2_307LV) #define VB2_RAMDAC202MHZBRIDGE (VB2_301C | VB2_307T) -/* I/O port access macros */ -#define inSISREG(base) inb(base) - -#define outSISREG(base,val) outb(val,base) - -#define orSISREG(base,val) \ - do { \ - u8 __Temp = inSISREG(base); \ - outSISREG(base, __Temp | (val));\ - } while (0) - -#define andSISREG(base,val) \ - do { \ - u8 __Temp = inSISREG(base); \ - outSISREG(base, __Temp & (val));\ - } while (0) - -#define inSISIDXREG(base,idx,var) \ - do { \ - outSISREG(base, idx); \ - var = inSISREG((base)+1); \ - } while (0) - -#define outSISIDXREG(base,idx,val) \ - do { \ - outSISREG(base, idx); \ - outSISREG((base)+1, val); \ - } while (0) - -#define orSISIDXREG(base,idx,val) \ - do { \ - u8 __Temp; \ - outSISREG(base, idx); \ - __Temp = inSISREG((base)+1) | (val); \ - outSISREG((base)+1, __Temp); \ - } while (0) - -#define andSISIDXREG(base,idx,and) \ - do { \ - u8 __Temp; \ - outSISREG(base, idx); \ - __Temp = inSISREG((base)+1) & (and); \ - outSISREG((base)+1, __Temp); \ - } while (0) - -#define setSISIDXREG(base,idx,and,or) \ - do { \ - u8 __Temp; \ - outSISREG(base, idx); \ - __Temp = (inSISREG((base)+1) & (and)) | (or); \ - outSISREG((base)+1, __Temp); \ - } while (0) +/* I/O port access functions */ + +void SiS_SetReg(SISIOADDRESS, u8, u8); +void SiS_SetRegByte(SISIOADDRESS, u8); +void SiS_SetRegShort(SISIOADDRESS, u16); +void SiS_SetRegLong(SISIOADDRESS, u32); +void SiS_SetRegANDOR(SISIOADDRESS, u8, u8, u8); +void SiS_SetRegAND(SISIOADDRESS, u8, u8); +void SiS_SetRegOR(SISIOADDRESS, u8, u8); +u8 SiS_GetReg(SISIOADDRESS, u8); +u8 SiS_GetRegByte(SISIOADDRESS); +u16 SiS_GetRegShort(SISIOADDRESS); +u32 SiS_GetRegLong(SISIOADDRESS); /* MMIO access macros */ #define MMIO_IN8(base, offset) readb((base+offset)) diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index 7e3370f115b6..2fb8c5a660fb 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -737,7 +737,7 @@ sisfb_bridgeisslave(struct sis_video_info *ivideo) if(!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) return false; - inSISIDXREG(SISPART1,0x00,P1_00); + P1_00 = SiS_GetReg(SISPART1, 0x00); if( ((ivideo->sisvga_engine == SIS_300_VGA) && (P1_00 & 0xa0) == 0x20) || ((ivideo->sisvga_engine == SIS_315_VGA) && (P1_00 & 0x50) == 0x10) ) { return true; @@ -751,11 +751,11 @@ sisfballowretracecrt1(struct sis_video_info *ivideo) { u8 temp; - inSISIDXREG(SISCR,0x17,temp); + temp = SiS_GetReg(SISCR, 0x17); if(!(temp & 0x80)) return false; - inSISIDXREG(SISSR,0x1f,temp); + temp = SiS_GetReg(SISSR, 0x1f); if(temp & 0xc0) return false; @@ -768,7 +768,7 @@ sisfbcheckvretracecrt1(struct sis_video_info *ivideo) if(!sisfballowretracecrt1(ivideo)) return false; - if(inSISREG(SISINPSTAT) & 0x08) + if (SiS_GetRegByte(SISINPSTAT) & 0x08) return true; else return false; @@ -783,9 +783,9 @@ sisfbwaitretracecrt1(struct sis_video_info *ivideo) return; watchdog = 65536; - while((!(inSISREG(SISINPSTAT) & 0x08)) && --watchdog); + while ((!(SiS_GetRegByte(SISINPSTAT) & 0x08)) && --watchdog); watchdog = 65536; - while((inSISREG(SISINPSTAT) & 0x08) && --watchdog); + while ((SiS_GetRegByte(SISINPSTAT) & 0x08) && --watchdog); } static bool @@ -799,7 +799,7 @@ sisfbcheckvretracecrt2(struct sis_video_info *ivideo) default: return false; } - inSISIDXREG(SISPART1, reg, temp); + temp = SiS_GetReg(SISPART1, reg); if(temp & 0x02) return true; else @@ -837,10 +837,10 @@ sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount) default: case SIS_315_VGA: idx = 0x30; break; } - inSISIDXREG(SISPART1,(idx+0),reg1); /* 30 */ - inSISIDXREG(SISPART1,(idx+1),reg2); /* 31 */ - inSISIDXREG(SISPART1,(idx+2),reg3); /* 32 */ - inSISIDXREG(SISPART1,(idx+3),reg4); /* 33 */ + reg1 = SiS_GetReg(SISPART1, (idx+0)); /* 30 */ + reg2 = SiS_GetReg(SISPART1, (idx+1)); /* 31 */ + reg3 = SiS_GetReg(SISPART1, (idx+2)); /* 32 */ + reg4 = SiS_GetReg(SISPART1, (idx+3)); /* 33 */ if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING; if(reg1 & 0x02) ret |= FB_VBLANK_VSYNCING; if(reg4 & 0x80) ret |= FB_VBLANK_HBLANKING; @@ -853,13 +853,13 @@ sisfb_setupvbblankflags(struct sis_video_info *ivideo, u32 *vcount, u32 *hcount) FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT | FB_VBLANK_HAVE_HCOUNT); - reg1 = inSISREG(SISINPSTAT); + reg1 = SiS_GetRegByte(SISINPSTAT); if(reg1 & 0x08) ret |= FB_VBLANK_VSYNCING; if(reg1 & 0x01) ret |= FB_VBLANK_VBLANKING; - inSISIDXREG(SISCR,0x20,reg1); - inSISIDXREG(SISCR,0x1b,reg1); - inSISIDXREG(SISCR,0x1c,reg2); - inSISIDXREG(SISCR,0x1d,reg3); + reg1 = SiS_GetReg(SISCR, 0x20); + reg1 = SiS_GetReg(SISCR, 0x1b); + reg2 = SiS_GetReg(SISCR, 0x1c); + reg3 = SiS_GetReg(SISCR, 0x1d); (*vcount) = reg2 | ((reg3 & 0x07) << 8); (*hcount) = (reg1 | ((reg3 & 0x10) << 4)) << 3; } @@ -930,12 +930,12 @@ sisfb_myblank(struct sis_video_info *ivideo, int blank) (ivideo->sisfb_thismonitor.feature & 0xe0))) { if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63); + SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xbf, cr63); } if(!(sisfb_bridgeisslave(ivideo))) { - setSISIDXREG(SISSR, 0x01, ~0x20, sr01); - setSISIDXREG(SISSR, 0x1f, 0x3f, sr1f); + SiS_SetRegANDOR(SISSR, 0x01, ~0x20, sr01); + SiS_SetRegANDOR(SISSR, 0x1f, 0x3f, sr1f); } } @@ -965,25 +965,25 @@ sisfb_myblank(struct sis_video_info *ivideo, int blank) (ivideo->vbflags2 & (VB2_301|VB2_30xBDH|VB2_LVDS))) || ((ivideo->sisvga_engine == SIS_315_VGA) && ((ivideo->vbflags2 & (VB2_LVDS | VB2_CHRONTEL)) == VB2_LVDS))) { - setSISIDXREG(SISSR, 0x11, ~0x0c, sr11); + SiS_SetRegANDOR(SISSR, 0x11, ~0x0c, sr11); } if(ivideo->sisvga_engine == SIS_300_VGA) { if((ivideo->vbflags2 & VB2_30xB) && (!(ivideo->vbflags2 & VB2_30xBDH))) { - setSISIDXREG(SISPART1, 0x13, 0x3f, p1_13); + SiS_SetRegANDOR(SISPART1, 0x13, 0x3f, p1_13); } } else if(ivideo->sisvga_engine == SIS_315_VGA) { if((ivideo->vbflags2 & VB2_30xB) && (!(ivideo->vbflags2 & VB2_30xBDH))) { - setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0); + SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0); } } } else if(ivideo->currentvbflags & CRT2_VGA) { if(ivideo->vbflags2 & VB2_30xB) { - setSISIDXREG(SISPART2, 0x00, 0x1f, p2_0); + SiS_SetRegANDOR(SISPART2, 0x00, 0x1f, p2_0); } } @@ -1114,15 +1114,15 @@ sisfb_set_pitch(struct sis_video_info *ivideo) /* We need to set pitch for CRT1 if bridge is in slave mode, too */ if((ivideo->currentvbflags & VB_DISPTYPE_DISP1) || (isslavemode)) { - outSISIDXREG(SISCR,0x13,(HDisplay1 & 0xFF)); - setSISIDXREG(SISSR,0x0E,0xF0,(HDisplay1 >> 8)); + SiS_SetReg(SISCR, 0x13, (HDisplay1 & 0xFF)); + SiS_SetRegANDOR(SISSR, 0x0E, 0xF0, (HDisplay1 >> 8)); } /* We must not set the pitch for CRT2 if bridge is in slave mode */ if((ivideo->currentvbflags & VB_DISPTYPE_DISP2) && (!isslavemode)) { - orSISIDXREG(SISPART1,ivideo->CRT2_write_enable,0x01); - outSISIDXREG(SISPART1,0x07,(HDisplay2 & 0xFF)); - setSISIDXREG(SISPART1,0x09,0xF0,(HDisplay2 >> 8)); + SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01); + SiS_SetReg(SISPART1, 0x07, (HDisplay2 & 0xFF)); + SiS_SetRegANDOR(SISPART1, 0x09, 0xF0, (HDisplay2 >> 8)); } } @@ -1167,7 +1167,7 @@ sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn) /* >=2.6.12's fbcon clears the screen anyway */ modeno |= 0x80; - outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); + SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); sisfb_pre_setmode(ivideo); @@ -1176,7 +1176,7 @@ sisfb_set_mode(struct sis_video_info *ivideo, int clrscrn) return -EINVAL; } - outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); + SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); sisfb_post_setmode(ivideo); @@ -1308,13 +1308,13 @@ sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *in static void sisfb_set_base_CRT1(struct sis_video_info *ivideo, unsigned int base) { - outSISIDXREG(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); + SiS_SetReg(SISSR, IND_SIS_PASSWORD, SIS_PASSWORD); - outSISIDXREG(SISCR, 0x0D, base & 0xFF); - outSISIDXREG(SISCR, 0x0C, (base >> 8) & 0xFF); - outSISIDXREG(SISSR, 0x0D, (base >> 16) & 0xFF); + SiS_SetReg(SISCR, 0x0D, base & 0xFF); + SiS_SetReg(SISCR, 0x0C, (base >> 8) & 0xFF); + SiS_SetReg(SISSR, 0x0D, (base >> 16) & 0xFF); if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISSR, 0x37, 0xFE, (base >> 24) & 0x01); + SiS_SetRegANDOR(SISSR, 0x37, 0xFE, (base >> 24) & 0x01); } } @@ -1322,12 +1322,12 @@ static void sisfb_set_base_CRT2(struct sis_video_info *ivideo, unsigned int base) { if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { - orSISIDXREG(SISPART1, ivideo->CRT2_write_enable, 0x01); - outSISIDXREG(SISPART1, 0x06, (base & 0xFF)); - outSISIDXREG(SISPART1, 0x05, ((base >> 8) & 0xFF)); - outSISIDXREG(SISPART1, 0x04, ((base >> 16) & 0xFF)); + SiS_SetRegOR(SISPART1, ivideo->CRT2_write_enable, 0x01); + SiS_SetReg(SISPART1, 0x06, (base & 0xFF)); + SiS_SetReg(SISPART1, 0x05, ((base >> 8) & 0xFF)); + SiS_SetReg(SISPART1, 0x04, ((base >> 16) & 0xFF)); if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7); + SiS_SetRegANDOR(SISPART1, 0x02, 0x7F, ((base >> 24) & 0x01) << 7); } } } @@ -1388,15 +1388,15 @@ sisfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, switch(info->var.bits_per_pixel) { case 8: - outSISREG(SISDACA, regno); - outSISREG(SISDACD, (red >> 10)); - outSISREG(SISDACD, (green >> 10)); - outSISREG(SISDACD, (blue >> 10)); + SiS_SetRegByte(SISDACA, regno); + SiS_SetRegByte(SISDACD, (red >> 10)); + SiS_SetRegByte(SISDACD, (green >> 10)); + SiS_SetRegByte(SISDACD, (blue >> 10)); if(ivideo->currentvbflags & VB_DISPTYPE_DISP2) { - outSISREG(SISDAC2A, regno); - outSISREG(SISDAC2D, (red >> 8)); - outSISREG(SISDAC2D, (green >> 8)); - outSISREG(SISDAC2D, (blue >> 8)); + SiS_SetRegByte(SISDAC2A, regno); + SiS_SetRegByte(SISDAC2D, (red >> 8)); + SiS_SetRegByte(SISDAC2D, (green >> 8)); + SiS_SetRegByte(SISDAC2D, (blue >> 8)); } break; case 16: @@ -1961,7 +1961,7 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) switch(ivideo->chip) { #ifdef CONFIG_FB_SIS_300 case SIS_300: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = ((reg & 0x3F) + 1) << 20; break; case SIS_540: @@ -1977,7 +1977,7 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) case SIS_315H: case SIS_315PRO: case SIS_315: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; switch((reg >> 2) & 0x03) { case 0x01: @@ -1989,31 +1989,31 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) } break; case SIS_330: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; if(reg & 0x0c) ivideo->video_size <<= 1; break; case SIS_550: case SIS_650: case SIS_740: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (((reg & 0x3f) + 1) << 2) << 20; break; case SIS_661: case SIS_741: - inSISIDXREG(SISCR, 0x79, reg); + reg = SiS_GetReg(SISCR, 0x79); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; break; case SIS_660: case SIS_760: case SIS_761: - inSISIDXREG(SISCR, 0x79, reg); + reg = SiS_GetReg(SISCR, 0x79); reg = (reg & 0xf0) >> 4; if(reg) { ivideo->video_size = (1 << reg) << 20; ivideo->UMAsize = ivideo->video_size; } - inSISIDXREG(SISCR, 0x78, reg); + reg = SiS_GetReg(SISCR, 0x78); reg &= 0x30; if(reg) { if(reg == 0x10) { @@ -2027,7 +2027,7 @@ sisfb_get_dram_size(struct sis_video_info *ivideo) case SIS_340: case XGI_20: case XGI_40: - inSISIDXREG(SISSR, 0x14, reg); + reg = SiS_GetReg(SISSR, 0x14); ivideo->video_size = (1 << ((reg & 0xf0) >> 4)) << 20; if(ivideo->chip != XGI_20) { reg = (reg & 0x0c) >> 2; @@ -2061,11 +2061,11 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_300 if(ivideo->sisvga_engine == SIS_300_VGA) { - inSISIDXREG(SISSR, 0x17, temp); + temp = SiS_GetReg(SISSR, 0x17); if((temp & 0x0F) && (ivideo->chip != SIS_300)) { /* PAL/NTSC is stored on SR16 on such machines */ if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN))) { - inSISIDXREG(SISSR, 0x16, temp); + temp = SiS_GetReg(SISSR, 0x16); if(temp & 0x20) ivideo->vbflags |= TV_PAL; else @@ -2075,7 +2075,7 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo) } #endif - inSISIDXREG(SISCR, 0x32, cr32); + cr32 = SiS_GetReg(SISCR, 0x32); if(cr32 & SIS_CRT1) { ivideo->sisfb_crt1off = 0; @@ -2151,15 +2151,15 @@ sisfb_detect_VB_connect(struct sis_video_info *ivideo) } if(!(ivideo->vbflags & (TV_PAL | TV_NTSC | TV_PALM | TV_PALN | TV_NTSCJ))) { if(ivideo->sisvga_engine == SIS_300_VGA) { - inSISIDXREG(SISSR, 0x38, temp); + temp = SiS_GetReg(SISSR, 0x38); if(temp & 0x01) ivideo->vbflags |= TV_PAL; else ivideo->vbflags |= TV_NTSC; } else if((ivideo->chip <= SIS_315PRO) || (ivideo->chip >= SIS_330)) { - inSISIDXREG(SISSR, 0x38, temp); + temp = SiS_GetReg(SISSR, 0x38); if(temp & 0x01) ivideo->vbflags |= TV_PAL; else ivideo->vbflags |= TV_NTSC; } else { - inSISIDXREG(SISCR, 0x79, temp); + temp = SiS_GetReg(SISCR, 0x79); if(temp & 0x20) ivideo->vbflags |= TV_PAL; else ivideo->vbflags |= TV_NTSC; } @@ -2198,26 +2198,26 @@ sisfb_sense_crt1(struct sis_video_info *ivideo) u16 temp = 0xffff; int i; - inSISIDXREG(SISSR,0x1F,sr1F); - orSISIDXREG(SISSR,0x1F,0x04); - andSISIDXREG(SISSR,0x1F,0x3F); + sr1F = SiS_GetReg(SISSR, 0x1F); + SiS_SetRegOR(SISSR, 0x1F, 0x04); + SiS_SetRegAND(SISSR, 0x1F, 0x3F); if(sr1F & 0xc0) mustwait = true; #ifdef CONFIG_FB_SIS_315 if(ivideo->sisvga_engine == SIS_315_VGA) { - inSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,cr63); + cr63 = SiS_GetReg(SISCR, ivideo->SiS_Pr.SiS_MyCR63); cr63 &= 0x40; - andSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF); + SiS_SetRegAND(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF); } #endif - inSISIDXREG(SISCR,0x17,cr17); + cr17 = SiS_GetReg(SISCR, 0x17); cr17 &= 0x80; if(!cr17) { - orSISIDXREG(SISCR,0x17,0x80); + SiS_SetRegOR(SISCR, 0x17, 0x80); mustwait = true; - outSISIDXREG(SISSR, 0x00, 0x01); - outSISIDXREG(SISSR, 0x00, 0x03); + SiS_SetReg(SISSR, 0x00, 0x01); + SiS_SetReg(SISSR, 0x00, 0x03); } if(mustwait) { @@ -2226,18 +2226,18 @@ sisfb_sense_crt1(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_315 if(ivideo->chip >= SIS_330) { - andSISIDXREG(SISCR,0x32,~0x20); + SiS_SetRegAND(SISCR, 0x32, ~0x20); if(ivideo->chip >= SIS_340) { - outSISIDXREG(SISCR, 0x57, 0x4a); + SiS_SetReg(SISCR, 0x57, 0x4a); } else { - outSISIDXREG(SISCR, 0x57, 0x5f); + SiS_SetReg(SISCR, 0x57, 0x5f); } - orSISIDXREG(SISCR, 0x53, 0x02); - while((inSISREG(SISINPSTAT)) & 0x01) break; - while(!((inSISREG(SISINPSTAT)) & 0x01)) break; - if((inSISREG(SISMISCW)) & 0x10) temp = 1; - andSISIDXREG(SISCR, 0x53, 0xfd); - andSISIDXREG(SISCR, 0x57, 0x00); + SiS_SetRegOR(SISCR, 0x53, 0x02); + while ((SiS_GetRegByte(SISINPSTAT)) & 0x01) break; + while (!((SiS_GetRegByte(SISINPSTAT)) & 0x01)) break; + if ((SiS_GetRegByte(SISMISCW)) & 0x10) temp = 1; + SiS_SetRegAND(SISCR, 0x53, 0xfd); + SiS_SetRegAND(SISCR, 0x57, 0x00); } #endif @@ -2254,18 +2254,18 @@ sisfb_sense_crt1(struct sis_video_info *ivideo) } if((temp) && (temp != 0xffff)) { - orSISIDXREG(SISCR,0x32,0x20); + SiS_SetRegOR(SISCR, 0x32, 0x20); } #ifdef CONFIG_FB_SIS_315 if(ivideo->sisvga_engine == SIS_315_VGA) { - setSISIDXREG(SISCR,ivideo->SiS_Pr.SiS_MyCR63,0xBF,cr63); + SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, 0xBF, cr63); } #endif - setSISIDXREG(SISCR,0x17,0x7F,cr17); + SiS_SetRegANDOR(SISCR, 0x17, 0x7F, cr17); - outSISIDXREG(SISSR,0x1F,sr1F); + SiS_SetReg(SISSR, 0x1F, sr1F); } /* Determine and detect attached devices on SiS30x */ @@ -2286,7 +2286,7 @@ SiS_SenseLCD(struct sis_video_info *ivideo) return; /* If LCD already set up by BIOS, skip it */ - inSISIDXREG(SISCR, 0x32, reg); + reg = SiS_GetReg(SISCR, 0x32); if(reg & 0x08) return; @@ -2349,10 +2349,10 @@ SiS_SenseLCD(struct sis_video_info *ivideo) else cr37 |= 0xc0; - outSISIDXREG(SISCR, 0x36, paneltype); + SiS_SetReg(SISCR, 0x36, paneltype); cr37 &= 0xf1; - setSISIDXREG(SISCR, 0x37, 0x0c, cr37); - orSISIDXREG(SISCR, 0x32, 0x08); + SiS_SetRegANDOR(SISCR, 0x37, 0x0c, cr37); + SiS_SetRegOR(SISCR, 0x32, 0x08); ivideo->SiS_Pr.PanelSelfDetected = true; } @@ -2366,19 +2366,19 @@ SISDoSense(struct sis_video_info *ivideo, u16 type, u16 test) result = 0; for(i = 0; i < 3; i++) { mytest = test; - outSISIDXREG(SISPART4,0x11,(type & 0x00ff)); + SiS_SetReg(SISPART4, 0x11, (type & 0x00ff)); temp = (type >> 8) | (mytest & 0x00ff); - setSISIDXREG(SISPART4,0x10,0xe0,temp); + SiS_SetRegANDOR(SISPART4, 0x10, 0xe0, temp); SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1500); mytest >>= 8; mytest &= 0x7f; - inSISIDXREG(SISPART4,0x03,temp); + temp = SiS_GetReg(SISPART4, 0x03); temp ^= 0x0e; temp &= mytest; if(temp == mytest) result++; #if 1 - outSISIDXREG(SISPART4,0x11,0x00); - andSISIDXREG(SISPART4,0x10,0xe0); + SiS_SetReg(SISPART4, 0x11, 0x00); + SiS_SetRegAND(SISPART4, 0x10, 0xe0); SiS_DDC2Delay(&ivideo->SiS_Pr, 0x1000); #endif } @@ -2400,7 +2400,7 @@ SiS_Sense30x(struct sis_video_info *ivideo) if(ivideo->vbflags2 & VB2_301) { svhs = 0x00b9; cvbs = 0x00b3; vga2 = 0x00d1; - inSISIDXREG(SISPART4,0x01,myflag); + myflag = SiS_GetReg(SISPART4, 0x01); if(myflag & 0x04) { svhs = 0x00dd; cvbs = 0x00ee; vga2 = 0x00fd; } @@ -2430,7 +2430,7 @@ SiS_Sense30x(struct sis_video_info *ivideo) } if(ivideo->chip == SIS_300) { - inSISIDXREG(SISSR,0x3b,myflag); + myflag = SiS_GetReg(SISSR, 0x3b); if(!(myflag & 0x01)) vga2 = vga2_c = 0; } @@ -2438,93 +2438,93 @@ SiS_Sense30x(struct sis_video_info *ivideo) vga2 = vga2_c = 0; } - inSISIDXREG(SISSR,0x1e,backupSR_1e); - orSISIDXREG(SISSR,0x1e,0x20); + backupSR_1e = SiS_GetReg(SISSR, 0x1e); + SiS_SetRegOR(SISSR, 0x1e, 0x20); - inSISIDXREG(SISPART4,0x0d,backupP4_0d); + backupP4_0d = SiS_GetReg(SISPART4, 0x0d); if(ivideo->vbflags2 & VB2_30xC) { - setSISIDXREG(SISPART4,0x0d,~0x07,0x01); + SiS_SetRegANDOR(SISPART4, 0x0d, ~0x07, 0x01); } else { - orSISIDXREG(SISPART4,0x0d,0x04); + SiS_SetRegOR(SISPART4, 0x0d, 0x04); } SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000); - inSISIDXREG(SISPART2,0x00,backupP2_00); - outSISIDXREG(SISPART2,0x00,((backupP2_00 | 0x1c) & 0xfc)); + backupP2_00 = SiS_GetReg(SISPART2, 0x00); + SiS_SetReg(SISPART2, 0x00, ((backupP2_00 | 0x1c) & 0xfc)); - inSISIDXREG(SISPART2,0x4d,backupP2_4d); + backupP2_4d = SiS_GetReg(SISPART2, 0x4d); if(ivideo->vbflags2 & VB2_SISYPBPRBRIDGE) { - outSISIDXREG(SISPART2,0x4d,(backupP2_4d & ~0x10)); + SiS_SetReg(SISPART2, 0x4d, (backupP2_4d & ~0x10)); } if(!(ivideo->vbflags2 & VB2_30xCLV)) { SISDoSense(ivideo, 0, 0); } - andSISIDXREG(SISCR, 0x32, ~0x14); + SiS_SetRegAND(SISCR, 0x32, ~0x14); if(vga2_c || vga2) { if(SISDoSense(ivideo, vga2, vga2_c)) { if(biosflag & 0x01) { printk(KERN_INFO "%s %s SCART output\n", stdstr, tvstr); - orSISIDXREG(SISCR, 0x32, 0x04); + SiS_SetRegOR(SISCR, 0x32, 0x04); } else { printk(KERN_INFO "%s secondary VGA connection\n", stdstr); - orSISIDXREG(SISCR, 0x32, 0x10); + SiS_SetRegOR(SISCR, 0x32, 0x10); } } } - andSISIDXREG(SISCR, 0x32, 0x3f); + SiS_SetRegAND(SISCR, 0x32, 0x3f); if(ivideo->vbflags2 & VB2_30xCLV) { - orSISIDXREG(SISPART4,0x0d,0x04); + SiS_SetRegOR(SISPART4, 0x0d, 0x04); } if((ivideo->sisvga_engine == SIS_315_VGA) && (ivideo->vbflags2 & VB2_SISYPBPRBRIDGE)) { - outSISIDXREG(SISPART2,0x4d,(backupP2_4d | 0x10)); + SiS_SetReg(SISPART2, 0x4d, (backupP2_4d | 0x10)); SiS_DDC2Delay(&ivideo->SiS_Pr, 0x2000); if((result = SISDoSense(ivideo, svhs, 0x0604))) { if((result = SISDoSense(ivideo, cvbs, 0x0804))) { printk(KERN_INFO "%s %s YPbPr component output\n", stdstr, tvstr); - orSISIDXREG(SISCR,0x32,0x80); + SiS_SetRegOR(SISCR, 0x32, 0x80); } } - outSISIDXREG(SISPART2,0x4d,backupP2_4d); + SiS_SetReg(SISPART2, 0x4d, backupP2_4d); } - andSISIDXREG(SISCR, 0x32, ~0x03); + SiS_SetRegAND(SISCR, 0x32, ~0x03); if(!(ivideo->vbflags & TV_YPBPR)) { if((result = SISDoSense(ivideo, svhs, svhs_c))) { printk(KERN_INFO "%s %s SVIDEO output\n", stdstr, tvstr); - orSISIDXREG(SISCR, 0x32, 0x02); + SiS_SetRegOR(SISCR, 0x32, 0x02); } if((biosflag & 0x02) || (!result)) { if(SISDoSense(ivideo, cvbs, cvbs_c)) { printk(KERN_INFO "%s %s COMPOSITE output\n", stdstr, tvstr); - orSISIDXREG(SISCR, 0x32, 0x01); + SiS_SetRegOR(SISCR, 0x32, 0x01); } } } SISDoSense(ivideo, 0, 0); - outSISIDXREG(SISPART2,0x00,backupP2_00); - outSISIDXREG(SISPART4,0x0d,backupP4_0d); - outSISIDXREG(SISSR,0x1e,backupSR_1e); + SiS_SetReg(SISPART2, 0x00, backupP2_00); + SiS_SetReg(SISPART4, 0x0d, backupP4_0d); + SiS_SetReg(SISSR, 0x1e, backupSR_1e); if(ivideo->vbflags2 & VB2_30xCLV) { - inSISIDXREG(SISPART2,0x00,biosflag); + biosflag = SiS_GetReg(SISPART2, 0x00); if(biosflag & 0x20) { for(myflag = 2; myflag > 0; myflag--) { biosflag ^= 0x20; - outSISIDXREG(SISPART2,0x00,biosflag); + SiS_SetReg(SISPART2, 0x00, biosflag); } } } - outSISIDXREG(SISPART2,0x00,backupP2_00); + SiS_SetReg(SISPART2, 0x00, backupP2_00); } /* Determine and detect attached TV's on Chrontel */ @@ -2588,20 +2588,20 @@ SiS_SenseCh(struct sis_video_info *ivideo) if(temp1 == 0x02) { printk(KERN_INFO "%s SVIDEO output\n", stdstr); ivideo->vbflags |= TV_SVIDEO; - orSISIDXREG(SISCR, 0x32, 0x02); - andSISIDXREG(SISCR, 0x32, ~0x05); + SiS_SetRegOR(SISCR, 0x32, 0x02); + SiS_SetRegAND(SISCR, 0x32, ~0x05); } else if (temp1 == 0x01) { printk(KERN_INFO "%s CVBS output\n", stdstr); ivideo->vbflags |= TV_AVIDEO; - orSISIDXREG(SISCR, 0x32, 0x01); - andSISIDXREG(SISCR, 0x32, ~0x06); + SiS_SetRegOR(SISCR, 0x32, 0x01); + SiS_SetRegAND(SISCR, 0x32, ~0x06); } else { SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8); - andSISIDXREG(SISCR, 0x32, ~0x07); + SiS_SetRegAND(SISCR, 0x32, ~0x07); } } else if(temp1 == 0) { SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x0e, 0x01, 0xF8); - andSISIDXREG(SISCR, 0x32, ~0x07); + SiS_SetRegAND(SISCR, 0x32, ~0x07); } /* Set general purpose IO for Chrontel communication */ SiS_SetChrontelGPIO(&ivideo->SiS_Pr, 0x00); @@ -2632,22 +2632,22 @@ SiS_SenseCh(struct sis_video_info *ivideo) case 0x01: printk(KERN_INFO "%s CVBS output\n", stdstr); ivideo->vbflags |= TV_AVIDEO; - orSISIDXREG(SISCR, 0x32, 0x01); - andSISIDXREG(SISCR, 0x32, ~0x06); + SiS_SetRegOR(SISCR, 0x32, 0x01); + SiS_SetRegAND(SISCR, 0x32, ~0x06); break; case 0x02: printk(KERN_INFO "%s SVIDEO output\n", stdstr); ivideo->vbflags |= TV_SVIDEO; - orSISIDXREG(SISCR, 0x32, 0x02); - andSISIDXREG(SISCR, 0x32, ~0x05); + SiS_SetRegOR(SISCR, 0x32, 0x02); + SiS_SetRegAND(SISCR, 0x32, ~0x05); break; case 0x04: printk(KERN_INFO "%s SCART output\n", stdstr); - orSISIDXREG(SISCR, 0x32, 0x04); - andSISIDXREG(SISCR, 0x32, ~0x03); + SiS_SetRegOR(SISCR, 0x32, 0x04); + SiS_SetRegAND(SISCR, 0x32, ~0x03); break; default: - andSISIDXREG(SISCR, 0x32, ~0x07); + SiS_SetRegAND(SISCR, 0x32, ~0x07); } #endif } @@ -2665,10 +2665,10 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) if(ivideo->chip == XGI_20) return; - inSISIDXREG(SISPART4, 0x00, vb_chipid); + vb_chipid = SiS_GetReg(SISPART4, 0x00); switch(vb_chipid) { case 0x01: - inSISIDXREG(SISPART4, 0x01, reg); + reg = SiS_GetReg(SISPART4, 0x01); if(reg < 0xb0) { ivideo->vbflags |= VB_301; /* Deprecated */ ivideo->vbflags2 |= VB2_301; @@ -2676,7 +2676,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) } else if(reg < 0xc0) { ivideo->vbflags |= VB_301B; /* Deprecated */ ivideo->vbflags2 |= VB2_301B; - inSISIDXREG(SISPART4,0x23,reg); + reg = SiS_GetReg(SISPART4, 0x23); if(!(reg & 0x02)) { ivideo->vbflags |= VB_30xBDH; /* Deprecated */ ivideo->vbflags2 |= VB2_30xBDH; @@ -2693,7 +2693,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) ivideo->vbflags2 |= VB2_301LV; printk(KERN_INFO "%s SiS301LV %s\n", stdstr, bridgestr); } else if(reg <= 0xe1) { - inSISIDXREG(SISPART4,0x39,reg); + reg = SiS_GetReg(SISPART4, 0x39); if(reg == 0xff) { ivideo->vbflags |= VB_302LV; /* Deprecated */ ivideo->vbflags2 |= VB2_302LV; @@ -2718,7 +2718,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) } if((!(ivideo->vbflags2 & VB2_VIDEOBRIDGE)) && (ivideo->chip != SIS_300)) { - inSISIDXREG(SISCR, 0x37, reg); + reg = SiS_GetReg(SISCR, 0x37); reg &= SIS_EXTERNAL_CHIP_MASK; reg >>= 1; if(ivideo->sisvga_engine == SIS_300_VGA) { @@ -2759,7 +2759,7 @@ sisfb_get_VB_type(struct sis_video_info *ivideo) #endif } else if(ivideo->chip >= SIS_661) { #ifdef CONFIG_FB_SIS_315 - inSISIDXREG(SISCR, 0x38, reg); + reg = SiS_GetReg(SISCR, 0x38); reg >>= 5; switch(reg) { case 0x02: @@ -2822,13 +2822,13 @@ sisfb_engine_init(struct sis_video_info *ivideo) tqueue_pos = (ivideo->video_size - ivideo->cmdQueueSize) / (64 * 1024); - inSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state); + tq_state = SiS_GetReg(SISSR, IND_SIS_TURBOQUEUE_SET); tq_state |= 0xf0; tq_state &= 0xfc; tq_state |= (u8)(tqueue_pos >> 8); - outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state); + SiS_SetReg(SISSR, IND_SIS_TURBOQUEUE_SET, tq_state); - outSISIDXREG(SISSR, IND_SIS_TURBOQUEUE_ADR, (u8)(tqueue_pos & 0xff)); + SiS_SetReg(SISSR, IND_SIS_TURBOQUEUE_ADR, (u8)(tqueue_pos & 0xff)); ivideo->caps |= TURBO_QUEUE_CAP; } @@ -2865,8 +2865,8 @@ sisfb_engine_init(struct sis_video_info *ivideo) } } - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_THRESHOLD, COMMAND_QUEUE_THRESHOLD); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); if((ivideo->chip >= XGI_40) && ivideo->modechanged) { /* Must disable dual pipe on XGI_40. Can't do @@ -2878,7 +2878,7 @@ sisfb_engine_init(struct sis_video_info *ivideo) MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, 0); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, (temp | SIS_VRAM_CMDQUEUE_ENABLE)); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, (temp | SIS_VRAM_CMDQUEUE_ENABLE)); tempq = MMIO_IN32(ivideo->mmio_vbase, Q_READ_PTR); MMIO_OUT32(ivideo->mmio_vbase, Q_WRITE_PTR, tempq); @@ -2895,7 +2895,7 @@ sisfb_engine_init(struct sis_video_info *ivideo) sisfb_syncaccel(ivideo); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, SIS_CMD_QUEUE_RESET); } } @@ -2904,7 +2904,7 @@ sisfb_engine_init(struct sis_video_info *ivideo) MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_WRITEPORT, tempq); temp |= (SIS_MMIO_CMD_ENABLE | SIS_CMD_AUTO_CORR); - outSISIDXREG(SISSR, IND_SIS_CMDQUEUE_SET, temp); + SiS_SetReg(SISSR, IND_SIS_CMDQUEUE_SET, temp); tempq = (u32)(ivideo->video_size - ivideo->cmdQueueSize); MMIO_OUT32(ivideo->mmio_vbase, MMIO_QUEUE_PHYBASE, tempq); @@ -2922,7 +2922,7 @@ sisfb_detect_lcd_type(struct sis_video_info *ivideo) u8 reg; int i; - inSISIDXREG(SISCR, 0x36, reg); + reg = SiS_GetReg(SISCR, 0x36); reg &= 0x0f; if(ivideo->sisvga_engine == SIS_300_VGA) { ivideo->CRT2LCDType = sis300paneltype[reg]; @@ -2941,8 +2941,8 @@ sisfb_detect_lcd_type(struct sis_video_info *ivideo) if(ivideo->CRT2LCDType == LCD_UNKNOWN) { /* For broken BIOSes: Assume 1024x768, RGB18 */ ivideo->CRT2LCDType = LCD_1024x768; - setSISIDXREG(SISCR,0x36,0xf0,0x02); - setSISIDXREG(SISCR,0x37,0xee,0x01); + SiS_SetRegANDOR(SISCR, 0x36, 0xf0, 0x02); + SiS_SetRegANDOR(SISCR, 0x37, 0xee, 0x01); printk(KERN_DEBUG "sisfb: Invalid panel ID (%02x), assuming 1024x768, RGB18\n", reg); } @@ -2980,10 +2980,10 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) if(ivideo->sisvga_engine == SIS_300_VGA) { if(ivideo->vbflags2 & (VB2_LVDS | VB2_30xBDH)) { int tmp; - inSISIDXREG(SISCR,0x30,tmp); + tmp = SiS_GetReg(SISCR, 0x30); if(tmp & 0x20) { /* Currently on LCD? If yes, read current pdc */ - inSISIDXREG(SISPART1,0x13,ivideo->detectedpdc); + ivideo->detectedpdc = SiS_GetReg(SISPART1, 0x13); ivideo->detectedpdc &= 0x3c; if(ivideo->SiS_Pr.PDC == -1) { /* Let option override detection */ @@ -3007,7 +3007,7 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) /* Try to find about LCDA */ if(ivideo->vbflags2 & VB2_SISLCDABRIDGE) { int tmp; - inSISIDXREG(SISPART1,0x13,tmp); + tmp = SiS_GetReg(SISPART1, 0x13); if(tmp & 0x04) { ivideo->SiS_Pr.SiS_UseLCDA = true; ivideo->detectedlcda = 0x03; @@ -3017,16 +3017,16 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) /* Save PDC */ if(ivideo->vbflags2 & VB2_SISLVDSBRIDGE) { int tmp; - inSISIDXREG(SISCR,0x30,tmp); + tmp = SiS_GetReg(SISCR, 0x30); if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) { /* Currently on LCD? If yes, read current pdc */ u8 pdc; - inSISIDXREG(SISPART1,0x2D,pdc); + pdc = SiS_GetReg(SISPART1, 0x2D); ivideo->detectedpdc = (pdc & 0x0f) << 1; ivideo->detectedpdca = (pdc & 0xf0) >> 3; - inSISIDXREG(SISPART1,0x35,pdc); + pdc = SiS_GetReg(SISPART1, 0x35); ivideo->detectedpdc |= ((pdc >> 7) & 0x01); - inSISIDXREG(SISPART1,0x20,pdc); + pdc = SiS_GetReg(SISPART1, 0x20); ivideo->detectedpdca |= ((pdc >> 6) & 0x01); if(ivideo->newrom) { /* New ROM invalidates other PDC resp. */ @@ -3060,10 +3060,10 @@ sisfb_save_pdc_emi(struct sis_video_info *ivideo) /* Save EMI */ if(ivideo->vbflags2 & VB2_SISEMIBRIDGE) { - inSISIDXREG(SISPART4,0x30,ivideo->SiS_Pr.EMI_30); - inSISIDXREG(SISPART4,0x31,ivideo->SiS_Pr.EMI_31); - inSISIDXREG(SISPART4,0x32,ivideo->SiS_Pr.EMI_32); - inSISIDXREG(SISPART4,0x33,ivideo->SiS_Pr.EMI_33); + ivideo->SiS_Pr.EMI_30 = SiS_GetReg(SISPART4, 0x30); + ivideo->SiS_Pr.EMI_31 = SiS_GetReg(SISPART4, 0x31); + ivideo->SiS_Pr.EMI_32 = SiS_GetReg(SISPART4, 0x32); + ivideo->SiS_Pr.EMI_33 = SiS_GetReg(SISPART4, 0x33); ivideo->SiS_Pr.HaveEMI = true; if((tmp & 0x20) || (ivideo->detectedlcda != 0xff)) { ivideo->SiS_Pr.HaveEMILCD = true; @@ -3488,8 +3488,8 @@ sisfb_check_engine_and_sync(struct sis_video_info *ivideo) * ivideo->accel here, as this might have * been changed before this is called. */ - inSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, cr30); - inSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, cr31); + cr30 = SiS_GetReg(SISSR, IND_SIS_PCI_ADDRESS_SET); + cr31 = SiS_GetReg(SISSR, IND_SIS_MODULE_ENABLE); /* MMIO and 2D/3D engine enabled? */ if((cr30 & SIS_MEM_MAP_IO_ENABLE) && (cr31 & 0x42)) { #ifdef CONFIG_FB_SIS_300 @@ -3507,7 +3507,7 @@ sisfb_check_engine_and_sync(struct sis_video_info *ivideo) * enabled, and that the queue * is not in the state of "reset" */ - inSISIDXREG(SISSR, 0x26, cr30); + cr30 = SiS_GetReg(SISSR, 0x26); if((cr30 & 0xe0) && (!(cr30 & 0x01))) { sisfb_syncaccel(ivideo); } @@ -3524,9 +3524,9 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) ivideo->currentvbflags &= (VB_VIDEOBRIDGE | VB_DISPTYPE_DISP2); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); - inSISIDXREG(SISCR, 0x31, cr31); + cr31 = SiS_GetReg(SISCR, 0x31); cr31 &= ~0x60; cr31 |= 0x04; @@ -3535,11 +3535,11 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_315 if(ivideo->sisvga_engine == SIS_315_VGA) { if(ivideo->chip >= SIS_661) { - inSISIDXREG(SISCR, 0x38, cr38); + cr38 = SiS_GetReg(SISCR, 0x38); cr38 &= ~0x07; /* Clear LCDA/DualEdge and YPbPr bits */ } else { tvregnum = 0x38; - inSISIDXREG(SISCR, tvregnum, cr38); + cr38 = SiS_GetReg(SISCR, tvregnum); cr38 &= ~0x3b; /* Clear LCDA/DualEdge and YPbPr bits */ } } @@ -3547,7 +3547,7 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) #ifdef CONFIG_FB_SIS_300 if(ivideo->sisvga_engine == SIS_300_VGA) { tvregnum = 0x35; - inSISIDXREG(SISCR, tvregnum, cr38); + cr38 = SiS_GetReg(SISCR, tvregnum); } #endif @@ -3654,20 +3654,20 @@ sisfb_pre_setmode(struct sis_video_info *ivideo) cr31 |= (SIS_DRIVER_MODE | SIS_VB_OUTPUT_DISABLE); } - outSISIDXREG(SISCR, 0x30, cr30); - outSISIDXREG(SISCR, 0x33, cr33); + SiS_SetReg(SISCR, 0x30, cr30); + SiS_SetReg(SISCR, 0x33, cr33); if(ivideo->chip >= SIS_661) { #ifdef CONFIG_FB_SIS_315 cr31 &= ~0x01; /* Clear PAL flag (now in CR35) */ - setSISIDXREG(SISCR, 0x35, ~0x10, cr35); /* Leave overscan bit alone */ + SiS_SetRegANDOR(SISCR, 0x35, ~0x10, cr35); /* Leave overscan bit alone */ cr38 &= 0x07; /* Use only LCDA and HiVision/YPbPr bits */ - setSISIDXREG(SISCR, 0x38, 0xf8, cr38); + SiS_SetRegANDOR(SISCR, 0x38, 0xf8, cr38); #endif } else if(ivideo->chip != SIS_300) { - outSISIDXREG(SISCR, tvregnum, cr38); + SiS_SetReg(SISCR, tvregnum, cr38); } - outSISIDXREG(SISCR, 0x31, cr31); + SiS_SetReg(SISCR, 0x31, cr31); ivideo->SiS_Pr.SiS_UseOEM = ivideo->sisfb_useoem; @@ -3682,15 +3682,15 @@ sisfb_fixup_SR11(struct sis_video_info *ivideo) u8 tmpreg; if(ivideo->chip >= SIS_661) { - inSISIDXREG(SISSR,0x11,tmpreg); + tmpreg = SiS_GetReg(SISSR, 0x11); if(tmpreg & 0x20) { - inSISIDXREG(SISSR,0x3e,tmpreg); + tmpreg = SiS_GetReg(SISSR, 0x3e); tmpreg = (tmpreg + 1) & 0xff; - outSISIDXREG(SISSR,0x3e,tmpreg); - inSISIDXREG(SISSR,0x11,tmpreg); + SiS_SetReg(SISSR, 0x3e, tmpreg); + tmpreg = SiS_GetReg(SISSR, 0x11); } if(tmpreg & 0xf0) { - andSISIDXREG(SISSR,0x11,0x0f); + SiS_SetRegAND(SISSR, 0x11, 0x0f); } } } @@ -3716,7 +3716,7 @@ sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val) case 1: x += val; if(x < 0) x = 0; - outSISIDXREG(SISSR,0x05,0x86); + SiS_SetReg(SISSR, 0x05, 0x86); SiS_SetCH700x(&ivideo->SiS_Pr, 0x0a, (x & 0xff)); SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((x & 0x0100) >> 7), 0xFD); break; @@ -3745,11 +3745,11 @@ sisfb_set_TVxposoffset(struct sis_video_info *ivideo, int val) temp += (val * 2); p2_43 = temp & 0xff; p2_42 = (temp & 0xf00) >> 4; - outSISIDXREG(SISPART2,0x1f,p2_1f); - setSISIDXREG(SISPART2,0x20,0x0F,p2_20); - setSISIDXREG(SISPART2,0x2b,0xF0,p2_2b); - setSISIDXREG(SISPART2,0x42,0x0F,p2_42); - outSISIDXREG(SISPART2,0x43,p2_43); + SiS_SetReg(SISPART2, 0x1f, p2_1f); + SiS_SetRegANDOR(SISPART2, 0x20, 0x0F, p2_20); + SiS_SetRegANDOR(SISPART2, 0x2b, 0xF0, p2_2b); + SiS_SetRegANDOR(SISPART2, 0x42, 0x0F, p2_42); + SiS_SetReg(SISPART2, 0x43, p2_43); } } } @@ -3774,7 +3774,7 @@ sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val) case 1: y -= val; if(y < 0) y = 0; - outSISIDXREG(SISSR,0x05,0x86); + SiS_SetReg(SISSR, 0x05, 0x86); SiS_SetCH700x(&ivideo->SiS_Pr, 0x0b, (y & 0xff)); SiS_SetCH70xxANDOR(&ivideo->SiS_Pr, 0x08, ((y & 0x0100) >> 8), 0xFE); break; @@ -3798,8 +3798,8 @@ sisfb_set_TVyposoffset(struct sis_video_info *ivideo, int val) p2_02 += 2; } } - outSISIDXREG(SISPART2,0x01,p2_01); - outSISIDXREG(SISPART2,0x02,p2_02); + SiS_SetReg(SISPART2, 0x01, p2_01); + SiS_SetReg(SISPART2, 0x02, p2_02); } } } @@ -3816,7 +3816,7 @@ sisfb_post_setmode(struct sis_video_info *ivideo) u8 reg1; #endif - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); #ifdef CONFIG_FB_SIS_315 sisfb_fixup_SR11(ivideo); @@ -3840,7 +3840,7 @@ sisfb_post_setmode(struct sis_video_info *ivideo) crt1isoff = false; reg = 0x80; } - setSISIDXREG(SISCR, 0x17, 0x7f, reg); + SiS_SetRegANDOR(SISCR, 0x17, 0x7f, reg); } #endif #ifdef CONFIG_FB_SIS_315 @@ -3854,8 +3854,8 @@ sisfb_post_setmode(struct sis_video_info *ivideo) reg = 0x00; reg1 = 0x00; } - setSISIDXREG(SISCR, ivideo->SiS_Pr.SiS_MyCR63, ~0x40, reg); - setSISIDXREG(SISSR, 0x1f, ~0xc0, reg1); + SiS_SetRegANDOR(SISCR, ivideo->SiS_Pr.SiS_MyCR63, ~0x40, reg); + SiS_SetRegANDOR(SISSR, 0x1f, 0x3f, reg1); } #endif @@ -3871,17 +3871,17 @@ sisfb_post_setmode(struct sis_video_info *ivideo) } } - andSISIDXREG(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04); + SiS_SetRegAND(SISSR, IND_SIS_RAMDAC_CONTROL, ~0x04); if(ivideo->currentvbflags & CRT2_TV) { if(ivideo->vbflags2 & VB2_SISBRIDGE) { - inSISIDXREG(SISPART2,0x1f,ivideo->p2_1f); - inSISIDXREG(SISPART2,0x20,ivideo->p2_20); - inSISIDXREG(SISPART2,0x2b,ivideo->p2_2b); - inSISIDXREG(SISPART2,0x42,ivideo->p2_42); - inSISIDXREG(SISPART2,0x43,ivideo->p2_43); - inSISIDXREG(SISPART2,0x01,ivideo->p2_01); - inSISIDXREG(SISPART2,0x02,ivideo->p2_02); + ivideo->p2_1f = SiS_GetReg(SISPART2, 0x1f); + ivideo->p2_20 = SiS_GetReg(SISPART2, 0x20); + ivideo->p2_2b = SiS_GetReg(SISPART2, 0x2b); + ivideo->p2_42 = SiS_GetReg(SISPART2, 0x42); + ivideo->p2_43 = SiS_GetReg(SISPART2, 0x43); + ivideo->p2_01 = SiS_GetReg(SISPART2, 0x01); + ivideo->p2_02 = SiS_GetReg(SISPART2, 0x02); } else if(ivideo->vbflags2 & VB2_CHRONTEL) { if(ivideo->chronteltype == 1) { ivideo->tvx = SiS_GetCH700x(&ivideo->SiS_Pr, 0x0a); @@ -4105,7 +4105,6 @@ sisfb_find_rom(struct pci_dev *pdev) struct sis_video_info *ivideo = pci_get_drvdata(pdev); void __iomem *rom_base; unsigned char *myrombase = NULL; - u32 temp; size_t romsize; /* First, try the official pci ROM functions (except @@ -4132,26 +4131,29 @@ sisfb_find_rom(struct pci_dev *pdev) /* Otherwise do it the conventional way. */ #if defined(__i386__) || defined(__x86_64__) + { + u32 temp; - for(temp = 0x000c0000; temp < 0x000f0000; temp += 0x00001000) { + for (temp = 0x000c0000; temp < 0x000f0000; temp += 0x00001000) { - rom_base = ioremap(temp, 65536); - if(!rom_base) - continue; + rom_base = ioremap(temp, 65536); + if (!rom_base) + continue; - if(!sisfb_check_rom(rom_base, ivideo)) { - iounmap(rom_base); - continue; - } + if (!sisfb_check_rom(rom_base, ivideo)) { + iounmap(rom_base); + continue; + } - if((myrombase = vmalloc(65536))) - memcpy_fromio(myrombase, rom_base, 65536); + if ((myrombase = vmalloc(65536))) + memcpy_fromio(myrombase, rom_base, 65536); - iounmap(rom_base); - break; + iounmap(rom_base); + break; - } + } + } #endif return myrombase; @@ -4192,10 +4194,10 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) unsigned char reg; int i, j; - andSISIDXREG(SISSR, 0x15, 0xFB); - orSISIDXREG(SISSR, 0x15, 0x04); - outSISIDXREG(SISSR, 0x13, 0x00); - outSISIDXREG(SISSR, 0x14, 0xBF); + SiS_SetRegAND(SISSR, 0x15, 0xFB); + SiS_SetRegOR(SISSR, 0x15, 0x04); + SiS_SetReg(SISSR, 0x13, 0x00); + SiS_SetReg(SISSR, 0x14, 0xBF); for(i = 0; i < 2; i++) { temp = 0x1234; @@ -4203,12 +4205,12 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) writew(temp, FBAddress); if(readw(FBAddress) == temp) break; - orSISIDXREG(SISSR, 0x3c, 0x01); - inSISIDXREG(SISSR, 0x05, reg); - inSISIDXREG(SISSR, 0x05, reg); - andSISIDXREG(SISSR, 0x3c, 0xfe); - inSISIDXREG(SISSR, 0x05, reg); - inSISIDXREG(SISSR, 0x05, reg); + SiS_SetRegOR(SISSR, 0x3c, 0x01); + reg = SiS_GetReg(SISSR, 0x05); + reg = SiS_GetReg(SISSR, 0x05); + SiS_SetRegAND(SISSR, 0x3c, 0xfe); + reg = SiS_GetReg(SISSR, 0x05); + reg = SiS_GetReg(SISSR, 0x05); temp++; } } @@ -4218,7 +4220,7 @@ sisfb_post_300_buswidth(struct sis_video_info *ivideo) writel(0x89ABCDEFL, (FBAddress + 8)); writel(0xCDEF0123L, (FBAddress + 12)); - inSISIDXREG(SISSR, 0x3b, reg); + reg = SiS_GetReg(SISSR, 0x3b); if(reg & 0x01) { if(readl((FBAddress + 12)) == 0xCDEF0123L) return 4; /* Channel A 128bit */ @@ -4281,13 +4283,13 @@ sisfb_post_300_rwtest(struct sis_video_info *ivideo, int iteration, int buswidth PhysicalAdrHalfPage = (PageCapacity / 2 + PhysicalAdrHigh) % PageCapacity; PhysicalAdrOtherPage = PageCapacity * SiS_DRAMType[k][2] + PhysicalAdrHigh; - andSISIDXREG(SISSR, 0x15, 0xFB); /* Test */ - orSISIDXREG(SISSR, 0x15, 0x04); /* Test */ + SiS_SetRegAND(SISSR, 0x15, 0xFB); /* Test */ + SiS_SetRegOR(SISSR, 0x15, 0x04); /* Test */ sr14 = (SiS_DRAMType[k][3] * buswidth) - 1; if(buswidth == 4) sr14 |= 0x80; else if(buswidth == 2) sr14 |= 0x40; - outSISIDXREG(SISSR, 0x13, SiS_DRAMType[k][4]); - outSISIDXREG(SISSR, 0x14, sr14); + SiS_SetReg(SISSR, 0x13, SiS_DRAMType[k][4]); + SiS_SetReg(SISSR, 0x14, sr14); BankNumHigh <<= 16; BankNumMid <<= 16; @@ -4354,13 +4356,13 @@ sisfb_post_sis300(struct pci_dev *pdev) if(!ivideo->SiS_Pr.UseROM) bios = NULL; - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); if(bios) { if(bios[0x52] & 0x80) { memtype = bios[0x52]; } else { - inSISIDXREG(SISSR, 0x3a, memtype); + memtype = SiS_GetReg(SISSR, 0x3a); } memtype &= 0x07; } @@ -4384,19 +4386,19 @@ sisfb_post_sis300(struct pci_dev *pdev) v6 = bios[rindex++]; } } - outSISIDXREG(SISSR, 0x28, v1); - outSISIDXREG(SISSR, 0x29, v2); - outSISIDXREG(SISSR, 0x2a, v3); - outSISIDXREG(SISSR, 0x2e, v4); - outSISIDXREG(SISSR, 0x2f, v5); - outSISIDXREG(SISSR, 0x30, v6); + SiS_SetReg(SISSR, 0x28, v1); + SiS_SetReg(SISSR, 0x29, v2); + SiS_SetReg(SISSR, 0x2a, v3); + SiS_SetReg(SISSR, 0x2e, v4); + SiS_SetReg(SISSR, 0x2f, v5); + SiS_SetReg(SISSR, 0x30, v6); v1 = 0x10; if(bios) v1 = bios[0xa4]; - outSISIDXREG(SISSR, 0x07, v1); /* DAC speed */ + SiS_SetReg(SISSR, 0x07, v1); /* DAC speed */ - outSISIDXREG(SISSR, 0x11, 0x0f); /* DDC, power save */ + SiS_SetReg(SISSR, 0x11, 0x0f); /* DDC, power save */ v1 = 0x01; v2 = 0x43; v3 = 0x1e; v4 = 0x2a; v5 = 0x06; v6 = 0x00; v7 = 0x00; v8 = 0x00; @@ -4413,87 +4415,87 @@ sisfb_post_sis300(struct pci_dev *pdev) } if(ivideo->revision_id >= 0x80) v3 &= 0xfd; - outSISIDXREG(SISSR, 0x15, v1); /* Ram type (assuming 0, BIOS 0xa5 step 8) */ - outSISIDXREG(SISSR, 0x16, v2); - outSISIDXREG(SISSR, 0x17, v3); - outSISIDXREG(SISSR, 0x18, v4); - outSISIDXREG(SISSR, 0x19, v5); - outSISIDXREG(SISSR, 0x1a, v6); - outSISIDXREG(SISSR, 0x1b, v7); - outSISIDXREG(SISSR, 0x1c, v8); /* ---- */ - andSISIDXREG(SISSR, 0x15 ,0xfb); - orSISIDXREG(SISSR, 0x15, 0x04); + SiS_SetReg(SISSR, 0x15, v1); /* Ram type (assuming 0, BIOS 0xa5 step 8) */ + SiS_SetReg(SISSR, 0x16, v2); + SiS_SetReg(SISSR, 0x17, v3); + SiS_SetReg(SISSR, 0x18, v4); + SiS_SetReg(SISSR, 0x19, v5); + SiS_SetReg(SISSR, 0x1a, v6); + SiS_SetReg(SISSR, 0x1b, v7); + SiS_SetReg(SISSR, 0x1c, v8); /* ---- */ + SiS_SetRegAND(SISSR, 0x15, 0xfb); + SiS_SetRegOR(SISSR, 0x15, 0x04); if(bios) { if(bios[0x53] & 0x02) { - orSISIDXREG(SISSR, 0x19, 0x20); + SiS_SetRegOR(SISSR, 0x19, 0x20); } } v1 = 0x04; /* DAC pedestal (BIOS 0xe5) */ if(ivideo->revision_id >= 0x80) v1 |= 0x01; - outSISIDXREG(SISSR, 0x1f, v1); - outSISIDXREG(SISSR, 0x20, 0xa4); /* linear & relocated io & disable a0000 */ + SiS_SetReg(SISSR, 0x1f, v1); + SiS_SetReg(SISSR, 0x20, 0xa4); /* linear & relocated io & disable a0000 */ v1 = 0xf6; v2 = 0x0d; v3 = 0x00; if(bios) { v1 = bios[0xe8]; v2 = bios[0xe9]; v3 = bios[0xea]; } - outSISIDXREG(SISSR, 0x23, v1); - outSISIDXREG(SISSR, 0x24, v2); - outSISIDXREG(SISSR, 0x25, v3); - outSISIDXREG(SISSR, 0x21, 0x84); - outSISIDXREG(SISSR, 0x22, 0x00); - outSISIDXREG(SISCR, 0x37, 0x00); - orSISIDXREG(SISPART1, 0x24, 0x01); /* unlock crt2 */ - outSISIDXREG(SISPART1, 0x00, 0x00); + SiS_SetReg(SISSR, 0x23, v1); + SiS_SetReg(SISSR, 0x24, v2); + SiS_SetReg(SISSR, 0x25, v3); + SiS_SetReg(SISSR, 0x21, 0x84); + SiS_SetReg(SISSR, 0x22, 0x00); + SiS_SetReg(SISCR, 0x37, 0x00); + SiS_SetRegOR(SISPART1, 0x24, 0x01); /* unlock crt2 */ + SiS_SetReg(SISPART1, 0x00, 0x00); v1 = 0x40; v2 = 0x11; if(bios) { v1 = bios[0xec]; v2 = bios[0xeb]; } - outSISIDXREG(SISPART1, 0x02, v1); + SiS_SetReg(SISPART1, 0x02, v1); if(ivideo->revision_id >= 0x80) v2 &= ~0x01; - inSISIDXREG(SISPART4, 0x00, reg); + reg = SiS_GetReg(SISPART4, 0x00); if((reg == 1) || (reg == 2)) { - outSISIDXREG(SISCR, 0x37, 0x02); - outSISIDXREG(SISPART2, 0x00, 0x1c); + SiS_SetReg(SISCR, 0x37, 0x02); + SiS_SetReg(SISPART2, 0x00, 0x1c); v4 = 0x00; v5 = 0x00; v6 = 0x10; if(ivideo->SiS_Pr.UseROM) { v4 = bios[0xf5]; v5 = bios[0xf6]; v6 = bios[0xf7]; } - outSISIDXREG(SISPART4, 0x0d, v4); - outSISIDXREG(SISPART4, 0x0e, v5); - outSISIDXREG(SISPART4, 0x10, v6); - outSISIDXREG(SISPART4, 0x0f, 0x3f); - inSISIDXREG(SISPART4, 0x01, reg); + SiS_SetReg(SISPART4, 0x0d, v4); + SiS_SetReg(SISPART4, 0x0e, v5); + SiS_SetReg(SISPART4, 0x10, v6); + SiS_SetReg(SISPART4, 0x0f, 0x3f); + reg = SiS_GetReg(SISPART4, 0x01); if(reg >= 0xb0) { - inSISIDXREG(SISPART4, 0x23, reg); + reg = SiS_GetReg(SISPART4, 0x23); reg &= 0x20; reg <<= 1; - outSISIDXREG(SISPART4, 0x23, reg); + SiS_SetReg(SISPART4, 0x23, reg); } } else { v2 &= ~0x10; } - outSISIDXREG(SISSR, 0x32, v2); + SiS_SetReg(SISSR, 0x32, v2); - andSISIDXREG(SISPART1, 0x24, 0xfe); /* Lock CRT2 */ + SiS_SetRegAND(SISPART1, 0x24, 0xfe); /* Lock CRT2 */ - inSISIDXREG(SISSR, 0x16, reg); + reg = SiS_GetReg(SISSR, 0x16); reg &= 0xc3; - outSISIDXREG(SISCR, 0x35, reg); - outSISIDXREG(SISCR, 0x83, 0x00); + SiS_SetReg(SISCR, 0x35, reg); + SiS_SetReg(SISCR, 0x83, 0x00); #if !defined(__i386__) && !defined(__x86_64__) if(sisfb_videoram) { - outSISIDXREG(SISSR, 0x13, 0x28); /* ? */ + SiS_SetReg(SISSR, 0x13, 0x28); /* ? */ reg = ((sisfb_videoram >> 10) - 1) | 0x40; - outSISIDXREG(SISSR, 0x14, reg); + SiS_SetReg(SISSR, 0x14, reg); } else { #endif /* Need to map max FB size for finding out about RAM size */ @@ -4506,8 +4508,8 @@ sisfb_post_sis300(struct pci_dev *pdev) } else { printk(KERN_DEBUG "sisfb: Failed to map memory for size detection, assuming 8MB\n"); - outSISIDXREG(SISSR, 0x13, 0x28); /* ? */ - outSISIDXREG(SISSR, 0x14, 0x47); /* 8MB, 64bit default */ + SiS_SetReg(SISSR, 0x13, 0x28); /* ? */ + SiS_SetReg(SISSR, 0x14, 0x47); /* 8MB, 64bit default */ } #if !defined(__i386__) && !defined(__x86_64__) } @@ -4516,7 +4518,7 @@ sisfb_post_sis300(struct pci_dev *pdev) v1 = bios[0xe6]; v2 = bios[0xe7]; } else { - inSISIDXREG(SISSR, 0x3a, reg); + reg = SiS_GetReg(SISSR, 0x3a); if((reg & 0x30) == 0x30) { v1 = 0x04; /* PCI */ v2 = 0x92; @@ -4525,8 +4527,8 @@ sisfb_post_sis300(struct pci_dev *pdev) v2 = 0xb2; } } - outSISIDXREG(SISSR, 0x21, v1); - outSISIDXREG(SISSR, 0x22, v2); + SiS_SetReg(SISSR, 0x21, v1); + SiS_SetReg(SISSR, 0x22, v2); /* Sense CRT1 */ sisfb_sense_crt1(ivideo); @@ -4539,13 +4541,13 @@ sisfb_post_sis300(struct pci_dev *pdev) ivideo->SiS_Pr.VideoMemorySize = 8 << 20; SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); /* Display off */ - orSISIDXREG(SISSR, 0x01, 0x20); + SiS_SetRegOR(SISSR, 0x01, 0x20); /* Save mode number in CR34 */ - outSISIDXREG(SISCR, 0x34, 0x2e); + SiS_SetReg(SISCR, 0x34, 0x2e); /* Let everyone know what the current mode is */ ivideo->modeprechange = 0x2e; @@ -4568,7 +4570,7 @@ sisfb_post_xgi_delay(struct sis_video_info *ivideo, int delay) u8 reg; for(i = 0; i <= (delay * 10 * 36); i++) { - inSISIDXREG(SISSR, 0x05, reg); + reg = SiS_GetReg(SISSR, 0x05); reg++; } } @@ -4660,7 +4662,7 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) * - if running on non-x86, there usually is no VGA window * at a0000. */ - orSISIDXREG(SISSR, 0x20, (0x80 | 0x04)); + SiS_SetRegOR(SISSR, 0x20, (0x80 | 0x04)); /* Need to map max FB size for finding out about RAM size */ mapsize = ivideo->video_size; @@ -4668,76 +4670,76 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) if(!ivideo->video_vbase) { printk(KERN_ERR "sisfb: Unable to detect RAM size. Setting default.\n"); - outSISIDXREG(SISSR, 0x13, 0x35); - outSISIDXREG(SISSR, 0x14, 0x41); + SiS_SetReg(SISSR, 0x13, 0x35); + SiS_SetReg(SISSR, 0x14, 0x41); /* TODO */ return; } /* Non-interleaving */ - outSISIDXREG(SISSR, 0x15, 0x00); + SiS_SetReg(SISSR, 0x15, 0x00); /* No tiling */ - outSISIDXREG(SISSR, 0x1c, 0x00); + SiS_SetReg(SISSR, 0x1c, 0x00); if(ivideo->chip == XGI_20) { channelab = 1; - inSISIDXREG(SISCR, 0x97, reg); + reg = SiS_GetReg(SISCR, 0x97); if(!(reg & 0x01)) { /* Single 32/16 */ buswidth = 32; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x52); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x52); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x02; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x31); - outSISIDXREG(SISSR, 0x14, 0x42); + SiS_SetReg(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x14, 0x42); sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 23, 23, mapsize)) goto bail_out; buswidth = 16; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x41); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x41); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x01; if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; else - outSISIDXREG(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x13, 0x31); } else { /* Dual 16/8 */ buswidth = 16; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x41); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x41); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x01; if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x31); - outSISIDXREG(SISSR, 0x14, 0x31); + SiS_SetReg(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x14, 0x31); sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 22, 22, mapsize)) goto bail_out; buswidth = 8; - outSISIDXREG(SISSR, 0x13, 0xb1); - outSISIDXREG(SISSR, 0x14, 0x30); + SiS_SetReg(SISSR, 0x13, 0xb1); + SiS_SetReg(SISSR, 0x14, 0x30); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x00; if(sisfb_post_xgi_rwtest(ivideo, 21, 22, mapsize)) goto bail_out; else - outSISIDXREG(SISSR, 0x13, 0x31); + SiS_SetReg(SISSR, 0x13, 0x31); } } else { /* XGI_40 */ - inSISIDXREG(SISCR, 0x97, reg); + reg = SiS_GetReg(SISCR, 0x97); if(!(reg & 0x10)) { - inSISIDXREG(SISSR, 0x39, reg); + reg = SiS_GetReg(SISSR, 0x39); reg >>= 1; } @@ -4745,52 +4747,52 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) buswidth = 32; if(ivideo->revision_id == 2) { channelab = 2; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x44); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x44); sr14 = 0x04; sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x34); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x34); if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; channelab = 1; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x40); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x40); sr14 = 0x00; if(sisfb_post_xgi_rwtest(ivideo, 22, 23, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x30); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x30); } else { channelab = 3; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x4c); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x4c); sr14 = 0x0c; sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, 23, 25, mapsize)) goto bail_out; channelab = 2; - outSISIDXREG(SISSR, 0x14, 0x48); + SiS_SetReg(SISSR, 0x14, 0x48); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x08; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x3c); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x3c); sr14 = 0x0c; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) { channelab = 3; } else { channelab = 2; - outSISIDXREG(SISSR, 0x14, 0x38); + SiS_SetReg(SISSR, 0x14, 0x38); sr14 = 0x08; } } @@ -4801,26 +4803,26 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) buswidth = 64; if(ivideo->revision_id == 2) { channelab = 1; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x52); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x52); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x02; if(sisfb_post_xgi_rwtest(ivideo, 23, 24, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x42); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x42); } else { channelab = 2; - outSISIDXREG(SISSR, 0x13, 0xa1); - outSISIDXREG(SISSR, 0x14, 0x5a); + SiS_SetReg(SISSR, 0x13, 0xa1); + SiS_SetReg(SISSR, 0x14, 0x5a); sisfb_post_xgi_delay(ivideo, 1); sr14 = 0x0a; if(sisfb_post_xgi_rwtest(ivideo, 24, 25, mapsize)) goto bail_out; - outSISIDXREG(SISSR, 0x13, 0x21); - outSISIDXREG(SISSR, 0x14, 0x4a); + SiS_SetReg(SISSR, 0x13, 0x21); + SiS_SetReg(SISSR, 0x14, 0x4a); } sisfb_post_xgi_delay(ivideo, 1); @@ -4828,7 +4830,7 @@ sisfb_post_xgi_ramsize(struct sis_video_info *ivideo) } bail_out: - setSISIDXREG(SISSR, 0x14, 0xf0, sr14); + SiS_SetRegANDOR(SISSR, 0x14, 0xf0, sr14); sisfb_post_xgi_delay(ivideo, 1); j = (ivideo->chip == XGI_20) ? 5 : 9; @@ -4838,13 +4840,13 @@ bail_out: reg = (ivideo->chip == XGI_20) ? dramsr13[(i * 5) + 4] : dramsr13_4[(i * 5) + 4]; - setSISIDXREG(SISSR, 0x13, 0x80, reg); + SiS_SetRegANDOR(SISSR, 0x13, 0x80, reg); sisfb_post_xgi_delay(ivideo, 50); ranksize = (ivideo->chip == XGI_20) ? dramsr13[(i * 5) + 3] : dramsr13_4[(i * 5) + 3]; - inSISIDXREG(SISSR, 0x13, reg); + reg = SiS_GetReg(SISSR, 0x13); if(reg & 0x80) ranksize <<= 1; if(ivideo->chip == XGI_20) { @@ -4863,7 +4865,7 @@ bail_out: if(!reg) continue; - setSISIDXREG(SISSR, 0x14, 0x0f, (reg & 0xf0)); + SiS_SetRegANDOR(SISSR, 0x14, 0x0f, (reg & 0xf0)); sisfb_post_xgi_delay(ivideo, 1); if(sisfb_post_xgi_rwtest(ivideo, j, ((reg >> 4) + channelab - 2 + 20), mapsize)) @@ -4908,9 +4910,9 @@ sisfb_post_xgi_setclocks(struct sis_video_info *ivideo, u8 regb) v2 = ivideo->bios_abase[0x90 + index + 1]; v3 = ivideo->bios_abase[0x90 + index + 2]; } - outSISIDXREG(SISSR, 0x28, v1); - outSISIDXREG(SISSR, 0x29, v2); - outSISIDXREG(SISSR, 0x2a, v3); + SiS_SetReg(SISSR, 0x28, v1); + SiS_SetReg(SISSR, 0x29, v2); + SiS_SetReg(SISSR, 0x2a, v3); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); @@ -4921,9 +4923,9 @@ sisfb_post_xgi_setclocks(struct sis_video_info *ivideo, u8 regb) v2 = ivideo->bios_abase[0xb8 + index + 1]; v3 = ivideo->bios_abase[0xb8 + index + 2]; } - outSISIDXREG(SISSR, 0x2e, v1); - outSISIDXREG(SISSR, 0x2f, v2); - outSISIDXREG(SISSR, 0x30, v3); + SiS_SetReg(SISSR, 0x2e, v1); + SiS_SetReg(SISSR, 0x2f, v2); + SiS_SetReg(SISSR, 0x30, v3); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); @@ -4996,29 +4998,29 @@ sisfb_post_xgi(struct pci_dev *pdev) }; /* VGA enable */ - reg = inSISREG(SISVGAENABLE) | 0x01; - outSISREG(SISVGAENABLE, reg); + reg = SiS_GetRegByte(SISVGAENABLE) | 0x01; + SiS_SetRegByte(SISVGAENABLE, reg); /* Misc */ - reg = inSISREG(SISMISCR) | 0x01; - outSISREG(SISMISCW, reg); + reg = SiS_GetRegByte(SISMISCR) | 0x01; + SiS_SetRegByte(SISMISCW, reg); /* Unlock SR */ - outSISIDXREG(SISSR, 0x05, 0x86); - inSISIDXREG(SISSR, 0x05, reg); + SiS_SetReg(SISSR, 0x05, 0x86); + reg = SiS_GetReg(SISSR, 0x05); if(reg != 0xa1) return 0; /* Clear some regs */ for(i = 0; i < 0x22; i++) { if(0x06 + i == 0x20) continue; - outSISIDXREG(SISSR, 0x06 + i, 0x00); + SiS_SetReg(SISSR, 0x06 + i, 0x00); } for(i = 0; i < 0x0b; i++) { - outSISIDXREG(SISSR, 0x31 + i, 0x00); + SiS_SetReg(SISSR, 0x31 + i, 0x00); } for(i = 0; i < 0x10; i++) { - outSISIDXREG(SISCR, 0x30 + i, 0x00); + SiS_SetReg(SISCR, 0x30 + i, 0x00); } ptr = cs78; @@ -5026,7 +5028,7 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x78]; } for(i = 0; i < 3; i++) { - outSISIDXREG(SISSR, 0x23 + i, ptr[i]); + SiS_SetReg(SISSR, 0x23 + i, ptr[i]); } ptr = cs76; @@ -5034,7 +5036,7 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x76]; } for(i = 0; i < 2; i++) { - outSISIDXREG(SISSR, 0x21 + i, ptr[i]); + SiS_SetReg(SISSR, 0x21 + i, ptr[i]); } v1 = 0x18; v2 = 0x00; @@ -5042,83 +5044,83 @@ sisfb_post_xgi(struct pci_dev *pdev) v1 = bios[0x74]; v2 = bios[0x75]; } - outSISIDXREG(SISSR, 0x07, v1); - outSISIDXREG(SISSR, 0x11, 0x0f); - outSISIDXREG(SISSR, 0x1f, v2); + SiS_SetReg(SISSR, 0x07, v1); + SiS_SetReg(SISSR, 0x11, 0x0f); + SiS_SetReg(SISSR, 0x1f, v2); /* PCI linear mode, RelIO enabled, A0000 decoding disabled */ - outSISIDXREG(SISSR, 0x20, 0x80 | 0x20 | 0x04); - outSISIDXREG(SISSR, 0x27, 0x74); + SiS_SetReg(SISSR, 0x20, 0x80 | 0x20 | 0x04); + SiS_SetReg(SISSR, 0x27, 0x74); ptr = cs7b; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x7b]; } for(i = 0; i < 3; i++) { - outSISIDXREG(SISSR, 0x31 + i, ptr[i]); + SiS_SetReg(SISSR, 0x31 + i, ptr[i]); } if(ivideo->chip == XGI_40) { if(ivideo->revision_id == 2) { - setSISIDXREG(SISSR, 0x3b, 0x3f, 0xc0); + SiS_SetRegANDOR(SISSR, 0x3b, 0x3f, 0xc0); } - outSISIDXREG(SISCR, 0x7d, 0xfe); - outSISIDXREG(SISCR, 0x7e, 0x0f); + SiS_SetReg(SISCR, 0x7d, 0xfe); + SiS_SetReg(SISCR, 0x7e, 0x0f); } if(ivideo->revision_id == 0) { /* 40 *and* 20? */ - andSISIDXREG(SISCR, 0x58, 0xd7); - inSISIDXREG(SISCR, 0xcb, reg); + SiS_SetRegAND(SISCR, 0x58, 0xd7); + reg = SiS_GetReg(SISCR, 0xcb); if(reg & 0x20) { - setSISIDXREG(SISCR, 0x58, 0xd7, (reg & 0x10) ? 0x08 : 0x20); /* =0x28 Z7 ? */ + SiS_SetRegANDOR(SISCR, 0x58, 0xd7, (reg & 0x10) ? 0x08 : 0x20); /* =0x28 Z7 ? */ } } reg = (ivideo->chip == XGI_40) ? 0x20 : 0x00; - setSISIDXREG(SISCR, 0x38, 0x1f, reg); + SiS_SetRegANDOR(SISCR, 0x38, 0x1f, reg); if(ivideo->chip == XGI_20) { - outSISIDXREG(SISSR, 0x36, 0x70); + SiS_SetReg(SISSR, 0x36, 0x70); } else { - outSISIDXREG(SISVID, 0x00, 0x86); - outSISIDXREG(SISVID, 0x32, 0x00); - outSISIDXREG(SISVID, 0x30, 0x00); - outSISIDXREG(SISVID, 0x32, 0x01); - outSISIDXREG(SISVID, 0x30, 0x00); - andSISIDXREG(SISVID, 0x2f, 0xdf); - andSISIDXREG(SISCAP, 0x00, 0x3f); - - outSISIDXREG(SISPART1, 0x2f, 0x01); - outSISIDXREG(SISPART1, 0x00, 0x00); - outSISIDXREG(SISPART1, 0x02, bios[0x7e]); - outSISIDXREG(SISPART1, 0x2e, 0x08); - andSISIDXREG(SISPART1, 0x35, 0x7f); - andSISIDXREG(SISPART1, 0x50, 0xfe); - - inSISIDXREG(SISPART4, 0x00, reg); + SiS_SetReg(SISVID, 0x00, 0x86); + SiS_SetReg(SISVID, 0x32, 0x00); + SiS_SetReg(SISVID, 0x30, 0x00); + SiS_SetReg(SISVID, 0x32, 0x01); + SiS_SetReg(SISVID, 0x30, 0x00); + SiS_SetRegAND(SISVID, 0x2f, 0xdf); + SiS_SetRegAND(SISCAP, 0x00, 0x3f); + + SiS_SetReg(SISPART1, 0x2f, 0x01); + SiS_SetReg(SISPART1, 0x00, 0x00); + SiS_SetReg(SISPART1, 0x02, bios[0x7e]); + SiS_SetReg(SISPART1, 0x2e, 0x08); + SiS_SetRegAND(SISPART1, 0x35, 0x7f); + SiS_SetRegAND(SISPART1, 0x50, 0xfe); + + reg = SiS_GetReg(SISPART4, 0x00); if(reg == 1 || reg == 2) { - outSISIDXREG(SISPART2, 0x00, 0x1c); - outSISIDXREG(SISPART4, 0x0d, bios[0x7f]); - outSISIDXREG(SISPART4, 0x0e, bios[0x80]); - outSISIDXREG(SISPART4, 0x10, bios[0x81]); - andSISIDXREG(SISPART4, 0x0f, 0x3f); + SiS_SetReg(SISPART2, 0x00, 0x1c); + SiS_SetReg(SISPART4, 0x0d, bios[0x7f]); + SiS_SetReg(SISPART4, 0x0e, bios[0x80]); + SiS_SetReg(SISPART4, 0x10, bios[0x81]); + SiS_SetRegAND(SISPART4, 0x0f, 0x3f); - inSISIDXREG(SISPART4, 0x01, reg); + reg = SiS_GetReg(SISPART4, 0x01); if((reg & 0xf0) >= 0xb0) { - inSISIDXREG(SISPART4, 0x23, reg); + reg = SiS_GetReg(SISPART4, 0x23); if(reg & 0x20) reg |= 0x40; - outSISIDXREG(SISPART4, 0x23, reg); + SiS_SetReg(SISPART4, 0x23, reg); reg = (reg & 0x20) ? 0x02 : 0x00; - setSISIDXREG(SISPART1, 0x1e, 0xfd, reg); + SiS_SetRegANDOR(SISPART1, 0x1e, 0xfd, reg); } } v1 = bios[0x77]; - inSISIDXREG(SISSR, 0x3b, reg); + reg = SiS_GetReg(SISSR, 0x3b); if(reg & 0x02) { - inSISIDXREG(SISSR, 0x3a, reg); + reg = SiS_GetReg(SISSR, 0x3a); v2 = (reg & 0x30) >> 3; if(!(v2 & 0x04)) v2 ^= 0x02; - inSISIDXREG(SISSR, 0x39, reg); + reg = SiS_GetReg(SISSR, 0x39); if(reg & 0x80) v2 |= 0x80; v2 |= 0x01; @@ -5151,36 +5153,36 @@ sisfb_post_xgi(struct pci_dev *pdev) v2 |= 0x08; } } - setSISIDXREG(SISCR, 0x5f, 0xf0, v2); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf0, v2); } - outSISIDXREG(SISSR, 0x22, v1); + SiS_SetReg(SISSR, 0x22, v1); if(ivideo->revision_id == 2) { - inSISIDXREG(SISSR, 0x3b, v1); - inSISIDXREG(SISSR, 0x3a, v2); + v1 = SiS_GetReg(SISSR, 0x3b); + v2 = SiS_GetReg(SISSR, 0x3a); regd = bios[0x90 + 3] | (bios[0x90 + 4] << 8); if( (!(v1 & 0x02)) && (v2 & 0x30) && (regd < 0xcf) ) - setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf1, 0x01); if((mypdev = pci_get_device(0x10de, 0x01e0, NULL))) { /* TODO: set CR5f &0xf1 | 0x01 for version 6570 * of nforce 2 ROM */ if(0) - setSISIDXREG(SISCR, 0x5f, 0xf1, 0x01); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf1, 0x01); pci_dev_put(mypdev); } } v1 = 0x30; - inSISIDXREG(SISSR, 0x3b, reg); - inSISIDXREG(SISCR, 0x5f, v2); + reg = SiS_GetReg(SISSR, 0x3b); + v2 = SiS_GetReg(SISCR, 0x5f); if((!(reg & 0x02)) && (v2 & 0x0e)) v1 |= 0x08; - outSISIDXREG(SISSR, 0x27, v1); + SiS_SetReg(SISSR, 0x27, v1); if(bios[0x64] & 0x01) { - setSISIDXREG(SISCR, 0x5f, 0xf0, bios[0x64]); + SiS_SetRegANDOR(SISCR, 0x5f, 0xf0, bios[0x64]); } v1 = bios[0x4f7]; @@ -5188,27 +5190,27 @@ sisfb_post_xgi(struct pci_dev *pdev) regd = (regd >> 20) & 0x0f; if(regd == 1) { v1 &= 0xfc; - orSISIDXREG(SISCR, 0x5f, 0x08); - } - outSISIDXREG(SISCR, 0x48, v1); - - setSISIDXREG(SISCR, 0x47, 0x04, bios[0x4f6] & 0xfb); - setSISIDXREG(SISCR, 0x49, 0xf0, bios[0x4f8] & 0x0f); - setSISIDXREG(SISCR, 0x4a, 0x60, bios[0x4f9] & 0x9f); - setSISIDXREG(SISCR, 0x4b, 0x08, bios[0x4fa] & 0xf7); - setSISIDXREG(SISCR, 0x4c, 0x80, bios[0x4fb] & 0x7f); - outSISIDXREG(SISCR, 0x70, bios[0x4fc]); - setSISIDXREG(SISCR, 0x71, 0xf0, bios[0x4fd] & 0x0f); - outSISIDXREG(SISCR, 0x74, 0xd0); - setSISIDXREG(SISCR, 0x74, 0xcf, bios[0x4fe] & 0x30); - setSISIDXREG(SISCR, 0x75, 0xe0, bios[0x4ff] & 0x1f); - setSISIDXREG(SISCR, 0x76, 0xe0, bios[0x500] & 0x1f); + SiS_SetRegOR(SISCR, 0x5f, 0x08); + } + SiS_SetReg(SISCR, 0x48, v1); + + SiS_SetRegANDOR(SISCR, 0x47, 0x04, bios[0x4f6] & 0xfb); + SiS_SetRegANDOR(SISCR, 0x49, 0xf0, bios[0x4f8] & 0x0f); + SiS_SetRegANDOR(SISCR, 0x4a, 0x60, bios[0x4f9] & 0x9f); + SiS_SetRegANDOR(SISCR, 0x4b, 0x08, bios[0x4fa] & 0xf7); + SiS_SetRegANDOR(SISCR, 0x4c, 0x80, bios[0x4fb] & 0x7f); + SiS_SetReg(SISCR, 0x70, bios[0x4fc]); + SiS_SetRegANDOR(SISCR, 0x71, 0xf0, bios[0x4fd] & 0x0f); + SiS_SetReg(SISCR, 0x74, 0xd0); + SiS_SetRegANDOR(SISCR, 0x74, 0xcf, bios[0x4fe] & 0x30); + SiS_SetRegANDOR(SISCR, 0x75, 0xe0, bios[0x4ff] & 0x1f); + SiS_SetRegANDOR(SISCR, 0x76, 0xe0, bios[0x500] & 0x1f); v1 = bios[0x501]; if((mypdev = pci_get_device(0x8086, 0x2530, NULL))) { v1 = 0xf0; pci_dev_put(mypdev); } - outSISIDXREG(SISCR, 0x77, v1); + SiS_SetReg(SISCR, 0x77, v1); } /* RAM type */ @@ -5219,14 +5221,14 @@ sisfb_post_xgi(struct pci_dev *pdev) if(ivideo->haveXGIROM) { v1 = bios[0x140 + regb]; } - outSISIDXREG(SISCR, 0x6d, v1); + SiS_SetReg(SISCR, 0x6d, v1); ptr = cs128; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x128]; } for(i = 0, j = 0; i < 3; i++, j += 8) { - outSISIDXREG(SISCR, 0x68 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x68 + i, ptr[j + regb]); } ptr = cs31a; @@ -5250,14 +5252,14 @@ sisfb_post_xgi(struct pci_dev *pdev) if(regd & 0x01) reg |= 0x04; if(regd & 0x02) reg |= 0x08; regd >>= 2; - outSISIDXREG(SISCR, rega, reg); - inSISIDXREG(SISCR, rega, reg); - inSISIDXREG(SISCR, rega, reg); + SiS_SetReg(SISCR, rega, reg); + reg = SiS_GetReg(SISCR, rega); + reg = SiS_GetReg(SISCR, rega); reg += 0x10; } } - andSISIDXREG(SISCR, 0x6e, 0xfc); + SiS_SetRegAND(SISCR, 0x6e, 0xfc); ptr = NULL; if(ivideo->haveXGIROM) { @@ -5265,7 +5267,7 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[index]; } for(i = 0; i < 4; i++) { - setSISIDXREG(SISCR, 0x6e, 0xfc, i); + SiS_SetRegANDOR(SISCR, 0x6e, 0xfc, i); reg = 0x00; for(j = 0; j < 2; j++) { regd = 0; @@ -5279,9 +5281,9 @@ sisfb_post_xgi(struct pci_dev *pdev) if(regd & 0x01) reg |= 0x01; if(regd & 0x02) reg |= 0x02; regd >>= 2; - outSISIDXREG(SISCR, 0x6f, reg); - inSISIDXREG(SISCR, 0x6f, reg); - inSISIDXREG(SISCR, 0x6f, reg); + SiS_SetReg(SISCR, 0x6f, reg); + reg = SiS_GetReg(SISCR, 0x6f); + reg = SiS_GetReg(SISCR, 0x6f); reg += 0x08; } } @@ -5292,10 +5294,10 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x148]; } for(i = 0, j = 0; i < 2; i++, j += 8) { - outSISIDXREG(SISCR, 0x80 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x80 + i, ptr[j + regb]); } - andSISIDXREG(SISCR, 0x89, 0x8f); + SiS_SetRegAND(SISCR, 0x89, 0x8f); ptr = cs45a; if(ivideo->haveXGIROM) { @@ -5309,9 +5311,9 @@ sisfb_post_xgi(struct pci_dev *pdev) if(regd & 0x01) reg |= 0x01; if(regd & 0x02) reg |= 0x02; regd >>= 2; - outSISIDXREG(SISCR, 0x89, reg); - inSISIDXREG(SISCR, 0x89, reg); - inSISIDXREG(SISCR, 0x89, reg); + SiS_SetReg(SISCR, 0x89, reg); + reg = SiS_GetReg(SISCR, 0x89); + reg = SiS_GetReg(SISCR, 0x89); reg += 0x10; } @@ -5322,27 +5324,27 @@ sisfb_post_xgi(struct pci_dev *pdev) v3 = bios[0x120 + regb]; v4 = bios[0x1ca]; } - outSISIDXREG(SISCR, 0x45, v1 & 0x0f); - outSISIDXREG(SISCR, 0x99, (v1 >> 4) & 0x07); - orSISIDXREG(SISCR, 0x40, v1 & 0x80); - outSISIDXREG(SISCR, 0x41, v2); + SiS_SetReg(SISCR, 0x45, v1 & 0x0f); + SiS_SetReg(SISCR, 0x99, (v1 >> 4) & 0x07); + SiS_SetRegOR(SISCR, 0x40, v1 & 0x80); + SiS_SetReg(SISCR, 0x41, v2); ptr = cs170; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x170]; } for(i = 0, j = 0; i < 7; i++, j += 8) { - outSISIDXREG(SISCR, 0x90 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x90 + i, ptr[j + regb]); } - outSISIDXREG(SISCR, 0x59, v3); + SiS_SetReg(SISCR, 0x59, v3); ptr = cs1a8; if(ivideo->haveXGIROM) { ptr = (const u8 *)&bios[0x1a8]; } for(i = 0, j = 0; i < 3; i++, j += 8) { - outSISIDXREG(SISCR, 0xc3 + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0xc3 + i, ptr[j + regb]); } ptr = cs100; @@ -5350,27 +5352,27 @@ sisfb_post_xgi(struct pci_dev *pdev) ptr = (const u8 *)&bios[0x100]; } for(i = 0, j = 0; i < 2; i++, j += 8) { - outSISIDXREG(SISCR, 0x8a + i, ptr[j + regb]); + SiS_SetReg(SISCR, 0x8a + i, ptr[j + regb]); } - outSISIDXREG(SISCR, 0xcf, v4); + SiS_SetReg(SISCR, 0xcf, v4); - outSISIDXREG(SISCR, 0x83, 0x09); - outSISIDXREG(SISCR, 0x87, 0x00); + SiS_SetReg(SISCR, 0x83, 0x09); + SiS_SetReg(SISCR, 0x87, 0x00); if(ivideo->chip == XGI_40) { if( (ivideo->revision_id == 1) || (ivideo->revision_id == 2) ) { - outSISIDXREG(SISCR, 0x8c, 0x87); + SiS_SetReg(SISCR, 0x8c, 0x87); } } - outSISIDXREG(SISSR, 0x17, 0x00); - outSISIDXREG(SISSR, 0x1a, 0x87); + SiS_SetReg(SISSR, 0x17, 0x00); + SiS_SetReg(SISSR, 0x1a, 0x87); if(ivideo->chip == XGI_20) { - outSISIDXREG(SISSR, 0x15, 0x00); - outSISIDXREG(SISSR, 0x1c, 0x00); + SiS_SetReg(SISSR, 0x15, 0x00); + SiS_SetReg(SISSR, 0x1c, 0x00); } ramtype = 0x00; v1 = 0x10; @@ -5380,16 +5382,16 @@ sisfb_post_xgi(struct pci_dev *pdev) } if(!(ramtype & 0x80)) { if(ivideo->chip == XGI_20) { - outSISIDXREG(SISCR, 0x97, v1); - inSISIDXREG(SISCR, 0x97, reg); + SiS_SetReg(SISCR, 0x97, v1); + reg = SiS_GetReg(SISCR, 0x97); if(reg & 0x10) { ramtype = (reg & 0x01) << 1; } } else { - inSISIDXREG(SISSR, 0x39, reg); + reg = SiS_GetReg(SISSR, 0x39); ramtype = reg & 0x02; if(!(ramtype)) { - inSISIDXREG(SISSR, 0x3a, reg); + reg = SiS_GetReg(SISSR, 0x3a); ramtype = (reg >> 1) & 0x01; } } @@ -5410,55 +5412,55 @@ sisfb_post_xgi(struct pci_dev *pdev) v2 = bios[regb + 0x160]; v3 = bios[regb + 0x168]; } - outSISIDXREG(SISCR, 0x82, v1); - outSISIDXREG(SISCR, 0x85, v2); - outSISIDXREG(SISCR, 0x86, v3); + SiS_SetReg(SISCR, 0x82, v1); + SiS_SetReg(SISCR, 0x85, v2); + SiS_SetReg(SISCR, 0x86, v3); } else { - outSISIDXREG(SISCR, 0x82, 0x88); - outSISIDXREG(SISCR, 0x86, 0x00); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, 0x88); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]); - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x85, 0x00); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, 0x88); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]); - outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]); + SiS_SetReg(SISCR, 0x82, 0x88); + SiS_SetReg(SISCR, 0x86, 0x00); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, 0x88); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, bios[regb + 0x168]); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x85, 0x00); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, 0x88); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, bios[regb + 0x160]); + SiS_SetReg(SISCR, 0x82, bios[regb + 0x158]); } if(ivideo->chip == XGI_40) { - outSISIDXREG(SISCR, 0x97, 0x00); + SiS_SetReg(SISCR, 0x97, 0x00); } - outSISIDXREG(SISCR, 0x98, 0x01); - outSISIDXREG(SISCR, 0x9a, 0x02); + SiS_SetReg(SISCR, 0x98, 0x01); + SiS_SetReg(SISCR, 0x9a, 0x02); - outSISIDXREG(SISSR, 0x18, 0x01); + SiS_SetReg(SISSR, 0x18, 0x01); if((ivideo->chip == XGI_20) || (ivideo->revision_id == 2)) { - outSISIDXREG(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x19, 0x40); } else { - outSISIDXREG(SISSR, 0x19, 0x20); + SiS_SetReg(SISSR, 0x19, 0x20); } - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); if((ivideo->chip == XGI_20) || (bios[0x1cb] != 0x0c)) { sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); - outSISIDXREG(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x18, 0x00); if((ivideo->chip == XGI_20) || (ivideo->revision_id == 2)) { - outSISIDXREG(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x19, 0x40); } else { - outSISIDXREG(SISSR, 0x19, 0x20); + SiS_SetReg(SISSR, 0x19, 0x20); } } else if((ivideo->chip == XGI_40) && (bios[0x1cb] == 0x0c)) { - /* outSISIDXREG(SISSR, 0x16, 0x0c); */ /* ? */ + /* SiS_SetReg(SISSR, 0x16, 0x0c); */ /* ? */ } - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); sisfb_post_xgi_delay(ivideo, 4); v1 = 0x31; v2 = 0x03; v3 = 0x83; v4 = 0x03; v5 = 0x83; if(ivideo->haveXGIROM) { @@ -5469,74 +5471,74 @@ sisfb_post_xgi(struct pci_dev *pdev) v4 = bios[index + 2]; v5 = bios[index + 3]; } - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, ((ivideo->chip == XGI_20) ? 0x02 : 0x01)); - outSISIDXREG(SISSR, 0x16, v2); - outSISIDXREG(SISSR, 0x16, v3); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, ((ivideo->chip == XGI_20) ? 0x02 : 0x01)); + SiS_SetReg(SISSR, 0x16, v2); + SiS_SetReg(SISSR, 0x16, v3); sisfb_post_xgi_delay(ivideo, 0x43); - outSISIDXREG(SISSR, 0x1b, 0x03); + SiS_SetReg(SISSR, 0x1b, 0x03); sisfb_post_xgi_delay(ivideo, 0x22); - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x00); - outSISIDXREG(SISSR, 0x16, v4); - outSISIDXREG(SISSR, 0x16, v5); - outSISIDXREG(SISSR, 0x1b, 0x00); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x00); + SiS_SetReg(SISSR, 0x16, v4); + SiS_SetReg(SISSR, 0x16, v5); + SiS_SetReg(SISSR, 0x1b, 0x00); break; case 1: - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x86, 0x00); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, 0x88); - inSISIDXREG(SISCR, 0x86, reg); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x86, 0x00); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, 0x88); + reg = SiS_GetReg(SISCR, 0x86); v1 = cs168[regb]; v2 = cs160[regb]; v3 = cs158[regb]; if(ivideo->haveXGIROM) { v1 = bios[regb + 0x168]; v2 = bios[regb + 0x160]; v3 = bios[regb + 0x158]; } - outSISIDXREG(SISCR, 0x86, v1); - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x85, 0x00); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, 0x88); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, v2); - outSISIDXREG(SISCR, 0x82, v3); - outSISIDXREG(SISCR, 0x98, 0x01); - outSISIDXREG(SISCR, 0x9a, 0x02); - - outSISIDXREG(SISSR, 0x28, 0x64); - outSISIDXREG(SISSR, 0x29, 0x63); + SiS_SetReg(SISCR, 0x86, v1); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x85, 0x00); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, 0x88); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, v2); + SiS_SetReg(SISCR, 0x82, v3); + SiS_SetReg(SISCR, 0x98, 0x01); + SiS_SetReg(SISCR, 0x9a, 0x02); + + SiS_SetReg(SISSR, 0x28, 0x64); + SiS_SetReg(SISSR, 0x29, 0x63); sisfb_post_xgi_delay(ivideo, 15); - outSISIDXREG(SISSR, 0x18, 0x00); - outSISIDXREG(SISSR, 0x19, 0x20); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); - outSISIDXREG(SISSR, 0x18, 0xc5); - outSISIDXREG(SISSR, 0x19, 0x23); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x19, 0x20); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0xc5); + SiS_SetReg(SISSR, 0x19, 0x23); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); sisfb_post_xgi_delay(ivideo, 1); - outSISIDXREG(SISCR, 0x97,0x11); + SiS_SetReg(SISCR, 0x97, 0x11); sisfb_post_xgi_setclocks(ivideo, regb); sisfb_post_xgi_delay(ivideo, 0x46); - outSISIDXREG(SISSR, 0x18, 0xc5); - outSISIDXREG(SISSR, 0x19, 0x23); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0xc5); + SiS_SetReg(SISSR, 0x19, 0x23); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); sisfb_post_xgi_delay(ivideo, 1); - outSISIDXREG(SISSR, 0x1b, 0x04); + SiS_SetReg(SISSR, 0x1b, 0x04); sisfb_post_xgi_delay(ivideo, 1); - outSISIDXREG(SISSR, 0x1b, 0x00); + SiS_SetReg(SISSR, 0x1b, 0x00); sisfb_post_xgi_delay(ivideo, 1); v1 = 0x31; if(ivideo->haveXGIROM) { v1 = bios[0xf0]; } - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x06); - outSISIDXREG(SISSR, 0x16, 0x04); - outSISIDXREG(SISSR, 0x16, 0x84); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x06); + SiS_SetReg(SISSR, 0x16, 0x04); + SiS_SetReg(SISSR, 0x16, 0x84); sisfb_post_xgi_delay(ivideo, 1); break; default: @@ -5544,85 +5546,85 @@ sisfb_post_xgi(struct pci_dev *pdev) if((ivideo->chip == XGI_40) && ((ivideo->revision_id == 1) || (ivideo->revision_id == 2))) { - outSISIDXREG(SISCR, 0x82, bios[regb + 0x158]); - outSISIDXREG(SISCR, 0x85, bios[regb + 0x160]); - outSISIDXREG(SISCR, 0x86, bios[regb + 0x168]); + SiS_SetReg(SISCR, 0x82, bios[regb + 0x158]); + SiS_SetReg(SISCR, 0x85, bios[regb + 0x160]); + SiS_SetReg(SISCR, 0x86, bios[regb + 0x168]); } else { - outSISIDXREG(SISCR, 0x82, 0x88); - outSISIDXREG(SISCR, 0x86, 0x00); - inSISIDXREG(SISCR, 0x86, reg); - outSISIDXREG(SISCR, 0x86, 0x88); - outSISIDXREG(SISCR, 0x82, 0x77); - outSISIDXREG(SISCR, 0x85, 0x00); - inSISIDXREG(SISCR, 0x85, reg); - outSISIDXREG(SISCR, 0x85, 0x88); - inSISIDXREG(SISCR, 0x85, reg); + SiS_SetReg(SISCR, 0x82, 0x88); + SiS_SetReg(SISCR, 0x86, 0x00); + reg = SiS_GetReg(SISCR, 0x86); + SiS_SetReg(SISCR, 0x86, 0x88); + SiS_SetReg(SISCR, 0x82, 0x77); + SiS_SetReg(SISCR, 0x85, 0x00); + reg = SiS_GetReg(SISCR, 0x85); + SiS_SetReg(SISCR, 0x85, 0x88); + reg = SiS_GetReg(SISCR, 0x85); v1 = cs160[regb]; v2 = cs158[regb]; if(ivideo->haveXGIROM) { v1 = bios[regb + 0x160]; v2 = bios[regb + 0x158]; } - outSISIDXREG(SISCR, 0x85, v1); - outSISIDXREG(SISCR, 0x82, v2); + SiS_SetReg(SISCR, 0x85, v1); + SiS_SetReg(SISCR, 0x82, v2); } if(ivideo->chip == XGI_40) { - outSISIDXREG(SISCR, 0x97, 0x11); + SiS_SetReg(SISCR, 0x97, 0x11); } if((ivideo->chip == XGI_40) && (ivideo->revision_id == 2)) { - outSISIDXREG(SISCR, 0x98, 0x01); + SiS_SetReg(SISCR, 0x98, 0x01); } else { - outSISIDXREG(SISCR, 0x98, 0x03); + SiS_SetReg(SISCR, 0x98, 0x03); } - outSISIDXREG(SISCR, 0x9a, 0x02); + SiS_SetReg(SISCR, 0x9a, 0x02); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x18, 0x01); + SiS_SetReg(SISSR, 0x18, 0x01); } else { - outSISIDXREG(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x18, 0x00); } - outSISIDXREG(SISSR, 0x19, 0x40); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); if((ivideo->chip == XGI_40) && (bios[0x1cb] != 0x0c)) { sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); sisfb_post_xgi_delay(ivideo, 0x43); - outSISIDXREG(SISSR, 0x18, 0x00); - outSISIDXREG(SISSR, 0x19, 0x40); - outSISIDXREG(SISSR, 0x16, 0x00); - outSISIDXREG(SISSR, 0x16, 0x80); + SiS_SetReg(SISSR, 0x18, 0x00); + SiS_SetReg(SISSR, 0x19, 0x40); + SiS_SetReg(SISSR, 0x16, 0x00); + SiS_SetReg(SISSR, 0x16, 0x80); } sisfb_post_xgi_delay(ivideo, 4); v1 = 0x31; if(ivideo->haveXGIROM) { v1 = bios[0xf0]; } - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x01); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x01); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x16, bios[0x53e]); - outSISIDXREG(SISSR, 0x16, bios[0x53f]); + SiS_SetReg(SISSR, 0x16, bios[0x53e]); + SiS_SetReg(SISSR, 0x16, bios[0x53f]); } else { - outSISIDXREG(SISSR, 0x16, 0x05); - outSISIDXREG(SISSR, 0x16, 0x85); + SiS_SetReg(SISSR, 0x16, 0x05); + SiS_SetReg(SISSR, 0x16, 0x85); } sisfb_post_xgi_delay(ivideo, 0x43); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x1b, 0x01); + SiS_SetReg(SISSR, 0x1b, 0x01); } else { - outSISIDXREG(SISSR, 0x1b, 0x03); + SiS_SetReg(SISSR, 0x1b, 0x03); } sisfb_post_xgi_delay(ivideo, 0x22); - outSISIDXREG(SISSR, 0x18, v1); - outSISIDXREG(SISSR, 0x19, 0x00); + SiS_SetReg(SISSR, 0x18, v1); + SiS_SetReg(SISSR, 0x19, 0x00); if(ivideo->chip == XGI_40) { - outSISIDXREG(SISSR, 0x16, bios[0x540]); - outSISIDXREG(SISSR, 0x16, bios[0x541]); + SiS_SetReg(SISSR, 0x16, bios[0x540]); + SiS_SetReg(SISSR, 0x16, bios[0x541]); } else { - outSISIDXREG(SISSR, 0x16, 0x05); - outSISIDXREG(SISSR, 0x16, 0x85); + SiS_SetReg(SISSR, 0x16, 0x05); + SiS_SetReg(SISSR, 0x16, 0x85); } - outSISIDXREG(SISSR, 0x1b, 0x00); + SiS_SetReg(SISSR, 0x1b, 0x00); } regb = 0; /* ! */ @@ -5630,7 +5632,7 @@ sisfb_post_xgi(struct pci_dev *pdev) if(ivideo->haveXGIROM) { v1 = bios[0x110 + regb]; } - outSISIDXREG(SISSR, 0x1b, v1); + SiS_SetReg(SISSR, 0x1b, v1); /* RAM size */ v1 = 0x00; v2 = 0x00; @@ -5642,8 +5644,8 @@ sisfb_post_xgi(struct pci_dev *pdev) regd = 1 << regb; if((v1 & 0x40) && (v2 & regd) && ivideo->haveXGIROM) { - outSISIDXREG(SISSR, 0x13, bios[regb + 0xe0]); - outSISIDXREG(SISSR, 0x14, bios[regb + 0xe0 + 8]); + SiS_SetReg(SISSR, 0x13, bios[regb + 0xe0]); + SiS_SetReg(SISSR, 0x14, bios[regb + 0xe0 + 8]); } else { @@ -5655,24 +5657,24 @@ sisfb_post_xgi(struct pci_dev *pdev) ivideo->SiS_Pr.VideoMemorySize = 8 << 20; SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); /* Disable read-cache */ - andSISIDXREG(SISSR, 0x21, 0xdf); + SiS_SetRegAND(SISSR, 0x21, 0xdf); sisfb_post_xgi_ramsize(ivideo); /* Enable read-cache */ - orSISIDXREG(SISSR, 0x21, 0x20); + SiS_SetRegOR(SISSR, 0x21, 0x20); } #if 0 printk(KERN_DEBUG "-----------------\n"); for(i = 0; i < 0xff; i++) { - inSISIDXREG(SISCR, i, reg); + reg = SiS_GetReg(SISCR, i); printk(KERN_DEBUG "CR%02x(%x) = 0x%02x\n", i, SISCR, reg); } for(i = 0; i < 0x40; i++) { - inSISIDXREG(SISSR, i, reg); + reg = SiS_GetReg(SISSR, i); printk(KERN_DEBUG "SR%02x(%x) = 0x%02x\n", i, SISSR, reg); } printk(KERN_DEBUG "-----------------\n"); @@ -5680,13 +5682,13 @@ sisfb_post_xgi(struct pci_dev *pdev) /* Sense CRT1 */ if(ivideo->chip == XGI_20) { - orSISIDXREG(SISCR, 0x32, 0x20); + SiS_SetRegOR(SISCR, 0x32, 0x20); } else { - inSISIDXREG(SISPART4, 0x00, reg); + reg = SiS_GetReg(SISPART4, 0x00); if((reg == 1) || (reg == 2)) { sisfb_sense_crt1(ivideo); } else { - orSISIDXREG(SISCR, 0x32, 0x20); + SiS_SetRegOR(SISCR, 0x32, 0x20); } } @@ -5697,20 +5699,20 @@ sisfb_post_xgi(struct pci_dev *pdev) ivideo->curFSTN = ivideo->curDSTN = 0; SiSSetMode(&ivideo->SiS_Pr, 0x2e | 0x80); - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); /* Display off */ - orSISIDXREG(SISSR, 0x01, 0x20); + SiS_SetRegOR(SISSR, 0x01, 0x20); /* Save mode number in CR34 */ - outSISIDXREG(SISCR, 0x34, 0x2e); + SiS_SetReg(SISCR, 0x34, 0x2e); /* Let everyone know what the current mode is */ ivideo->modeprechange = 0x2e; if(ivideo->chip == XGI_40) { - inSISIDXREG(SISCR, 0xca, reg); - inSISIDXREG(SISCR, 0xcc, v1); + reg = SiS_GetReg(SISCR, 0xca); + v1 = SiS_GetReg(SISCR, 0xcc); if((reg & 0x10) && (!(v1 & 0x04))) { printk(KERN_ERR "sisfb: Please connect power to the card.\n"); @@ -5953,7 +5955,7 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } #endif - outSISIDXREG(SISSR, 0x05, 0x86); + SiS_SetReg(SISSR, 0x05, 0x86); if( (!ivideo->sisvga_enabled) #if !defined(__i386__) && !defined(__x86_64__) @@ -5961,13 +5963,13 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #endif ) { for(i = 0x30; i <= 0x3f; i++) { - outSISIDXREG(SISCR, i, 0x00); + SiS_SetReg(SISCR, i, 0x00); } } /* Find out about current video mode */ ivideo->modeprechange = 0x03; - inSISIDXREG(SISCR, 0x34, reg); + reg = SiS_GetReg(SISCR, 0x34); if(reg & 0x7f) { ivideo->modeprechange = reg & 0x7f; } else if(ivideo->sisvga_enabled) { @@ -6064,9 +6066,9 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if((ivideo->sisfb_mode_idx < 0) || ((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni]) != 0xFF)) { /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE */ - orSISIDXREG(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE)); + SiS_SetRegOR(SISSR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE)); /* Enable 2D accelerator engine */ - orSISIDXREG(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D); + SiS_SetRegOR(SISSR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D); } if(sisfb_pdc != 0xff) { diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c new file mode 100644 index 000000000000..020589a6bf02 --- /dev/null +++ b/drivers/video/udlfb.c @@ -0,0 +1,1879 @@ +/* + * udlfb.c -- Framebuffer driver for DisplayLink USB controller + * + * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> + * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> + * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + * + * Layout is based on skeletonfb by James Simmons and Geert Uytterhoeven, + * usb-skeleton by GregKH. + * + * Device-specific portions based on information from Displaylink, with work + * from Florian Echtler, Henrik Bjerregaard Pedersen, and others. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/uaccess.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <video/udlfb.h> +#include "edid.h" + +static struct fb_fix_screeninfo dlfb_fix = { + .id = "udlfb", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static const u32 udlfb_info_flags = FBINFO_DEFAULT | FBINFO_READS_FAST | + FBINFO_VIRTFB | + FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_COPYAREA | FBINFO_MISC_ALWAYS_SETPAR; + +/* + * There are many DisplayLink-based products, all with unique PIDs. We are able + * to support all volume ones (circa 2009) with a single driver, so we match + * globally on VID. TODO: Probe() needs to detect when we might be running + * "future" chips, and bail on those, so a compatible driver can match. + */ +static struct usb_device_id id_table[] = { + {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,}, + {}, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* module options */ +static int console; /* Optionally allow fbcon to consume first framebuffer */ +static int fb_defio; /* Optionally enable experimental fb_defio mmap support */ + +/* dlfb keeps a list of urbs for efficient bulk transfers */ +static void dlfb_urb_completion(struct urb *urb); +static struct urb *dlfb_get_urb(struct dlfb_data *dev); +static int dlfb_submit_urb(struct dlfb_data *dev, struct urb * urb, size_t len); +static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size); +static void dlfb_free_urb_list(struct dlfb_data *dev); + +/* + * All DisplayLink bulk operations start with 0xAF, followed by specific code + * All operations are written to buffers which then later get sent to device + */ +static char *dlfb_set_register(char *buf, u8 reg, u8 val) +{ + *buf++ = 0xAF; + *buf++ = 0x20; + *buf++ = reg; + *buf++ = val; + return buf; +} + +static char *dlfb_vidreg_lock(char *buf) +{ + return dlfb_set_register(buf, 0xFF, 0x00); +} + +static char *dlfb_vidreg_unlock(char *buf) +{ + return dlfb_set_register(buf, 0xFF, 0xFF); +} + +/* + * On/Off for driving the DisplayLink framebuffer to the display + * 0x00 H and V sync on + * 0x01 H and V sync off (screen blank but powered) + * 0x07 DPMS powerdown (requires modeset to come back) + */ +static char *dlfb_enable_hvsync(char *buf, bool enable) +{ + if (enable) + return dlfb_set_register(buf, 0x1F, 0x00); + else + return dlfb_set_register(buf, 0x1F, 0x07); +} + +static char *dlfb_set_color_depth(char *buf, u8 selection) +{ + return dlfb_set_register(buf, 0x00, selection); +} + +static char *dlfb_set_base16bpp(char *wrptr, u32 base) +{ + /* the base pointer is 16 bits wide, 0x20 is hi byte. */ + wrptr = dlfb_set_register(wrptr, 0x20, base >> 16); + wrptr = dlfb_set_register(wrptr, 0x21, base >> 8); + return dlfb_set_register(wrptr, 0x22, base); +} + +/* + * DisplayLink HW has separate 16bpp and 8bpp framebuffers. + * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer + */ +static char *dlfb_set_base8bpp(char *wrptr, u32 base) +{ + wrptr = dlfb_set_register(wrptr, 0x26, base >> 16); + wrptr = dlfb_set_register(wrptr, 0x27, base >> 8); + return dlfb_set_register(wrptr, 0x28, base); +} + +static char *dlfb_set_register_16(char *wrptr, u8 reg, u16 value) +{ + wrptr = dlfb_set_register(wrptr, reg, value >> 8); + return dlfb_set_register(wrptr, reg+1, value); +} + +/* + * This is kind of weird because the controller takes some + * register values in a different byte order than other registers. + */ +static char *dlfb_set_register_16be(char *wrptr, u8 reg, u16 value) +{ + wrptr = dlfb_set_register(wrptr, reg, value); + return dlfb_set_register(wrptr, reg+1, value >> 8); +} + +/* + * LFSR is linear feedback shift register. The reason we have this is + * because the display controller needs to minimize the clock depth of + * various counters used in the display path. So this code reverses the + * provided value into the lfsr16 value by counting backwards to get + * the value that needs to be set in the hardware comparator to get the + * same actual count. This makes sense once you read above a couple of + * times and think about it from a hardware perspective. + */ +static u16 dlfb_lfsr16(u16 actual_count) +{ + u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */ + + while (actual_count--) { + lv = ((lv << 1) | + (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1)) + & 0xFFFF; + } + + return (u16) lv; +} + +/* + * This does LFSR conversion on the value that is to be written. + * See LFSR explanation above for more detail. + */ +static char *dlfb_set_register_lfsr16(char *wrptr, u8 reg, u16 value) +{ + return dlfb_set_register_16(wrptr, reg, dlfb_lfsr16(value)); +} + +/* + * This takes a standard fbdev screeninfo struct and all of its monitor mode + * details and converts them into the DisplayLink equivalent register commands. + */ +static char *dlfb_set_vid_cmds(char *wrptr, struct fb_var_screeninfo *var) +{ + u16 xds, yds; + u16 xde, yde; + u16 yec; + + /* x display start */ + xds = var->left_margin + var->hsync_len; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x01, xds); + /* x display end */ + xde = xds + var->xres; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x03, xde); + + /* y display start */ + yds = var->upper_margin + var->vsync_len; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x05, yds); + /* y display end */ + yde = yds + var->yres; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x07, yde); + + /* x end count is active + blanking - 1 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x09, + xde + var->right_margin - 1); + + /* libdlo hardcodes hsync start to 1 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x0B, 1); + + /* hsync end is width of sync pulse + 1 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x0D, var->hsync_len + 1); + + /* hpixels is active pixels */ + wrptr = dlfb_set_register_16(wrptr, 0x0F, var->xres); + + /* yendcount is vertical active + vertical blanking */ + yec = var->yres + var->upper_margin + var->lower_margin + + var->vsync_len; + wrptr = dlfb_set_register_lfsr16(wrptr, 0x11, yec); + + /* libdlo hardcodes vsync start to 0 */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x13, 0); + + /* vsync end is width of vsync pulse */ + wrptr = dlfb_set_register_lfsr16(wrptr, 0x15, var->vsync_len); + + /* vpixels is active pixels */ + wrptr = dlfb_set_register_16(wrptr, 0x17, var->yres); + + /* convert picoseconds to 5kHz multiple for pclk5k = x * 1E12/5k */ + wrptr = dlfb_set_register_16be(wrptr, 0x1B, + 200*1000*1000/var->pixclock); + + return wrptr; +} + +/* + * This takes a standard fbdev screeninfo struct that was fetched or prepared + * and then generates the appropriate command sequence that then drives the + * display controller. + */ +static int dlfb_set_video_mode(struct dlfb_data *dev, + struct fb_var_screeninfo *var) +{ + char *buf; + char *wrptr; + int retval = 0; + int writesize; + struct urb *urb; + + if (!atomic_read(&dev->usb_active)) + return -EPERM; + + urb = dlfb_get_urb(dev); + if (!urb) + return -ENOMEM; + + buf = (char *) urb->transfer_buffer; + + /* + * This first section has to do with setting the base address on the + * controller * associated with the display. There are 2 base + * pointers, currently, we only * use the 16 bpp segment. + */ + wrptr = dlfb_vidreg_lock(buf); + wrptr = dlfb_set_color_depth(wrptr, 0x00); + /* set base for 16bpp segment to 0 */ + wrptr = dlfb_set_base16bpp(wrptr, 0); + /* set base for 8bpp segment to end of fb */ + wrptr = dlfb_set_base8bpp(wrptr, dev->info->fix.smem_len); + + wrptr = dlfb_set_vid_cmds(wrptr, var); + wrptr = dlfb_enable_hvsync(wrptr, true); + wrptr = dlfb_vidreg_unlock(wrptr); + + writesize = wrptr - buf; + + retval = dlfb_submit_urb(dev, urb, writesize); + + return retval; +} + +static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long page, pos; + + if (offset + size > info->fix.smem_len) + return -EINVAL; + + pos = (unsigned long)info->fix.smem_start + offset; + + pr_notice("mmap() framebuffer addr:%lu size:%lu\n", + pos, size); + + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + return 0; +} + +/* + * Trims identical data from front and back of line + * Sets new front buffer address and width + * And returns byte count of identical pixels + * Assumes CPU natural alignment (unsigned long) + * for back and front buffer ptrs and width + */ +static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) +{ + int j, k; + const unsigned long *back = (const unsigned long *) bback; + const unsigned long *front = (const unsigned long *) *bfront; + const int width = *width_bytes / sizeof(unsigned long); + int identical = width; + int start = width; + int end = width; + + prefetch((void *) front); + prefetch((void *) back); + + for (j = 0; j < width; j++) { + if (back[j] != front[j]) { + start = j; + break; + } + } + + for (k = width - 1; k > j; k--) { + if (back[k] != front[k]) { + end = k+1; + break; + } + } + + identical = start + (width - end); + *bfront = (u8 *) &front[start]; + *width_bytes = (end - start) * sizeof(unsigned long); + + return identical * sizeof(unsigned long); +} + +/* + * Render a command stream for an encoded horizontal line segment of pixels. + * + * A command buffer holds several commands. + * It always begins with a fresh command header + * (the protocol doesn't require this, but we enforce it to allow + * multiple buffers to be potentially encoded and sent in parallel). + * A single command encodes one contiguous horizontal line of pixels + * + * The function relies on the client to do all allocation, so that + * rendering can be done directly to output buffers (e.g. USB URBs). + * The function fills the supplied command buffer, providing information + * on where it left off, so the client may call in again with additional + * buffers if the line will take several buffers to complete. + * + * A single command can transmit a maximum of 256 pixels, + * regardless of the compression ratio (protocol design limit). + * To the hardware, 0 for a size byte means 256 + * + * Rather than 256 pixel commands which are either rl or raw encoded, + * the rlx command simply assumes alternating raw and rl spans within one cmd. + * This has a slightly larger header overhead, but produces more even results. + * It also processes all data (read and write) in a single pass. + * Performance benchmarks of common cases show it having just slightly better + * compression than 256 pixel raw or rle commands, with similar CPU consumpion. + * But for very rl friendly data, will compress not quite as well. + */ +static void dlfb_compress_hline( + const uint16_t **pixel_start_ptr, + const uint16_t *const pixel_end, + uint32_t *device_address_ptr, + uint8_t **command_buffer_ptr, + const uint8_t *const cmd_buffer_end) +{ + const uint16_t *pixel = *pixel_start_ptr; + uint32_t dev_addr = *device_address_ptr; + uint8_t *cmd = *command_buffer_ptr; + const int bpp = 2; + + while ((pixel_end > pixel) && + (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) { + uint8_t *raw_pixels_count_byte = 0; + uint8_t *cmd_pixels_count_byte = 0; + const uint16_t *raw_pixel_start = 0; + const uint16_t *cmd_pixel_start, *cmd_pixel_end = 0; + + prefetchw((void *) cmd); /* pull in one cache line at least */ + + *cmd++ = 0xAF; + *cmd++ = 0x6B; + *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF); + *cmd++ = (uint8_t) ((dev_addr) & 0xFF); + + cmd_pixels_count_byte = cmd++; /* we'll know this later */ + cmd_pixel_start = pixel; + + raw_pixels_count_byte = cmd++; /* we'll know this later */ + raw_pixel_start = pixel; + + cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1, + min((int)(pixel_end - pixel), + (int)(cmd_buffer_end - cmd) / bpp)); + + prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp); + + while (pixel < cmd_pixel_end) { + const uint16_t * const repeating_pixel = pixel; + + *(uint16_t *)cmd = cpu_to_be16p(pixel); + cmd += 2; + pixel++; + + if (unlikely((pixel < cmd_pixel_end) && + (*pixel == *repeating_pixel))) { + /* go back and fill in raw pixel count */ + *raw_pixels_count_byte = ((repeating_pixel - + raw_pixel_start) + 1) & 0xFF; + + while ((pixel < cmd_pixel_end) + && (*pixel == *repeating_pixel)) { + pixel++; + } + + /* immediately after raw data is repeat byte */ + *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF; + + /* Then start another raw pixel span */ + raw_pixel_start = pixel; + raw_pixels_count_byte = cmd++; + } + } + + if (pixel > raw_pixel_start) { + /* finalize last RAW span */ + *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF; + } + + *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF; + dev_addr += (pixel - cmd_pixel_start) * bpp; + } + + if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) { + /* Fill leftover bytes with no-ops */ + if (cmd_buffer_end > cmd) + memset(cmd, 0xAF, cmd_buffer_end - cmd); + cmd = (uint8_t *) cmd_buffer_end; + } + + *command_buffer_ptr = cmd; + *pixel_start_ptr = pixel; + *device_address_ptr = dev_addr; + + return; +} + +/* + * There are 3 copies of every pixel: The front buffer that the fbdev + * client renders to, the actual framebuffer across the USB bus in hardware + * (that we can only write to, slowly, and can never read), and (optionally) + * our shadow copy that tracks what's been sent to that hardware buffer. + */ +static int dlfb_render_hline(struct dlfb_data *dev, struct urb **urb_ptr, + const char *front, char **urb_buf_ptr, + u32 byte_offset, u32 byte_width, + int *ident_ptr, int *sent_ptr) +{ + const u8 *line_start, *line_end, *next_pixel; + u32 dev_addr = dev->base16 + byte_offset; + struct urb *urb = *urb_ptr; + u8 *cmd = *urb_buf_ptr; + u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; + + line_start = (u8 *) (front + byte_offset); + next_pixel = line_start; + line_end = next_pixel + byte_width; + + if (dev->backing_buffer) { + int offset; + const u8 *back_start = (u8 *) (dev->backing_buffer + + byte_offset); + + *ident_ptr += dlfb_trim_hline(back_start, &next_pixel, + &byte_width); + + offset = next_pixel - line_start; + line_end = next_pixel + byte_width; + dev_addr += offset; + back_start += offset; + line_start += offset; + + memcpy((char *)back_start, (char *) line_start, + byte_width); + } + + while (next_pixel < line_end) { + + dlfb_compress_hline((const uint16_t **) &next_pixel, + (const uint16_t *) line_end, &dev_addr, + (u8 **) &cmd, (u8 *) cmd_end); + + if (cmd >= cmd_end) { + int len = cmd - (u8 *) urb->transfer_buffer; + if (dlfb_submit_urb(dev, urb, len)) + return 1; /* lost pixels is set */ + *sent_ptr += len; + urb = dlfb_get_urb(dev); + if (!urb) + return 1; /* lost_pixels is set */ + *urb_ptr = urb; + cmd = urb->transfer_buffer; + cmd_end = &cmd[urb->transfer_buffer_length]; + } + } + + *urb_buf_ptr = cmd; + + return 0; +} + +int dlfb_handle_damage(struct dlfb_data *dev, int x, int y, + int width, int height, char *data) +{ + int i, ret; + char *cmd; + cycles_t start_cycles, end_cycles; + int bytes_sent = 0; + int bytes_identical = 0; + struct urb *urb; + int aligned_x; + + start_cycles = get_cycles(); + + aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); + width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); + x = aligned_x; + + if ((width <= 0) || + (x + width > dev->info->var.xres) || + (y + height > dev->info->var.yres)) + return -EINVAL; + + if (!atomic_read(&dev->usb_active)) + return 0; + + urb = dlfb_get_urb(dev); + if (!urb) + return 0; + cmd = urb->transfer_buffer; + + for (i = y; i < y + height ; i++) { + const int line_offset = dev->info->fix.line_length * i; + const int byte_offset = line_offset + (x * BPP); + + if (dlfb_render_hline(dev, &urb, + (char *) dev->info->fix.smem_start, + &cmd, byte_offset, width * BPP, + &bytes_identical, &bytes_sent)) + goto error; + } + + if (cmd > (char *) urb->transfer_buffer) { + /* Send partial buffer remaining before exiting */ + int len = cmd - (char *) urb->transfer_buffer; + ret = dlfb_submit_urb(dev, urb, len); + bytes_sent += len; + } else + dlfb_urb_completion(urb); + +error: + atomic_add(bytes_sent, &dev->bytes_sent); + atomic_add(bytes_identical, &dev->bytes_identical); + atomic_add(width*height*2, &dev->bytes_rendered); + end_cycles = get_cycles(); + atomic_add(((unsigned int) ((end_cycles - start_cycles) + >> 10)), /* Kcycles */ + &dev->cpu_kcycles_used); + + return 0; +} + +/* + * Path triggered by usermode clients who write to filesystem + * e.g. cat filename > /dev/fb1 + * Not used by X Windows or text-mode console. But useful for testing. + * Slow because of extra copy and we must assume all pixels dirty. + */ +static ssize_t dlfb_ops_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t result; + struct dlfb_data *dev = info->par; + u32 offset = (u32) *ppos; + + result = fb_sys_write(info, buf, count, ppos); + + if (result > 0) { + int start = max((int)(offset / info->fix.line_length) - 1, 0); + int lines = min((u32)((result / info->fix.line_length) + 1), + (u32)info->var.yres); + + dlfb_handle_damage(dev, 0, start, info->var.xres, + lines, info->screen_base); + } + + return result; +} + +/* hardware has native COPY command (see libdlo), but not worth it for fbcon */ +static void dlfb_ops_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + + struct dlfb_data *dev = info->par; + + sys_copyarea(info, area); + + dlfb_handle_damage(dev, area->dx, area->dy, + area->width, area->height, info->screen_base); +} + +static void dlfb_ops_imageblit(struct fb_info *info, + const struct fb_image *image) +{ + struct dlfb_data *dev = info->par; + + sys_imageblit(info, image); + + dlfb_handle_damage(dev, image->dx, image->dy, + image->width, image->height, info->screen_base); +} + +static void dlfb_ops_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct dlfb_data *dev = info->par; + + sys_fillrect(info, rect); + + dlfb_handle_damage(dev, rect->dx, rect->dy, rect->width, + rect->height, info->screen_base); +} + +/* + * NOTE: fb_defio.c is holding info->fbdefio.mutex + * Touching ANY framebuffer memory that triggers a page fault + * in fb_defio will cause a deadlock, when it also tries to + * grab the same mutex. + */ +static void dlfb_dpy_deferred_io(struct fb_info *info, + struct list_head *pagelist) +{ + struct page *cur; + struct fb_deferred_io *fbdefio = info->fbdefio; + struct dlfb_data *dev = info->par; + struct urb *urb; + char *cmd; + cycles_t start_cycles, end_cycles; + int bytes_sent = 0; + int bytes_identical = 0; + int bytes_rendered = 0; + + if (!fb_defio) + return; + + if (!atomic_read(&dev->usb_active)) + return; + + start_cycles = get_cycles(); + + urb = dlfb_get_urb(dev); + if (!urb) + return; + + cmd = urb->transfer_buffer; + + /* walk the written page list and render each to device */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + + if (dlfb_render_hline(dev, &urb, (char *) info->fix.smem_start, + &cmd, cur->index << PAGE_SHIFT, + PAGE_SIZE, &bytes_identical, &bytes_sent)) + goto error; + bytes_rendered += PAGE_SIZE; + } + + if (cmd > (char *) urb->transfer_buffer) { + /* Send partial buffer remaining before exiting */ + int len = cmd - (char *) urb->transfer_buffer; + dlfb_submit_urb(dev, urb, len); + bytes_sent += len; + } else + dlfb_urb_completion(urb); + +error: + atomic_add(bytes_sent, &dev->bytes_sent); + atomic_add(bytes_identical, &dev->bytes_identical); + atomic_add(bytes_rendered, &dev->bytes_rendered); + end_cycles = get_cycles(); + atomic_add(((unsigned int) ((end_cycles - start_cycles) + >> 10)), /* Kcycles */ + &dev->cpu_kcycles_used); +} + +static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) +{ + int i; + int ret; + char *rbuf; + + rbuf = kmalloc(2, GFP_KERNEL); + if (!rbuf) + return 0; + + for (i = 0; i < len; i++) { + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), (0x02), + (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, + HZ); + if (ret < 1) { + pr_err("Read EDID byte %d failed err %x\n", i, ret); + i--; + break; + } + edid[i] = rbuf[1]; + } + + kfree(rbuf); + + return i; +} + +static int dlfb_ops_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + + struct dlfb_data *dev = info->par; + struct dloarea *area = NULL; + + if (!atomic_read(&dev->usb_active)) + return 0; + + /* TODO: Update X server to get this from sysfs instead */ + if (cmd == DLFB_IOCTL_RETURN_EDID) { + char *edid = (char *)arg; + if (copy_to_user(edid, dev->edid, dev->edid_size)) + return -EFAULT; + return 0; + } + + /* TODO: Help propose a standard fb.h ioctl to report mmap damage */ + if (cmd == DLFB_IOCTL_REPORT_DAMAGE) { + + /* + * If we have a damage-aware client, turn fb_defio "off" + * To avoid perf imact of unecessary page fault handling. + * Done by resetting the delay for this fb_info to a very + * long period. Pages will become writable and stay that way. + * Reset to normal value when all clients have closed this fb. + */ + if (info->fbdefio) + info->fbdefio->delay = DL_DEFIO_WRITE_DISABLE; + + area = (struct dloarea *)arg; + + if (area->x < 0) + area->x = 0; + + if (area->x > info->var.xres) + area->x = info->var.xres; + + if (area->y < 0) + area->y = 0; + + if (area->y > info->var.yres) + area->y = info->var.yres; + + dlfb_handle_damage(dev, area->x, area->y, area->w, area->h, + info->screen_base); + } + + return 0; +} + +/* taken from vesafb */ +static int +dlfb_ops_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, struct fb_info *info) +{ + int err = 0; + + if (regno >= info->cmap.len) + return 1; + + if (regno < 16) { + if (info->var.red.offset == 10) { + /* 1:5:5:5 */ + ((u32 *) (info->pseudo_palette))[regno] = + ((red & 0xf800) >> 1) | + ((green & 0xf800) >> 6) | ((blue & 0xf800) >> 11); + } else { + /* 0:5:6:5 */ + ((u32 *) (info->pseudo_palette))[regno] = + ((red & 0xf800)) | + ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); + } + } + + return err; +} + +/* + * It's common for several clients to have framebuffer open simultaneously. + * e.g. both fbcon and X. Makes things interesting. + * Assumes caller is holding info->lock (for open and release at least) + */ +static int dlfb_ops_open(struct fb_info *info, int user) +{ + struct dlfb_data *dev = info->par; + + /* + * fbcon aggressively connects to first framebuffer it finds, + * preventing other clients (X) from working properly. Usually + * not what the user wants. Fail by default with option to enable. + */ + if ((user == 0) & (!console)) + return -EBUSY; + + /* If the USB device is gone, we don't accept new opens */ + if (dev->virtualized) + return -ENODEV; + + dev->fb_count++; + + kref_get(&dev->kref); + + if (fb_defio && (info->fbdefio == NULL)) { + /* enable defio at last moment if not disabled by client */ + + struct fb_deferred_io *fbdefio; + + fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL); + + if (fbdefio) { + fbdefio->delay = DL_DEFIO_WRITE_DELAY; + fbdefio->deferred_io = dlfb_dpy_deferred_io; + } + + info->fbdefio = fbdefio; + fb_deferred_io_init(info); + } + + pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n", + info->node, user, info, dev->fb_count); + + return 0; +} + +/* + * Called when all client interfaces to start transactions have been disabled, + * and all references to our device instance (dlfb_data) are released. + * Every transaction must have a reference, so we know are fully spun down + */ +static void dlfb_free(struct kref *kref) +{ + struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref); + + /* this function will wait for all in-flight urbs to complete */ + if (dev->urbs.count > 0) + dlfb_free_urb_list(dev); + + if (dev->backing_buffer) + vfree(dev->backing_buffer); + + kfree(dev->edid); + + pr_warn("freeing dlfb_data %p\n", dev); + + kfree(dev); +} + +static void dlfb_release_urb_work(struct work_struct *work) +{ + struct urb_node *unode = container_of(work, struct urb_node, + release_urb_work.work); + + up(&unode->dev->urbs.limit_sem); +} + +static void dlfb_free_framebuffer_work(struct work_struct *work) +{ + struct dlfb_data *dev = container_of(work, struct dlfb_data, + free_framebuffer_work.work); + struct fb_info *info = dev->info; + int node = info->node; + + unregister_framebuffer(info); + + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + dev->info = 0; + + /* Assume info structure is freed after this point */ + framebuffer_release(info); + + pr_warn("fb_info for /dev/fb%d has been freed\n", node); + + /* ref taken in probe() as part of registering framebfufer */ + kref_put(&dev->kref, dlfb_free); +} + +/* + * Assumes caller is holding info->lock mutex (for open and release at least) + */ +static int dlfb_ops_release(struct fb_info *info, int user) +{ + struct dlfb_data *dev = info->par; + + dev->fb_count--; + + /* We can't free fb_info here - fbmem will touch it when we return */ + if (dev->virtualized && (dev->fb_count == 0)) + schedule_delayed_work(&dev->free_framebuffer_work, HZ); + + if ((dev->fb_count == 0) && (info->fbdefio)) { + fb_deferred_io_cleanup(info); + kfree(info->fbdefio); + info->fbdefio = NULL; + info->fbops->fb_mmap = dlfb_ops_mmap; + } + + pr_warn("released /dev/fb%d user=%d count=%d\n", + info->node, user, dev->fb_count); + + kref_put(&dev->kref, dlfb_free); + + return 0; +} + +/* + * Check whether a video mode is supported by the DisplayLink chip + * We start from monitor's modes, so don't need to filter that here + */ +static int dlfb_is_valid_mode(struct fb_videomode *mode, + struct fb_info *info) +{ + struct dlfb_data *dev = info->par; + + if (mode->xres * mode->yres > dev->sku_pixel_limit) { + pr_warn("%dx%d beyond chip capabilities\n", + mode->xres, mode->yres); + return 0; + } + + pr_info("%dx%d valid mode\n", mode->xres, mode->yres); + + return 1; +} + +static void dlfb_var_color_format(struct fb_var_screeninfo *var) +{ + const struct fb_bitfield red = { 11, 5, 0 }; + const struct fb_bitfield green = { 5, 6, 0 }; + const struct fb_bitfield blue = { 0, 5, 0 }; + + var->bits_per_pixel = 16; + var->red = red; + var->green = green; + var->blue = blue; +} + +static int dlfb_ops_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct fb_videomode mode; + + /* TODO: support dynamically changing framebuffer size */ + if ((var->xres * var->yres * 2) > info->fix.smem_len) + return -EINVAL; + + /* set device-specific elements of var unrelated to mode */ + dlfb_var_color_format(var); + + fb_var_to_videomode(&mode, var); + + if (!dlfb_is_valid_mode(&mode, info)) + return -EINVAL; + + return 0; +} + +static int dlfb_ops_set_par(struct fb_info *info) +{ + struct dlfb_data *dev = info->par; + int result; + u16 *pix_framebuffer; + int i; + + pr_notice("set_par mode %dx%d\n", info->var.xres, info->var.yres); + + result = dlfb_set_video_mode(dev, &info->var); + + if ((result == 0) && (dev->fb_count == 0)) { + + /* paint greenscreen */ + + pix_framebuffer = (u16 *) info->screen_base; + for (i = 0; i < info->fix.smem_len / 2; i++) + pix_framebuffer[i] = 0x37e6; + + dlfb_handle_damage(dev, 0, 0, info->var.xres, info->var.yres, + info->screen_base); + } + + return result; +} + +/* + * In order to come back from full DPMS off, we need to set the mode again + */ +static int dlfb_ops_blank(int blank_mode, struct fb_info *info) +{ + struct dlfb_data *dev = info->par; + + if (blank_mode != FB_BLANK_UNBLANK) { + char *bufptr; + struct urb *urb; + + urb = dlfb_get_urb(dev); + if (!urb) + return 0; + + bufptr = (char *) urb->transfer_buffer; + bufptr = dlfb_vidreg_lock(bufptr); + bufptr = dlfb_enable_hvsync(bufptr, false); + bufptr = dlfb_vidreg_unlock(bufptr); + + dlfb_submit_urb(dev, urb, bufptr - + (char *) urb->transfer_buffer); + } else { + dlfb_set_video_mode(dev, &info->var); + } + + return 0; +} + +static struct fb_ops dlfb_ops = { + .owner = THIS_MODULE, + .fb_read = fb_sys_read, + .fb_write = dlfb_ops_write, + .fb_setcolreg = dlfb_ops_setcolreg, + .fb_fillrect = dlfb_ops_fillrect, + .fb_copyarea = dlfb_ops_copyarea, + .fb_imageblit = dlfb_ops_imageblit, + .fb_mmap = dlfb_ops_mmap, + .fb_ioctl = dlfb_ops_ioctl, + .fb_open = dlfb_ops_open, + .fb_release = dlfb_ops_release, + .fb_blank = dlfb_ops_blank, + .fb_check_var = dlfb_ops_check_var, + .fb_set_par = dlfb_ops_set_par, +}; + + +/* + * Assumes &info->lock held by caller + * Assumes no active clients have framebuffer open + */ +static int dlfb_realloc_framebuffer(struct dlfb_data *dev, struct fb_info *info) +{ + int retval = -ENOMEM; + int old_len = info->fix.smem_len; + int new_len; + unsigned char *old_fb = info->screen_base; + unsigned char *new_fb; + unsigned char *new_back; + + pr_warn("Reallocating framebuffer. Addresses will change!\n"); + + new_len = info->fix.line_length * info->var.yres; + + if (PAGE_ALIGN(new_len) > old_len) { + /* + * Alloc system memory for virtual framebuffer + */ + new_fb = vmalloc(new_len); + if (!new_fb) { + pr_err("Virtual framebuffer alloc failed\n"); + goto error; + } + + if (info->screen_base) { + memcpy(new_fb, old_fb, old_len); + vfree(info->screen_base); + } + + info->screen_base = new_fb; + info->fix.smem_len = PAGE_ALIGN(new_len); + info->fix.smem_start = (unsigned long) new_fb; + info->flags = udlfb_info_flags; + + /* + * Second framebuffer copy to mirror the framebuffer state + * on the physical USB device. We can function without this. + * But with imperfect damage info we may send pixels over USB + * that were, in fact, unchanged - wasting limited USB bandwidth + */ + new_back = vmalloc(new_len); + if (!new_back) + pr_info("No shadow/backing buffer allcoated\n"); + else { + if (dev->backing_buffer) + vfree(dev->backing_buffer); + dev->backing_buffer = new_back; + memset(dev->backing_buffer, 0, new_len); + } + } + + retval = 0; + +error: + return retval; +} + +/* + * 1) Get EDID from hw, or use sw default + * 2) Parse into various fb_info structs + * 3) Allocate virtual framebuffer memory to back highest res mode + * + * Parses EDID into three places used by various parts of fbdev: + * fb_var_screeninfo contains the timing of the monitor's preferred mode + * fb_info.monspecs is full parsed EDID info, including monspecs.modedb + * fb_info.modelist is a linked list of all monitor & VESA modes which work + * + * If EDID is not readable/valid, then modelist is all VESA modes, + * monspecs is NULL, and fb_var_screeninfo is set to safe VESA mode + * Returns 0 if successful + */ +static int dlfb_setup_modes(struct dlfb_data *dev, + struct fb_info *info, + char *default_edid, size_t default_edid_size) +{ + int i; + const struct fb_videomode *default_vmode = NULL; + int result = 0; + char *edid; + int tries = 3; + + if (info->dev) /* only use mutex if info has been registered */ + mutex_lock(&info->lock); + + edid = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (!edid) { + result = -ENOMEM; + goto error; + } + + fb_destroy_modelist(&info->modelist); + memset(&info->monspecs, 0, sizeof(info->monspecs)); + + /* + * Try to (re)read EDID from hardware first + * EDID data may return, but not parse as valid + * Try again a few times, in case of e.g. analog cable noise + */ + while (tries--) { + + i = dlfb_get_edid(dev, edid, EDID_LENGTH); + + if (i >= EDID_LENGTH) + fb_edid_to_monspecs(edid, &info->monspecs); + + if (info->monspecs.modedb_len > 0) { + dev->edid = edid; + dev->edid_size = i; + break; + } + } + + /* If that fails, use a previously returned EDID if available */ + if (info->monspecs.modedb_len == 0) { + + pr_err("Unable to get valid EDID from device/display\n"); + + if (dev->edid) { + fb_edid_to_monspecs(dev->edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) + pr_err("Using previously queried EDID\n"); + } + } + + /* If that fails, use the default EDID we were handed */ + if (info->monspecs.modedb_len == 0) { + if (default_edid_size >= EDID_LENGTH) { + fb_edid_to_monspecs(default_edid, &info->monspecs); + if (info->monspecs.modedb_len > 0) { + memcpy(edid, default_edid, default_edid_size); + dev->edid = edid; + dev->edid_size = default_edid_size; + pr_err("Using default/backup EDID\n"); + } + } + } + + /* If we've got modes, let's pick a best default mode */ + if (info->monspecs.modedb_len > 0) { + + for (i = 0; i < info->monspecs.modedb_len; i++) { + if (dlfb_is_valid_mode(&info->monspecs.modedb[i], info)) + fb_add_videomode(&info->monspecs.modedb[i], + &info->modelist); + else /* if we've removed top/best mode */ + info->monspecs.misc &= ~FB_MISC_1ST_DETAIL; + } + + default_vmode = fb_find_best_display(&info->monspecs, + &info->modelist); + } + + /* If everything else has failed, fall back to safe default mode */ + if (default_vmode == NULL) { + + struct fb_videomode fb_vmode = {0}; + + /* + * Add the standard VESA modes to our modelist + * Since we don't have EDID, there may be modes that + * overspec monitor and/or are incorrect aspect ratio, etc. + * But at least the user has a chance to choose + */ + for (i = 0; i < VESA_MODEDB_SIZE; i++) { + if (dlfb_is_valid_mode((struct fb_videomode *) + &vesa_modes[i], info)) + fb_add_videomode(&vesa_modes[i], + &info->modelist); + } + + /* + * default to resolution safe for projectors + * (since they are most common case without EDID) + */ + fb_vmode.xres = 800; + fb_vmode.yres = 600; + fb_vmode.refresh = 60; + default_vmode = fb_find_nearest_mode(&fb_vmode, + &info->modelist); + } + + /* If we have good mode and no active clients*/ + if ((default_vmode != NULL) && (dev->fb_count == 0)) { + + fb_videomode_to_var(&info->var, default_vmode); + dlfb_var_color_format(&info->var); + + /* + * with mode size info, we can now alloc our framebuffer. + */ + memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); + info->fix.line_length = info->var.xres * + (info->var.bits_per_pixel / 8); + + result = dlfb_realloc_framebuffer(dev, info); + + } else + result = -EINVAL; + +error: + if (edid && (dev->edid != edid)) + kfree(edid); + + if (info->dev) + mutex_unlock(&info->lock); + + return result; +} + +static ssize_t metrics_bytes_rendered_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->bytes_rendered)); +} + +static ssize_t metrics_bytes_identical_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->bytes_identical)); +} + +static ssize_t metrics_bytes_sent_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->bytes_sent)); +} + +static ssize_t metrics_cpu_kcycles_used_show(struct device *fbdev, + struct device_attribute *a, char *buf) { + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + return snprintf(buf, PAGE_SIZE, "%u\n", + atomic_read(&dev->cpu_kcycles_used)); +} + +static ssize_t edid_show( + struct file *filp, + struct kobject *kobj, struct bin_attribute *a, + char *buf, loff_t off, size_t count) { + struct device *fbdev = container_of(kobj, struct device, kobj); + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + if (dev->edid == NULL) + return 0; + + if ((off >= dev->edid_size) || (count > dev->edid_size)) + return 0; + + if (off + count > dev->edid_size) + count = dev->edid_size - off; + + pr_info("sysfs edid copy %p to %p, %d bytes\n", + dev->edid, buf, (int) count); + + memcpy(buf, dev->edid, count); + + return count; +} + +static ssize_t edid_store( + struct file *filp, + struct kobject *kobj, struct bin_attribute *a, + char *src, loff_t src_off, size_t src_size) { + struct device *fbdev = container_of(kobj, struct device, kobj); + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + /* We only support write of entire EDID at once, no offset*/ + if ((src_size != EDID_LENGTH) || (src_off != 0)) + return 0; + + dlfb_setup_modes(dev, fb_info, src, src_size); + + if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) { + pr_info("sysfs written EDID is new default\n"); + dlfb_ops_set_par(fb_info); + return src_size; + } else + return 0; +} + +static ssize_t metrics_reset_store(struct device *fbdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *fb_info = dev_get_drvdata(fbdev); + struct dlfb_data *dev = fb_info->par; + + atomic_set(&dev->bytes_rendered, 0); + atomic_set(&dev->bytes_identical, 0); + atomic_set(&dev->bytes_sent, 0); + atomic_set(&dev->cpu_kcycles_used, 0); + + return count; +} + +static struct bin_attribute edid_attr = { + .attr.name = "edid", + .attr.mode = 0666, + .size = EDID_LENGTH, + .read = edid_show, + .write = edid_store +}; + +static struct device_attribute fb_device_attrs[] = { + __ATTR_RO(metrics_bytes_rendered), + __ATTR_RO(metrics_bytes_identical), + __ATTR_RO(metrics_bytes_sent), + __ATTR_RO(metrics_cpu_kcycles_used), + __ATTR(metrics_reset, S_IWUSR, NULL, metrics_reset_store), +}; + +/* + * This is necessary before we can communicate with the display controller. + */ +static int dlfb_select_std_channel(struct dlfb_data *dev) +{ + int ret; + u8 set_def_chn[] = { 0x57, 0xCD, 0xDC, 0xA7, + 0x1C, 0x88, 0x5E, 0x15, + 0x60, 0xFE, 0xC6, 0x97, + 0x16, 0x3D, 0x47, 0xF2 }; + + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + NR_USB_REQUEST_CHANNEL, + (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0, + set_def_chn, sizeof(set_def_chn), USB_CTRL_SET_TIMEOUT); + return ret; +} + +static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev, + struct usb_device *usbdev) +{ + char *desc; + char *buf; + char *desc_end; + + u8 total_len = 0; + + buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL); + if (!buf) + return false; + desc = buf; + + total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ + 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); + if (total_len > 5) { + pr_info("vendor descriptor length:%x data:%02x %02x %02x %02x" \ + "%02x %02x %02x %02x %02x %02x %02x\n", + total_len, desc[0], + desc[1], desc[2], desc[3], desc[4], desc[5], desc[6], + desc[7], desc[8], desc[9], desc[10]); + + if ((desc[0] != total_len) || /* descriptor length */ + (desc[1] != 0x5f) || /* vendor descriptor type */ + (desc[2] != 0x01) || /* version (2 bytes) */ + (desc[3] != 0x00) || + (desc[4] != total_len - 2)) /* length after type */ + goto unrecognized; + + desc_end = desc + total_len; + desc += 5; /* the fixed header we've already parsed */ + + while (desc < desc_end) { + u8 length; + u16 key; + + key = *((u16 *) desc); + desc += sizeof(u16); + length = *desc; + desc++; + + switch (key) { + case 0x0200: { /* max_area */ + u32 max_area; + max_area = le32_to_cpu(*((u32 *)desc)); + pr_warn("DL chip limited to %d pixel modes\n", + max_area); + dev->sku_pixel_limit = max_area; + break; + } + default: + break; + } + desc += length; + } + } + + goto success; + +unrecognized: + /* allow udlfb to load for now even if firmware unrecognized */ + pr_err("Unrecognized vendor firmware descriptor\n"); + +success: + kfree(buf); + return true; +} +static int dlfb_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct dlfb_data *dev = 0; + struct fb_info *info = 0; + int retval = -ENOMEM; + int i; + + /* usb initialization */ + + usbdev = interface_to_usbdev(interface); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + err("dlfb_usb_probe: failed alloc of dev struct\n"); + goto error; + } + + /* we need to wait for both usb and fbdev to spin down on disconnect */ + kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */ + kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */ + + dev->udev = usbdev; + dev->gdev = &usbdev->dev; /* our generic struct device * */ + usb_set_intfdata(interface, dev); + + pr_info("%s %s - serial #%s\n", + usbdev->manufacturer, usbdev->product, usbdev->serial); + pr_info("vid_%04x&pid_%04x&rev_%04x driver's dlfb_data struct at %p\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->descriptor.bcdDevice, dev); + pr_info("console enable=%d\n", console); + pr_info("fb_defio enable=%d\n", fb_defio); + + dev->sku_pixel_limit = 2048 * 1152; /* default to maximum */ + + if (!dlfb_parse_vendor_descriptor(dev, usbdev)) { + pr_err("firmware not recognized. Assume incompatible device\n"); + goto error; + } + + if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { + retval = -ENOMEM; + pr_err("dlfb_alloc_urb_list failed\n"); + goto error; + } + + /* We don't register a new USB class. Our client interface is fbdev */ + + /* allocates framebuffer driver structure, not framebuffer memory */ + info = framebuffer_alloc(0, &usbdev->dev); + if (!info) { + retval = -ENOMEM; + pr_err("framebuffer_alloc failed\n"); + goto error; + } + + dev->info = info; + info->par = dev; + info->pseudo_palette = dev->pseudo_palette; + info->fbops = &dlfb_ops; + + retval = fb_alloc_cmap(&info->cmap, 256, 0); + if (retval < 0) { + pr_err("fb_alloc_cmap failed %x\n", retval); + goto error; + } + + INIT_DELAYED_WORK(&dev->free_framebuffer_work, + dlfb_free_framebuffer_work); + + INIT_LIST_HEAD(&info->modelist); + + retval = dlfb_setup_modes(dev, info, NULL, 0); + if (retval != 0) { + pr_err("unable to find common mode for display and adapter\n"); + goto error; + } + + /* ready to begin using device */ + + atomic_set(&dev->usb_active, 1); + dlfb_select_std_channel(dev); + + dlfb_ops_check_var(&info->var, info); + dlfb_ops_set_par(info); + + retval = register_framebuffer(info); + if (retval < 0) { + pr_err("register_framebuffer failed %d\n", retval); + goto error; + } + + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_create_file(info->dev, &fb_device_attrs[i]); + + device_create_bin_file(info->dev, &edid_attr); + + pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution." + " Using %dK framebuffer memory\n", info->node, + info->var.xres, info->var.yres, + ((dev->backing_buffer) ? + info->fix.smem_len * 2 : info->fix.smem_len) >> 10); + return 0; + +error: + if (dev) { + + if (info) { + if (info->cmap.len != 0) + fb_dealloc_cmap(&info->cmap); + if (info->monspecs.modedb) + fb_destroy_modedb(info->monspecs.modedb); + if (info->screen_base) + vfree(info->screen_base); + + fb_destroy_modelist(&info->modelist); + + framebuffer_release(info); + } + + if (dev->backing_buffer) + vfree(dev->backing_buffer); + + kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */ + kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */ + + /* dev has been deallocated. Do not dereference */ + } + + return retval; +} + +static void dlfb_usb_disconnect(struct usb_interface *interface) +{ + struct dlfb_data *dev; + struct fb_info *info; + int i; + + dev = usb_get_intfdata(interface); + info = dev->info; + + pr_info("USB disconnect starting\n"); + + /* we virtualize until all fb clients release. Then we free */ + dev->virtualized = true; + + /* When non-active we'll update virtual framebuffer, but no new urbs */ + atomic_set(&dev->usb_active, 0); + + /* remove udlfb's sysfs interfaces */ + for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) + device_remove_file(info->dev, &fb_device_attrs[i]); + device_remove_bin_file(info->dev, &edid_attr); + + usb_set_intfdata(interface, NULL); + + /* if clients still have us open, will be freed on last close */ + if (dev->fb_count == 0) + schedule_delayed_work(&dev->free_framebuffer_work, 0); + + /* release reference taken by kref_init in probe() */ + kref_put(&dev->kref, dlfb_free); + + /* consider dlfb_data freed */ + + return; +} + +static struct usb_driver dlfb_driver = { + .name = "udlfb", + .probe = dlfb_usb_probe, + .disconnect = dlfb_usb_disconnect, + .id_table = id_table, +}; + +static int __init dlfb_module_init(void) +{ + int res; + + res = usb_register(&dlfb_driver); + if (res) + err("usb_register failed. Error number %d", res); + + return res; +} + +static void __exit dlfb_module_exit(void) +{ + usb_deregister(&dlfb_driver); +} + +module_init(dlfb_module_init); +module_exit(dlfb_module_exit); + +static void dlfb_urb_completion(struct urb *urb) +{ + struct urb_node *unode = urb->context; + struct dlfb_data *dev = unode->dev; + unsigned long flags; + + /* sync/async unlink faults aren't errors */ + if (urb->status) { + if (!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + pr_err("%s - nonzero write bulk status received: %d\n", + __func__, urb->status); + atomic_set(&dev->lost_pixels, 1); + } + } + + urb->transfer_buffer_length = dev->urbs.size; /* reset to actual */ + + spin_lock_irqsave(&dev->urbs.lock, flags); + list_add_tail(&unode->entry, &dev->urbs.list); + dev->urbs.available++; + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + /* + * When using fb_defio, we deadlock if up() is called + * while another is waiting. So queue to another process. + */ + if (fb_defio) + schedule_delayed_work(&unode->release_urb_work, 0); + else + up(&dev->urbs.limit_sem); +} + +static void dlfb_free_urb_list(struct dlfb_data *dev) +{ + int count = dev->urbs.count; + struct list_head *node; + struct urb_node *unode; + struct urb *urb; + int ret; + unsigned long flags; + + pr_notice("Waiting for completes and freeing all render urbs\n"); + + /* keep waiting and freeing, until we've got 'em all */ + while (count--) { + + /* Getting interrupted means a leak, but ok at shutdown*/ + ret = down_interruptible(&dev->urbs.limit_sem); + if (ret) + break; + + spin_lock_irqsave(&dev->urbs.lock, flags); + + node = dev->urbs.list.next; /* have reserved one with sem */ + list_del_init(node); + + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + unode = list_entry(node, struct urb_node, entry); + urb = unode->urb; + + /* Free each separately allocated piece */ + usb_free_coherent(urb->dev, dev->urbs.size, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + kfree(node); + } + +} + +static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size) +{ + int i = 0; + struct urb *urb; + struct urb_node *unode; + char *buf; + + spin_lock_init(&dev->urbs.lock); + + dev->urbs.size = size; + INIT_LIST_HEAD(&dev->urbs.list); + + while (i < count) { + unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); + if (!unode) + break; + unode->dev = dev; + + INIT_DELAYED_WORK(&unode->release_urb_work, + dlfb_release_urb_work); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + kfree(unode); + break; + } + unode->urb = urb; + + buf = usb_alloc_coherent(dev->udev, MAX_TRANSFER, GFP_KERNEL, + &urb->transfer_dma); + if (!buf) { + kfree(unode); + usb_free_urb(urb); + break; + } + + /* urb->transfer_buffer_length set to actual before submit */ + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, 1), + buf, size, dlfb_urb_completion, unode); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + list_add_tail(&unode->entry, &dev->urbs.list); + + i++; + } + + sema_init(&dev->urbs.limit_sem, i); + dev->urbs.count = i; + dev->urbs.available = i; + + pr_notice("allocated %d %d byte urbs\n", i, (int) size); + + return i; +} + +static struct urb *dlfb_get_urb(struct dlfb_data *dev) +{ + int ret = 0; + struct list_head *entry; + struct urb_node *unode; + struct urb *urb = NULL; + unsigned long flags; + + /* Wait for an in-flight buffer to complete and get re-queued */ + ret = down_timeout(&dev->urbs.limit_sem, GET_URB_TIMEOUT); + if (ret) { + atomic_set(&dev->lost_pixels, 1); + pr_warn("wait for urb interrupted: %x available: %d\n", + ret, dev->urbs.available); + goto error; + } + + spin_lock_irqsave(&dev->urbs.lock, flags); + + BUG_ON(list_empty(&dev->urbs.list)); /* reserved one with limit_sem */ + entry = dev->urbs.list.next; + list_del_init(entry); + dev->urbs.available--; + + spin_unlock_irqrestore(&dev->urbs.lock, flags); + + unode = list_entry(entry, struct urb_node, entry); + urb = unode->urb; + +error: + return urb; +} + +static int dlfb_submit_urb(struct dlfb_data *dev, struct urb *urb, size_t len) +{ + int ret; + + BUG_ON(len > dev->urbs.size); + + urb->transfer_buffer_length = len; /* set to actual payload len */ + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + dlfb_urb_completion(urb); /* because no one else will */ + atomic_set(&dev->lost_pixels, 1); + pr_err("usb_submit_urb error %x\n", ret); + } + return ret; +} + +module_param(console, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(console, "Allow fbcon to consume first framebuffer found"); + +module_param(fb_defio, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); +MODULE_PARM_DESC(fb_defio, "Enable fb_defio mmap support. *Experimental*"); + +MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, " + "Jaya Kumar <jayakumar.lkml@gmail.com>, " + "Bernie Thompson <bernie@plugable.com>"); +MODULE_DESCRIPTION("DisplayLink kernel framebuffer driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index a3aa91709503..6723d6910cde 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c @@ -15,6 +15,9 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/pm.h> +#include <asm/olpc.h> /* * The default port config. @@ -29,6 +32,19 @@ static struct via_port_cfg adap_configs[] = { }; /* + * The OLPC XO-1.5 puts the camera power and reset lines onto + * GPIO 2C. + */ +static const struct via_port_cfg olpc_adap_configs[] = { + [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x26 }, + [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 }, + [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, + [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c }, + [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, + { 0, 0, 0, 0 } +}; + +/* * We currently only support one viafb device (will there ever be * more than one?), so just declare it globally here. */ @@ -575,6 +591,78 @@ static void via_teardown_subdevs(void) } } +/* + * Power management functions + */ +#ifdef CONFIG_PM +static LIST_HEAD(viafb_pm_hooks); +static DEFINE_MUTEX(viafb_pm_hooks_lock); + +void viafb_pm_register(struct viafb_pm_hooks *hooks) +{ + INIT_LIST_HEAD(&hooks->list); + + mutex_lock(&viafb_pm_hooks_lock); + list_add_tail(&hooks->list, &viafb_pm_hooks); + mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_register); + +void viafb_pm_unregister(struct viafb_pm_hooks *hooks) +{ + mutex_lock(&viafb_pm_hooks_lock); + list_del(&hooks->list); + mutex_unlock(&viafb_pm_hooks_lock); +} +EXPORT_SYMBOL_GPL(viafb_pm_unregister); + +static int via_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct viafb_pm_hooks *hooks; + + if (state.event != PM_EVENT_SUSPEND) + return 0; + /* + * "I've occasionally hit a few drivers that caused suspend + * failures, and each and every time it was a driver bug, and + * the right thing to do was to just ignore the error and suspend + * anyway - returning an error code and trying to undo the suspend + * is not what anybody ever really wants, even if our model + *_allows_ for it." + * -- Linus Torvalds, Dec. 7, 2009 + */ + mutex_lock(&viafb_pm_hooks_lock); + list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list) + hooks->suspend(hooks->private); + mutex_unlock(&viafb_pm_hooks_lock); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int via_resume(struct pci_dev *pdev) +{ + struct viafb_pm_hooks *hooks; + + /* Get the bus side powered up */ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + if (pci_enable_device(pdev)) + return 0; + + pci_set_master(pdev); + + /* Now bring back any subdevs */ + mutex_lock(&viafb_pm_hooks_lock); + list_for_each_entry(hooks, &viafb_pm_hooks, list) + hooks->resume(hooks->private); + mutex_unlock(&viafb_pm_hooks_lock); + + return 0; +} +#endif /* CONFIG_PM */ static int __devinit via_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -584,6 +672,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) return ret; + /* * Global device initialization. */ @@ -591,6 +680,9 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, global_dev.pdev = pdev; global_dev.chip_type = ent->driver_data; global_dev.port_cfg = adap_configs; + if (machine_is_olpc()) + global_dev.port_cfg = olpc_adap_configs; + spin_lock_init(&global_dev.reg_lock); ret = via_pci_setup_mmio(&global_dev); if (ret) @@ -663,8 +755,8 @@ static struct pci_driver via_driver = { .probe = via_pci_probe, .remove = __devexit_p(via_pci_remove), #ifdef CONFIG_PM - .suspend = viafb_suspend, - .resume = viafb_resume, + .suspend = via_suspend, + .resume = via_resume, #endif }; diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c index 39acb37e7a1d..c2a0a1cfd3b3 100644 --- a/drivers/video/via/via-gpio.c +++ b/drivers/video/via/via-gpio.c @@ -172,6 +172,28 @@ static void viafb_gpio_disable(struct viafb_gpio *gpio) via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02); } +#ifdef CONFIG_PM + +static int viafb_gpio_suspend(void *private) +{ + return 0; +} + +static int viafb_gpio_resume(void *private) +{ + int i; + + for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) + viafb_gpio_enable(gpio_config.active_gpios[i]); + return 0; +} + +static struct viafb_pm_hooks viafb_gpio_pm_hooks = { + .suspend = viafb_gpio_suspend, + .resume = viafb_gpio_resume +}; +#endif /* CONFIG_PM */ + /* * Look up a specific gpio and return the number it was assigned. */ @@ -236,6 +258,9 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev) printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); gpio_config.gpio_chip.ngpio = 0; } +#ifdef CONFIG_PM + viafb_pm_register(&viafb_gpio_pm_hooks); +#endif return ret; } @@ -245,6 +270,10 @@ static int viafb_gpio_remove(struct platform_device *platdev) unsigned long flags; int ret = 0, i; +#ifdef CONFIG_PM + viafb_pm_unregister(&viafb_gpio_pm_hooks); +#endif + /* * Get unregistered. */ diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index d298cfccd6fc..289edd519527 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c @@ -1672,31 +1672,19 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres) #ifdef CONFIG_PM -int viafb_suspend(struct pci_dev *pdev, pm_message_t state) +static int viafb_suspend(void *unused) { - if (state.event == PM_EVENT_SUSPEND) { - acquire_console_sem(); - fb_set_suspend(viafbinfo, 1); - - viafb_sync(viafbinfo); - - pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - release_console_sem(); - } + acquire_console_sem(); + fb_set_suspend(viafbinfo, 1); + viafb_sync(viafbinfo); + release_console_sem(); return 0; } -int viafb_resume(struct pci_dev *pdev) +static int viafb_resume(void *unused) { acquire_console_sem(); - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - if (pci_enable_device(pdev)) - goto fail; - pci_set_master(pdev); if (viaparinfo->shared->vdev->engine_mmio) viafb_reset_engine(viaparinfo); viafb_set_par(viafbinfo); @@ -1704,11 +1692,15 @@ int viafb_resume(struct pci_dev *pdev) viafb_set_par(viafbinfo1); fb_set_suspend(viafbinfo, 0); -fail: release_console_sem(); return 0; } +static struct viafb_pm_hooks viafb_fb_pm_hooks = { + .suspend = viafb_suspend, + .resume = viafb_resume +}; + #endif @@ -1899,6 +1891,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) viafb_init_proc(viaparinfo->shared); viafb_init_dac(IGA2); + +#ifdef CONFIG_PM + viafb_pm_register(&viafb_fb_pm_hooks); +#endif return 0; out_fb_unreg: diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 4960e3da6645..d66f963e930e 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h @@ -108,6 +108,4 @@ void via_fb_pci_remove(struct pci_dev *pdev); /* Temporary */ int viafb_init(void); void viafb_exit(void); -int viafb_suspend(struct pci_dev *pdev, pm_message_t state); -int viafb_resume(struct pci_dev *pdev); #endif /* __VIAFBDEV_H__ */ diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c new file mode 100644 index 000000000000..7617f12e4fd7 --- /dev/null +++ b/drivers/video/vt8500lcdfb.c @@ -0,0 +1,447 @@ +/* + * linux/drivers/video/vt8500lcdfb.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * Based on skeletonfb.c and pxafb.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/wait.h> + +#include <mach/vt8500fb.h> + +#include "vt8500lcdfb.h" +#include "wmt_ge_rops.h" + +#define to_vt8500lcd_info(__info) container_of(__info, \ + struct vt8500lcd_info, fb) + +static int vt8500lcd_set_par(struct fb_info *info) +{ + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + int reg_bpp = 5; /* 16bpp */ + int i; + unsigned long control0; + + if (!fbi) + return -EINVAL; + + if (info->var.bits_per_pixel <= 8) { + /* palettized */ + info->var.red.offset = 0; + info->var.red.length = info->var.bits_per_pixel; + info->var.red.msb_right = 0; + + info->var.green.offset = 0; + info->var.green.length = info->var.bits_per_pixel; + info->var.green.msb_right = 0; + + info->var.blue.offset = 0; + info->var.blue.length = info->var.bits_per_pixel; + info->var.blue.msb_right = 0; + + info->var.transp.offset = 0; + info->var.transp.length = 0; + info->var.transp.msb_right = 0; + + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + info->fix.line_length = info->var.xres_virtual / + (8/info->var.bits_per_pixel); + } else { + /* non-palettized */ + info->var.transp.offset = 0; + info->var.transp.length = 0; + info->var.transp.msb_right = 0; + + if (info->var.bits_per_pixel == 16) { + /* RGB565 */ + info->var.red.offset = 11; + info->var.red.length = 5; + info->var.red.msb_right = 0; + info->var.green.offset = 5; + info->var.green.length = 6; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = 5; + info->var.blue.msb_right = 0; + } else { + /* Equal depths per channel */ + info->var.red.offset = info->var.bits_per_pixel + * 2 / 3; + info->var.red.length = info->var.bits_per_pixel / 3; + info->var.red.msb_right = 0; + info->var.green.offset = info->var.bits_per_pixel / 3; + info->var.green.length = info->var.bits_per_pixel / 3; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = info->var.bits_per_pixel / 3; + info->var.blue.msb_right = 0; + } + + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.line_length = info->var.bits_per_pixel > 16 ? + info->var.xres_virtual << 2 : + info->var.xres_virtual << 1; + } + + for (i = 0; i < 8; i++) { + if (bpp_values[i] == info->var.bits_per_pixel) { + reg_bpp = i; + continue; + } + } + + control0 = readl(fbi->regbase) & ~0xf; + writel(0, fbi->regbase); + while (readl(fbi->regbase + 0x38) & 0x10) + /* wait */; + writel((((info->var.hsync_len - 1) & 0x3f) << 26) + | ((info->var.left_margin & 0xff) << 18) + | (((info->var.xres - 1) & 0x3ff) << 8) + | (info->var.right_margin & 0xff), fbi->regbase + 0x4); + writel((((info->var.vsync_len - 1) & 0x3f) << 26) + | ((info->var.upper_margin & 0xff) << 18) + | (((info->var.yres - 1) & 0x3ff) << 8) + | (info->var.lower_margin & 0xff), fbi->regbase + 0x8); + writel((((info->var.yres - 1) & 0x400) << 2) + | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10); + writel(0x80000000, fbi->regbase + 0x20); + writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase); + + return 0; +} + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) { + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + int ret = 1; + unsigned int val; + if (regno >= 256) + return -EINVAL; + + if (info->var.grayscale) + red = green = blue = + (19595 * red + 38470 * green + 7471 * blue) >> 16; + + switch (fbi->fb.fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + u32 *pal = fbi->fb.pseudo_palette; + + val = chan_to_field(red, &fbi->fb.var.red); + val |= chan_to_field(green, &fbi->fb.var.green); + val |= chan_to_field(blue, &fbi->fb.var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_STATIC_PSEUDOCOLOR: + case FB_VISUAL_PSEUDOCOLOR: + writew((red & 0xf800) + | ((green >> 5) & 0x7e0) + | ((blue >> 11) & 0x1f), + fbi->palette_cpu + sizeof(u16) * regno); + break; + } + + return ret; +} + +static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + + if (cmd == FBIO_WAITFORVSYNC) { + /* Unmask End of Frame interrupt */ + writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c); + ret = wait_event_interruptible_timeout(fbi->wait, + readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10); + /* Mask back to reduce unwanted interrupt traffic */ + writel(0xffffffff, fbi->regbase + 0x3c); + if (ret < 0) + return ret; + if (ret == 0) + return -ETIMEDOUT; + } + + return ret; +} + +static int vt8500lcd_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + unsigned pixlen = info->fix.line_length / info->var.xres_virtual; + unsigned off = pixlen * var->xoffset + + info->fix.line_length * var->yoffset; + struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); + + writel((1 << 31) + | (((var->xres_virtual - var->xres) * pixlen / 4) << 20) + | (off >> 2), fbi->regbase + 0x20); + return 0; +} + +static struct fb_ops vt8500lcd_ops = { + .owner = THIS_MODULE, + .fb_set_par = vt8500lcd_set_par, + .fb_setcolreg = vt8500lcd_setcolreg, + .fb_fillrect = wmt_ge_fillrect, + .fb_copyarea = wmt_ge_copyarea, + .fb_imageblit = sys_imageblit, + .fb_sync = wmt_ge_sync, + .fb_ioctl = vt8500lcd_ioctl, + .fb_pan_display = vt8500lcd_pan_display, +}; + +static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id) +{ + struct vt8500lcd_info *fbi = dev_id; + + if (readl(fbi->regbase + 0x38) & (1 << 3)) + wake_up_interruptible(&fbi->wait); + + writel(0xffffffff, fbi->regbase + 0x38); + return IRQ_HANDLED; +} + +static int __devinit vt8500lcd_probe(struct platform_device *pdev) +{ + struct vt8500lcd_info *fbi; + struct resource *res; + struct vt8500fb_platform_data *pdata = pdev->dev.platform_data; + void *addr; + int irq, ret; + + ret = -ENOMEM; + fbi = NULL; + + fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16, + GFP_KERNEL); + if (!fbi) { + dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); + ret = -ENOMEM; + goto failed; + } + + strcpy(fbi->fb.fix.id, "VT8500 LCD"); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.xpanstep = 0; + fbi->fb.fix.ypanstep = 1; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; + + fbi->fb.fbops = &vt8500lcd_ops; + fbi->fb.flags = FBINFO_DEFAULT + | FBINFO_HWACCEL_COPYAREA + | FBINFO_HWACCEL_FILLRECT + | FBINFO_HWACCEL_YPAN + | FBINFO_VIRTFB + | FBINFO_PARTIAL_PAN_OK; + fbi->fb.node = -1; + + addr = fbi; + addr = addr + sizeof(struct vt8500lcd_info); + fbi->fb.pseudo_palette = addr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto failed_fbi; + } + + res = request_mem_region(res->start, resource_size(res), "vt8500lcd"); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto failed_fbi; + } + + fbi->regbase = ioremap(res->start, resource_size(res)); + if (fbi->regbase == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto failed_free_res; + } + + fbi->fb.fix.smem_start = pdata->video_mem_phys; + fbi->fb.fix.smem_len = pdata->video_mem_len; + fbi->fb.screen_base = pdata->video_mem_virt; + + fbi->palette_size = PAGE_ALIGN(512); + fbi->palette_cpu = dma_alloc_coherent(&pdev->dev, + fbi->palette_size, + &fbi->palette_phys, + GFP_KERNEL); + if (fbi->palette_cpu == NULL) { + dev_err(&pdev->dev, "Failed to allocate palette buffer\n"); + ret = -ENOMEM; + goto failed_free_io; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ defined\n"); + ret = -ENODEV; + goto failed_free_palette; + } + + ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi); + if (ret) { + dev_err(&pdev->dev, "request_irq failed: %d\n", ret); + ret = -EBUSY; + goto failed_free_palette; + } + + init_waitqueue_head(&fbi->wait); + + if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { + dev_err(&pdev->dev, "Failed to allocate color map\n"); + ret = -ENOMEM; + goto failed_free_irq; + } + + fb_videomode_to_var(&fbi->fb.var, &pdata->mode); + fbi->fb.var.bits_per_pixel = pdata->bpp; + fbi->fb.var.xres_virtual = pdata->xres_virtual; + fbi->fb.var.yres_virtual = pdata->yres_virtual; + + ret = vt8500lcd_set_par(&fbi->fb); + if (ret) { + dev_err(&pdev->dev, "Failed to set parameters\n"); + goto failed_free_cmap; + } + + writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c); + writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18); + + platform_set_drvdata(pdev, fbi); + + ret = register_framebuffer(&fbi->fb); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to register framebuffer device: %d\n", ret); + goto failed_free_cmap; + } + + /* + * Ok, now enable the LCD controller + */ + writel(readl(fbi->regbase) | 1, fbi->regbase); + + return 0; + +failed_free_cmap: + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); +failed_free_irq: + free_irq(irq, fbi); +failed_free_palette: + dma_free_coherent(&pdev->dev, fbi->palette_size, + fbi->palette_cpu, fbi->palette_phys); +failed_free_io: + iounmap(fbi->regbase); +failed_free_res: + release_mem_region(res->start, resource_size(res)); +failed_fbi: + platform_set_drvdata(pdev, NULL); + kfree(fbi); +failed: + return ret; +} + +static int __devexit vt8500lcd_remove(struct platform_device *pdev) +{ + struct vt8500lcd_info *fbi = platform_get_drvdata(pdev); + struct resource *res; + int irq; + + unregister_framebuffer(&fbi->fb); + + writel(0, fbi->regbase); + + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); + + irq = platform_get_irq(pdev, 0); + free_irq(irq, fbi); + + dma_free_coherent(&pdev->dev, fbi->palette_size, + fbi->palette_cpu, fbi->palette_phys); + + iounmap(fbi->regbase); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(fbi); + + return 0; +} + +static struct platform_driver vt8500lcd_driver = { + .probe = vt8500lcd_probe, + .remove = __devexit_p(vt8500lcd_remove), + .driver = { + .owner = THIS_MODULE, + .name = "vt8500-lcd", + }, +}; + +static int __init vt8500lcd_init(void) +{ + return platform_driver_register(&vt8500lcd_driver); +} + +static void __exit vt8500lcd_exit(void) +{ + platform_driver_unregister(&vt8500lcd_driver); +} + +module_init(vt8500lcd_init); +module_exit(vt8500lcd_exit); + +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); +MODULE_DESCRIPTION("LCD controller driver for VIA VT8500"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h new file mode 100644 index 000000000000..36ca3ca09d83 --- /dev/null +++ b/drivers/video/vt8500lcdfb.h @@ -0,0 +1,34 @@ +/* + * linux/drivers/video/vt8500lcdfb.h + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +struct vt8500lcd_info { + struct fb_info fb; + void __iomem *regbase; + void __iomem *palette_cpu; + dma_addr_t palette_phys; + size_t palette_size; + wait_queue_head_t wait; +}; + +static int bpp_values[] = { + 1, + 2, + 4, + 8, + 12, + 16, + 18, + 24, +}; diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c new file mode 100644 index 000000000000..96e34a569169 --- /dev/null +++ b/drivers/video/wm8505fb.c @@ -0,0 +1,422 @@ +/* + * WonderMedia WM8505 Frame Buffer device driver + * + * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> + * Based on vt8500lcdfb.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/wait.h> + +#include <mach/vt8500fb.h> + +#include "wm8505fb_regs.h" +#include "wmt_ge_rops.h" + +#define DRIVER_NAME "wm8505-fb" + +#define to_wm8505fb_info(__info) container_of(__info, \ + struct wm8505fb_info, fb) +struct wm8505fb_info { + struct fb_info fb; + void __iomem *regbase; + unsigned int contrast; +}; + + +static int wm8505fb_init_hw(struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + int i; + + /* I know the purpose only of few registers, so clear unknown */ + for (i = 0; i < 0x200; i += 4) + writel(0, fbi->regbase + i); + + /* Set frame buffer address */ + writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR); + writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1); + + /* Set in-memory picture format to RGB 32bpp */ + writel(0x1c, fbi->regbase + WMT_GOVR_COLORSPACE); + writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1); + + /* Virtual buffer size */ + writel(info->var.xres, fbi->regbase + WMT_GOVR_XRES); + writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL); + + /* black magic ;) */ + writel(0xf, fbi->regbase + WMT_GOVR_FHI); + writel(4, fbi->regbase + WMT_GOVR_DVO_SET); + writel(1, fbi->regbase + WMT_GOVR_MIF_ENABLE); + writel(1, fbi->regbase + WMT_GOVR_REG_UPDATE); + + return 0; +} + +static int wm8505fb_set_timing(struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + int h_start = info->var.left_margin; + int h_end = h_start + info->var.xres; + int h_all = h_end + info->var.right_margin; + int h_sync = info->var.hsync_len; + + int v_start = info->var.upper_margin; + int v_end = v_start + info->var.yres; + int v_all = v_end + info->var.lower_margin; + int v_sync = info->var.vsync_len; + + writel(0, fbi->regbase + WMT_GOVR_TG); + + writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START); + writel(h_end, fbi->regbase + WMT_GOVR_TIMING_H_END); + writel(h_all, fbi->regbase + WMT_GOVR_TIMING_H_ALL); + writel(h_sync, fbi->regbase + WMT_GOVR_TIMING_H_SYNC); + + writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START); + writel(v_end, fbi->regbase + WMT_GOVR_TIMING_V_END); + writel(v_all, fbi->regbase + WMT_GOVR_TIMING_V_ALL); + writel(v_sync, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); + + writel(1, fbi->regbase + WMT_GOVR_TG); + + return 0; +} + + +static int wm8505fb_set_par(struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + if (!fbi) + return -EINVAL; + + if (info->var.bits_per_pixel == 32) { + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.red.msb_right = 0; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.green.msb_right = 0; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->var.blue.msb_right = 0; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.line_length = info->var.xres_virtual << 2; + } + + wm8505fb_set_timing(info); + + writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast, + fbi->regbase + WMT_GOVR_CONTRAST); + + return 0; +} + +static ssize_t contrast_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + return sprintf(buf, "%d\n", fbi->contrast); +} + +static ssize_t contrast_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + unsigned long tmp; + + if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff)) + return -EINVAL; + fbi->contrast = tmp; + + wm8505fb_set_par(info); + + return count; +} + +static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store); + +static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) { + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + int ret = 1; + unsigned int val; + if (regno >= 256) + return -EINVAL; + + if (info->var.grayscale) + red = green = blue = + (19595 * red + 38470 * green + 7471 * blue) >> 16; + + switch (fbi->fb.fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + u32 *pal = info->pseudo_palette; + + val = chan_to_field(red, &fbi->fb.var.red); + val |= chan_to_field(green, &fbi->fb.var.green); + val |= chan_to_field(blue, &fbi->fb.var.blue); + + pal[regno] = val; + ret = 0; + } + break; + } + + return ret; +} + +static int wm8505fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN); + writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN); + return 0; +} + +static int wm8505fb_blank(int blank, struct fb_info *info) +{ + struct wm8505fb_info *fbi = to_wm8505fb_info(info); + + switch (blank) { + case FB_BLANK_UNBLANK: + wm8505fb_set_timing(info); + break; + default: + writel(0, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); + break; + } + + return 0; +} + +static struct fb_ops wm8505fb_ops = { + .owner = THIS_MODULE, + .fb_set_par = wm8505fb_set_par, + .fb_setcolreg = wm8505fb_setcolreg, + .fb_fillrect = wmt_ge_fillrect, + .fb_copyarea = wmt_ge_copyarea, + .fb_imageblit = sys_imageblit, + .fb_sync = wmt_ge_sync, + .fb_pan_display = wm8505fb_pan_display, + .fb_blank = wm8505fb_blank, +}; + +static int __devinit wm8505fb_probe(struct platform_device *pdev) +{ + struct wm8505fb_info *fbi; + struct resource *res; + void *addr; + struct vt8500fb_platform_data *pdata; + int ret; + + pdata = pdev->dev.platform_data; + + ret = -ENOMEM; + fbi = NULL; + + fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16, + GFP_KERNEL); + if (!fbi) { + dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); + ret = -ENOMEM; + goto failed; + } + + strcpy(fbi->fb.fix.id, DRIVER_NAME); + + fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fbi->fb.fix.xpanstep = 1; + fbi->fb.fix.ypanstep = 1; + fbi->fb.fix.ywrapstep = 0; + fbi->fb.fix.accel = FB_ACCEL_NONE; + + fbi->fb.fbops = &wm8505fb_ops; + fbi->fb.flags = FBINFO_DEFAULT + | FBINFO_HWACCEL_COPYAREA + | FBINFO_HWACCEL_FILLRECT + | FBINFO_HWACCEL_XPAN + | FBINFO_HWACCEL_YPAN + | FBINFO_VIRTFB + | FBINFO_PARTIAL_PAN_OK; + fbi->fb.node = -1; + + addr = fbi; + addr = addr + sizeof(struct wm8505fb_info); + fbi->fb.pseudo_palette = addr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + ret = -ENODEV; + goto failed_fbi; + } + + res = request_mem_region(res->start, resource_size(res), DRIVER_NAME); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto failed_fbi; + } + + fbi->regbase = ioremap(res->start, resource_size(res)); + if (fbi->regbase == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + ret = -EBUSY; + goto failed_free_res; + } + + fb_videomode_to_var(&fbi->fb.var, &pdata->mode); + + fbi->fb.var.nonstd = 0; + fbi->fb.var.activate = FB_ACTIVATE_NOW; + + fbi->fb.var.height = -1; + fbi->fb.var.width = -1; + fbi->fb.var.xres_virtual = pdata->xres_virtual; + fbi->fb.var.yres_virtual = pdata->yres_virtual; + fbi->fb.var.bits_per_pixel = pdata->bpp; + + fbi->fb.fix.smem_start = pdata->video_mem_phys; + fbi->fb.fix.smem_len = pdata->video_mem_len; + fbi->fb.screen_base = pdata->video_mem_virt; + fbi->fb.screen_size = pdata->video_mem_len; + + if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { + dev_err(&pdev->dev, "Failed to allocate color map\n"); + ret = -ENOMEM; + goto failed_free_io; + } + + wm8505fb_init_hw(&fbi->fb); + + fbi->contrast = 0x80; + ret = wm8505fb_set_par(&fbi->fb); + if (ret) { + dev_err(&pdev->dev, "Failed to set parameters\n"); + goto failed_free_cmap; + } + + platform_set_drvdata(pdev, fbi); + + ret = register_framebuffer(&fbi->fb); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to register framebuffer device: %d\n", ret); + goto failed_free_cmap; + } + + ret = device_create_file(&pdev->dev, &dev_attr_contrast); + if (ret < 0) { + printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n", + fbi->fb.node, ret); + } + + printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n", + fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start, + fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1); + + return 0; + +failed_free_cmap: + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); +failed_free_io: + iounmap(fbi->regbase); +failed_free_res: + release_mem_region(res->start, resource_size(res)); +failed_fbi: + platform_set_drvdata(pdev, NULL); + kfree(fbi); +failed: + return ret; +} + +static int __devexit wm8505fb_remove(struct platform_device *pdev) +{ + struct wm8505fb_info *fbi = platform_get_drvdata(pdev); + struct resource *res; + + device_remove_file(&pdev->dev, &dev_attr_contrast); + + unregister_framebuffer(&fbi->fb); + + writel(0, fbi->regbase); + + if (fbi->fb.cmap.len) + fb_dealloc_cmap(&fbi->fb.cmap); + + iounmap(fbi->regbase); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(fbi); + + return 0; +} + +static struct platform_driver wm8505fb_driver = { + .probe = wm8505fb_probe, + .remove = __devexit_p(wm8505fb_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init wm8505fb_init(void) +{ + return platform_driver_register(&wm8505fb_driver); +} + +static void __exit wm8505fb_exit(void) +{ + platform_driver_unregister(&wm8505fb_driver); +} + +module_init(wm8505fb_init); +module_exit(wm8505fb_exit); + +MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>"); +MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h new file mode 100644 index 000000000000..4dd41668c6d1 --- /dev/null +++ b/drivers/video/wm8505fb_regs.h @@ -0,0 +1,76 @@ +/* + * GOVR registers list for WM8505 chips + * + * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> + * Based on VIA/WonderMedia wm8510-govrh-reg.h + * http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/ + * drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WM8505FB_REGS_H +#define _WM8505FB_REGS_H + +/* + * Color space select register, default value 0x1c + * BIT0 GOVRH_DVO_YUV2RGB_ENABLE + * BIT1 GOVRH_VGA_YUV2RGB_ENABLE + * BIT2 GOVRH_RGB_MODE + * BIT3 GOVRH_DAC_CLKINV + * BIT4 GOVRH_BLANK_ZERO + */ +#define WMT_GOVR_COLORSPACE 0x1e4 +/* + * Another colorspace select register, default value 1 + * BIT0 GOVRH_DVO_RGB + * BIT1 GOVRH_DVO_YUV422 + */ +#define WMT_GOVR_COLORSPACE1 0x30 + +#define WMT_GOVR_CONTRAST 0x1b8 +#define WMT_GOVR_BRGHTNESS 0x1bc /* incompatible with RGB? */ + +/* Framubeffer address */ +#define WMT_GOVR_FBADDR 0x90 +#define WMT_GOVR_FBADDR1 0x94 /* UV offset in YUV mode */ + +/* Offset of visible window */ +#define WMT_GOVR_XPAN 0xa4 +#define WMT_GOVR_YPAN 0xa0 + +#define WMT_GOVR_XRES 0x98 +#define WMT_GOVR_XRES_VIRTUAL 0x9c + +#define WMT_GOVR_MIF_ENABLE 0x80 +#define WMT_GOVR_FHI 0xa8 +#define WMT_GOVR_REG_UPDATE 0xe4 + +/* + * BIT0 GOVRH_DVO_OUTWIDTH + * BIT1 GOVRH_DVO_SYNC_POLAR + * BIT2 GOVRH_DVO_ENABLE + */ +#define WMT_GOVR_DVO_SET 0x148 + +/* Timing generator? */ +#define WMT_GOVR_TG 0x100 + +/* Timings */ +#define WMT_GOVR_TIMING_H_ALL 0x108 +#define WMT_GOVR_TIMING_V_ALL 0x10c +#define WMT_GOVR_TIMING_V_START 0x110 +#define WMT_GOVR_TIMING_V_END 0x114 +#define WMT_GOVR_TIMING_H_START 0x118 +#define WMT_GOVR_TIMING_H_END 0x11c +#define WMT_GOVR_TIMING_V_SYNC 0x128 +#define WMT_GOVR_TIMING_H_SYNC 0x12c + +#endif /* _WM8505FB_REGS_H */ diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c new file mode 100644 index 000000000000..45832b7ef7d2 --- /dev/null +++ b/drivers/video/wmt_ge_rops.c @@ -0,0 +1,186 @@ +/* + * linux/drivers/video/wmt_ge_rops.c + * + * Accelerators for raster operations using WonderMedia Graphics Engine + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/fb.h> +#include <linux/platform_device.h> +#include "fb_draw.h" + +#define GE_COMMAND_OFF 0x00 +#define GE_DEPTH_OFF 0x04 +#define GE_HIGHCOLOR_OFF 0x08 +#define GE_ROPCODE_OFF 0x14 +#define GE_FIRE_OFF 0x18 +#define GE_SRCBASE_OFF 0x20 +#define GE_SRCDISPW_OFF 0x24 +#define GE_SRCDISPH_OFF 0x28 +#define GE_SRCAREAX_OFF 0x2c +#define GE_SRCAREAY_OFF 0x30 +#define GE_SRCAREAW_OFF 0x34 +#define GE_SRCAREAH_OFF 0x38 +#define GE_DESTBASE_OFF 0x3c +#define GE_DESTDISPW_OFF 0x40 +#define GE_DESTDISPH_OFF 0x44 +#define GE_DESTAREAX_OFF 0x48 +#define GE_DESTAREAY_OFF 0x4c +#define GE_DESTAREAW_OFF 0x50 +#define GE_DESTAREAH_OFF 0x54 +#define GE_PAT0C_OFF 0x88 /* Pattern 0 color */ +#define GE_ENABLE_OFF 0xec +#define GE_INTEN_OFF 0xf0 +#define GE_STATUS_OFF 0xf8 + +static void __iomem *regbase; + +void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + unsigned long fg, pat; + + if (p->state != FBINFO_STATE_RUNNING) + return; + + if (p->fix.visual == FB_VISUAL_TRUECOLOR || + p->fix.visual == FB_VISUAL_DIRECTCOLOR) + fg = ((u32 *) (p->pseudo_palette))[rect->color]; + else + fg = rect->color; + + pat = pixel_to_pat(p->var.bits_per_pixel, fg); + + if (p->fbops->fb_sync) + p->fbops->fb_sync(p); + + writel(p->var.bits_per_pixel == 32 ? 3 : + (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF); + writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF); + writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); + writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); + writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); + writel(rect->dx, regbase + GE_DESTAREAX_OFF); + writel(rect->dy, regbase + GE_DESTAREAY_OFF); + writel(rect->width - 1, regbase + GE_DESTAREAW_OFF); + writel(rect->height - 1, regbase + GE_DESTAREAH_OFF); + + writel(pat, regbase + GE_PAT0C_OFF); + writel(1, regbase + GE_COMMAND_OFF); + writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF); + writel(1, regbase + GE_FIRE_OFF); +} +EXPORT_SYMBOL_GPL(wmt_ge_fillrect); + +void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + if (p->state != FBINFO_STATE_RUNNING) + return; + + if (p->fbops->fb_sync) + p->fbops->fb_sync(p); + + writel(p->var.bits_per_pixel > 16 ? 3 : + (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF); + + writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF); + writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF); + writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF); + writel(area->sx, regbase + GE_SRCAREAX_OFF); + writel(area->sy, regbase + GE_SRCAREAY_OFF); + writel(area->width - 1, regbase + GE_SRCAREAW_OFF); + writel(area->height - 1, regbase + GE_SRCAREAH_OFF); + + writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); + writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); + writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); + writel(area->dx, regbase + GE_DESTAREAX_OFF); + writel(area->dy, regbase + GE_DESTAREAY_OFF); + writel(area->width - 1, regbase + GE_DESTAREAW_OFF); + writel(area->height - 1, regbase + GE_DESTAREAH_OFF); + + writel(0xcc, regbase + GE_ROPCODE_OFF); + writel(1, regbase + GE_COMMAND_OFF); + writel(1, regbase + GE_FIRE_OFF); +} +EXPORT_SYMBOL_GPL(wmt_ge_copyarea); + +int wmt_ge_sync(struct fb_info *p) +{ + int loops = 5000000; + while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops) + cpu_relax(); + return loops > 0 ? 0 : -EBUSY; +} +EXPORT_SYMBOL_GPL(wmt_ge_sync); + +static int __devinit wmt_ge_rops_probe(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no I/O memory resource defined\n"); + return -ENODEV; + } + + /* Only one ROP engine is presently supported. */ + if (unlikely(regbase)) { + WARN_ON(1); + return -EBUSY; + } + + regbase = ioremap(res->start, resource_size(res)); + if (regbase == NULL) { + dev_err(&pdev->dev, "failed to map I/O memory\n"); + return -EBUSY; + } + + writel(1, regbase + GE_ENABLE_OFF); + printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n"); + + return 0; +} + +static int __devexit wmt_ge_rops_remove(struct platform_device *pdev) +{ + iounmap(regbase); + return 0; +} + +static struct platform_driver wmt_ge_rops_driver = { + .probe = wmt_ge_rops_probe, + .remove = __devexit_p(wmt_ge_rops_remove), + .driver = { + .owner = THIS_MODULE, + .name = "wmt_ge_rops", + }, +}; + +static int __init wmt_ge_rops_init(void) +{ + return platform_driver_register(&wmt_ge_rops_driver); +} + +static void __exit wmt_ge_rops_exit(void) +{ + platform_driver_unregister(&wmt_ge_rops_driver); +} + +module_init(wmt_ge_rops_init); +module_exit(wmt_ge_rops_exit); + +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com"); +MODULE_DESCRIPTION("Accelerators for raster operations using " + "WonderMedia Graphics Engine"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h new file mode 100644 index 000000000000..87380751a443 --- /dev/null +++ b/drivers/video/wmt_ge_rops.h @@ -0,0 +1,5 @@ +extern void wmt_ge_fillrect(struct fb_info *info, + const struct fb_fillrect *rect); +extern void wmt_ge_copyarea(struct fb_info *info, + const struct fb_copyarea *area); +extern int wmt_ge_sync(struct fb_info *info); diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 428d273be727..4abb0b9ed653 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -492,7 +492,7 @@ xenfb_make_preferred_console(void) return; acquire_console_sem(); - for (c = console_drivers; c; c = c->next) { + for_each_console(c) { if (!strcmp(c->name, "tty") && c->index == 0) break; } |