Hi all, I don't know where to begin to interface the iMX233 MAXI with an SSD1963 LCD controller, the idea is to connect it with the LCD0-7 GPIO's and some control GPIO's. inicializate the display and then read the fb0.
The thing is that i cant acces those GPIO's and i want to no how do i acces them or if there is yet an intelligent LCD driver..
Thanks and excuse my english
I have successfully connected SSD1963 to iMX233. Just configure iMX233 LCD controller in 8080 mode.
Can You explain your solution in detail ? I have a http://www.winstar.com.tw/UserFiles/File/WF35DTIBCDE.pdf LCD display based on SSD1963, but i completely don't know how to connect it and how to prepare Linux to work with.
Thanks a lot.
Yes sure, I will draw connection scheme. And can give you draft of linux driver.
It would be very nice Alex-y! I'm very interested too..
Alex-y, many thanks for your offer, i will wait for more information from you.
Heya, im trying to do the exact same thing.
I would appreciate your input on this Alex-y :D
Still don't have all the hardware at place, but researching!
If anyone has more info please post your updates :D
Thanks!
Quote from: tomasmigone on January 24, 2013, 11:58:59 PM
Still don't have all the hardware at place, but researching!
Bought today this:
7"TFT LCD Module[800*480] with Touch Panel(SSD1963),ARM STM32 code Cortex-M3
http://bbs.openmcu.com/forum.php?mod=viewthread&tid=484&extra (http://bbs.openmcu.com/forum.php?mod=viewthread&tid=484&extra)
Search it on Ebay, very easy to find.In that Chinese forum (you must register to see the post attachments) they give you several STM32(only) projects with display/touch/SD/DATAFLASH functions and complete datasheets. If you need, I have them all.
Hi all,
In code listing, at end of mesage, draft of kernel driver. I used it when ran android (v1.5) on imx233 (dev board SK-IMX233). This code I wrote long time ago and don't remember works it, or should be corrected something. Anyway I hope this will help to understand how to connect SSD1963 to IMX233.
Byte array 'lcd_init_data' contains confuration words.
Look at the pin confuration structure 'lcd_ssd1963_desc':
PINID_LCD_D00 - PINID_LCD_D07
PINID_LCD_RESET
PINID_LCD_RS
PINID_LCD_CS
PINID_LCD_WR
Except PINID_LCD_VSYNC, forget about it. This is my experiments, to avoid 'tearing' effect.
Listed pins should be connected with SSD1963 by following shcheme:
imx233 SSD1963
LCD_D0-D7 - D0-D7 (D8-D15 floating)
LCD_RS - D/C#
LCD_CS - CS#
LCD_RESET - RESET#
LCD_WR - WR#
-- - RD# - (pull up, not used)
Please feel free, ask questions.
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/notifier.h>
#include <linux/regulator/consumer.h>
#include <mach/regs-lcdif.h>
#include <mach/regs-pinctrl.h>
#include <mach/regs-clkctrl.h>
#include <mach/regs-apbh.h>
#include <mach/gpio.h>
#include <mach/pins.h>
#include <mach/pinmux.h>
#include <mach/lcdif.h>
#include <mach/stmp3xxx.h>
#include <mach/platform.h>
#include <mach/regs-pxp.h>
#define CMD 0
#define DATA 1
#define LCD_WIDTH 480
#define LCD_HEIGHT 272
#define lcdif_read(reg) __raw_readl(REGS_LCDIF_BASE + reg)
#define lcdif_write(reg,val) __raw_writel(val, REGS_LCDIF_BASE + reg)
#define pxp_read(reg) __raw_readl(REGS_PXP_BASE + reg)
#define pxp_write(reg,val) __raw_writel(val, REGS_PXP_BASE + reg)
enum
{
CTRL = 0,
RGBBUF,
RGBBUF2,
RGBSIZE,
S0BUF,
S0UBUF,
S0VBUF,
S0PARAM,
S0BACKGROUND,
S0CROP,
S0SCALE,
S0OFFSET,
S0COLORKEYLOW,
S0COLORKEYHIGH,
OLCOLORKEYLOW,
OLCOLORKEYHIGH
};
struct pxps
{
u32 * outb_virt;
dma_addr_t outb_phys;
/* PXP_NEXT */
u32 * regs_virt;
dma_addr_t regs_phys;
struct tasklet_struct tasklet;
};
static struct pin_desc lcd_ssd1963_desc[] =
{
{ PINID_LCD_D00, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D01, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D02, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D03, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D04, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D05, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D06, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_D07, PIN_FUN1, PIN_8MA, PIN_3_3V, 0 },
{ PINID_LCD_RESET, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
{ PINID_LCD_RS, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
{ PINID_LCD_CS, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
{ PINID_LCD_WR, PIN_FUN1, PIN_8MA, PIN_3_3V, 1 },
{ PINID_LCD_VSYNC, PIN_FUN2, PIN_8MA, PIN_3_3V, 1 }
};
struct pin_group lcd_ssd1963_pins = {
.pins = lcd_ssd1963_desc,
.nr_pins = ARRAY_SIZE(lcd_ssd1963_desc),
};
static struct clk *lcd_clk;
static struct pxps *pxp;
static int inited = 0;
/* taked from freescale forum */
static char lcd_init_data[] =
{
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0xe0, 0x01, 0x01, 0x00, 0xe0,
0x01, 0x03, 0x00, 0x36, 0x01, 0x08, 0x00, 0xb0, 0x01, 0x0c, 0x01, 0x80,
0x01, 0x01, 0x01, 0xdf, 0x01, 0x01, 0x01, 0x0f, 0x01, 0x00, 0x00, 0xf0,
0x01, 0x00, 0x00, 0x3a, 0x01, 0x60, 0x00, 0xe6, 0x01, 0x01, 0x01, 0x45,
0x01, 0x47, 0x00, 0xb4, 0x01, 0x02, 0x01, 0x0d, 0x01, 0x00, 0x01, 0x2b,
0x01, 0x28, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xb6, 0x01, 0x01,
0x01, 0x1d, 0x01, 0x00, 0x01, 0x0c, 0x01, 0x09, 0x01, 0x00, 0x01, 0x00,
0x00, 0x2a, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0xdf, 0x00, 0x2b,
0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x0f, 0x00, 0x29, 0x00, 0x2c,
CMD, 0x35, /* Set Tear On */
DATA, 0x00, /* The tearing effect output line consists of V-blanking information only.*/
};
static void mpulcd_setup_pannel_register(char data, char val)
{
lcdif_write(HW_LCDIF_CTRL_CLR,
BM_LCDIF_CTRL_LCDIF_MASTER | BM_LCDIF_CTRL_RUN);
lcdif_write(HW_LCDIF_TRANSFER_COUNT,
BF_LCDIF_TRANSFER_COUNT_V_COUNT(1) |
BF_LCDIF_TRANSFER_COUNT_H_COUNT(1));
if(data)
lcdif_write(HW_LCDIF_CTRL_SET, BM_LCDIF_CTRL_DATA_SELECT);
else
lcdif_write(HW_LCDIF_CTRL_CLR, BM_LCDIF_CTRL_DATA_SELECT);
lcdif_write(HW_LCDIF_CTRL_SET, BM_LCDIF_CTRL_RUN);
lcdif_write(HW_LCDIF_DATA, val);
while(lcdif_read(HW_LCDIF_CTRL) & BM_LCDIF_CTRL_RUN);
lcdif_write(HW_LCDIF_CTRL1_CLR, BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ);
}
#define WRITE_CMD(val) mpulcd_setup_pannel_register(0, (val));
#define WRITE_DATA(val) mpulcd_setup_pannel_register(1, (val));
void mpulcd_start_refresh(void)
{
// mpulcd_setup_pannel_register(CMD, 0x2c);
// lcdif_write(HW_LCDIF_CTRL_SET, BM_LCDIF_CTRL_DATA_SELECT);
// lcdif_write(HW_LCDIF_TRANSFER_COUNT,
// BF_LCDIF_TRANSFER_COUNT_V_COUNT(272) |
// BF_LCDIF_TRANSFER_COUNT_H_COUNT(480*3));
// stmp3xxx_lcdif_run();
__raw_writel(pxp->regs_phys, HW_PXP_NEXT_ADDR);
lcdif_write(HW_LCDIF_CTRL1_CLR, BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN);
tasklet_schedule(&pxp->tasklet);
}
static void mpulcd_tasklet_func(unsigned long pxp)
{
mpulcd_setup_pannel_register(CMD, 0x2c);
lcdif_write(HW_LCDIF_CTRL_SET, BM_LCDIF_CTRL_DATA_SELECT);
lcdif_write(HW_LCDIF_TRANSFER_COUNT,
BF_LCDIF_TRANSFER_COUNT_V_COUNT(LCD_HEIGHT) |
BF_LCDIF_TRANSFER_COUNT_H_COUNT(LCD_WIDTH * 3));
lcdif_write( HW_LCDIF_CTRL1_SET, BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN );
stmp3xxx_lcdif_run();
}
static void mpulcd_init_lcdif(void)
{
printk("-> %s \r\n", __PRETTY_FUNCTION__);
lcdif_write( HW_LCDIF_CTRL_CLR,
BM_LCDIF_CTRL_CLKGATE | BM_LCDIF_CTRL_SFTRST);
lcdif_write( HW_LCDIF_CTRL,
BF_LCDIF_CTRL_LCD_DATABUS_WIDTH(BV_LCDIF_CTRL_LCD_DATABUS_WIDTH__8_BIT) |
BF_LCDIF_CTRL_WORD_LENGTH(BV_LCDIF_CTRL_WORD_LENGTH__8_BIT));
lcdif_write( HW_LCDIF_CTRL1,
BV_LCDIF_CTRL1_MODE86__8080_MODE |
BM_LCDIF_CTRL1_BUSY_ENABLE |
BF_LCDIF_CTRL1_BYTE_PACKING_FORMAT(0xf) |
BM_LCDIF_CTRL1_OVERFLOW_IRQ_EN |
BM_LCDIF_CTRL1_UNDERFLOW_IRQ_EN |
BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN |
BM_LCDIF_CTRL1_RECOVER_ON_UNDERFLOW |
BM_LCDIF_CTRL1_BM_ERROR_IRQ_EN );
lcdif_write( HW_LCDIF_TIMING, 0x02020202 );
printk("<- %s \r\n", __PRETTY_FUNCTION__);
}
static void mpulcd_init_panel_hw(void)
{
int i;
printk("-> %s \r\n", __PRETTY_FUNCTION__);
for( i = 0; i < sizeof(lcd_init_data); i += 2 )
{
mpulcd_setup_pannel_register(lcd_init_data[i], lcd_init_data[i + 1]);
mdelay(5);
}
/**/
/* lcdif_write(HW_LCDIF_CTRL_SET,
BM_LCDIF_CTRL_VSYNC_MODE );//| BM_LCDIF_CTRL_WAIT_FOR_VSYNC_EDGE );
lcdif_write(HW_LCDIF_CTRL1_SET,
BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ_EN | BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ );
lcdif_write( HW_LCDIF_VDCTRL0,
BM_LCDIF_VDCTRL0_VSYNC_OEB |
BF_LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH(0) );
lcdif_write( HW_LCDIF_VDCTRL3, BM_LCDIF_VDCTRL3_VSYNC_ONLY );
lcdif_write( HW_LCDIF_VDCTRL4, BM_LCDIF_VDCTRL4_SYNC_SIGNALS_ON );
*/
printk("<- %s \r\n", __PRETTY_FUNCTION__);
}
static int mpulcd_init_pxp(dma_addr_t fb_phys)
{
int ret = 0;
printk("-> %s \r\n", __PRETTY_FUNCTION__);
if( !request_mem_region(REGS_PXP_PHYS, REGS_PXP_SIZE, "ssd1963_pxp"))
{
ret = -EBUSY;
goto out;
}
pxp = kzalloc(sizeof(*pxp), GFP_KERNEL);
if (!pxp)
{
ret = -ENOMEM;
goto out1;
}
pxp->outb_virt = kmalloc(LCD_WIDTH * LCD_HEIGHT * 3, GFP_KERNEL);
if( !pxp->outb_virt )
{
ret = -ENOMEM;
goto out2;
}
pxp->outb_phys = virt_to_phys(pxp->outb_virt);
dma_map_single( NULL, pxp->outb_virt, LCD_WIDTH * LCD_HEIGHT * 3, DMA_FROM_DEVICE );
/* */
pxp->regs_virt = dma_alloc_coherent( NULL,
PAGE_ALIGN(sizeof(u32) * REGS_PXP_SIZE),
&pxp->regs_phys,
GFP_KERNEL | __GFP_ZERO);
if( !pxp->regs_virt )
{
printk("%s: Failed to allocate pxp_register object.\r\n", __PRETTY_FUNCTION__ );
ret = -ENOMEM;
goto out3;
}
/* Pull PxP out of reset */
__raw_writel(0, HW_PXP_CTRL_ADDR);
pxp->regs_virt[S0BUF] = fb_phys;
pxp->regs_virt[S0UBUF] = fb_phys + LCD_WIDTH * LCD_HEIGHT;
pxp->regs_virt[S0VBUF] = fb_phys + LCD_WIDTH * LCD_HEIGHT + LCD_WIDTH * LCD_HEIGHT / 4;
pxp->regs_virt[S0PARAM] = BF(LCD_WIDTH >> 3, PXP_S0PARAM_WIDTH) | BF(LCD_HEIGHT >> 3, PXP_S0PARAM_HEIGHT);
pxp->regs_virt[RGBBUF] = pxp->outb_phys;
pxp->regs_virt[RGBSIZE] = BF(0xff, PXP_RGBSIZE_ALPHA) | BF(LCD_WIDTH, PXP_RGBSIZE_WIDTH) | BF(LCD_HEIGHT, PXP_RGBSIZE_HEIGHT);
pxp->regs_virt[S0COLORKEYLOW] = 0xFFFFFF;
pxp->regs_virt[S0COLORKEYHIGH] = 0;
pxp->regs_virt[OLCOLORKEYLOW] = 0xFFFFFF;
pxp->regs_virt[OLCOLORKEYHIGH] = 0;
pxp->regs_virt[CTRL] = BF(BV_PXP_CTRL_S0_FORMAT__RGB565, PXP_CTRL_S0_FORMAT) |
BF(BV_PXP_CTRL_OUTPUT_RGB_FORMAT__RGB888P, PXP_CTRL_OUTPUT_RGB_FORMAT) | BM_PXP_CTRL_ENABLE;
tasklet_init(&pxp->tasklet, mpulcd_tasklet_func, (unsigned long)pxp);
printk("<- %s \r\n", __PRETTY_FUNCTION__);
return 0;
out3:
kfree(pxp->outb_virt);
out2:
kfree(pxp);
out1:
release_mem_region(REGS_PXP_PHYS, REGS_PXP_SIZE);
out:
return ret;
}
static void mpulcd_release_pxp(void)
{
printk("-> %s \r\n", __PRETTY_FUNCTION__);
tasklet_kill(&pxp->tasklet);
kfree(pxp->regs_virt);
kfree(pxp->outb_virt);
kfree(pxp);
release_mem_region(REGS_PXP_PHYS, REGS_PXP_SIZE);
printk("<- %s \r\n", __PRETTY_FUNCTION__);
}
static int mpulcd_init_panel(struct device *dev, dma_addr_t phys, int memsize,
struct stmp3xxx_platform_fb_entry *pentry)
{
int ret = 0;
if( inited )
{
printk("%s: Already inited! \r\n", __PRETTY_FUNCTION__);
stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry);
return -EALREADY;
}
printk("-> %s \r\n", __PRETTY_FUNCTION__);
lcd_clk = clk_get(dev, "lcdif");
if (IS_ERR(lcd_clk)) {
ret = PTR_ERR(lcd_clk);
goto out;
}
ret = clk_enable(lcd_clk);
if (ret)
goto out1;
ret = clk_set_rate(lcd_clk, 24000);
if (ret)
goto out2;
ret = mpulcd_init_pxp(phys);
if(ret)
goto out2;
mpulcd_init_lcdif();
//stmp3xxx_lcdif_dma_init(dev, phys, memsize, 1);
stmp3xxx_lcdif_dma_init(dev, pxp->outb_phys, LCD_WIDTH * LCD_HEIGHT * 3, 1);
/* */
stmp3xxx_clearl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* low */
mdelay(50);
stmp3xxx_setl(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1); /* high */
mdelay(100);
stmp3xxx_request_pin_group(&lcd_ssd1963_pins, "mpulcd_pin");
gpio_request(PINID_LCD_ENABLE, "ssd1963");
mpulcd_init_panel_hw();
stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_INIT, pentry);
inited = 1;
return 0;
out2:
clk_disable(lcd_clk);
out1:
clk_put(lcd_clk);
out:
inited = 0;
return ret;
}
static void mpulcd_display_on(void)
{
mpulcd_setup_pannel_register(CMD, 0x29);
// lcdif_write(HW_LCDIF_CTRL1_SET, BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN);
mpulcd_start_refresh();
}
static void mpulcd_display_off(void)
{
mpulcd_setup_pannel_register(CMD, 0x28);
// lcdif_write(HW_LCDIF_CTRL1_CLR, BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ_EN);
}
static void mpulcd_release_panel(struct device *dev,
struct stmp3xxx_platform_fb_entry *pentry)
{
if( !inited )
{
printk("%s: panel not inited!\r\n", __PRETTY_FUNCTION__);
return;
}
printk("-> %s \r\n", __PRETTY_FUNCTION__);
stmp3xxx_lcdif_notify_clients(STMP3XXX_LCDIF_PANEL_RELEASE, pentry);
mpulcd_display_off();
gpio_free(PINID_LCD_ENABLE);
stmp3xxx_release_pin_group(&lcd_ssd1963_pins, "mpulcd_pin");
stmp3xxx_lcdif_dma_release();
mpulcd_release_pxp();
clk_disable(lcd_clk);
clk_put(lcd_clk);
inited = 0;
printk("<- %s \r\n", __PRETTY_FUNCTION__);
}
static int mpulcd_blank_panel(int blank)
{
int ret = 0;
switch (blank)
{
case FB_BLANK_NORMAL:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_UNBLANK:
break;
default:
ret = -EINVAL;
}
return ret;
}
/*
int mpulcd_pan_display(dma_addr_t addr)
{
lcdif_write(HW_LCDIF_CUR_BUF, addr);
return 0;
}
*/
static struct stmp3xxx_platform_fb_entry fb_entry = {
.name = "ssd1963",
.x_res = LCD_HEIGHT,
.y_res = LCD_WIDTH,
.bpp = 24,
.cycle_time_ns = 150,
.lcd_type = STMP3XXX_LCD_PANEL_SYSTEM,
.init_panel = mpulcd_init_panel,
.release_panel = mpulcd_release_panel,
.blank_panel = mpulcd_blank_panel,
.run_panel = mpulcd_display_on,
.stop_panel = mpulcd_display_off,
.pan_display = stmp3xxx_lcdif_pan_display,
};
static int __init register_devices(void)
{
stmp3xxx_lcd_register_entry(&fb_entry, stmp3xxx_framebuffer.dev.platform_data);
return 0;
}
subsys_initcall(register_devices);
Thank you Alex-y for your example, as soon as I'll get my brand new display I'll try it out !
Meanwhile, I was wandering around the net, and stumbled across these :
https://github.com/lipeandres/Stamp-Kernel-Extentions/tree/master/linux-2.6.35.3-17042011-km233/drivers/video/mxs (https://github.com/lipeandres/Stamp-Kernel-Extentions/tree/master/linux-2.6.35.3-17042011-km233/drivers/video/mxs)
and
https://github.com/fbrausse/ssd1963
The first is a iMX233 kernel with SSD1963 framebuffer implementation, the second one is a SSD1963 user-space driver library for Raspberry Pi. I'm fairly new in Linux/driver thingy , but I think they are both good examples/implementation :)
Hi Dataedge,
I viewed links from your message. It very similar to drivers which I used early, as example.
I think it also good examples. ;)
Hi everyone,
a little update with that site :
http://www.starterkit.ru/html/index.php?name=shop&op=view&id=41 (http://www.starterkit.ru/html/index.php?name=shop&op=view&id=41)
as you can see they have a LCD addon (480x272 - SSD1963 - TS)
I cannot read cyrillic Russian, but in their forum they claim to use original Freescale EVK kernel (linux-2.6.31.12) and that patch:
http://sasamy.narod.ru/sk_mxs.patch (http://sasamy.narod.ru/sk_mxs.patch)
from this thread (sasamy reply):
http://www.starterkit.ru/html/index.php?name=forum&op=view&id=9628&num=2#9644 (http://www.starterkit.ru/html/index.php?name=forum&op=view&id=9628&num=2#9644)