>From 40bb744e91d8058faa38fb953ade1964c865e846 Mon Sep 17 00:00:00 2001 From: Jun Nie <njun@xxxxxxxxxxx> Date: Fri, 13 Nov 2009 14:02:20 +0800 Subject: [PATCH] pxa: support pxa168 LCD controller SPI operation Signed-off-by: Jun Nie <njun@xxxxxxxxxxx> --- drivers/video/pxa168fb.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/video/pxa168fb.h | 24 +------------- include/video/pxa168fb.h | 47 ++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 23 deletions(-) diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 84d8327..aec95c4 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -27,12 +27,90 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/uaccess.h> +#include <linux/gpio.h> #include <video/pxa168fb.h> #include "pxa168fb.h" #define DEFAULT_REFRESH 60 /* Hz */ +static inline void spi_gpio_assert(int spi_gpio_cs, int val) +{ + if (gpio_is_valid(spi_gpio_cs)) + gpio_set_value(spi_gpio_cs, val); +} + +int pxa168fb_spi_send(struct pxa168fb_info *fbi, void *value, int count, + unsigned int spi_gpio_cs, unsigned int cs_active, + unsigned int interval_us) +{ + u32 x, spi_byte_len; + u8 *cmd = (u8 *)value; + int i, isr, iopad, ret = 0; + + if (gpio_is_valid(spi_gpio_cs)) { + ret = gpio_direction_output(spi_gpio_cs, !cs_active); + if (ret) + goto spi_exit; + } + spi_byte_len = readl(fbi->reg_base + LCD_SPU_SPI_CTRL); + spi_byte_len = (spi_byte_len >> 8) & 0xff; + /* Alignment should be (spi_byte_len + 7) >> 3, but + * spi controller request set one less than bit length */ + spi_byte_len = (spi_byte_len + 8) >> 3; + /* spi command provided by platform should be 1, 2, or 4 byte aligned */ + if (3 == spi_byte_len) + spi_byte_len = 4; + + iopad = readl(fbi->reg_base + SPU_IOPAD_CONTROL); + if ((iopad & CFG_IOPADMODE_MASK) != PIN_MODE_DUMB_18_SPI) + writel(PIN_MODE_DUMB_18_SPI, fbi->reg_base + SPU_IOPAD_CONTROL); + isr = readl(fbi->reg_base + SPU_IRQ_ISR); + writel((isr & ~SPI_IRQ_ENA_MASK), fbi->reg_base + SPU_IRQ_ISR); + for (i = 0; i < count; i++) { + spi_gpio_assert(spi_gpio_cs, cs_active); + udelay(interval_us); + switch (spi_byte_len) { + case 1: + writel(*cmd, fbi->reg_base + LCD_SPU_SPI_TXDATA); + break; + case 2: + writel(*(u16 *)cmd, fbi->reg_base + LCD_SPU_SPI_TXDATA); + break; + case 4: + writel(*(u32 *)cmd, fbi->reg_base + LCD_SPU_SPI_TXDATA); + break; + default: + dev_err(fbi->dev, "Wrong spi bit length\n"); + spi_gpio_assert(spi_gpio_cs, !cs_active); + ret = -EINVAL; + goto spi_exit; + } + cmd += spi_byte_len; + x = readl(fbi->reg_base + LCD_SPU_SPI_CTRL); + x |= CFG_SPI_START_MASK; + writel(x, fbi->reg_base + LCD_SPU_SPI_CTRL); + isr = readl(fbi->reg_base + SPU_IRQ_ISR); + while (!(isr & SPI_IRQ_ENA_MASK)) { + udelay(1); + isr = readl(fbi->reg_base + SPU_IRQ_ISR); + } + x = readl(fbi->reg_base + LCD_SPU_SPI_CTRL); + x &= ~CFG_SPI_START_MASK; + writel(x, fbi->reg_base + LCD_SPU_SPI_CTRL); + spi_gpio_assert(spi_gpio_cs, !cs_active); + } + +spi_exit: + if (gpio_is_valid(spi_gpio_cs)) + gpio_direction_input(spi_gpio_cs); + if ((iopad & CFG_IOPADMODE_MASK) != PIN_MODE_DUMB_18_SPI) + writel(iopad, fbi->reg_base + SPU_IOPAD_CONTROL); + + return ret; +} +EXPORT_SYMBOL(pxa168fb_spi_send); + static int determine_best_pix_fmt(struct fb_var_screeninfo *var) { /* @@ -728,6 +806,9 @@ static int __init pxa168fb_probe(struct platform_device *pdev) writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0); writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1), fbi->reg_base + LCD_SPU_SRAM_PARA1); + if ((mi->spi_ctrl != -1) && (mi->spi_ctrl & CFG_SPI_ENA_MASK)) + writel(mi->spi_ctrl, fbi->reg_base + LCD_SPU_SPI_CTRL); + /* * Allocate color map. diff --git a/drivers/video/pxa168fb.h b/drivers/video/pxa168fb.h index eee0927..55ec4dc 100644 --- a/drivers/video/pxa168fb.h +++ b/drivers/video/pxa168fb.h @@ -170,29 +170,7 @@ #define DMA_FRAME_CNT_MASK 0x00000003 /* Video */ /* SPI Control Register. */ -#define LCD_SPU_SPI_CTRL 0x0180 -#define CFG_SCLKCNT(div) ((div) << 24) /* 0xFF~0x2 */ -#define CFG_SCLKCNT_MASK 0xFF000000 -#define CFG_RXBITS(rx) ((rx) << 16) /* 0x1F~0x1 */ -#define CFG_RXBITS_MASK 0x00FF0000 -#define CFG_TXBITS(tx) ((tx) << 8) /* 0x1F~0x1 */ -#define CFG_TXBITS_MASK 0x0000FF00 -#define CFG_CLKINV(clk) ((clk) << 7) -#define CFG_CLKINV_MASK 0x00000080 -#define CFG_KEEPXFER(transfer) ((transfer) << 6) -#define CFG_KEEPXFER_MASK 0x00000040 -#define CFG_RXBITSTO0(rx) ((rx) << 5) -#define CFG_RXBITSTO0_MASK 0x00000020 -#define CFG_TXBITSTO0(tx) ((tx) << 4) -#define CFG_TXBITSTO0_MASK 0x00000010 -#define CFG_SPI_ENA(spi) ((spi) << 3) -#define CFG_SPI_ENA_MASK 0x00000008 -#define CFG_SPI_SEL(spi) ((spi) << 2) -#define CFG_SPI_SEL_MASK 0x00000004 -#define CFG_SPI_3W4WB(wire) ((wire) << 1) -#define CFG_SPI_3W4WB_MASK 0x00000002 -#define CFG_SPI_START(start) (start) -#define CFG_SPI_START_MASK 0x00000001 +/* For SPI register, please refer to include/video/pxa168fb.h */ /* SPI Tx Data Register */ #define LCD_SPU_SPI_TXDATA 0x0184 diff --git a/include/video/pxa168fb.h b/include/video/pxa168fb.h index b5cc72f..01c18bc 100644 --- a/include/video/pxa168fb.h +++ b/include/video/pxa168fb.h @@ -14,6 +14,32 @@ #include <linux/fb.h> #include <linux/interrupt.h> +/* SPI Control Register. */ +#define LCD_SPU_SPI_CTRL 0x0180 +#define CFG_SCLKCNT(div) ((div) << 24) /* 0xFF~0x2 */ +#define CFG_SCLKCNT_MASK 0xFF000000 +/* SPI RX/TX bit length, 0x1F~0x1, 0x1: 2bits ... 0x1F: 32bits */ +#define CFG_RXBITS(rx) ((rx - 1) << 16) +#define CFG_RXBITS_MASK 0x00FF0000 +#define CFG_TXBITS(tx) ((tx - 1) << 8) +#define CFG_TXBITS_MASK 0x0000FF00 +#define CFG_CLKINV(clk) ((clk) << 7) +#define CFG_CLKINV_MASK 0x00000080 +#define CFG_KEEPXFER(transfer) ((transfer) << 6) +#define CFG_KEEPXFER_MASK 0x00000040 +#define CFG_RXBITSTO0(rx) ((rx) << 5) +#define CFG_RXBITSTO0_MASK 0x00000020 +#define CFG_TXBITSTO0(tx) ((tx) << 4) +#define CFG_TXBITSTO0_MASK 0x00000010 +#define CFG_SPI_ENA(spi) ((spi) << 3) +#define CFG_SPI_ENA_MASK 0x00000008 +#define CFG_SPI_SEL(spi) ((spi) << 2) /* 1: port1; 0: port0 */ +#define CFG_SPI_SEL_MASK 0x00000004 +#define CFG_SPI_3W4WB(wire) ((wire)<<1) /* 1: 3-wire; 0: 4-wire */ +#define CFG_SPI_3W4WB_MASK 0x00000002 +#define CFG_SPI_START(start) (start) +#define CFG_SPI_START_MASK 0x00000001 + /* Dumb interface */ #define PIN_MODE_DUMB_24 0 #define PIN_MODE_DUMB_18_SPI 1 @@ -122,6 +148,27 @@ struct pxa168fb_mach_info { unsigned panel_rbswap:1; unsigned active:1; unsigned enable_lcd:1; + /* + * SPI control + */ + unsigned int spi_ctrl; + unsigned int spi_gpio_cs; + unsigned int spi_gpio_reset; + /* + * power on/off function. + */ + int (*pxa168fb_lcd_power)(struct pxa168fb_info *, + unsigned int, unsigned int, int); }; +/* SPI utility for configure panel SPI command. + * value: command array, element should be 1, 2 or 4 byte aligned. + * count: command array length + * spi_gpio_cs: gpio number for spi chip select + * cs_active: CS active polarity for SPI transaction + * interval_us: time interval between two commands, us as unit */ +int pxa168fb_spi_send(struct pxa168fb_info *fbi, void *value, int count, + unsigned int spi_gpio_cs, unsigned int cs_active, + unsigned int interval_us); + #endif /* __ASM_MACH_PXA168FB_H */ -- 1.5.4.3
From 40bb744e91d8058faa38fb953ade1964c865e846 Mon Sep 17 00:00:00 2001 From: Jun Nie <njun@xxxxxxxxxxx> Date: Fri, 13 Nov 2009 14:02:20 +0800 Subject: [PATCH] pxa: support pxa168 LCD controller SPI operation Signed-off-by: Jun Nie <njun@xxxxxxxxxxx> --- drivers/video/pxa168fb.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/video/pxa168fb.h | 24 +------------- include/video/pxa168fb.h | 47 ++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 23 deletions(-) diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index 84d8327..aec95c4 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -27,12 +27,90 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/uaccess.h> +#include <linux/gpio.h> #include <video/pxa168fb.h> #include "pxa168fb.h" #define DEFAULT_REFRESH 60 /* Hz */ +static inline void spi_gpio_assert(int spi_gpio_cs, int val) +{ + if (gpio_is_valid(spi_gpio_cs)) + gpio_set_value(spi_gpio_cs, val); +} + +int pxa168fb_spi_send(struct pxa168fb_info *fbi, void *value, int count, + unsigned int spi_gpio_cs, unsigned int cs_active, + unsigned int interval_us) +{ + u32 x, spi_byte_len; + u8 *cmd = (u8 *)value; + int i, isr, iopad, ret = 0; + + if (gpio_is_valid(spi_gpio_cs)) { + ret = gpio_direction_output(spi_gpio_cs, !cs_active); + if (ret) + goto spi_exit; + } + spi_byte_len = readl(fbi->reg_base + LCD_SPU_SPI_CTRL); + spi_byte_len = (spi_byte_len >> 8) & 0xff; + /* Alignment should be (spi_byte_len + 7) >> 3, but + * spi controller request set one less than bit length */ + spi_byte_len = (spi_byte_len + 8) >> 3; + /* spi command provided by platform should be 1, 2, or 4 byte aligned */ + if (3 == spi_byte_len) + spi_byte_len = 4; + + iopad = readl(fbi->reg_base + SPU_IOPAD_CONTROL); + if ((iopad & CFG_IOPADMODE_MASK) != PIN_MODE_DUMB_18_SPI) + writel(PIN_MODE_DUMB_18_SPI, fbi->reg_base + SPU_IOPAD_CONTROL); + isr = readl(fbi->reg_base + SPU_IRQ_ISR); + writel((isr & ~SPI_IRQ_ENA_MASK), fbi->reg_base + SPU_IRQ_ISR); + for (i = 0; i < count; i++) { + spi_gpio_assert(spi_gpio_cs, cs_active); + udelay(interval_us); + switch (spi_byte_len) { + case 1: + writel(*cmd, fbi->reg_base + LCD_SPU_SPI_TXDATA); + break; + case 2: + writel(*(u16 *)cmd, fbi->reg_base + LCD_SPU_SPI_TXDATA); + break; + case 4: + writel(*(u32 *)cmd, fbi->reg_base + LCD_SPU_SPI_TXDATA); + break; + default: + dev_err(fbi->dev, "Wrong spi bit length\n"); + spi_gpio_assert(spi_gpio_cs, !cs_active); + ret = -EINVAL; + goto spi_exit; + } + cmd += spi_byte_len; + x = readl(fbi->reg_base + LCD_SPU_SPI_CTRL); + x |= CFG_SPI_START_MASK; + writel(x, fbi->reg_base + LCD_SPU_SPI_CTRL); + isr = readl(fbi->reg_base + SPU_IRQ_ISR); + while (!(isr & SPI_IRQ_ENA_MASK)) { + udelay(1); + isr = readl(fbi->reg_base + SPU_IRQ_ISR); + } + x = readl(fbi->reg_base + LCD_SPU_SPI_CTRL); + x &= ~CFG_SPI_START_MASK; + writel(x, fbi->reg_base + LCD_SPU_SPI_CTRL); + spi_gpio_assert(spi_gpio_cs, !cs_active); + } + +spi_exit: + if (gpio_is_valid(spi_gpio_cs)) + gpio_direction_input(spi_gpio_cs); + if ((iopad & CFG_IOPADMODE_MASK) != PIN_MODE_DUMB_18_SPI) + writel(iopad, fbi->reg_base + SPU_IOPAD_CONTROL); + + return ret; +} +EXPORT_SYMBOL(pxa168fb_spi_send); + static int determine_best_pix_fmt(struct fb_var_screeninfo *var) { /* @@ -728,6 +806,9 @@ static int __init pxa168fb_probe(struct platform_device *pdev) writel(0, fbi->reg_base + LCD_SPU_SRAM_PARA0); writel(CFG_CSB_256x32(0x1)|CFG_CSB_256x24(0x1)|CFG_CSB_256x8(0x1), fbi->reg_base + LCD_SPU_SRAM_PARA1); + if ((mi->spi_ctrl != -1) && (mi->spi_ctrl & CFG_SPI_ENA_MASK)) + writel(mi->spi_ctrl, fbi->reg_base + LCD_SPU_SPI_CTRL); + /* * Allocate color map. diff --git a/drivers/video/pxa168fb.h b/drivers/video/pxa168fb.h index eee0927..55ec4dc 100644 --- a/drivers/video/pxa168fb.h +++ b/drivers/video/pxa168fb.h @@ -170,29 +170,7 @@ #define DMA_FRAME_CNT_MASK 0x00000003 /* Video */ /* SPI Control Register. */ -#define LCD_SPU_SPI_CTRL 0x0180 -#define CFG_SCLKCNT(div) ((div) << 24) /* 0xFF~0x2 */ -#define CFG_SCLKCNT_MASK 0xFF000000 -#define CFG_RXBITS(rx) ((rx) << 16) /* 0x1F~0x1 */ -#define CFG_RXBITS_MASK 0x00FF0000 -#define CFG_TXBITS(tx) ((tx) << 8) /* 0x1F~0x1 */ -#define CFG_TXBITS_MASK 0x0000FF00 -#define CFG_CLKINV(clk) ((clk) << 7) -#define CFG_CLKINV_MASK 0x00000080 -#define CFG_KEEPXFER(transfer) ((transfer) << 6) -#define CFG_KEEPXFER_MASK 0x00000040 -#define CFG_RXBITSTO0(rx) ((rx) << 5) -#define CFG_RXBITSTO0_MASK 0x00000020 -#define CFG_TXBITSTO0(tx) ((tx) << 4) -#define CFG_TXBITSTO0_MASK 0x00000010 -#define CFG_SPI_ENA(spi) ((spi) << 3) -#define CFG_SPI_ENA_MASK 0x00000008 -#define CFG_SPI_SEL(spi) ((spi) << 2) -#define CFG_SPI_SEL_MASK 0x00000004 -#define CFG_SPI_3W4WB(wire) ((wire) << 1) -#define CFG_SPI_3W4WB_MASK 0x00000002 -#define CFG_SPI_START(start) (start) -#define CFG_SPI_START_MASK 0x00000001 +/* For SPI register, please refer to include/video/pxa168fb.h */ /* SPI Tx Data Register */ #define LCD_SPU_SPI_TXDATA 0x0184 diff --git a/include/video/pxa168fb.h b/include/video/pxa168fb.h index b5cc72f..01c18bc 100644 --- a/include/video/pxa168fb.h +++ b/include/video/pxa168fb.h @@ -14,6 +14,32 @@ #include <linux/fb.h> #include <linux/interrupt.h> +/* SPI Control Register. */ +#define LCD_SPU_SPI_CTRL 0x0180 +#define CFG_SCLKCNT(div) ((div) << 24) /* 0xFF~0x2 */ +#define CFG_SCLKCNT_MASK 0xFF000000 +/* SPI RX/TX bit length, 0x1F~0x1, 0x1: 2bits ... 0x1F: 32bits */ +#define CFG_RXBITS(rx) ((rx - 1) << 16) +#define CFG_RXBITS_MASK 0x00FF0000 +#define CFG_TXBITS(tx) ((tx - 1) << 8) +#define CFG_TXBITS_MASK 0x0000FF00 +#define CFG_CLKINV(clk) ((clk) << 7) +#define CFG_CLKINV_MASK 0x00000080 +#define CFG_KEEPXFER(transfer) ((transfer) << 6) +#define CFG_KEEPXFER_MASK 0x00000040 +#define CFG_RXBITSTO0(rx) ((rx) << 5) +#define CFG_RXBITSTO0_MASK 0x00000020 +#define CFG_TXBITSTO0(tx) ((tx) << 4) +#define CFG_TXBITSTO0_MASK 0x00000010 +#define CFG_SPI_ENA(spi) ((spi) << 3) +#define CFG_SPI_ENA_MASK 0x00000008 +#define CFG_SPI_SEL(spi) ((spi) << 2) /* 1: port1; 0: port0 */ +#define CFG_SPI_SEL_MASK 0x00000004 +#define CFG_SPI_3W4WB(wire) ((wire)<<1) /* 1: 3-wire; 0: 4-wire */ +#define CFG_SPI_3W4WB_MASK 0x00000002 +#define CFG_SPI_START(start) (start) +#define CFG_SPI_START_MASK 0x00000001 + /* Dumb interface */ #define PIN_MODE_DUMB_24 0 #define PIN_MODE_DUMB_18_SPI 1 @@ -122,6 +148,27 @@ struct pxa168fb_mach_info { unsigned panel_rbswap:1; unsigned active:1; unsigned enable_lcd:1; + /* + * SPI control + */ + unsigned int spi_ctrl; + unsigned int spi_gpio_cs; + unsigned int spi_gpio_reset; + /* + * power on/off function. + */ + int (*pxa168fb_lcd_power)(struct pxa168fb_info *, + unsigned int, unsigned int, int); }; +/* SPI utility for configure panel SPI command. + * value: command array, element should be 1, 2 or 4 byte aligned. + * count: command array length + * spi_gpio_cs: gpio number for spi chip select + * cs_active: CS active polarity for SPI transaction + * interval_us: time interval between two commands, us as unit */ +int pxa168fb_spi_send(struct pxa168fb_info *fbi, void *value, int count, + unsigned int spi_gpio_cs, unsigned int cs_active, + unsigned int interval_us); + #endif /* __ASM_MACH_PXA168FB_H */ -- 1.5.4.3