From 9f1442bbf9fd68d8e190c91ab294131dd5c289ee Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Sun, 28 Dec 2008 00:40:29 +0800 Subject: [ARM] pxa/tavorevb: update board support (smartpanel LCD + keypad) Signed-off-by: Eric Miao --- arch/arm/mach-pxa/include/mach/mfp-pxa930.h | 1 + arch/arm/mach-pxa/tavorevb.c | 412 ++++++++++++++++++++++++++++ 2 files changed, 413 insertions(+) diff --git a/arch/arm/mach-pxa/include/mach/mfp-pxa930.h b/arch/arm/mach-pxa/include/mach/mfp-pxa930.h index fabd9b4df82..fa73f56a137 100644 --- a/arch/arm/mach-pxa/include/mach/mfp-pxa930.h +++ b/arch/arm/mach-pxa/include/mach/mfp-pxa930.h @@ -421,6 +421,7 @@ #define GPIO20_PWM0 MFP_CFG_LPM(GPIO20, AF2, PULL_LOW) #define GPIO21_PWM2 MFP_CFG_LPM(GPIO21, AF3, PULL_LOW) #define GPIO22_PWM3 MFP_CFG_LPM(GPIO22, AF3, PULL_LOW) +#define GPIO32_PWM0 MFP_CFG_LPM(GPIO32, AF4, PULL_LOW) /* CIR */ #define GPIO46_CIR_OUT MFP_CFG(GPIO46, AF1) diff --git a/arch/arm/mach-pxa/tavorevb.c b/arch/arm/mach-pxa/tavorevb.c index 589d32b4fc4..58ef08a5224 100644 --- a/arch/arm/mach-pxa/tavorevb.c +++ b/arch/arm/mach-pxa/tavorevb.c @@ -18,12 +18,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include "devices.h" #include "generic.h" @@ -33,6 +36,45 @@ static mfp_cfg_t tavorevb_mfp_cfg[] __initdata = { /* Ethernet */ DF_nCS1_nCS3, GPIO47_GPIO, + + /* LCD */ + GPIO23_LCD_DD0, + GPIO24_LCD_DD1, + GPIO25_LCD_DD2, + GPIO26_LCD_DD3, + GPIO27_LCD_DD4, + GPIO28_LCD_DD5, + GPIO29_LCD_DD6, + GPIO44_LCD_DD7, + GPIO21_LCD_CS, + GPIO22_LCD_CS2, + + GPIO17_LCD_FCLK_RD, + GPIO18_LCD_LCLK_A0, + GPIO19_LCD_PCLK_WR, + + /* LCD Backlight */ + GPIO43_PWM3, /* primary backlight */ + GPIO32_PWM0, /* secondary backlight */ + + /* Keypad */ + GPIO0_KP_MKIN_0, + GPIO2_KP_MKIN_1, + GPIO4_KP_MKIN_2, + GPIO6_KP_MKIN_3, + GPIO8_KP_MKIN_4, + GPIO10_KP_MKIN_5, + GPIO12_KP_MKIN_6, + GPIO1_KP_MKOUT_0, + GPIO3_KP_MKOUT_1, + GPIO5_KP_MKOUT_2, + GPIO7_KP_MKOUT_3, + GPIO9_KP_MKOUT_4, + GPIO11_KP_MKOUT_5, + GPIO13_KP_MKOUT_6, + + GPIO14_KP_DKIN_2, + GPIO15_KP_DKIN_3, }; #define TAVOREVB_ETH_PHYS (0x14000000) @@ -64,12 +106,382 @@ static struct platform_device smc91x_device = { }, }; +#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE) +static unsigned int tavorevb_matrix_key_map[] = { + /* KEY(row, col, key_code) */ + KEY(0, 4, KEY_A), KEY(0, 5, KEY_B), KEY(0, 6, KEY_C), + KEY(1, 4, KEY_E), KEY(1, 5, KEY_F), KEY(1, 6, KEY_G), + KEY(2, 4, KEY_I), KEY(2, 5, KEY_J), KEY(2, 6, KEY_K), + KEY(3, 4, KEY_M), KEY(3, 5, KEY_N), KEY(3, 6, KEY_O), + KEY(4, 5, KEY_R), KEY(4, 6, KEY_S), + KEY(5, 4, KEY_U), KEY(5, 4, KEY_V), KEY(5, 6, KEY_W), + + KEY(6, 4, KEY_Y), KEY(6, 5, KEY_Z), + + KEY(0, 3, KEY_0), KEY(2, 0, KEY_1), KEY(2, 1, KEY_2), KEY(2, 2, KEY_3), + KEY(2, 3, KEY_4), KEY(1, 0, KEY_5), KEY(1, 1, KEY_6), KEY(1, 2, KEY_7), + KEY(1, 3, KEY_8), KEY(0, 2, KEY_9), + + KEY(6, 6, KEY_SPACE), + KEY(0, 0, KEY_KPASTERISK), /* * */ + KEY(0, 1, KEY_KPDOT), /* # */ + + KEY(4, 1, KEY_UP), + KEY(4, 3, KEY_DOWN), + KEY(4, 0, KEY_LEFT), + KEY(4, 2, KEY_RIGHT), + KEY(6, 0, KEY_HOME), + KEY(3, 2, KEY_END), + KEY(6, 1, KEY_DELETE), + KEY(5, 2, KEY_BACK), + KEY(6, 3, KEY_CAPSLOCK), /* KEY_LEFTSHIFT), */ + + KEY(4, 4, KEY_ENTER), /* scroll push */ + KEY(6, 2, KEY_ENTER), /* keypad action */ + + KEY(3, 1, KEY_SEND), + KEY(5, 3, KEY_RECORD), + KEY(5, 0, KEY_VOLUMEUP), + KEY(5, 1, KEY_VOLUMEDOWN), + + KEY(3, 0, KEY_F22), /* soft1 */ + KEY(3, 3, KEY_F23), /* soft2 */ +}; + +static struct pxa27x_keypad_platform_data tavorevb_keypad_info = { + .matrix_key_rows = 7, + .matrix_key_cols = 7, + .matrix_key_map = tavorevb_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(tavorevb_matrix_key_map), + .debounce_interval = 30, +}; + +static void __init tavorevb_init_keypad(void) +{ + pxa_set_keypad_info(&tavorevb_keypad_info); +} +#else +static inline void tavorevb_init_keypad(void) {} +#endif /* CONFIG_KEYBOARD_PXA27x || CONFIG_KEYBOARD_PXA27x_MODULE */ + +#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE) +static struct platform_pwm_backlight_data tavorevb_backlight_data[] = { + [0] = { + /* primary backlight */ + .pwm_id = 2, + .max_brightness = 100, + .dft_brightness = 100, + .pwm_period_ns = 100000, + }, + [1] = { + /* secondary backlight */ + .pwm_id = 0, + .max_brightness = 100, + .dft_brightness = 100, + .pwm_period_ns = 100000, + }, +}; + +static struct platform_device tavorevb_backlight_devices[] = { + [0] = { + .name = "pwm-backlight", + .id = 0, + .dev = { + .platform_data = &tavorevb_backlight_data[0], + }, + }, + [1] = { + .name = "pwm-backlight", + .id = 1, + .dev = { + .platform_data = &tavorevb_backlight_data[1], + }, + }, +}; + +static uint16_t panel_init[] = { + /* DSTB OUT */ + SMART_CMD(0x00), + SMART_CMD_NOOP, + SMART_DELAY(1), + + SMART_CMD(0x00), + SMART_CMD_NOOP, + SMART_DELAY(1), + + SMART_CMD(0x00), + SMART_CMD_NOOP, + SMART_DELAY(1), + + /* STB OUT */ + SMART_CMD(0x00), + SMART_CMD(0x1D), + SMART_DAT(0x00), + SMART_DAT(0x05), + SMART_DELAY(1), + + /* P-ON Init sequence */ + SMART_CMD(0x00), /* OSC ON */ + SMART_CMD(0x00), + SMART_DAT(0x00), + SMART_DAT(0x01), + SMART_CMD(0x00), + SMART_CMD(0x01), /* SOURCE DRIVER SHIFT DIRECTION and display RAM setting */ + SMART_DAT(0x01), + SMART_DAT(0x27), + SMART_CMD(0x00), + SMART_CMD(0x02), /* LINE INV */ + SMART_DAT(0x02), + SMART_DAT(0x00), + SMART_CMD(0x00), + SMART_CMD(0x03), /* IF mode(1) */ + SMART_DAT(0x01), /* 8bit smart mode(8-8),high speed write mode */ + SMART_DAT(0x30), + SMART_CMD(0x07), + SMART_CMD(0x00), /* RAM Write Mode */ + SMART_DAT(0x00), + SMART_DAT(0x03), + SMART_CMD(0x00), + + /* DISPLAY Setting, 262K, fixed(NO scroll), no split screen */ + SMART_CMD(0x07), + SMART_DAT(0x40), /* 16/18/19 BPP */ + SMART_DAT(0x00), + SMART_CMD(0x00), + SMART_CMD(0x08), /* BP, FP Seting, BP=2H, FP=3H */ + SMART_DAT(0x03), + SMART_DAT(0x02), + SMART_CMD(0x00), + SMART_CMD(0x0C), /* IF mode(2), using internal clock & MPU */ + SMART_DAT(0x00), + SMART_DAT(0x00), + SMART_CMD(0x00), + SMART_CMD(0x0D), /* Frame setting, 1Min. Frequence, 16CLK */ + SMART_DAT(0x00), + SMART_DAT(0x10), + SMART_CMD(0x00), + SMART_CMD(0x12), /* Timing(1),ASW W=4CLK, ASW ST=1CLK */ + SMART_DAT(0x03), + SMART_DAT(0x02), + SMART_CMD(0x00), + SMART_CMD(0x13), /* Timing(2),OEV ST=0.5CLK, OEV ED=1CLK */ + SMART_DAT(0x01), + SMART_DAT(0x02), + SMART_CMD(0x00), + SMART_CMD(0x14), /* Timing(3), ASW HOLD=0.5CLK */ + SMART_DAT(0x00), + SMART_DAT(0x00), + SMART_CMD(0x00), + SMART_CMD(0x15), /* Timing(4), CKV ST=0CLK, CKV ED=1CLK */ + SMART_DAT(0x20), + SMART_DAT(0x00), + SMART_CMD(0x00), + SMART_CMD(0x1C), + SMART_DAT(0x00), + SMART_DAT(0x00), + SMART_CMD(0x03), + SMART_CMD(0x00), + SMART_DAT(0x04), + SMART_DAT(0x03), + SMART_CMD(0x03), + SMART_CMD(0x01), + SMART_DAT(0x03), + SMART_DAT(0x04), + SMART_CMD(0x03), + SMART_CMD(0x02), + SMART_DAT(0x04), + SMART_DAT(0x03), + SMART_CMD(0x03), + SMART_CMD(0x03), + SMART_DAT(0x03), + SMART_DAT(0x03), + SMART_CMD(0x03), + SMART_CMD(0x04), + SMART_DAT(0x01), + SMART_DAT(0x01), + SMART_CMD(0x03), + SMART_CMD(0x05), + SMART_DAT(0x00), + SMART_DAT(0x00), + SMART_CMD(0x04), + SMART_CMD(0x02), + SMART_DAT(0x00), + SMART_DAT(0x00), + SMART_CMD(0x04), + SMART_CMD(0x03), + SMART_DAT(0x01), + SMART_DAT(0x3F), + SMART_DELAY(0), + + /* DISP RAM setting: 240*320 */ + SMART_CMD(0x04), /* HADDR, START 0 */ + SMART_CMD(0x06), + SMART_DAT(0x00), + SMART_DAT(0x00), /* x1,3 */ + SMART_CMD(0x04), /* HADDR, END 4 */ + SMART_CMD(0x07), + SMART_DAT(0x00), + SMART_DAT(0xEF), /* x2, 7 */ + SMART_CMD(0x04), /* VADDR, START 8 */ + SMART_CMD(0x08), + SMART_DAT(0x00), /* y1, 10 */ + SMART_DAT(0x00), /* y1, 11 */ + SMART_CMD(0x04), /* VADDR, END 12 */ + SMART_CMD(0x09), + SMART_DAT(0x01), /* y2, 14 */ + SMART_DAT(0x3F), /* y2, 15 */ + SMART_CMD(0x02), /* RAM ADDR SETTING 16 */ + SMART_CMD(0x00), + SMART_DAT(0x00), + SMART_DAT(0x00), /* x1, 19 */ + SMART_CMD(0x02), /* RAM ADDR SETTING 20 */ + SMART_CMD(0x01), + SMART_DAT(0x00), /* y1, 22 */ + SMART_DAT(0x00), /* y1, 23 */ +}; + +static uint16_t panel_on[] = { + /* Power-IC ON */ + SMART_CMD(0x01), + SMART_CMD(0x02), + SMART_DAT(0x07), + SMART_DAT(0x7D), + SMART_CMD(0x01), + SMART_CMD(0x03), + SMART_DAT(0x00), + SMART_DAT(0x05), + SMART_CMD(0x01), + SMART_CMD(0x04), + SMART_DAT(0x00), + SMART_DAT(0x00), + SMART_CMD(0x01), + SMART_CMD(0x05), + SMART_DAT(0x00), + SMART_DAT(0x15), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0xC0), + SMART_DAT(0x10), + SMART_DELAY(30), + + /* DISP ON */ + SMART_CMD(0x01), + SMART_CMD(0x01), + SMART_DAT(0x00), + SMART_DAT(0x01), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0xFF), + SMART_DAT(0xFE), + SMART_DELAY(150), +}; + +static uint16_t panel_off[] = { + SMART_CMD(0x00), + SMART_CMD(0x1E), + SMART_DAT(0x00), + SMART_DAT(0x0A), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0xFF), + SMART_DAT(0xEE), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0xF8), + SMART_DAT(0x12), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0xE8), + SMART_DAT(0x11), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0xC0), + SMART_DAT(0x11), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0x40), + SMART_DAT(0x11), + SMART_CMD(0x01), + SMART_CMD(0x00), + SMART_DAT(0x00), + SMART_DAT(0x10), +}; + +static uint16_t update_framedata[] = { + /* write ram */ + SMART_CMD(0x02), + SMART_CMD(0x02), + + /* write frame data */ + SMART_CMD_WRITE_FRAME, +}; + +static void ltm020d550_lcd_power(int on, struct fb_var_screeninfo *var) +{ + struct fb_info *info = container_of(var, struct fb_info, var); + + if (on) { + pxafb_smart_queue(info, ARRAY_AND_SIZE(panel_init)); + pxafb_smart_queue(info, ARRAY_AND_SIZE(panel_on)); + } else { + pxafb_smart_queue(info, ARRAY_AND_SIZE(panel_off)); + } + + if (pxafb_smart_flush(info)) + pr_err("%s: timed out\n", __func__); +} + +static void ltm020d550_update(struct fb_info *info) +{ + pxafb_smart_queue(info, ARRAY_AND_SIZE(update_framedata)); + pxafb_smart_flush(info); +} + +static struct pxafb_mode_info toshiba_ltm020d550_modes[] = { + [0] = { + .xres = 240, + .yres = 320, + .bpp = 16, + .a0csrd_set_hld = 30, + .a0cswr_set_hld = 30, + .wr_pulse_width = 30, + .rd_pulse_width = 170, + .op_hold_time = 30, + .cmd_inh_time = 60, + + /* L_LCLK_A0 and L_LCLK_RD active low */ + .sync = FB_SYNC_HOR_HIGH_ACT | + FB_SYNC_VERT_HIGH_ACT, + }, +}; + +static struct pxafb_mach_info tavorevb_lcd_info = { + .modes = toshiba_ltm020d550_modes, + .num_modes = 1, + .lcd_conn = LCD_SMART_PANEL_8BPP | LCD_PCLK_EDGE_FALL, + .pxafb_lcd_power = ltm020d550_lcd_power, + .smart_update = ltm020d550_update, +}; + +static void __init tavorevb_init_lcd(void) +{ + platform_device_register(&tavorevb_backlight_devices[0]); + platform_device_register(&tavorevb_backlight_devices[1]); + set_pxa_fb_info(&tavorevb_lcd_info); +} +#else +static inline void tavorevb_init_lcd(void) {} +#endif /* CONFIG_FB_PXA || CONFIG_FB_PXA_MODULE */ + static void __init tavorevb_init(void) { /* initialize MFP configurations */ pxa3xx_mfp_config(ARRAY_AND_SIZE(tavorevb_mfp_cfg)); platform_device_register(&smc91x_device); + + tavorevb_init_lcd(); + tavorevb_init_keypad(); } MACHINE_START(TAVOREVB, "PXA930 Evaluation Board (aka TavorEVB)") -- cgit v1.2.3 From 6769717d5d51596618f6b143008d8ace11ec8a69 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 18 Dec 2008 11:10:32 +0800 Subject: [ARM] rtc-sa1100: don't assume CLOCK_TICK_RATE to be a constant As Nicolas and Russell pointed out, CLOCK_TICK_RATE is no more a constant on PXA when multiple processors and platforms are selected, change TIMER_FREQ in rtc-sa1100.c into a variable. Since the code to decide the clock tick rate is re-used from timer.c, introduce a common get_clock_tick_rate() for this. Signed-off-by: Eric Miao Acked-by: Nicolas Pitre --- arch/arm/mach-pxa/generic.c | 16 ++++++++++++++++ arch/arm/mach-pxa/include/mach/hardware.h | 2 ++ arch/arm/mach-pxa/include/mach/timex.h | 8 ++++++++ arch/arm/mach-pxa/time.c | 10 +--------- arch/arm/mach-sa1100/include/mach/hardware.h | 4 ++++ drivers/rtc/rtc-sa1100.c | 12 +++++++----- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c index 85ed0b33331..0ccc91c92c4 100644 --- a/arch/arm/mach-pxa/generic.c +++ b/arch/arm/mach-pxa/generic.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,21 @@ void clear_reset_status(unsigned int mask) pxa3xx_clear_reset_status(mask); } +unsigned long get_clock_tick_rate(void) +{ + unsigned long clock_tick_rate; + + if (cpu_is_pxa25x()) + clock_tick_rate = 3686400; + else if (machine_is_mainstone()) + clock_tick_rate = 3249600; + else + clock_tick_rate = 3250000; + + return clock_tick_rate; +} +EXPORT_SYMBOL(get_clock_tick_rate); + /* * Get the clock frequency as reflected by CCCR and the turbo flag. * We assume these values have been applied via a fcs. diff --git a/arch/arm/mach-pxa/include/mach/hardware.h b/arch/arm/mach-pxa/include/mach/hardware.h index e2d6784aa7e..c666796911c 100644 --- a/arch/arm/mach-pxa/include/mach/hardware.h +++ b/arch/arm/mach-pxa/include/mach/hardware.h @@ -291,6 +291,8 @@ */ extern unsigned int get_memclk_frequency_10khz(void); +/* return the clock tick rate of the OS timer */ +extern unsigned long get_clock_tick_rate(void); #endif #if defined(CONFIG_MACH_ARMCORE) && defined(CONFIG_PCI) diff --git a/arch/arm/mach-pxa/include/mach/timex.h b/arch/arm/mach-pxa/include/mach/timex.h index b05fc6683c4..af6760a50e1 100644 --- a/arch/arm/mach-pxa/include/mach/timex.h +++ b/arch/arm/mach-pxa/include/mach/timex.h @@ -10,6 +10,14 @@ * published by the Free Software Foundation. */ +/* Various drivers are still using the constant of CLOCK_TICK_RATE, for + * those drivers to at least work, the definition is provided here. + * + * NOTE: this is no longer accurate when multiple processors and boards + * are selected, newer drivers should not depend on this any more. Use + * either the clocksource/clockevent or get this at run-time by calling + * get_clock_tick_rate() (as defined in generic.c). + */ #if defined(CONFIG_PXA25x) /* PXA250/210 timer base */ diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c index f8a9a62959e..986d494a183 100644 --- a/arch/arm/mach-pxa/time.c +++ b/arch/arm/mach-pxa/time.c @@ -23,7 +23,6 @@ #include #include #include -#include /* * This is PXA's sched_clock implementation. This has a resolution @@ -150,18 +149,11 @@ static struct irqaction pxa_ost0_irq = { static void __init pxa_timer_init(void) { - unsigned long clock_tick_rate; + unsigned long clock_tick_rate = get_clock_tick_rate(); OIER = 0; OSSR = OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3; - if (cpu_is_pxa25x()) - clock_tick_rate = 3686400; - else if (machine_is_mainstone()) - clock_tick_rate = 3249600; - else - clock_tick_rate = 3250000; - set_oscr2ns_scale(clock_tick_rate); ckevt_pxa_osmr0.mult = diff --git a/arch/arm/mach-sa1100/include/mach/hardware.h b/arch/arm/mach-sa1100/include/mach/hardware.h index b70846c096a..60711822b12 100644 --- a/arch/arm/mach-sa1100/include/mach/hardware.h +++ b/arch/arm/mach-sa1100/include/mach/hardware.h @@ -59,6 +59,10 @@ # define __REG(x) (*((volatile unsigned long *)io_p2v(x))) # define __PREG(x) (io_v2p((unsigned long)&(x))) +static inline unsigned long get_clock_tick_rate(void) +{ + return 3686400; +} #else # define __REG(x) io_p2v(x) diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 66a9bb85bbe..d26a5f82aab 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -38,11 +38,11 @@ #include #endif -#define TIMER_FREQ CLOCK_TICK_RATE #define RTC_DEF_DIVIDER 32768 - 1 #define RTC_DEF_TRIM 0 static unsigned long rtc_freq = 1024; +static unsigned long timer_freq; static struct rtc_time rtc_alarm; static DEFINE_SPINLOCK(sa1100_rtc_lock); @@ -157,7 +157,7 @@ static irqreturn_t timer1_interrupt(int irq, void *dev_id) rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF); if (rtc_timer1_count == 1) - rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))); + rtc_timer1_count = (rtc_freq * ((1 << 30) / (timer_freq >> 2))); return IRQ_HANDLED; } @@ -166,7 +166,7 @@ static int sa1100_rtc_read_callback(struct device *dev, int data) { if (data & RTC_PF) { /* interpolate missed periods and set match for the next */ - unsigned long period = TIMER_FREQ/rtc_freq; + unsigned long period = timer_freq / rtc_freq; unsigned long oscr = OSCR; unsigned long osmr1 = OSMR1; unsigned long missed = (oscr - osmr1)/period; @@ -263,7 +263,7 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd, return 0; case RTC_PIE_ON: spin_lock_irq(&sa1100_rtc_lock); - OSMR1 = TIMER_FREQ/rtc_freq + OSCR; + OSMR1 = timer_freq / rtc_freq + OSCR; OIER |= OIER_E1; rtc_timer1_count = 1; spin_unlock_irq(&sa1100_rtc_lock); @@ -271,7 +271,7 @@ static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd, case RTC_IRQP_READ: return put_user(rtc_freq, (unsigned long *)arg); case RTC_IRQP_SET: - if (arg < 1 || arg > TIMER_FREQ) + if (arg < 1 || arg > timer_freq) return -EINVAL; rtc_freq = arg; return 0; @@ -352,6 +352,8 @@ static int sa1100_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; + timer_freq = get_clock_tick_rate(); + /* * According to the manual we should be able to let RTTR be zero * and then a default diviser for a 32.768KHz clock is used. -- cgit v1.2.3 From 28a623855f737a905b90a1616107ad813bfcd6e9 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Wed, 24 Dec 2008 11:32:45 +0800 Subject: [ARM] sa1100_wdt: don't assume CLOCK_TICK_RATE to be a constant See description of commit: [ARM] rtc-sa1100: don't assume CLOCK_TICK_RATE to be a constant for additional information. Signed-off-by: Eric Miao --- drivers/watchdog/sa1100_wdt.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index ed01e4c2bef..87977156ddb 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -35,8 +35,7 @@ #include #include -#define OSCR_FREQ CLOCK_TICK_RATE - +static unsigned long oscr_freq; static unsigned long sa1100wdt_users; static int pre_margin; static int boot_status; @@ -123,12 +122,12 @@ static long sa1100dog_ioctl(struct file *file, unsigned int cmd, break; } - pre_margin = OSCR_FREQ * time; + pre_margin = oscr_freq * time; OSMR3 = OSCR + pre_margin; /*fall through*/ case WDIOC_GETTIMEOUT: - ret = put_user(pre_margin / OSCR_FREQ, p); + ret = put_user(pre_margin / oscr_freq, p); break; } return ret; @@ -155,6 +154,8 @@ static int __init sa1100dog_init(void) { int ret; + oscr_freq = get_clock_tick_rate(); + /* * Read the reset status, and save it for later. If * we suspend, RCSR will be cleared, and the watchdog @@ -162,7 +163,7 @@ static int __init sa1100dog_init(void) */ boot_status = (reset_status & RESET_STATUS_WATCHDOG) ? WDIOF_CARDRESET : 0; - pre_margin = OSCR_FREQ * margin; + pre_margin = oscr_freq * margin; ret = misc_register(&sa1100dog_miscdev); if (ret == 0) -- cgit v1.2.3 From 5bfb4093be6ac7b6c06c8e6461d85241654acc61 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 25 Dec 2008 17:19:02 +0800 Subject: [ARM] pxa: add document on the MFP design and how to use it Signed-off-by: Eric Miao --- Documentation/arm/pxa/mfp.txt | 286 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 Documentation/arm/pxa/mfp.txt diff --git a/Documentation/arm/pxa/mfp.txt b/Documentation/arm/pxa/mfp.txt new file mode 100644 index 00000000000..a179e5bc02c --- /dev/null +++ b/Documentation/arm/pxa/mfp.txt @@ -0,0 +1,286 @@ + MFP Configuration for PXA2xx/PXA3xx Processors + + Eric Miao + +MFP stands for Multi-Function Pin, which is the pin-mux logic on PXA3xx and +later PXA series processors. This document describes the existing MFP API, +and how board/platform driver authors could make use of it. + + Basic Concept +=============== + +Unlike the GPIO alternate function settings on PXA25x and PXA27x, a new MFP +mechanism is introduced from PXA3xx to completely move the pin-mux functions +out of the GPIO controller. In addition to pin-mux configurations, the MFP +also controls the low power state, driving strength, pull-up/down and event +detection of each pin. Below is a diagram of internal connections between +the MFP logic and the remaining SoC peripherals: + + +--------+ + | |--(GPIO19)--+ + | GPIO | | + | |--(GPIO...) | + +--------+ | + | +---------+ + +--------+ +------>| | + | PWM2 |--(PWM_OUT)-------->| MFP | + +--------+ +------>| |-------> to external PAD + | +---->| | + +--------+ | | +-->| | + | SSP2 |---(TXD)----+ | | +---------+ + +--------+ | | + | | + +--------+ | | + | Keypad |--(MKOUT4)----+ | + +--------+ | + | + +--------+ | + | UART2 |---(TXD)--------+ + +--------+ + +NOTE: the external pad is named as MFP_PIN_GPIO19, it doesn't necessarily +mean it's dedicated for GPIO19, only as a hint that internally this pin +can be routed from GPIO19 of the GPIO controller. + +To better understand the change from PXA25x/PXA27x GPIO alternate function +to this new MFP mechanism, here are several key points: + + 1. GPIO controller on PXA3xx is now a dedicated controller, same as other + internal controllers like PWM, SSP and UART, with 128 internal signals + which can be routed to external through one or more MFPs (e.g. GPIO<0> + can be routed through either MFP_PIN_GPIO0 as well as MFP_PIN_GPIO0_2, + see arch/arm/mach-pxa/mach/include/mfp-pxa300.h) + + 2. Alternate function configuration is removed from this GPIO controller, + the remaining functions are pure GPIO-specific, i.e. + + - GPIO signal level control + - GPIO direction control + - GPIO level change detection + + 3. Low power state for each pin is now controlled by MFP, this means the + PGSRx registers on PXA2xx are now useless on PXA3xx + + 4. Wakeup detection is now controlled by MFP, PWER does not control the + wakeup from GPIO(s) any more, depending on the sleeping state, ADxER + (as defined in pxa3xx-regs.h) controls the wakeup from MFP + +NOTE: with such a clear separation of MFP and GPIO, by GPIO we normally +mean it is a GPIO signal, and by MFP or pin xxx, we mean a physical +pad (or ball). + + MFP API Usage +=============== + +For board code writers, here are some guidelines: + +1. include ONE of the following header files in your .c: + + - #include + - #include + - #include + - #include + - #include + + NOTE: only one file in your .c, depending on the processors used, + because pin configuration definitions may conflict in these file (i.e. + same name, different meaning and settings on different processors). E.g. + for zylonite platform, which support both PXA300/PXA310 and PXA320, two + separate files are introduced: zylonite_pxa300.c and zylonite_pxa320.c + (in addition to handle MFP configuration differences, they also handle + the other differences between the two combinations). + + NOTE: PXA300 and PXA310 are almost identical in pin configurations (with + PXA310 supporting some additional ones), thus the difference is actually + covered in a single mfp-pxa300.h. + +2. prepare an array for the initial pin configurations, e.g.: + + static unsigned long mainstone_pin_config[] __initdata = { + /* Chip Select */ + GPIO15_nCS_1, + + /* LCD - 16bpp Active TFT */ + GPIOxx_TFT_LCD_16BPP, + GPIO16_PWM0_OUT, /* Backlight */ + + /* MMC */ + GPIO32_MMC_CLK, + GPIO112_MMC_CMD, + GPIO92_MMC_DAT_0, + GPIO109_MMC_DAT_1, + GPIO110_MMC_DAT_2, + GPIO111_MMC_DAT_3, + + ... + + /* GPIO */ + GPIO1_GPIO | WAKEUP_ON_EDGE_BOTH, + }; + + a) once the pin configurations are passed to pxa{2xx,3xx}_mfp_config(), + and written to the actual registers, they are useless and may discard, + adding '__initdata' will help save some additional bytes here. + + b) when there is only one possible pin configurations for a component, + some simplified definitions can be used, e.g. GPIOxx_TFT_LCD_16BPP on + PXA25x and PXA27x processors + + c) if by board design, a pin can be configured to wake up the system + from low power state, it can be 'OR'ed with any of: + + WAKEUP_ON_EDGE_BOTH + WAKEUP_ON_EDGE_RISE + WAKEUP_ON_EDGE_FALL + WAKEUP_ON_LEVEL_HIGH - specifically for enabling of keypad GPIOs, + + to indicate that this pin has the capability of wake-up the system, + and on which edge(s). This, however, doesn't necessarily mean the + pin _will_ wakeup the system, it will only when set_irq_wake() is + invoked with the corresponding GPIO IRQ (GPIO_IRQ(xx) or gpio_to_irq()) + and eventually calls gpio_set_wake() for the actual register setting. + + d) although PXA3xx MFP supports edge detection on each pin, the + internal logic will only wakeup the system when those specific bits + in ADxER registers are set, which can be well mapped to the + corresponding peripheral, thus set_irq_wake() can be called with + the peripheral IRQ to enable the wakeup. + + + MFP on PXA3xx +=============== + +Every external I/O pad on PXA3xx (excluding those for special purpose) has +one MFP logic associated, and is controlled by one MFP register (MFPR). + +The MFPR has the following bit definitions (for PXA300/PXA310/PXA320): + + 31 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +-------------------------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | RESERVED |PS|PU|PD| DRIVE |SS|SD|SO|EC|EF|ER|--| AF_SEL | + +-------------------------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + Bit 3: RESERVED + Bit 4: EDGE_RISE_EN - enable detection of rising edge on this pin + Bit 5: EDGE_FALL_EN - enable detection of falling edge on this pin + Bit 6: EDGE_CLEAR - disable edge detection on this pin + Bit 7: SLEEP_OE_N - enable outputs during low power modes + Bit 8: SLEEP_DATA - output data on the pin during low power modes + Bit 9: SLEEP_SEL - selection control for low power modes signals + Bit 13: PULLDOWN_EN - enable the internal pull-down resistor on this pin + Bit 14: PULLUP_EN - enable the internal pull-up resistor on this pin + Bit 15: PULL_SEL - pull state controlled by selected alternate function + (0) or by PULL{UP,DOWN}_EN bits (1) + + Bit 0 - 2: AF_SEL - alternate function selection, 8 possibilities, from 0-7 + Bit 10-12: DRIVE - drive strength and slew rate + 0b000 - fast 1mA + 0b001 - fast 2mA + 0b002 - fast 3mA + 0b003 - fast 4mA + 0b004 - slow 6mA + 0b005 - fast 6mA + 0b006 - slow 10mA + 0b007 - fast 10mA + + MFP Design for PXA2xx/PXA3xx +============================== + +Due to the difference of pin-mux handling between PXA2xx and PXA3xx, a unified +MFP API is introduced to cover both series of processors. + +The basic idea of this design is to introduce definitions for all possible pin +configurations, these definitions are processor and platform independent, and +the actual API invoked to convert these definitions into register settings and +make them effective there-after. + + Files Involved + -------------- + + - arch/arm/mach-pxa/include/mach/mfp.h + + for + 1. Unified pin definitions - enum constants for all configurable pins + 2. processor-neutral bit definitions for a possible MFP configuration + + - arch/arm/mach-pxa/include/mach/mfp-pxa3xx.h + + for PXA3xx specific MFPR register bit definitions and PXA3xx common pin + configurations + + - arch/arm/mach-pxa/include/mach/mfp-pxa2xx.h + + for PXA2xx specific definitions and PXA25x/PXA27x common pin configurations + + - arch/arm/mach-pxa/include/mach/mfp-pxa25x.h + arch/arm/mach-pxa/include/mach/mfp-pxa27x.h + arch/arm/mach-pxa/include/mach/mfp-pxa300.h + arch/arm/mach-pxa/include/mach/mfp-pxa320.h + arch/arm/mach-pxa/include/mach/mfp-pxa930.h + + for processor specific definitions + + - arch/arm/mach-pxa/mfp-pxa3xx.c + - arch/arm/mach-pxa/mfp-pxa2xx.c + + for implementation of the pin configuration to take effect for the actual + processor. + + Pin Configuration + ----------------- + + The following comments are copied from mfp.h (see the actual source code + for most updated info) + + /* + * a possible MFP configuration is represented by a 32-bit integer + * + * bit 0.. 9 - MFP Pin Number (1024 Pins Maximum) + * bit 10..12 - Alternate Function Selection + * bit 13..15 - Drive Strength + * bit 16..18 - Low Power Mode State + * bit 19..20 - Low Power Mode Edge Detection + * bit 21..22 - Run Mode Pull State + * + * to facilitate the definition, the following macros are provided + * + * MFP_CFG_DEFAULT - default MFP configuration value, with + * alternate function = 0, + * drive strength = fast 3mA (MFP_DS03X) + * low power mode = default + * edge detection = none + * + * MFP_CFG - default MFPR value with alternate function + * MFP_CFG_DRV - default MFPR value with alternate function and + * pin drive strength + * MFP_CFG_LPM - default MFPR value with alternate function and + * low power mode + * MFP_CFG_X - default MFPR value with alternate function, + * pin drive strength and low power mode + */ + + Examples of pin configurations are: + + #define GPIO94_SSP3_RXD MFP_CFG_X(GPIO94, AF1, DS08X, FLOAT) + + which reads GPIO94 can be configured as SSP3_RXD, with alternate function + selection of 1, driving strength of 0b101, and a float state in low power + modes. + + NOTE: this is the default setting of this pin being configured as SSP3_RXD + which can be modified a bit in board code, though it is not recommended to + do so, simply because this default setting is usually carefully encoded, + and is supposed to work in most cases. + + Register Settings + ----------------- + + Register settings on PXA3xx for a pin configuration is actually very + straight-forward, most bits can be converted directly into MFPR value + in a easier way. Two sets of MFPR values are calculated: the run-time + ones and the low power mode ones, to allow different settings. + + The conversion from a generic pin configuration to the actual register + settings on PXA2xx is a bit complicated: many registers are involved, + including GAFRx, GPDRx, PGSRx, PWER, PKWR, PFER and PRER. Please see + mfp-pxa2xx.c for how the conversion is made. -- cgit v1.2.3 From 77e196752bdd76a0c58ab082658d28c6a90fa40e Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 16 Dec 2008 11:54:34 +0800 Subject: [ARM] pxafb: allow video memory size to be configurable The amount of video memory size is decided according to the following order: 1. x x by default, which is the backward compatible way 2. size specified in platform data 3. size specified in module parameter 'options' string or specified in kernel boot command line (see updated Documentation/fb/pxafb.txt) And now since the memory is allocated from system memory, the pxafb_mmap can be removed and the default fb_mmap() should be working all right. Also, since we now have introduced the 'struct pxafb_dma_buff' for DMA descriptors and palettes, the allocation can be separated cleanly. NOTE: the LCD DMA actually supports chained transfer (i.e. page-based transfers), to simplify the logic and keep the performance (with less TLB misses when accessing from memory mapped user space), the memory is allocated by alloc_pages_*() to ensures it's physical contiguous. Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- Documentation/fb/pxafb.txt | 8 +- arch/arm/mach-pxa/include/mach/pxafb.h | 1 + drivers/video/pxafb.c | 131 ++++++++++++++------------------- drivers/video/pxafb.h | 17 +---- 4 files changed, 65 insertions(+), 92 deletions(-) diff --git a/Documentation/fb/pxafb.txt b/Documentation/fb/pxafb.txt index db9b8500b43..ad94b5ca009 100644 --- a/Documentation/fb/pxafb.txt +++ b/Documentation/fb/pxafb.txt @@ -5,9 +5,13 @@ The driver supports the following options, either via options= when modular or video=pxafb: when built in. For example: - modprobe pxafb options=mode:640x480-8,passive + modprobe pxafb options=vmem:2M,mode:640x480-8,passive or on the kernel command line - video=pxafb:mode:640x480-8,passive + video=pxafb:vmem:2M,mode:640x480-8,passive + +vmem: VIDEO_MEM_SIZE + Amount of video memory to allocate (can be suffixed with K or M + for kilobytes or megabytes) mode:XRESxYRES[-BPP] XRES == LCCR1_PPL + 1 diff --git a/arch/arm/mach-pxa/include/mach/pxafb.h b/arch/arm/mach-pxa/include/mach/pxafb.h index 4201a889ff4..6932720ba04 100644 --- a/arch/arm/mach-pxa/include/mach/pxafb.h +++ b/arch/arm/mach-pxa/include/mach/pxafb.h @@ -113,6 +113,7 @@ struct pxafb_mach_info { unsigned int num_modes; unsigned int lcd_conn; + unsigned long video_mem_size; u_int fixed_modes:1, cmap_inverse:1, diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index ab689597f25..25bf4b8b6b5 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -72,6 +72,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); +static unsigned long video_mem_size = 0; + static inline unsigned long lcd_readl(struct pxafb_info *fbi, unsigned int off) { @@ -498,20 +500,6 @@ static int pxafb_blank(int blank, struct fb_info *info) return 0; } -static int pxafb_mmap(struct fb_info *info, - struct vm_area_struct *vma) -{ - struct pxafb_info *fbi = (struct pxafb_info *)info; - unsigned long off = vma->vm_pgoff << PAGE_SHIFT; - - if (off < info->fix.smem_len) { - vma->vm_pgoff += fbi->video_offset / PAGE_SIZE; - return dma_mmap_writecombine(fbi->dev, vma, fbi->map_cpu, - fbi->map_dma, fbi->map_size); - } - return -EINVAL; -} - static struct fb_ops pxafb_ops = { .owner = THIS_MODULE, .fb_check_var = pxafb_check_var, @@ -521,7 +509,6 @@ static struct fb_ops pxafb_ops = { .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_blank = pxafb_blank, - .fb_mmap = pxafb_mmap, }; /* @@ -614,7 +601,7 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, dma_desc = &fbi->dma_buff->dma_desc[dma]; dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); - dma_desc->fsadr = fbi->screen_dma + offset; + dma_desc->fsadr = fbi->video_mem_phys + offset; dma_desc->fidr = 0; dma_desc->ldcmd = size; @@ -1267,69 +1254,30 @@ static int pxafb_resume(struct platform_device *dev) #define pxafb_resume NULL #endif -/* - * pxafb_map_video_memory(): - * Allocates the DRAM memory for the frame buffer. This buffer is - * remapped into a non-cached, non-buffered, memory region to - * allow palette and pixel writes to occur without flushing the - * cache. Once this area is remapped, all virtual memory - * access to the video memory should occur at the new region. - */ -static int __devinit pxafb_map_video_memory(struct pxafb_info *fbi) +static int __devinit pxafb_init_video_memory(struct pxafb_info *fbi) { - /* - * We reserve one page for the palette, plus the size - * of the framebuffer. - */ - fbi->video_offset = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); - fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + fbi->video_offset); - fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size, - &fbi->map_dma, GFP_KERNEL); - - if (fbi->map_cpu) { - /* prevent initial garbage on screen */ - memset(fbi->map_cpu, 0, fbi->map_size); - fbi->fb.screen_base = fbi->map_cpu + fbi->video_offset; - fbi->screen_dma = fbi->map_dma + fbi->video_offset; - - /* - * FIXME: this is actually the wrong thing to place in - * smem_start. But fbdev suffers from the problem that - * it needs an API which doesn't exist (in this case, - * dma_writecombine_mmap) - */ - fbi->fb.fix.smem_start = fbi->screen_dma; - fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16; - - fbi->dma_buff = (void *) fbi->map_cpu; - fbi->dma_buff_phys = fbi->map_dma; - fbi->palette_cpu = (u16 *) fbi->dma_buff->palette; + int size = PAGE_ALIGN(fbi->video_mem_size); - pr_debug("pxafb: palette_mem_size = 0x%08x\n", fbi->palette_size*sizeof(u16)); - } - - return fbi->map_cpu ? 0 : -ENOMEM; -} + fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); + if (fbi->video_mem == NULL) + return -ENOMEM; -static void pxafb_decode_mode_info(struct pxafb_info *fbi, - struct pxafb_mode_info *modes, - unsigned int num_modes) -{ - unsigned int i, smemlen; + fbi->video_mem_phys = virt_to_phys(fbi->video_mem); + fbi->video_mem_size = size; - pxafb_setmode(&fbi->fb.var, &modes[0]); + fbi->fb.fix.smem_start = fbi->video_mem_phys; + fbi->fb.fix.smem_len = fbi->video_mem_size; + fbi->fb.screen_base = fbi->video_mem; - for (i = 0; i < num_modes; i++) { - smemlen = modes[i].xres * modes[i].yres * modes[i].bpp / 8; - if (smemlen > fbi->fb.fix.smem_len) - fbi->fb.fix.smem_len = smemlen; - } + return fbi->video_mem ? 0 : -ENOMEM; } static void pxafb_decode_mach_info(struct pxafb_info *fbi, struct pxafb_mach_info *inf) { unsigned int lcd_conn = inf->lcd_conn; + struct pxafb_mode_info *m; + int i; fbi->cmap_inverse = inf->cmap_inverse; fbi->cmap_static = inf->cmap_static; @@ -1371,7 +1319,22 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi, fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL) ? LCCR3_PCP : 0; decode_mode: - pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes); + pxafb_setmode(&fbi->fb.var, &inf->modes[0]); + + /* decide video memory size as follows: + * 1. default to mode of maximum resolution + * 2. allow platform to override + * 3. allow module parameter to override + */ + for (i = 0, m = &inf->modes[0]; i < inf->num_modes; i++, m++) + fbi->video_mem_size = max_t(size_t, fbi->video_mem_size, + m->xres * m->yres * m->bpp / 8); + + if (inf->video_mem_size > fbi->video_mem_size) + fbi->video_mem_size = inf->video_mem_size; + + if (video_mem_size > fbi->video_mem_size) + fbi->video_mem_size = video_mem_size; } static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) @@ -1499,7 +1462,9 @@ static int __devinit parse_opt(struct device *dev, char *this_opt) s[0] = '\0'; - if (!strncmp(this_opt, "mode:", 5)) { + if (!strncmp(this_opt, "vmem:", 5)) { + video_mem_size = memparse(this_opt + 5, NULL); + } else if (!strncmp(this_opt, "mode:", 5)) { return parse_opt_mode(dev, this_opt); } else if (!strncmp(this_opt, "pixclock:", 9)) { mode->pixclock = simple_strtoul(this_opt+9, NULL, 0); @@ -1736,12 +1701,20 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed_free_res; } - /* Initialize video memory */ - ret = pxafb_map_video_memory(fbi); + fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); + fbi->dma_buff = dma_alloc_coherent(fbi->dev, fbi->dma_buff_size, + &fbi->dma_buff_phys, GFP_KERNEL); + if (fbi->dma_buff == NULL) { + dev_err(&dev->dev, "failed to allocate memory for DMA\n"); + ret = -ENOMEM; + goto failed_free_io; + } + + ret = pxafb_init_video_memory(fbi); if (ret) { dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret); ret = -ENOMEM; - goto failed_free_io; + goto failed_free_dma; } irq = platform_get_irq(dev, 0); @@ -1811,8 +1784,10 @@ failed_free_cmap: failed_free_irq: free_irq(irq, fbi); failed_free_mem: - dma_free_writecombine(&dev->dev, fbi->map_size, - fbi->map_cpu, fbi->map_dma); + free_pages_exact(fbi->video_mem, fbi->video_mem_size); +failed_free_dma: + dma_free_coherent(&dev->dev, fbi->dma_buff_size, + fbi->dma_buff, fbi->dma_buff_phys); failed_free_io: iounmap(fbi->mmio_base); failed_free_res: @@ -1847,8 +1822,10 @@ static int __devexit pxafb_remove(struct platform_device *dev) irq = platform_get_irq(dev, 0); free_irq(irq, fbi); - dma_free_writecombine(&dev->dev, fbi->map_size, - fbi->map_cpu, fbi->map_dma); + free_pages_exact(fbi->video_mem, fbi->video_mem_size); + + dma_free_writecombine(&dev->dev, fbi->dma_buff_size, + fbi->dma_buff, fbi->dma_buff_phys); iounmap(fbi->mmio_base); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index d8eb93fa03a..0981938682e 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -69,24 +69,15 @@ struct pxafb_info { void __iomem *mmio_base; struct pxafb_dma_buff *dma_buff; + size_t dma_buff_size; dma_addr_t dma_buff_phys; dma_addr_t fdadr[DMA_MAX]; - /* - * These are the addresses we mapped - * the framebuffer memory region to. - */ - /* raw memory addresses */ - dma_addr_t map_dma; /* physical */ - u_char * map_cpu; /* virtual */ - u_int map_size; - - /* addresses of pieces placed in raw buffer */ - u_char * screen_cpu; /* virtual address of frame buffer */ - dma_addr_t screen_dma; /* physical address of frame buffer */ + void __iomem *video_mem; /* virtual address of frame buffer */ + unsigned long video_mem_phys; /* physical address of frame buffer */ + size_t video_mem_size; /* size of the frame buffer */ u16 * palette_cpu; /* virtual address of palette memory */ u_int palette_size; - ssize_t video_offset; u_int lccr0; u_int lccr3; -- cgit v1.2.3 From 7e4b19c95c8632b543bd510ec6c710bebb53b840 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Wed, 17 Dec 2008 14:56:54 +0800 Subject: [ARM] pxafb: allow pxafb_set_par() to start from arbitrary yoffset Note the var->yres_virtual is only re-calculated from the fix.smem_len when text mode acceleration is enabled (which is default), this is due to the issue as Russell suggested below: Previous experience of doing this with the X server and acornfb is that it causes all sorts of problems - it seems to force the X server into assuming that the framebuffer should be panned no matter what settings you ask it for. The recommended workaround (implemented in acornfb) is to only do these kinds of adjustments if text mode acceleration is enabled. IIRC, the X server should be disabling text mode acceleration when it maps the framebuffer. I seem to remember that there are X servers which forget to do that though. Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- drivers/video/pxafb.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 25bf4b8b6b5..ab816cadb47 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -306,8 +306,6 @@ static void pxafb_setmode(struct fb_var_screeninfo *var, var->lower_margin = mode->lower_margin; var->sync = mode->sync; var->grayscale = mode->cmap_greyscale; - var->xres_virtual = var->xres; - var->yres_virtual = var->yres; } /* @@ -345,10 +343,14 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return -EINVAL; } - var->xres_virtual = - max(var->xres_virtual, var->xres); - var->yres_virtual = - max(var->yres_virtual, var->yres); + /* we don't support xpan, force xres_virtual to be equal to xres */ + var->xres_virtual = var->xres; + + if (var->accel_flags & FB_ACCELF_TEXT) + var->yres_virtual = fbi->fb.fix.smem_len / + (var->xres_virtual * var->bits_per_pixel / 8); + else + var->yres_virtual = max(var->yres_virtual, var->yres); /* * Setup the RGB parameters for this display. @@ -878,7 +880,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi) { u_long flags; - size_t nbytes; + size_t nbytes, offset; #if DEBUG_VAR if (!(fbi->lccr0 & LCCR0_LCDT)) { @@ -939,17 +941,18 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, fbi->reg_lccr3 |= pxafb_bpp_to_lccr3(var); - nbytes = var->yres * fbi->fb.fix.line_length; + nbytes = fbi->fb.fix.line_length * var->yres; + offset = fbi->fb.fix.line_length * var->yoffset; if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) { nbytes = nbytes / 2; - setup_frame_dma(fbi, DMA_LOWER, PAL_NONE, nbytes, nbytes); + setup_frame_dma(fbi, DMA_LOWER, PAL_NONE, offset + nbytes, nbytes); } if ((var->bits_per_pixel >= 16) || (fbi->lccr0 & LCCR0_LCDT)) - setup_frame_dma(fbi, DMA_BASE, PAL_NONE, 0, nbytes); + setup_frame_dma(fbi, DMA_BASE, PAL_NONE, offset, nbytes); else - setup_frame_dma(fbi, DMA_BASE, PAL_BASE, 0, nbytes); + setup_frame_dma(fbi, DMA_BASE, PAL_BASE, offset, nbytes); fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); @@ -1362,7 +1365,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; fbi->fb.fix.type_aux = 0; fbi->fb.fix.xpanstep = 0; - fbi->fb.fix.ypanstep = 0; + fbi->fb.fix.ypanstep = 1; fbi->fb.fix.ywrapstep = 0; fbi->fb.fix.accel = FB_ACCEL_NONE; @@ -1370,7 +1373,7 @@ static struct pxafb_info * __devinit pxafb_init_fbinfo(struct device *dev) fbi->fb.var.activate = FB_ACTIVATE_NOW; fbi->fb.var.height = -1; fbi->fb.var.width = -1; - fbi->fb.var.accel_flags = 0; + fbi->fb.var.accel_flags = FB_ACCELF_TEXT; fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; fbi->fb.fbops = &pxafb_ops; -- cgit v1.2.3 From 6e354846e807e037751fdc8faaee8ad492177113 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Wed, 17 Dec 2008 16:50:43 +0800 Subject: [ARM] pxafb: add support for FBIOPAN_DISPLAY by dma braching dma branching is enabled by extending the current setup_frame_dma() function to allow a 2nd set of frame/palette dma descriptors to be used. As a result, pxafb_dma_buff.dma_desc[], pxafb_dma_buff.pal_desc[] and pxafb_info.fdadr[] are doubled. This allows maximum re-use of the current dma setup code, although the pxafb_info.fdadr[xx] for FBRx register values looks a bit odd. Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- arch/arm/mach-pxa/include/mach/regs-lcd.h | 10 ++++- drivers/video/pxafb.c | 61 +++++++++++++++++++++++-------- drivers/video/pxafb.h | 9 +++-- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h index f817878d256..c15df557fa8 100644 --- a/arch/arm/mach-pxa/include/mach/regs-lcd.h +++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h @@ -12,13 +12,19 @@ #define LCCR3 (0x00C) /* LCD Controller Control Register 3 */ #define LCCR4 (0x010) /* LCD Controller Control Register 4 */ #define LCCR5 (0x014) /* LCD Controller Control Register 5 */ -#define DFBR0 (0x020) /* DMA Channel 0 Frame Branch Register */ -#define DFBR1 (0x024) /* DMA Channel 1 Frame Branch Register */ #define LCSR (0x038) /* LCD Controller Status Register */ #define LIIDR (0x03C) /* LCD Controller Interrupt ID Register */ #define TMEDRGBR (0x040) /* TMED RGB Seed Register */ #define TMEDCR (0x044) /* TMED Control Register */ +#define FBR0 (0x020) /* DMA Channel 0 Frame Branch Register */ +#define FBR1 (0x024) /* DMA Channel 1 Frame Branch Register */ +#define FBR2 (0x028) /* DMA Channel 2 Frame Branch Register */ +#define FBR3 (0x02C) /* DMA Channel 2 Frame Branch Register */ +#define FBR4 (0x030) /* DMA Channel 2 Frame Branch Register */ +#define FBR5 (0x110) /* DMA Channel 2 Frame Branch Register */ +#define FBR6 (0x114) /* DMA Channel 2 Frame Branch Register */ + #define CMDCR (0x100) /* Command Control Register */ #define PRSR (0x104) /* Panel Read Status Register */ diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index ab816cadb47..b43907d36d6 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -71,6 +71,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); +static void setup_base_frame(struct pxafb_info *fbi, int branch); static unsigned long video_mem_size = 0; @@ -467,6 +468,24 @@ static int pxafb_set_par(struct fb_info *info) return 0; } +static int pxafb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct pxafb_info *fbi = (struct pxafb_info *)info; + int dma = DMA_MAX + DMA_BASE; + + if (fbi->state != C_ENABLE) + return 0; + + setup_base_frame(fbi, 1); + + if (fbi->lccr0 & LCCR0_SDS) + lcd_writel(fbi, FBR1, fbi->fdadr[dma + 1] | 0x1); + + lcd_writel(fbi, FBR0, fbi->fdadr[dma] | 0x1); + return 0; +} + /* * pxafb_blank(): * Blank the display by setting all palette values to zero. Note, the @@ -506,6 +525,7 @@ static struct fb_ops pxafb_ops = { .owner = THIS_MODULE, .fb_check_var = pxafb_check_var, .fb_set_par = pxafb_set_par, + .fb_pan_display = pxafb_pan_display, .fb_setcolreg = pxafb_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, @@ -597,7 +617,7 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, struct pxafb_dma_descriptor *dma_desc, *pal_desc; unsigned int dma_desc_off, pal_desc_off; - if (dma < 0 || dma >= DMA_MAX) + if (dma < 0 || dma >= DMA_MAX * 2) return -EINVAL; dma_desc = &fbi->dma_buff->dma_desc[dma]; @@ -607,7 +627,7 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, dma_desc->fidr = 0; dma_desc->ldcmd = size; - if (pal < 0 || pal >= PAL_MAX) { + if (pal < 0 || pal >= PAL_MAX * 2) { dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off; fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off; } else { @@ -633,6 +653,27 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, return 0; } +static void setup_base_frame(struct pxafb_info *fbi, int branch) +{ + struct fb_var_screeninfo *var = &fbi->fb.var; + struct fb_fix_screeninfo *fix = &fbi->fb.fix; + unsigned int nbytes, offset; + int dma, pal, bpp = var->bits_per_pixel; + + dma = DMA_BASE + (branch ? DMA_MAX : 0); + pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); + + nbytes = fix->line_length * var->yres; + offset = fix->line_length * var->yoffset; + + if (fbi->lccr0 & LCCR0_SDS) { + nbytes = nbytes / 2; + setup_frame_dma(fbi, dma + 1, PAL_NONE, offset + nbytes, nbytes); + } + + setup_frame_dma(fbi, dma, pal, offset, nbytes); +} + #ifdef CONFIG_FB_PXA_SMARTPANEL static int setup_smart_dma(struct pxafb_info *fbi) { @@ -880,7 +921,6 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi) { u_long flags; - size_t nbytes, offset; #if DEBUG_VAR if (!(fbi->lccr0 & LCCR0_LCDT)) { @@ -935,25 +975,14 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, #endif setup_parallel_timing(fbi, var); + setup_base_frame(fbi, 0); + fbi->reg_lccr0 = fbi->lccr0 | (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM | LCCR0_OUM); fbi->reg_lccr3 |= pxafb_bpp_to_lccr3(var); - nbytes = fbi->fb.fix.line_length * var->yres; - offset = fbi->fb.fix.line_length * var->yoffset; - - if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual) { - nbytes = nbytes / 2; - setup_frame_dma(fbi, DMA_LOWER, PAL_NONE, offset + nbytes, nbytes); - } - - if ((var->bits_per_pixel >= 16) || (fbi->lccr0 & LCCR0_LCDT)) - setup_frame_dma(fbi, DMA_BASE, PAL_NONE, offset, nbytes); - else - setup_frame_dma(fbi, DMA_BASE, PAL_BASE, offset, nbytes); - fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); local_irq_restore(flags); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index 0981938682e..e0f90f4c467 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -54,11 +54,14 @@ enum { #define PALETTE_SIZE (256 * 4) #define CMD_BUFF_SIZE (1024 * 50) +/* NOTE: the palette and frame dma descriptors are doubled to allow + * the 2nd set for branch settings (FBRx) + */ struct pxafb_dma_buff { unsigned char palette[PAL_MAX * PALETTE_SIZE]; uint16_t cmd_buff[CMD_BUFF_SIZE]; - struct pxafb_dma_descriptor pal_desc[PAL_MAX]; - struct pxafb_dma_descriptor dma_desc[DMA_MAX]; + struct pxafb_dma_descriptor pal_desc[PAL_MAX * 2]; + struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2]; }; struct pxafb_info { @@ -71,7 +74,7 @@ struct pxafb_info { struct pxafb_dma_buff *dma_buff; size_t dma_buff_size; dma_addr_t dma_buff_phys; - dma_addr_t fdadr[DMA_MAX]; + dma_addr_t fdadr[DMA_MAX * 2]; void __iomem *video_mem; /* virtual address of frame buffer */ unsigned long video_mem_phys; /* physical address of frame buffer */ -- cgit v1.2.3 From a0427509a76c61984fbba4e206b617c689f419ef Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 18 Dec 2008 22:10:00 +0800 Subject: [ARM] pxafb: add palette format support for LCCR4_PAL_FOR_3 Add the palette format support for LCCR4_PAL_FOR_3, and fix the issue of LCCR4 being never assigned. Also remove the useless pxafb_set_truecolor(). Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- arch/arm/mach-pxa/include/mach/regs-lcd.h | 1 + drivers/video/pxafb.c | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h index c15df557fa8..5c522263e40 100644 --- a/arch/arm/mach-pxa/include/mach/regs-lcd.h +++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h @@ -48,6 +48,7 @@ #define LCCR4_PAL_FOR_0 (0 << 15) #define LCCR4_PAL_FOR_1 (1 << 15) #define LCCR4_PAL_FOR_2 (2 << 15) +#define LCCR4_PAL_FOR_3 (3 << 15) #define LCCR4_PAL_FOR_MASK (3 << 15) #define FDADR0 (0x200) /* DMA Channel 0 Frame Descriptor Address Register */ diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index b43907d36d6..c57f909cb7a 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -155,6 +155,12 @@ pxafb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue, val |= ((blue >> 8) & 0x000000fc); ((u32 *)(fbi->palette_cpu))[regno] = val; break; + case LCCR4_PAL_FOR_3: + val = ((red << 8) & 0x00ff0000); + val |= ((green >> 0) & 0x0000ff00); + val |= ((blue >> 8) & 0x000000ff); + ((u32 *)(fbi->palette_cpu))[regno] = val; + break; } return 0; @@ -416,11 +422,6 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } -static inline void pxafb_set_truecolor(u_int is_true_color) -{ - /* do your machine-specific setup if needed */ -} - /* * pxafb_set_par(): * Set the user defined part of the display for the specified console @@ -453,11 +454,6 @@ static int pxafb_set_par(struct fb_info *info) fbi->palette_cpu = (u16 *)&fbi->dma_buff->palette[0]; - /* - * Set (any) board control register to handle new color depth - */ - pxafb_set_truecolor(fbi->fb.fix.visual == FB_VISUAL_TRUECOLOR); - if (fbi->fb.var.bits_per_pixel >= 16) fb_dealloc_cmap(&fbi->fb.cmap); else @@ -727,6 +723,7 @@ int pxafb_smart_flush(struct fb_info *info) lcd_writel(fbi, LCCR1, fbi->reg_lccr1); lcd_writel(fbi, LCCR2, fbi->reg_lccr2); lcd_writel(fbi, LCCR3, fbi->reg_lccr3); + lcd_writel(fbi, LCCR4, fbi->reg_lccr4); lcd_writel(fbi, FDADR0, fbi->fdadr[0]); lcd_writel(fbi, FDADR6, fbi->fdadr[6]); @@ -995,6 +992,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, (lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) || (lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) || (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) || + (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) || (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) || (lcd_readl(fbi, FDADR1) != fbi->fdadr[1])) pxafb_schedule_work(fbi, C_REENABLE); @@ -1041,6 +1039,7 @@ static void pxafb_enable_controller(struct pxafb_info *fbi) return; /* Sequence from 11.7.10 */ + lcd_writel(fbi, LCCR4, fbi->reg_lccr4); lcd_writel(fbi, LCCR3, fbi->reg_lccr3); lcd_writel(fbi, LCCR2, fbi->reg_lccr2); lcd_writel(fbi, LCCR1, fbi->reg_lccr1); @@ -1313,6 +1312,7 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi, fbi->cmap_inverse = inf->cmap_inverse; fbi->cmap_static = inf->cmap_static; + fbi->lccr4 = inf->lccr4; switch (lcd_conn & LCD_TYPE_MASK) { case LCD_TYPE_MONO_STN: @@ -1337,7 +1337,6 @@ static void pxafb_decode_mach_info(struct pxafb_info *fbi, /* fall back to backward compatibility way */ fbi->lccr0 = inf->lccr0; fbi->lccr3 = inf->lccr3; - fbi->lccr4 = inf->lccr4; goto decode_mode; } -- cgit v1.2.3 From 878f5783199a95cfa91db45a6e34d2f72756fa18 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 18 Dec 2008 22:36:26 +0800 Subject: [ARM] pxafb: cleanup of the color format manipulation code 1. introduce var_to_depth() to calculate the color depth including the transparency bit 2. the conversion from 'fb_var_screeninfo' to LCCR3 BPP bits can be re- used by overlays (in OVLxC1), thus an individual pxafb_var_to_bpp() has been separated out. 3. pxafb_setmode() should really set the color bitfields correctly at begining, introduce a pxafb_set_pixfmt() for this 4. allow user apps to specify color formats within fb_var_screeninfo, and checking of this in pxafb_check_var() has been simplified as below: a) pxafb_var_to_bpp() should pass - which means a basically correct bits_per_pixel and color depth setting b) the RGBT bitfields are then forced into supported values by pxafb_set_pixfmt() Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- arch/arm/mach-pxa/include/mach/regs-lcd.h | 15 +-- drivers/video/pxafb.c | 183 +++++++++++++++++------------- 2 files changed, 107 insertions(+), 91 deletions(-) diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h index 5c522263e40..aff3b876a7b 100644 --- a/arch/arm/mach-pxa/include/mach/regs-lcd.h +++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h @@ -28,17 +28,7 @@ #define CMDCR (0x100) /* Command Control Register */ #define PRSR (0x104) /* Panel Read Status Register */ -#define LCCR3_1BPP (0 << 24) -#define LCCR3_2BPP (1 << 24) -#define LCCR3_4BPP (2 << 24) -#define LCCR3_8BPP (3 << 24) -#define LCCR3_16BPP (4 << 24) -#define LCCR3_18BPP (5 << 24) -#define LCCR3_18BPP_P (6 << 24) -#define LCCR3_19BPP (7 << 24) -#define LCCR3_19BPP_P (1 << 29) -#define LCCR3_24BPP ((1 << 29) | (1 << 24)) -#define LCCR3_25BPP ((1 << 29) | (2 << 24)) +#define LCCR3_BPP(x) ((((x) & 0x7) << 24) | (((x) & 0x8) ? (1 << 29) : 0)) #define LCCR3_PDFOR_0 (0 << 30) #define LCCR3_PDFOR_1 (1 << 30) @@ -133,9 +123,6 @@ #define LCCR3_PCD Fld (8, 0) /* Pixel Clock Divisor */ #define LCCR3_PixClkDiv(Div) (((Div) << FShft (LCCR3_PCD))) -#define LCCR3_BPP Fld (3, 24) /* Bit Per Pixel */ -#define LCCR3_Bpp(Bpp) (((Bpp) << FShft (LCCR3_BPP))) - #define LCCR3_ACB Fld (8, 8) /* AC Bias */ #define LCCR3_Acb(Acb) (((Acb) << FShft (LCCR3_ACB))) diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index c57f909cb7a..1faf52642f5 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -66,7 +66,7 @@ LCCR0_SFM | LCCR0_LDM | LCCR0_ENB) #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP | LCCR3_VSP |\ - LCCR3_PCD | LCCR3_BPP) + LCCR3_PCD | LCCR3_BPP(0xf)) static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); @@ -221,37 +221,110 @@ pxafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, return ret; } -/* - * pxafb_bpp_to_lccr3(): - * Convert a bits per pixel value to the correct bit pattern for LCCR3 - */ -static int pxafb_bpp_to_lccr3(struct fb_var_screeninfo *var) +/* calculate pixel depth, transparency bit included, >=16bpp formats _only_ */ +static inline int var_to_depth(struct fb_var_screeninfo *var) { - int ret = 0; + return var->red.length + var->green.length + + var->blue.length + var->transp.length; +} + +/* calculate 4-bit BPP value for LCCR3 and OVLxC1 */ +static int pxafb_var_to_bpp(struct fb_var_screeninfo *var) +{ + int bpp = -EINVAL; + switch (var->bits_per_pixel) { - case 1: ret = LCCR3_1BPP; break; - case 2: ret = LCCR3_2BPP; break; - case 4: ret = LCCR3_4BPP; break; - case 8: ret = LCCR3_8BPP; break; - case 16: ret = LCCR3_16BPP; break; + case 1: bpp = 0; break; + case 2: bpp = 1; break; + case 4: bpp = 2; break; + case 8: bpp = 3; break; + case 16: bpp = 4; break; case 24: - switch (var->red.length + var->green.length + - var->blue.length + var->transp.length) { - case 18: ret = LCCR3_18BPP_P | LCCR3_PDFOR_3; break; - case 19: ret = LCCR3_19BPP_P; break; + switch (var_to_depth(var)) { + case 18: bpp = 6; break; /* 18-bits/pixel packed */ + case 19: bpp = 8; break; /* 19-bits/pixel packed */ + case 24: bpp = 9; break; } break; case 32: - switch (var->red.length + var->green.length + - var->blue.length + var->transp.length) { - case 18: ret = LCCR3_18BPP | LCCR3_PDFOR_3; break; - case 19: ret = LCCR3_19BPP; break; - case 24: ret = LCCR3_24BPP | LCCR3_PDFOR_3; break; - case 25: ret = LCCR3_25BPP; break; + switch (var_to_depth(var)) { + case 18: bpp = 5; break; /* 18-bits/pixel unpacked */ + case 19: bpp = 7; break; /* 19-bits/pixel unpacked */ + case 25: bpp = 10; break; } break; } - return ret; + return bpp; +} + +/* + * pxafb_var_to_lccr3(): + * Convert a bits per pixel value to the correct bit pattern for LCCR3 + * + * NOTE: for PXA27x with overlays support, the LCCR3_PDFOR_x bits have an + * implication of the acutal use of transparency bit, which we handle it + * here separatedly. See PXA27x Developer's Manual, Section <<7.4.6 Pixel + * Formats>> for the valid combination of PDFOR, PAL_FOR for various BPP. + * + * Transparency for palette pixel formats is not supported at the moment. + */ +static uint32_t pxafb_var_to_lccr3(struct fb_var_screeninfo *var) +{ + int bpp = pxafb_var_to_bpp(var); + uint32_t lccr3; + + if (bpp < 0) + return 0; + + lccr3 = LCCR3_BPP(bpp); + + switch (var_to_depth(var)) { + case 16: lccr3 |= var->transp.length ? LCCR3_PDFOR_3 : 0; break; + case 18: lccr3 |= LCCR3_PDFOR_3; break; + case 24: lccr3 |= var->transp.length ? LCCR3_PDFOR_2 : LCCR3_PDFOR_3; + break; + case 19: + case 25: lccr3 |= LCCR3_PDFOR_0; break; + } + return lccr3; +} + +#define SET_PIXFMT(v, r, g, b, t) \ +({ \ + (v)->transp.offset = (t) ? (r) + (g) + (b) : 0; \ + (v)->transp.length = (t) ? (t) : 0; \ + (v)->blue.length = (b); (v)->blue.offset = 0; \ + (v)->green.length = (g); (v)->green.offset = (b); \ + (v)->red.length = (r); (v)->red.offset = (b) + (g); \ +}) + +/* set the RGBT bitfields of fb_var_screeninf according to + * var->bits_per_pixel and given depth + */ +static void pxafb_set_pixfmt(struct fb_var_screeninfo *var, int depth) +{ + if (depth == 0) + depth = var->bits_per_pixel; + + if (var->bits_per_pixel < 16) { + /* indexed pixel formats */ + var->red.offset = 0; var->red.length = 8; + var->green.offset = 0; var->green.length = 8; + var->blue.offset = 0; var->blue.length = 8; + var->transp.offset = 0; var->transp.length = 8; + } + + switch (depth) { + case 16: var->transp.length ? + SET_PIXFMT(var, 5, 5, 5, 1) : /* RGBT555 */ + SET_PIXFMT(var, 5, 6, 5, 0); break; /* RGB565 */ + case 18: SET_PIXFMT(var, 6, 6, 6, 0); break; /* RGB666 */ + case 19: SET_PIXFMT(var, 6, 6, 6, 1); break; /* RGBT666 */ + case 24: var->transp.length ? + SET_PIXFMT(var, 8, 8, 7, 1) : /* RGBT887 */ + SET_PIXFMT(var, 8, 8, 8, 0); break; /* RGB888 */ + case 25: SET_PIXFMT(var, 8, 8, 8, 1); break; /* RGBT888 */ + } } #ifdef CONFIG_CPU_FREQ @@ -313,6 +386,9 @@ static void pxafb_setmode(struct fb_var_screeninfo *var, var->lower_margin = mode->lower_margin; var->sync = mode->sync; var->grayscale = mode->cmap_greyscale; + + /* set the initial RGBA bitfields */ + pxafb_set_pixfmt(var, mode->depth); } /* @@ -328,6 +404,7 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct pxafb_info *fbi = (struct pxafb_info *)info; struct pxafb_mach_info *inf = fbi->dev->platform_data; + int err; if (var->xres < MIN_XRES) var->xres = MIN_XRES; @@ -359,60 +436,12 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) else var->yres_virtual = max(var->yres_virtual, var->yres); - /* - * Setup the RGB parameters for this display. - * - * The pixel packing format is described on page 7-11 of the - * PXA2XX Developer's Manual. - */ - if (var->bits_per_pixel == 16) { - var->red.offset = 11; var->red.length = 5; - var->green.offset = 5; var->green.length = 6; - var->blue.offset = 0; var->blue.length = 5; - var->transp.offset = var->transp.length = 0; - } else if (var->bits_per_pixel > 16) { - struct pxafb_mode_info *mode; + /* do a test conversion to BPP fields to check the color formats */ + err = pxafb_var_to_bpp(var); + if (err < 0) + return err; - mode = pxafb_getmode(inf, var); - if (!mode) - return -EINVAL; - - switch (mode->depth) { - case 18: /* RGB666 */ - var->transp.offset = var->transp.length = 0; - var->red.offset = 12; var->red.length = 6; - var->green.offset = 6; var->green.length = 6; - var->blue.offset = 0; var->blue.length = 6; - break; - case 19: /* RGBT666 */ - var->transp.offset = 18; var->transp.length = 1; - var->red.offset = 12; var->red.length = 6; - var->green.offset = 6; var->green.length = 6; - var->blue.offset = 0; var->blue.length = 6; - break; - case 24: /* RGB888 */ - var->transp.offset = var->transp.length = 0; - var->red.offset = 16; var->red.length = 8; - var->green.offset = 8; var->green.length = 8; - var->blue.offset = 0; var->blue.length = 8; - break; - case 25: /* RGBT888 */ - var->transp.offset = 24; var->transp.length = 1; - var->red.offset = 16; var->red.length = 8; - var->green.offset = 8; var->green.length = 8; - var->blue.offset = 0; var->blue.length = 8; - break; - default: - return -EINVAL; - } - } else { - var->red.offset = var->green.offset = 0; - var->blue.offset = var->transp.offset = 0; - var->red.length = 8; - var->green.length = 8; - var->blue.length = 8; - var->transp.length = 0; - } + pxafb_set_pixfmt(var, var_to_depth(var)); #ifdef CONFIG_CPU_FREQ pr_debug("pxafb: dma period = %d ps\n", @@ -978,7 +1007,7 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, (LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_QDM | LCCR0_BM | LCCR0_OUM); - fbi->reg_lccr3 |= pxafb_bpp_to_lccr3(var); + fbi->reg_lccr3 |= pxafb_var_to_lccr3(var); fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK; fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK); -- cgit v1.2.3 From 3f16ff608a75c8bf28c8cafed12e076d67a3602a Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 18 Dec 2008 22:51:54 +0800 Subject: [ARM] pxafb: cleanup of the timing checking code Signed-off-by: Eric Miao Signed-off-by: Eric Miao --- drivers/video/pxafb.c | 108 ++++++++++++++++++++------------------------------ drivers/video/pxafb.h | 6 +++ 2 files changed, 50 insertions(+), 64 deletions(-) diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 1faf52642f5..7935706a756 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -391,6 +391,46 @@ static void pxafb_setmode(struct fb_var_screeninfo *var, pxafb_set_pixfmt(var, mode->depth); } +static int pxafb_adjust_timing(struct pxafb_info *fbi, + struct fb_var_screeninfo *var) +{ + int line_length; + + var->xres = max_t(int, var->xres, MIN_XRES); + var->yres = max_t(int, var->yres, MIN_YRES); + + if (!(fbi->lccr0 & LCCR0_LCDT)) { + clamp_val(var->hsync_len, 1, 64); + clamp_val(var->vsync_len, 1, 64); + clamp_val(var->left_margin, 1, 255); + clamp_val(var->right_margin, 1, 255); + clamp_val(var->upper_margin, 1, 255); + clamp_val(var->lower_margin, 1, 255); + } + + /* make sure each line is aligned on word boundary */ + line_length = var->xres * var->bits_per_pixel / 8; + line_length = ALIGN(line_length, 4); + var->xres = line_length * 8 / var->bits_per_pixel; + + /* we don't support xpan, force xres_virtual to be equal to xres */ + var->xres_virtual = var->xres; + + if (var->accel_flags & FB_ACCELF_TEXT) + var->yres_virtual = fbi->fb.fix.smem_len / line_length; + else + var->yres_virtual = max(var->yres_virtual, var->yres); + + /* check for limits */ + if (var->xres > MAX_XRES || var->yres > MAX_YRES) + return -EINVAL; + + if (var->yres > var->yres_virtual) + return -EINVAL; + + return 0; +} + /* * pxafb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, @@ -406,11 +446,6 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) struct pxafb_mach_info *inf = fbi->dev->platform_data; int err; - if (var->xres < MIN_XRES) - var->xres = MIN_XRES; - if (var->yres < MIN_YRES) - var->yres = MIN_YRES; - if (inf->fixed_modes) { struct pxafb_mode_info *mode; @@ -418,24 +453,8 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) if (!mode) return -EINVAL; pxafb_setmode(var, mode); - } else { - if (var->xres > inf->modes->xres) - return -EINVAL; - if (var->yres > inf->modes->yres) - return -EINVAL; - if (var->bits_per_pixel > inf->modes->bpp) - return -EINVAL; } - /* we don't support xpan, force xres_virtual to be equal to xres */ - var->xres_virtual = var->xres; - - if (var->accel_flags & FB_ACCELF_TEXT) - var->yres_virtual = fbi->fb.fix.smem_len / - (var->xres_virtual * var->bits_per_pixel / 8); - else - var->yres_virtual = max(var->yres_virtual, var->yres); - /* do a test conversion to BPP fields to check the color formats */ err = pxafb_var_to_bpp(var); if (err < 0) @@ -443,6 +462,10 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) pxafb_set_pixfmt(var, var_to_depth(var)); + err = pxafb_adjust_timing(fbi, var); + if (err) + return err; + #ifdef CONFIG_CPU_FREQ pr_debug("pxafb: dma period = %d ps\n", pxafb_display_dma_period(var)); @@ -948,49 +971,6 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, { u_long flags; -#if DEBUG_VAR - if (!(fbi->lccr0 & LCCR0_LCDT)) { - if (var->xres < 16 || var->xres > 1024) - printk(KERN_ERR "%s: invalid xres %d\n", - fbi->fb.fix.id, var->xres); - switch (var->bits_per_pixel) { - case 1: - case 2: - case 4: - case 8: - case 16: - case 24: - case 32: - break; - default: - printk(KERN_ERR "%s: invalid bit depth %d\n", - fbi->fb.fix.id, var->bits_per_pixel); - break; - } - - if (var->hsync_len < 1 || var->hsync_len > 64) - printk(KERN_ERR "%s: invalid hsync_len %d\n", - fbi->fb.fix.id, var->hsync_len); - if (var->left_margin < 1 || var->left_margin > 255) - printk(KERN_ERR "%s: invalid left_margin %d\n", - fbi->fb.fix.id, var->left_margin); - if (var->right_margin < 1 || var->right_margin > 255) - printk(KERN_ERR "%s: invalid right_margin %d\n", - fbi->fb.fix.id, var->right_margin); - if (var->yres < 1 || var->yres > 1024) - printk(KERN_ERR "%s: invalid yres %d\n", - fbi->fb.fix.id, var->yres); - if (var->vsync_len < 1 || var->vsync_len > 64) - printk(KERN_ERR "%s: invalid vsync_len %d\n", - fbi->fb.fix.id, var->vsync_len); - if (var->upper_margin < 0 || var->upper_margin > 255) - printk(KERN_ERR "%s: invalid upper_margin %d\n", - fbi->fb.fix.id, var->upper_margin); - if (var->lower_margin < 0 || var->lower_margin > 255) - printk(KERN_ERR "%s: invalid lower_margin %d\n", - fbi->fb.fix.id, var->lower_margin); - } -#endif /* Update shadow copy atomically */ local_irq_save(flags); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index e0f90f4c467..ae3cbc1ca64 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -145,4 +145,10 @@ struct pxafb_info { #define MIN_XRES 64 #define MIN_YRES 64 +/* maximum X and Y resolutions - note these are limits from the register + * bits length instead of the real ones + */ +#define MAX_XRES 1024 +#define MAX_YRES 1024 + #endif /* __PXAFB_H__ */ -- cgit v1.2.3 From 198fc108ee4c2cd3f08954eae6a819c81c03214b Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Tue, 23 Dec 2008 17:49:43 +0800 Subject: [ARM] pxafb: add support for overlay1 and overlay2 as framebuffer devices PXA27x and later processors support overlay1 and overlay2 on-top of the base framebuffer (although under-neath the base is also possible). They support palette and no-palette RGB formats, as well as YUV formats (only available on overlay2). These overlays have dedicated DMA channels and behave in a similar way as a framebuffer. This heavily simplified and re-structured work is based on the original pxafb_overlay.c (which is pending for mainline merge for a long time). The major problems with this pxafb_overlay.c are (if you are interested in the history): 1. heavily redundant (the control logics for overlay1 and overlay2 are actually identical except for some small operations, which are now abstracted into a 'pxafb_layer_ops' structure) 2. a lot of useless and un-tested code (two workarounds which are now fixed on mature silicons) 3. cursorfb is actually useless, hardware cursor should not be used this way, and the code was actually un-tested for a long time. The code in this patch should be self-explanatory, I tried to add minimum comments. As said, this is basically simplified, there are several things still on the pending list: 1. palette mode is un-supported and un-tested (although re-using the palette code of the base framebuffer is actually very easy now with previous clean-up patches) 2. fb_pan_display for overlay(s) is un-supported 3. the base framebuffer can actually be abstracted by 'pxafb_layer' as well, which will help further re-use of the code and keep a better and consistent structure. (This is the reason I named it 'pxafb_layer' instead of 'pxafb_overlay' or something alike) See Documentation/fb/pxafb.txt for additional usage information. Signed-off-by: Eric Miao Cc: Rodolfo Giometti Signed-off-by: Eric Miao --- Documentation/fb/pxafb.txt | 84 +++++++ arch/arm/mach-pxa/include/mach/regs-lcd.h | 34 ++- drivers/video/Kconfig | 5 + drivers/video/pxafb.c | 364 +++++++++++++++++++++++++++++- drivers/video/pxafb.h | 45 ++++ 5 files changed, 516 insertions(+), 16 deletions(-) diff --git a/Documentation/fb/pxafb.txt b/Documentation/fb/pxafb.txt index ad94b5ca009..d143a0a749f 100644 --- a/Documentation/fb/pxafb.txt +++ b/Documentation/fb/pxafb.txt @@ -56,3 +56,87 @@ outputen:POLARITY pixclockpol:POLARITY pixel clock polarity 0 => falling edge, 1 => rising edge + + +Overlay Support for PXA27x and later LCD controllers +==================================================== + + PXA27x and later processors support overlay1 and overlay2 on-top of the + base framebuffer (although under-neath the base is also possible). They + support palette and no-palette RGB formats, as well as YUV formats (only + available on overlay2). These overlays have dedicated DMA channels and + behave in a similar way as a framebuffer. + + However, there are some differences between these overlay framebuffers + and normal framebuffers, as listed below: + + 1. overlay can start at a 32-bit word aligned position within the base + framebuffer, which means they have a start (x, y). This information + is encoded into var->nonstd (no, var->xoffset and var->yoffset are + not for such purpose). + + 2. overlay framebuffer is allocated dynamically according to specified + 'struct fb_var_screeninfo', the amount is decided by: + + var->xres_virtual * var->yres_virtual * bpp + + bpp = 16 -- for RGB565 or RGBT555 + = 24 -- for YUV444 packed + = 24 -- for YUV444 planar + = 16 -- for YUV422 planar (1 pixel = 1 Y + 1/2 Cb + 1/2 Cr) + = 12 -- for YUV420 planar (1 pixel = 1 Y + 1/4 Cb + 1/4 Cr) + + NOTE: + + a. overlay does not support panning in x-direction, thus + var->xres_virtual will always be equal to var->xres + + b. line length of overlay(s) must be on a 32-bit word boundary, + for YUV planar modes, it is a requirement for the component + with minimum bits per pixel, e.g. for YUV420, Cr component + for one pixel is actually 2-bits, it means the line length + should be a multiple of 16-pixels + + c. starting horizontal position (XPOS) should start on a 32-bit + word boundary, otherwise the fb_check_var() will just fail. + + d. the rectangle of the overlay should be within the base plane, + otherwise fail + + Applications should follow the sequence below to operate an overlay + framebuffer: + + a. open("/dev/fb[1-2]", ...) + b. ioctl(fd, FBIOGET_VSCREENINFO, ...) + c. modify 'var' with desired parameters: + 1) var->xres and var->yres + 2) larger var->yres_virtual if more memory is required, + usually for double-buffering + 3) var->nonstd for starting (x, y) and color format + 4) var->{red, green, blue, transp} if RGB mode is to be used + d. ioctl(fd, FBIOPUT_VSCREENINFO, ...) + e. ioctl(fd, FBIOGET_FSCREENINFO, ...) + f. mmap + g. ... + + 3. for YUV planar formats, these are actually not supported within the + framebuffer framework, application has to take care of the offsets + and lengths of each component within the framebuffer. + + 4. var->nonstd is used to pass starting (x, y) position and color format, + the detailed bit fields are shown below: + + 31 23 20 10 0 + +-----------------+---+----------+----------+ + | ... unused ... |FOR| XPOS | YPOS | + +-----------------+---+----------+----------+ + + FOR - color format, as defined by OVERLAY_FORMAT_* in pxafb.h + 0 - RGB + 1 - YUV444 PACKED + 2 - YUV444 PLANAR + 3 - YUV422 PLANAR + 4 - YUR420 PLANAR + + XPOS - starting horizontal position + YPOS - starting vertical position diff --git a/arch/arm/mach-pxa/include/mach/regs-lcd.h b/arch/arm/mach-pxa/include/mach/regs-lcd.h index aff3b876a7b..f82dcea792d 100644 --- a/arch/arm/mach-pxa/include/mach/regs-lcd.h +++ b/arch/arm/mach-pxa/include/mach/regs-lcd.h @@ -12,7 +12,8 @@ #define LCCR3 (0x00C) /* LCD Controller Control Register 3 */ #define LCCR4 (0x010) /* LCD Controller Control Register 4 */ #define LCCR5 (0x014) /* LCD Controller Control Register 5 */ -#define LCSR (0x038) /* LCD Controller Status Register */ +#define LCSR (0x038) /* LCD Controller Status Register 0 */ +#define LCSR1 (0x034) /* LCD Controller Status Register 1 */ #define LIIDR (0x03C) /* LCD Controller Interrupt ID Register */ #define TMEDRGBR (0x040) /* TMED RGB Seed Register */ #define TMEDCR (0x044) /* TMED Control Register */ @@ -25,6 +26,11 @@ #define FBR5 (0x110) /* DMA Channel 2 Frame Branch Register */ #define FBR6 (0x114) /* DMA Channel 2 Frame Branch Register */ +#define OVL1C1 (0x050) /* Overlay 1 Control Register 1 */ +#define OVL1C2 (0x060) /* Overlay 1 Control Register 2 */ +#define OVL2C1 (0x070) /* Overlay 2 Control Register 1 */ +#define OVL2C2 (0x080) /* Overlay 2 Control Register 2 */ + #define CMDCR (0x100) /* Command Control Register */ #define PRSR (0x104) /* Panel Read Status Register */ @@ -42,16 +48,12 @@ #define LCCR4_PAL_FOR_MASK (3 << 15) #define FDADR0 (0x200) /* DMA Channel 0 Frame Descriptor Address Register */ -#define FSADR0 (0x204) /* DMA Channel 0 Frame Source Address Register */ -#define FIDR0 (0x208) /* DMA Channel 0 Frame ID Register */ -#define LDCMD0 (0x20C) /* DMA Channel 0 Command Register */ #define FDADR1 (0x210) /* DMA Channel 1 Frame Descriptor Address Register */ -#define FSADR1 (0x214) /* DMA Channel 1 Frame Source Address Register */ -#define FIDR1 (0x218) /* DMA Channel 1 Frame ID Register */ -#define LDCMD1 (0x21C) /* DMA Channel 1 Command Register */ +#define FDADR2 (0x220) /* DMA Channel 2 Frame Descriptor Address Register */ +#define FDADR3 (0x230) /* DMA Channel 3 Frame Descriptor Address Register */ +#define FDADR4 (0x240) /* DMA Channel 4 Frame Descriptor Address Register */ +#define FDADR5 (0x250) /* DMA Channel 5 Frame Descriptor Address Register */ #define FDADR6 (0x260) /* DMA Channel 6 Frame Descriptor Address Register */ -#define FSADR6 (0x264) /* DMA Channel 6 Frame Source Address Register */ -#define FIDR6 (0x268) /* DMA Channel 6 Frame ID Register */ #define LCCR0_ENB (1 << 0) /* LCD Controller enable */ #define LCCR0_CMS (1 << 1) /* Color/Monochrome Display Select */ @@ -151,8 +153,22 @@ #define LCSR_RD_ST (1 << 11) /* read status */ #define LCSR_CMD_INT (1 << 12) /* command interrupt */ +#define LCSR1_IU(x) (1 << ((x) + 23)) /* Input FIFO underrun */ +#define LCSR1_BS(x) (1 << ((x) + 15)) /* Branch Status */ +#define LCSR1_EOF(x) (1 << ((x) + 7)) /* End of Frame Status */ +#define LCSR1_SOF(x) (1 << ((x) - 1)) /* Start of Frame Status */ + #define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */ +/* overlay control registers */ +#define OVLxC1_PPL(x) ((((x) - 1) & 0x3ff) << 0) /* Pixels Per Line */ +#define OVLxC1_LPO(x) ((((x) - 1) & 0x3ff) << 10) /* Number of Lines */ +#define OVLxC1_BPP(x) (((x) & 0xf) << 20) /* Bits Per Pixel */ +#define OVLxC1_OEN (1 << 31) /* Enable bit for Overlay */ +#define OVLxC2_XPOS(x) (((x) & 0x3ff) << 0) /* Horizontal Position */ +#define OVLxC2_YPOS(x) (((x) & 0x3ff) << 10) /* Vertical Position */ +#define OVL2C2_PFOR(x) (((x) & 0x7) << 20) /* Pixel Format */ + /* smartpanel related */ #define PRSR_DATA(x) ((x) & 0xff) /* Panel Data */ #define PRSR_A0 (1 << 8) /* Read Data Source */ diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3f3ce13fef4..486d81ca02a 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1817,6 +1817,11 @@ config FB_PXA If unsure, say N. +config FB_PXA_OVERLAY + bool "Support PXA27x/PXA3xx Overlay(s) as framebuffer" + default n + depends on FB_PXA && (PXA27x || PXA3xx) + config FB_PXA_SMARTPANEL bool "PXA Smartpanel LCD support" default n diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 7935706a756..3a41ea10e8e 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -20,6 +20,16 @@ * * linux-arm-kernel@lists.arm.linux.org.uk * + * Add support for overlay1 and overlay2 based on pxafb_overlay.c: + * + * Copyright (C) 2004, Intel Corporation + * + * 2003/08/27: + * 2004/03/10: + * 2004/10/28: + * + * Copyright (C) 2006-2008 Marvell International Ltd. + * All Rights Reserved */ #include @@ -72,6 +82,8 @@ static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); static void setup_base_frame(struct pxafb_info *fbi, int branch); +static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, + unsigned long offset, size_t size); static unsigned long video_mem_size = 0; @@ -581,6 +593,330 @@ static struct fb_ops pxafb_ops = { .fb_blank = pxafb_blank, }; +#ifdef CONFIG_FB_PXA_OVERLAY +static void overlay1fb_setup(struct pxafb_layer *ofb) +{ + int size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; + unsigned long start = ofb->video_mem_phys; + setup_frame_dma(ofb->fbi, DMA_OV1, PAL_NONE, start, size); +} + +/* Depending on the enable status of overlay1/2, the DMA should be + * updated from FDADRx (when disabled) or FBRx (when enabled). + */ +static void overlay1fb_enable(struct pxafb_layer *ofb) +{ + int enabled = lcd_readl(ofb->fbi, OVL1C1) & OVLxC1_OEN; + uint32_t fdadr1 = ofb->fbi->fdadr[DMA_OV1] | (enabled ? 0x1 : 0); + + lcd_writel(ofb->fbi, enabled ? FBR1 : FDADR1, fdadr1); + lcd_writel(ofb->fbi, OVL1C2, ofb->control[1]); + lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] | OVLxC1_OEN); +} + +static void overlay1fb_disable(struct pxafb_layer *ofb) +{ + uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5); + + lcd_writel(ofb->fbi, OVL1C1, ofb->control[0] & ~OVLxC1_OEN); + + lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(1)); + lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(1)); + lcd_writel(ofb->fbi, FBR1, ofb->fbi->fdadr[DMA_OV1] | 0x3); + + if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) + pr_warning("%s: timeout disabling overlay1\n", __func__); + + lcd_writel(ofb->fbi, LCCR5, lccr5); +} + +static void overlay2fb_setup(struct pxafb_layer *ofb) +{ + int size, div = 1, pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); + unsigned long start[3] = { ofb->video_mem_phys, 0, 0 }; + + if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) { + size = ofb->fb.fix.line_length * ofb->fb.var.yres_virtual; + setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); + } else { + size = ofb->fb.var.xres_virtual * ofb->fb.var.yres_virtual; + switch (pfor) { + case OVERLAY_FORMAT_YUV444_PLANAR: div = 1; break; + case OVERLAY_FORMAT_YUV422_PLANAR: div = 2; break; + case OVERLAY_FORMAT_YUV420_PLANAR: div = 4; break; + } + start[1] = start[0] + size; + start[2] = start[1] + size / div; + setup_frame_dma(ofb->fbi, DMA_OV2_Y, -1, start[0], size); + setup_frame_dma(ofb->fbi, DMA_OV2_Cb, -1, start[1], size / div); + setup_frame_dma(ofb->fbi, DMA_OV2_Cr, -1, start[2], size / div); + } +} + +static void overlay2fb_enable(struct pxafb_layer *ofb) +{ + int pfor = NONSTD_TO_PFOR(ofb->fb.var.nonstd); + int enabled = lcd_readl(ofb->fbi, OVL2C1) & OVLxC1_OEN; + uint32_t fdadr2 = ofb->fbi->fdadr[DMA_OV2_Y] | (enabled ? 0x1 : 0); + uint32_t fdadr3 = ofb->fbi->fdadr[DMA_OV2_Cb] | (enabled ? 0x1 : 0); + uint32_t fdadr4 = ofb->fbi->fdadr[DMA_OV2_Cr] | (enabled ? 0x1 : 0); + + if (pfor == OVERLAY_FORMAT_RGB || pfor == OVERLAY_FORMAT_YUV444_PACKED) + lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); + else { + lcd_writel(ofb->fbi, enabled ? FBR2 : FDADR2, fdadr2); + lcd_writel(ofb->fbi, enabled ? FBR3 : FDADR3, fdadr3); + lcd_writel(ofb->fbi, enabled ? FBR4 : FDADR4, fdadr4); + } + lcd_writel(ofb->fbi, OVL2C2, ofb->control[1]); + lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] | OVLxC1_OEN); +} + +static void overlay2fb_disable(struct pxafb_layer *ofb) +{ + uint32_t lccr5 = lcd_readl(ofb->fbi, LCCR5); + + lcd_writel(ofb->fbi, OVL2C1, ofb->control[0] & ~OVLxC1_OEN); + + lcd_writel(ofb->fbi, LCSR1, LCSR1_BS(2)); + lcd_writel(ofb->fbi, LCCR5, lccr5 & ~LCSR1_BS(2)); + lcd_writel(ofb->fbi, FBR2, ofb->fbi->fdadr[DMA_OV2_Y] | 0x3); + lcd_writel(ofb->fbi, FBR3, ofb->fbi->fdadr[DMA_OV2_Cb] | 0x3); + lcd_writel(ofb->fbi, FBR4, ofb->fbi->fdadr[DMA_OV2_Cr] | 0x3); + + if (wait_for_completion_timeout(&ofb->branch_done, 1 * HZ) == 0) + pr_warning("%s: timeout disabling overlay2\n", __func__); +} + +static struct pxafb_layer_ops ofb_ops[] = { + [0] = { + .enable = overlay1fb_enable, + .disable = overlay1fb_disable, + .setup = overlay1fb_setup, + }, + [1] = { + .enable = overlay2fb_enable, + .disable = overlay2fb_disable, + .setup = overlay2fb_setup, + }, +}; + +static int overlayfb_open(struct fb_info *info, int user) +{ + struct pxafb_layer *ofb = (struct pxafb_layer *)info; + + /* no support for framebuffer console on overlay */ + if (user == 0) + return -ENODEV; + + /* allow only one user at a time */ + if (atomic_inc_and_test(&ofb->usage)) + return -EBUSY; + + /* unblank the base framebuffer */ + fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK); + return 0; +} + +static int overlayfb_release(struct fb_info *info, int user) +{ + struct pxafb_layer *ofb = (struct pxafb_layer*) info; + + atomic_dec(&ofb->usage); + ofb->ops->disable(ofb); + + free_pages_exact(ofb->video_mem, ofb->video_mem_size); + ofb->video_mem = NULL; + ofb->video_mem_size = 0; + return 0; +} + +static int overlayfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct pxafb_layer *ofb = (struct pxafb_layer *)info; + struct fb_var_screeninfo *base_var = &ofb->fbi->fb.var; + int xpos, ypos, pfor, bpp; + + xpos = NONSTD_TO_XPOS(var->nonstd); + ypos = NONSTD_TO_XPOS(var->nonstd); + pfor = NONSTD_TO_PFOR(var->nonstd); + + bpp = pxafb_var_to_bpp(var); + if (bpp < 0) + return -EINVAL; + + /* no support for YUV format on overlay1 */ + if (ofb->id == OVERLAY1 && pfor != 0) + return -EINVAL; + + /* for YUV packed formats, bpp = 'minimum bpp of YUV components' */ + switch (pfor) { + case OVERLAY_FORMAT_RGB: + bpp = pxafb_var_to_bpp(var); + if (bpp < 0) + return -EINVAL; + + pxafb_set_pixfmt(var, var_to_depth(var)); + break; + case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; + case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 8; break; + case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 4; break; + case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 2; break; + default: + return -EINVAL; + } + + /* each line must start at a 32-bit word boundary */ + if ((xpos * bpp) % 32) + return -EINVAL; + + /* xres must align on 32-bit word boundary */ + var->xres = roundup(var->xres * bpp, 32) / bpp; + + if ((xpos + var->xres > base_var->xres) || + (ypos + var->yres > base_var->yres)) + return -EINVAL; + + var->xres_virtual = var->xres; + var->yres_virtual = max(var->yres, var->yres_virtual); + return 0; +} + +static int overlayfb_map_video_memory(struct pxafb_layer *ofb) +{ + struct fb_var_screeninfo *var = &ofb->fb.var; + int pfor = NONSTD_TO_PFOR(var->nonstd); + int size, bpp = 0; + + switch (pfor) { + case OVERLAY_FORMAT_RGB: bpp = var->bits_per_pixel; break; + case OVERLAY_FORMAT_YUV444_PACKED: bpp = 24; break; + case OVERLAY_FORMAT_YUV444_PLANAR: bpp = 24; break; + case OVERLAY_FORMAT_YUV422_PLANAR: bpp = 16; break; + case OVERLAY_FORMAT_YUV420_PLANAR: bpp = 12; break; + } + + ofb->fb.fix.line_length = var->xres_virtual * bpp / 8; + + size = PAGE_ALIGN(ofb->fb.fix.line_length * var->yres_virtual); + + /* don't re-allocate if the original video memory is enough */ + if (ofb->video_mem) { + if (ofb->video_mem_size >= size) + return 0; + + free_pages_exact(ofb->video_mem, ofb->video_mem_size); + } + + ofb->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); + if (ofb->video_mem == NULL) + return -ENOMEM; + + ofb->video_mem_phys = virt_to_phys(ofb->video_mem); + ofb->video_mem_size = size; + + ofb->fb.fix.smem_start = ofb->video_mem_phys; + ofb->fb.fix.smem_len = ofb->fb.fix.line_length * var->yres_virtual; + ofb->fb.screen_base = ofb->video_mem; + return 0; +} + +static int overlayfb_set_par(struct fb_info *info) +{ + struct pxafb_layer *ofb = (struct pxafb_layer *)info; + struct fb_var_screeninfo *var = &info->var; + int xpos, ypos, pfor, bpp, ret; + + ret = overlayfb_map_video_memory(ofb); + if (ret) + return ret; + + bpp = pxafb_var_to_bpp(var); + xpos = NONSTD_TO_XPOS(var->nonstd); + ypos = NONSTD_TO_XPOS(var->nonstd); + pfor = NONSTD_TO_PFOR(var->nonstd); + + ofb->control[0] = OVLxC1_PPL(var->xres) | OVLxC1_LPO(var->yres) | + OVLxC1_BPP(bpp); + ofb->control[1] = OVLxC2_XPOS(xpos) | OVLxC2_YPOS(ypos); + + if (ofb->id == OVERLAY2) + ofb->control[1] |= OVL2C2_PFOR(pfor); + + ofb->ops->setup(ofb); + ofb->ops->enable(ofb); + return 0; +} + +static struct fb_ops overlay_fb_ops = { + .owner = THIS_MODULE, + .fb_open = overlayfb_open, + .fb_release = overlayfb_release, + .fb_check_var = overlayfb_check_var, + .fb_set_par = overlayfb_set_par, +}; + +static void __devinit init_pxafb_overlay(struct pxafb_info *fbi, + struct pxafb_layer *ofb, int id) +{ + sprintf(ofb->fb.fix.id, "overlay%d", id + 1); + + ofb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + ofb->fb.fix.xpanstep = 0; + ofb->fb.fix.ypanstep = 1; + + ofb->fb.var.activate = FB_ACTIVATE_NOW; + ofb->fb.var.height = -1; + ofb->fb.var.width = -1; + ofb->fb.var.vmode = FB_VMODE_NONINTERLACED; + + ofb->fb.fbops = &overlay_fb_ops; + ofb->fb.flags = FBINFO_FLAG_DEFAULT; + ofb->fb.node = -1; + ofb->fb.pseudo_palette = NULL; + + ofb->id = id; + ofb->ops = &ofb_ops[id]; + atomic_set(&ofb->usage, 0); + ofb->fbi = fbi; + init_completion(&ofb->branch_done); +} + +static int __devinit pxafb_overlay_init(struct pxafb_info *fbi) +{ + int i, ret; + + for (i = 0; i < 2; i++) { + init_pxafb_overlay(fbi, &fbi->overlay[i], i); + ret = register_framebuffer(&fbi->overlay[i].fb); + if (ret) { + dev_err(fbi->dev, "failed to register overlay %d\n", i); + return ret; + } + } + + /* mask all IU/BS/EOF/SOF interrupts */ + lcd_writel(fbi, LCCR5, ~0); + + /* place overlay(s) on top of base */ + fbi->lccr0 |= LCCR0_OUC; + pr_info("PXA Overlay driver loaded successfully!\n"); + return 0; +} + +static void __devexit pxafb_overlay_exit(struct pxafb_info *fbi) +{ + int i; + + for (i = 0; i < 2; i++) + unregister_framebuffer(&fbi->overlay[i].fb); +} +#else +static inline void pxafb_overlay_init(struct pxafb_info *fbi) {} +static inline void pxafb_overlay_exit(struct pxafb_info *fbi) {} +#endif /* CONFIG_FB_PXA_OVERLAY */ + /* * Calculate the PCD value from the clock rate (in picoseconds). * We take account of the PPCR clock setting. @@ -660,7 +996,7 @@ unsigned long pxafb_get_hsync_time(struct device *dev) EXPORT_SYMBOL(pxafb_get_hsync_time); static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, - unsigned int offset, size_t size) + unsigned long start, size_t size) { struct pxafb_dma_descriptor *dma_desc, *pal_desc; unsigned int dma_desc_off, pal_desc_off; @@ -671,7 +1007,7 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal, dma_desc = &fbi->dma_buff->dma_desc[dma]; dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]); - dma_desc->fsadr = fbi->video_mem_phys + offset; + dma_desc->fsadr = start; dma_desc->fidr = 0; dma_desc->ldcmd = size; @@ -705,14 +1041,14 @@ static void setup_base_frame(struct pxafb_info *fbi, int branch) { struct fb_var_screeninfo *var = &fbi->fb.var; struct fb_fix_screeninfo *fix = &fbi->fb.fix; - unsigned int nbytes, offset; - int dma, pal, bpp = var->bits_per_pixel; + int nbytes, dma, pal, bpp = var->bits_per_pixel; + unsigned long offset; dma = DMA_BASE + (branch ? DMA_MAX : 0); pal = (bpp >= 16) ? PAL_NONE : PAL_BASE + (branch ? PAL_MAX : 0); nbytes = fix->line_length * var->yres; - offset = fix->line_length * var->yoffset; + offset = fix->line_length * var->yoffset + fbi->video_mem_phys; if (fbi->lccr0 & LCCR0_SDS) { nbytes = nbytes / 2; @@ -1090,8 +1426,9 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) { struct pxafb_info *fbi = dev_id; - unsigned int lccr0, lcsr = lcd_readl(fbi, LCSR); + unsigned int lccr0, lcsr, lcsr1; + lcsr = lcd_readl(fbi, LCSR); if (lcsr & LCSR_LDD) { lccr0 = lcd_readl(fbi, LCCR0); lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM); @@ -1102,8 +1439,18 @@ static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) if (lcsr & LCSR_CMD_INT) complete(&fbi->command_done); #endif - lcd_writel(fbi, LCSR, lcsr); + +#ifdef CONFIG_FB_PXA_OVERLAY + lcsr1 = lcd_readl(fbi, LCSR1); + if (lcsr1 & LCSR1_BS(1)) + complete(&fbi->overlay[0].branch_done); + + if (lcsr1 & LCSR1_BS(2)) + complete(&fbi->overlay[1].branch_done); + + lcd_writel(fbi, LCSR1, lcsr1); +#endif return IRQ_HANDLED; } @@ -1802,6 +2149,8 @@ static int __devinit pxafb_probe(struct platform_device *dev) goto failed_free_cmap; } + pxafb_overlay_init(fbi); + #ifdef CONFIG_CPU_FREQ fbi->freq_transition.notifier_call = pxafb_freq_transition; fbi->freq_policy.notifier_call = pxafb_freq_policy; @@ -1852,6 +2201,7 @@ static int __devexit pxafb_remove(struct platform_device *dev) info = &fbi->fb; + pxafb_overlay_exit(fbi); unregister_framebuffer(info); pxafb_disable_controller(fbi); diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h index ae3cbc1ca64..2353521c5c8 100644 --- a/drivers/video/pxafb.h +++ b/drivers/video/pxafb.h @@ -64,6 +64,47 @@ struct pxafb_dma_buff { struct pxafb_dma_descriptor dma_desc[DMA_MAX * 2]; }; +enum { + OVERLAY1, + OVERLAY2, +}; + +enum { + OVERLAY_FORMAT_RGB = 0, + OVERLAY_FORMAT_YUV444_PACKED, + OVERLAY_FORMAT_YUV444_PLANAR, + OVERLAY_FORMAT_YUV422_PLANAR, + OVERLAY_FORMAT_YUV420_PLANAR, +}; + +#define NONSTD_TO_XPOS(x) (((x) >> 0) & 0x3ff) +#define NONSTD_TO_YPOS(x) (((x) >> 10) & 0x3ff) +#define NONSTD_TO_PFOR(x) (((x) >> 20) & 0x7) + +struct pxafb_layer; + +struct pxafb_layer_ops { + void (*enable)(struct pxafb_layer *); + void (*disable)(struct pxafb_layer *); + void (*setup)(struct pxafb_layer *); +}; + +struct pxafb_layer { + struct fb_info fb; + int id; + atomic_t usage; + uint32_t control[2]; + + struct pxafb_layer_ops *ops; + + void __iomem *video_mem; + unsigned long video_mem_phys; + size_t video_mem_size; + struct completion branch_done; + + struct pxafb_info *fbi; +}; + struct pxafb_info { struct fb_info fb; struct device *dev; @@ -114,6 +155,10 @@ struct pxafb_info { struct task_struct *smart_thread; #endif +#ifdef CONFIG_FB_PXA_OVERLAY + struct pxafb_layer overlay[2]; +#endif + #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; struct notifier_block freq_policy; -- cgit v1.2.3