[SOLVED]interfacing OLINUXINO - iMX233 - Maxi with intelligent LCD display

Started by patxiespain, January 14, 2013, 06:09:18 PM

Previous topic - Next topic

patxiespain

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

Alex-y

I have successfully connected SSD1963 to iMX233. Just configure iMX233 LCD controller in 8080 mode.


Eskimo

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.

Alex-y

Yes sure, I will draw connection scheme.  And can give you draft of linux driver.

Dataedge


Eskimo

Alex-y, many thanks for your offer, i will wait for more information from you.

tomasmigone

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!

Dataedge

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
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.


Alex-y

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);


Dataedge

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
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  :)

Alex-y

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.   ;)

Dataedge

Hi everyone,

a little update with that site :
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
from this thread (sasamy reply):
http://www.starterkit.ru/html/index.php?name=forum&op=view&id=9628&num=2#9644