Linux2.6.32 for Utu2440(7)

Watchdog

Change the configuraiton of Watchdog in kernel:
/images/2440watchdog.jpg

Watchdog configuraiton in kernel:

$ grep "CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME" ./ -r
./drivers/watchdog/s3c2410_wdt.c:#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME   (15)

Test Watchdog:

~ # dmesg | grep watchdog
s3c2410-wdt s3c2410-wdt: watchdog inactive, reset disabled, irq enabled
~ # 
~ # echo 0>/dev/watchdog

s3c2410-wdt s3c2410-wdt: Unexpected close, not stopping watchdog

Then after 15 seconds your board will reset, disable watchdog via:

~ # echo -n V>/dev/watchdog

LED

The connection for LED is:
/images/2440leds.jpg

Saw s3c2440 chip connection:
/images/2440leds1.jpg

Connection in text:

EINT4 GPF4 DL1
EINT5 GPF5 DL2
EINT6 GPF6 DL3
EINT7 GPF7 DL4 

Code changes:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#define DEVICE_NAME "leds" //设备名(/dev/leds)
//LED 对应的GPIO 端口列表
static unsigned long led_table [] = {
 S3C2410_GPF(4),
 S3C2410_GPF(5),
 S3C2410_GPF(6),
 S3C2410_GPF(7),
};
//LED 对应端口将要输出的状态列表
static unsigned int led_cfg_table [] = {
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
 S3C2410_GPIO_OUTPUT,
};
/*ioctl 函数的实现
* 在应用/用户层将通过ioctl 函数向内核传递参数,以控制LED 的输出状态
*/
static int leds_ioctl(struct inode *inode,
          struct file *file,
          unsigned int cmd,
          unsigned long arg)
{
 switch(cmd) {
  case 0:
  case 1:
    if (arg > 4) {
     return -EINVAL;
    }
  //根据应用/用户层传递来的参数(取反),通过s3c2410_gpio_setpin 函数设置LED 对应的端口寄存器
  s3c2410_gpio_setpin(led_table[arg], !cmd);
   return 0;
  default:
    return -EINVAL;
 }
}
/*
* 设备函数操作集,在此只有ioctl 函数,通常还有read, write, open, close 等,因为本LED 驱动在下面已经
* 注册为misc 设备,因此也可以不用open/close
*/
static struct file_operations dev_fops = {
 .owner = THIS_MODULE,
 .ioctl = leds_ioctl,
};
/*
* 把LED 驱动注册为MISC 设备
*/
static struct miscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR, //动态设备号
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};
/*
* 设备初始化
*/
static int __init dev_init(void)
{
 int ret;
 int i;
 for (i = 0; i < 4; i++) {
  //设置LED 对应的端口寄存器为输出(OUTPUT)
  s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
  //设置LED 对应的端口寄存器为低电平输出,在模块加载结束后,四个LED 应该是全部都是发光状态
  s3c2410_gpio_setpin(led_table[i], 0);
 }
 ret = misc_register(&misc); //注册设备

 if(ret < 0)
 {
        printk(DEVICE_NAME "register falid!\n");
        return ret;
  }

 printk (DEVICE_NAME "\tinitialized\n"); //打印初始化信息
 return 0;
}
static void __exit dev_exit(void)
{
 misc_deregister(&misc);
}
//模块初始化,仅当使用insmod/podprobe 命令加载时有用,如果设备不是通过模块方式加载,此处将不会被调用
module_init(dev_init); 
//卸载模块,当该设备通过模块方式加载后,可以通过rmmod 命令卸载,将调用此函数
module_exit(dev_exit); 
MODULE_LICENSE("GPL"); //版权信息
MODULE_AUTHOR("singleboy."); //开发者信息

Change Kconfig file:

config LEDS_SMDK2440
 tristate "LED Support for SMDK2440 GPIO LEDs"
 depends on SMDK2440_CPU2440
 default y if SMDK2440_CPU2440
 help
  This option enables support for LEDs connected to GPIO lines
  on SMDK2440 boards.

And the Makefile

obj-$(CONFIG_LEDS_SMDK2440)     += smdk2440_leds.o

Rebuild the kernel and verify.

Test file:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(int argc, char **argv)
{
 int on;
 int led_no;
 int fd;
 if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||
     on < 0 || on > 1 || led_no < 0 || led_no > 3) {
  fprintf(stderr, "Usage: leds led_no 0|1\n");
  exit(1);
 }
 fd = open("/dev/leds0", 0);
 if (fd < 0) {
  fd = open("/dev/leds", 0);
 }
 if (fd < 0) {
  perror("open device leds");
  exit(1);
 }
 ioctl(fd, on, led_no);
 close(fd);
 return 0;
}

Then run test like:

/root # ./led 0 0
/root # ./led 1 0
/root # ./led 2 0
/root # ./led 2 1
/root # ./led 2 0
/root # ./led 3 0

Write a simple script

while true
 do
./led 0 0
sleep 1
./led 0 1
sleep 1
./led 1 0
sleep 1
./led 1 1
sleep 1
./led 2 0
sleep 1
./led 2 1
sleep 1
./led 3 0
sleep 1
./led 3 1
done

Now you could see led blinks.

Keys

First view the schematic to find the circle connection:
/images/2440buttons.jpg

Keys connection:

K1	EINT0		GPF0
K2	EINT1		GPF1 
K3	EINT2		GPF2
K4	EINT3		GPF3	
K5	EINT11		GPG3
K6	EINT19		GPG11

The definition should be adjusted to the keys connection definition.

Edit the drivers/misc/smdk2440_buttons.c:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#define DEVICE_NAME "buttons" //设备名称
/*定义中断所用的结构体*/
struct button_irq_desc {
 int irq; //按键对应的中断号
 int pin; //按键所对应的GPIO 端口
 int pin_setting; //按键对应的引脚描述,实际并未用到,保留
 int number; //定义键值,以传递给应用层/用户态
 char *name; //每个按键的名称
};
/*结构体实体定义*/
static struct button_irq_desc button_irqs [] = {
 {IRQ_EINT0, S3C2410_GPF(0), S3C2410_GPF0_EINT0, 0, "KEY0"},
 {IRQ_EINT1, S3C2410_GPF(1), S3C2410_GPF1_EINT1, 1, "KEY1"},
 {IRQ_EINT2, S3C2410_GPF(2), S3C2410_GPF2_EINT2, 2, "KEY2"},
 {IRQ_EINT3, S3C2410_GPF(3), S3C2410_GPF3_EINT3, 3, "KEY3"},
 {IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 4, "KEY4"},
 {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"},
};
/*开发板上按键的状态变量,注意这里是’0’,对应的ASCII 码为30*/
static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
/*因为本驱动是基于中断方式的,在此创建一个等待队列,以配合中断函数使用;当有按键按下并读取到键
值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用户态;当没有按
键按下时,系统并不会轮询按键状态,以节省时钟资源*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为1,read 函数会把它清零*/
static volatile int ev_press = 0;
/*本按键驱动的中断服务程序*/
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
 struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
 int down;
 // udelay(0);
 /*获取被按下的按键状态*/
 down = !s3c2410_gpio_getpin(button_irqs->pin);
 /*状态改变,按键被按下,从这句可以看出,当按键没有被按下的时候,寄存器的值为1(上拉),但按
  键被按下的时候,寄存器对应的值为0*/
 if (down != (key_values[button_irqs->number] & 1)) { // Changed
  /*如果key1 被按下,则key_value[0]就变为’1’,对应的ASCII 码为31*/
  key_values[button_irqs->number] = '0' + down;
  ev_press = 1; /*设置中断标志为1*/
  wake_up_interruptible(&button_waitq); /*唤醒等待队列*/
 }
 return IRQ_RETVAL(IRQ_HANDLED);
}
/*
*在应用程序执行open(“/dev/buttons”,…)时会调用到此函数,在这里,它的作用主要是注册6 个按键的中断。
*所用的中断类型是IRQ_TYPE_EDGE_BOTH,也就是双沿触发,在上升沿和下降沿均会产生中断,这样做
是为了更加有效地判断按键状态
*/
static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
 int i;
 int err = 0;
 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
  if (button_irqs[i].irq < 0) {
   continue;
  }
  /*注册中断函数*/
  err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
        button_irqs[i].name, (void *)&button_irqs[i]);
  if (err)
   break;
 }
 if (err) { /*如果出错,释放已经注册的中断,并返回*/
  i--;
  for (; i >= 0; i--) {
   if (button_irqs[i].irq < 0) {
    continue;
   }
   disable_irq(button_irqs[i].irq);
   free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
  }
  return -EBUSY;
 }
 /*注册成功,则中断队列标记为1,表示可以通过read 读取*/
 ev_press = 1;
 /*正常返回*/
 return 0;
}
/*
*此函数对应应用程序的系统调用close(fd)函数,在此,它的主要作用是当关闭设备时释放6 个按键的中断*
处理函数
*/
static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
 int i;
 for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
  if (button_irqs[i].irq < 0) {
   continue;
  }
  /*释放中断号,并注销中断处理函数*/
  free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
 }
 return 0;
}
/*
*对应应用程序的read(fd,…)函数,主要用来向用户空间传递键值
*/
static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
 unsigned long err;
 if (!ev_press) {
  if (filp->f_flags & O_NONBLOCK)
   /*当中断标识为0 时,并且该设备是以非阻塞方式打开时,返回*/
   return -EAGAIN;
  else
   /*当中断标识为0 时,并且该设备是以阻塞方式打开时,进入休眠状态,等待被唤醒*/
   wait_event_interruptible(button_waitq, ev_press);
 }
 /*把中断标识清零*/
 ev_press = 0;
 /*一组键值被传递到用户空间*/
 err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
 return err ? -EFAULT : min(sizeof(key_values), count);
}
static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
 unsigned int mask = 0;
 /*把调用poll 或者select 的进程挂入队列,以便被驱动程序唤醒*/
 poll_wait(file, &button_waitq, wait);
 if (ev_press)
  mask |= POLLIN | POLLRDNORM;
 return mask;
}
/*设备操作集*/
static struct file_operations dev_fops = {
 .owner = THIS_MODULE,
 .open = s3c24xx_buttons_open,
 .release = s3c24xx_buttons_close,
 .read = s3c24xx_buttons_read,
 .poll = s3c24xx_buttons_poll,
};
static struct miscdevice misc = {
 .minor = MISC_DYNAMIC_MINOR,
 .name = DEVICE_NAME,
 .fops = &dev_fops,
};
/*设备初始化,主要是注册设备*/
static int __init dev_init(void)
{
 int ret;
 /*把按键设备注册为misc 设备,其设备号是自动分配的*/
 ret = misc_register(&misc);

 if(ret < 0)
  {
        printk(DEVICE_NAME "register falid!\n");
        return ret;
   }
 printk (DEVICE_NAME"\tinitialized\n");
 return 0;
}
/*注销设备*/
static void __exit dev_exit(void)
{
 misc_deregister(&misc);
}
module_init(dev_init); //模块初始化,仅当使用insmod/podprobe 命令加载时有用,如果设备不是通过模块方式加载,此处将不会被调用
module_exit(dev_exit); //卸载模块,当该设备通过模块方式加载后,可以通过rmmod 命令卸载,将调用此函数
MODULE_LICENSE("GPL"); //版权信息
MODULE_AUTHOR("singleboy."); //作者名字

Edit the Kconfig file:

 config SMDK2440_BUTTONS
  tristate "Buttons driver for SMDK2440 development boards"
  depends on SMDK2440_CPU2440
  default y if SMDK2440_CPU2440
  help
   this is buttons driver for SMDK2440 development boards

Also change the Makefile:

obj-$(CONFIG_SMDK2440_BUTTONS)  += smdk2440_buttons.o

Make the kernel out and verify it on board.

Test code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

int main(void)
{
 int buttons_fd;
 char buttons[6] = {'0', '0', '0', '0', '0', '0'}; //定义按键值变量,对于驱动函数中的key_values 数组

 buttons_fd = open("/dev/buttons", 0); /*打开按键设备/dev/buttons*/
 if (buttons_fd < 0) {
  perror("open device buttons"); /*打开失败则退出*/
  exit(1);
 }

 for (;;) { /*永读按键并打印键值和状态*/
  char current_buttons[6];
  int count_of_changed_key;
  int i;

/*使用read 函数读取一组按键值(6 个)*/
  if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) {
   perror("read buttons:");
   exit(1);
  }

/*逐个分析读取到的按键值*/

  for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) {
   if (buttons[i] != current_buttons[i]) {
    buttons[i] = current_buttons[i];

/*打印按键值,并标明按键按下/抬起的状态*/
    printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
    count_of_changed_key++;
   }
  }
  if (count_of_changed_key) {
   printf("\n");
  }
 }

 close(buttons_fd); /*关闭按键设备文件*/
 return 0;
}

Test Result:

/root # ./buttons
key 1 is down
key 1 is up
key 2 is down
key 2 is up
key 3 is down
key 3 is up
key 4 is down

Linux2.6.32 for Utu2440(6)

Flash Disk

Enable the scsi device support in kernel configuration:
/images/scsidriver.jpg
Then enable the usb support:

* Support for Host-side USB
* USB device filesystem(DEPRECATED)
* OHCI HCD support
* USB Mass Storage support

Also enable the filesystem support for FAT/FAT16/FAT32/EXT4:

change the default u-boot parameters:

utu-bootloader=>>>printenv bootcmd
bootcmd=nand read.i 0x32000000 0x60000 0x200000; bootm
utu-bootloader=>>>setenv bootcmd 'tftp 30000000 uImage; bootm'
utu-bootloader=>>>printenv bootcmd
bootcmd=tftp 30000000 uImage; bootm
utu-bootloader=>>>saveenv
Saving Environment to NAND...
Erasing Nand...Writing to Nand... done

Next time the reboot will directly download the kernel file from tftp server and bootm it from memory.

Also please enable the filesystem support on FAT/FAT32/ext4, etc, then next time you insert the USB Disk, it will automatically scanned and recognize your partitions.

MMC/SD

From the schematic image we could see:
/images/sdio.jpg

Add the following definition in arch/arm/mach-s3c2440/mach-smdk2440.c:

// Added for mmc
#include <linux/mmc/host.h>
#include <plat/mci.h>

/* MMC/SD */
static struct s3c24xx_mci_pdata smdk2440_mmc_cfg = {
    .gpio_detect = S3C2410_GPG(8),
    .gpio_wprotect = S3C2410_GPH(8),
    .set_power = NULL,
    .ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34,
};

static struct platform_device *smdk2440_devices[] __initdata = {
	//........
	&s3c_device_sdi,  // Added SD card into the platform equipments 
};

static void __init smdk2440_machine_init(void)
{

+    	/* Add support for mmc in smdk2440 */
+	s3c_device_sdi.dev.platform_data = &smdk2440_mmc_cfg;


Kernel configuration:

Device Drivers --->
<*> MMC/SD/SDIO card support --->
<*> MMC block device driver
<*> Secure Digital Host Controller Interface support
<*> Samsung S3C SD/MMC Card Interface support

Then re-compile the kernel and verify.
Only 1G’s mmc card could be supported currently. I didn’t test other volumns.

Sound Card

The sound card’s schematic is listed as:
/images/soundcard.jpg
Then made code changes in:

// Sound Card
#include <sound/s3c24xx_uda134x.h>


/*Sound card*/
static struct s3c24xx_uda134x_platform_data s3c24xx_uda134x_data = {
    .l3_clk = S3C2410_GPB(4),
    .l3_data = S3C2410_GPB(3),
    .l3_mode = S3C2410_GPB(2),
    .model = UDA134X_UDA1341,
};


static struct platform_device s3c24xx_uda134x = {
    .name = "s3c24xx_uda134x",
         .dev = {
         .platform_data = &s3c24xx_uda134x_data,
    }
};

static struct platform_device *smdk2440_devices[] __initdata = {
	//.........
        &s3c24xx_uda134x, // Register the UDA1341 platform device to kernel
};


Kernel Configuration:
/images/soundcard2.jpg

/images/soundcard1.jpg
Then re-compile the kernel and verify it on utu2440.

Test steps:

$ cat /root/test.wav>/dev/dsp
$ cat /proc/devices
10 misc
 13 input
 14 sound
 21 sg
 29 fb

Now cross-compile mad-player for playing mp3.

Download the madplayer and cross-compiler.

$ ./configure CC=arm-linux-gcc --host=arm --prefix=/srv/nfs4/rootfs/usr/
$ make clean && make
$ sudo make install

Trouble shooting:

/bin # /bin/sh madplay 
madplay: line 1: syntax error: unexpected word (expecting ")")

[Trusty@/media/y/embedded/utu2440/App/madplay-0.15.2b]$ arm-linux-readelf -a madplay | grep interpreter
      [Requesting program interpreter: /lib/ld-linux.so.3]

Solution: copy all of the dynamic library from the cross-compiler to the root file system:

$ sudo cp -f /opt/cross/arm-linux-gcc_4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib/*so* /srv/nfs4/rootfs/lib -a

Download the libmad from:
http://sourceforge.net/projects/mad/files/libmad/0.15.1b/libmad-0.15.1b.tar.gz/download

$ ./configure --prefix=/srv/nfs4/rootfs/usr/ --host=arm-linux CC=arm-linux-gcc
$ make && make install

When run madplay, it will complains the Bus Error, need further debugging.

ShadowSocks on DigitalOcean

Mainly recorded the steps for installation:

# apt-get install python-pip
# pip install shadowsocks
# vim /etc/shadowsocks.json
{
    "server":"1xx.xxx.xxx.xxx",
    "server_port":xxxx,
    "local_address": "127.0.0.1",
    "local_port":1080,
    "password":"Pass!Pass!Pass",
    "timeout":300,
    "method":"aes-256-cfb",
    "fast_open": false,
    "workers": 1
}
# apt-get install supervisor
# vim /etc/supervisor/conf.d/shadowsocks.conf
[program:shadowsocks]
command=ssserver -c /etc/shadowsocks.json
autorestart=true
user=nobody
# vim /etc/default/supervisor
ulimit -n 51200
# service supervisor start

Then in client you could use a shadownsocks client for connecting to remote servers and enjoy the free internet.

System Trouble Shooting

SSHD Timeout

Trouble

When you login to some remote ssh machine, after 1 or 2 minutes, the server will hint you:
Connection closed by foreign host.

Solution

This trouble happens on Ubuntu14.04, because we don’t set the timeout intervals. So we could add following 2 lines into the /etc/ssh/sshd_config

ClientAliveInterval 60
ClientAliveCountMax 3

AliveInterval means we could keep the client alive for 60 minutes. AliveCountMax means when server send 3 times request to ssh client, if client didn’t send back reply message, then the ssh connection will be lost.
Restart the ssh via service ssh restart and now you won’t got this annoying trouble.

DNS

Since I use dnsencrypt at home, so once it enabled, all of the query goes to the 127.0.0.1, I have to turn it back at company.

[Trusty@~]$ cat /etc/resolv.conf
# Generated by resolvconf
nameserver 10.0.0.1
search lan
alias disdns='sudo systemctl stop dnscrypt-proxy.service'
$ disdns

By doing this you could reach the internet.

Linux2.6.32 for Utu2440(5)

In this chapter we will continue to migrate driver on LCD, first we want to get the LCD information.

LCD Info

From the LCD we could see “LTV350QV-F0E”, Search it on google and we know it’s SAMSUNG TFT 3.5-inch 320*240 LTV350QV-F0E lcd panel.
Its datasheet could be downloaded from:
http://www.datasheet-pdf.com/datasheet-html/L/T/V/LTV350QV-F0E_Samsung.pdf.html

TFT means (Thin Film Transistor).
Beware, the kernel we should choose is uImage_T1_F0E_320x240_ts, because we bought the Package 1 of the development board, they shipped us with Samsung screen .

Code Modification

Kernel configuration modification:

Device Drivers --->
	Graphics support --->
		<*> Support for frame buffer devices
			Support for buffer devices --->
				<*> S3C2410 LCD framebuffer support
		Backlight & LCD device sipport --->
			<*>Lowlevel LCD controls
		Display device support --->
			<*> Display panel/monitor support
		Console display driver support --->
			<*> Framebuffer Console support
			[*] Framebuffer Console Rotation
			[*] Select compiled-in fonts
			[*] VGA 8x8 font
			[*] VGA 8x16 font
			[*] Mini 4x6 font
			[*] Sparc console 8x16 font
		[*] Bootup logo --->
			--- Bootup logo
			[*] Standard 224-color Linux logo

arch/arm/mach-s3c2440/mach-smdk2440.c:

static struct s3c2410fb_display smdk2440_lcd_cfg __initdata = {
 
        .lcdcon5        = S3C2410_LCDCON5_FRM565 |
                          S3C2410_LCDCON5_INVVCLK |
                          S3C2410_LCDCON5_INVVLINE |
                          S3C2410_LCDCON5_INVVFRAME |
                        // S3C2410_LCDCON5_PWREN |
                          S3C2410_LCDCON5_HWSWP,
 
        .type           = S3C2410_LCDCON1_TFT,
 
        .width          = 320,    // 240,
        .height         = 240,    //320,
 
        .pixclock       = 100000,   //HCLK/10   166667,
        .xres           = 320,    //240,
        .yres           = 240,    //320,
        .bpp            = 16,
        .left_margin    = 17,     //HFPD+1     20,
        .right_margin   = 17,     //HBPD+1     8,
        .hsync_len      = 13,     //HSPW+1     4,
        .upper_margin   = 5,      //VBPD+1     8,
        .lower_margin   = 5,      //VFPD+1     7,
        .vsync_len      = 9,      //VSPW+1     4,
};

static struct s3c2410fb_mach_info smdk2440_fb_info __initdata = {
        .displays       = &smdk2440_lcd_cfg,
        .num_displays   =  1, 
        .default_display = 0,
 //#if 0
       
        .gpccon         = 0xaaaa56a9,    //0xaa940659,
        .gpccon_mask    = 0xffffffff,
        .gpcup          = 0xffffffff,    //0x0000ffff,
        .gpcup_mask     = 0xffffffff,
        .gpdcon         = 0xaaaaaaaa,    //0xaa84aaa0,
        .gpdcon_mask    = 0xffffffff,
        .gpdup          = 0xffffffff,    //0x0000faff,
        .gpdup_mask     = 0xffffffff,
//#endif 
        .lpcsel         = 0,     //((0xCE6) & ~7) | 1<<4,
};

Enable the lcd:

static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd, 

arch/arm/mach-s3c2410/include/mach/regs-lcd.h:

#define S3C2410_LCDCON1_MMODE (0<<7)

drivers/video/s3c2410fb.c, after around line 52:

//LTV350QV_FOE_V0.0
 static void LTV350QV_Short_Delay(u_char time)
 {
  ndelay(150);
 }


#define LTV350QV_FOE 0x1d //device ID
typedef struct _LTV350qv_spi_data_{
unsigned char Device_ID; //ID of the device
unsigned int Index; //index of register
unsigned long Structure; //structure to be writed
}LTV350QV_SPI_Data;
//micro for LTV350QV_FOE
#define CS_H __raw_writel(__raw_readl(S3C2410_GPCDAT) |(1<< 8),S3C2410_GPCDAT)//MAKE_HIGH(LTV350QV_CS)
#define CS_L __raw_writel(__raw_readl(S3C2410_GPCDAT)&~(1<< 8),S3C2410_GPCDAT)//MAKE_LOW(LTV350QV_CS)
#define SCLK_H __raw_writel(__raw_readl(S3C2410_GPCDAT) |(1<< 9),S3C2410_GPCDAT)//MAKE_HIGH(LTV350QV_SCL)
#define SCLK_L __raw_writel(__raw_readl(S3C2410_GPCDAT)&~(1<< 9),S3C2410_GPCDAT)//MAKE_LOW(LTV350QV_SCL)
#define SDI_H __raw_writel(__raw_readl(S3C2410_GPCDAT) |(1<<10),S3C2410_GPCDAT)//MAKE_HIGH(LTV350QV_SDI)
#define SDI_L __raw_writel(__raw_readl(S3C2410_GPCDAT)&~(1<<10),S3C2410_GPCDAT)//MAKE_LOW(LTV350QV_SDI)
#define RST_H __raw_writel(__raw_readl(S3C2410_GPDDAT) |(1<< 0),S3C2410_GPDDAT)//MAKE_HIGH(LTV350QV_RST)
#define RST_L __raw_writel(__raw_readl(S3C2410_GPDDAT)&~(1<< 0),S3C2410_GPDDAT)//MAKE_LOW(LTV350QV_RST)
//
static void LTV350QV_Register_Write(LTV350QV_SPI_Data regdata)
{
u_char i,temp1;
u_int temp2;
u_long temp3;
unsigned long flags;
//write index
temp1=regdata.Device_ID<<2 | 0<<1 | 0<<0; //register index
temp2=regdata.Index;
temp3=(temp1<<24) | (temp2<<8);
local_irq_save(flags);
CS_L;
LTV350QV_Short_Delay(1);
for(i=0;i<24;i++)
{
SCLK_L;
if(temp3 & (1<<(31-i)) ) //if is H
SDI_H;
else
SDI_L;
LTV350QV_Short_Delay(1); //setup time
SCLK_H;
LTV350QV_Short_Delay(1); //hold time
}
CS_H;
LTV350QV_Short_Delay(5);
//write instruction
temp1=regdata.Device_ID<<2 | 1<<1 | 0<<0; //instruction
temp2=regdata.Structure;
temp3=(temp1<<24) | (temp2<<8);
CS_L;
LTV350QV_Short_Delay(1);
for(i=0;i<24;i++)
{
SCLK_L;
if(temp3 & (1<<(31-i)) ) //if is H
SDI_H;
else
SDI_L;
LTV350QV_Short_Delay(1);
SCLK_H;
LTV350QV_Short_Delay(1);
}
CS_H;
local_irq_restore(flags);
}
 
static void LTV350QV_Write(u_int index, u_int regdata)
{
LTV350QV_SPI_Data WriteData;
WriteData.Device_ID = LTV350QV_FOE;
WriteData.Index = index;
WriteData.Structure = regdata;
LTV350QV_Register_Write(WriteData);
}
 
static void LTV350QV_Power_ON(void)
{
LTV350QV_Write( 9, 0x0000);
mdelay(150);
LTV350QV_Write( 9, 0x4000);
LTV350QV_Write(10, 0x2000);
LTV350QV_Write( 9, 0x4055);
mdelay(550);
LTV350QV_Write( 1, 0x409d);
LTV350QV_Write( 2, 0x0204);
LTV350QV_Write( 3, 0x0100);
LTV350QV_Write( 4, 0x3000);
LTV350QV_Write( 5, 0x4003);
LTV350QV_Write( 6, 0x000a);
LTV350QV_Write( 7, 0x0021);
LTV350QV_Write( 8, 0x0c00);
LTV350QV_Write(10, 0x0103);
LTV350QV_Write(11, 0x0301);
LTV350QV_Write(12, 0x1f0f);
LTV350QV_Write(13, 0x1f0f);
LTV350QV_Write(14, 0x0707);
LTV350QV_Write(15, 0x0307);
LTV350QV_Write(16, 0x0707);
LTV350QV_Write(17, 0x0000);
LTV350QV_Write(18, 0x0004);
LTV350QV_Write(19, 0x0000);
mdelay(200);
LTV350QV_Write( 9, 0x4a55);
LTV350QV_Write( 5, 0x5003);
}
 
 
static void s3c2440fb_init_ltv350qv(void)
{
__raw_writel(0xaa9556a9, S3C2410_GPCCON); //Initialize VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND
//LCDVF[0],[1],[2]---output;VD[0],[1],[2]----output.
__raw_writel(0xffffffff, S3C2410_GPCUP); // Disable Pull-up register
LTV350QV_Power_ON() ; //init LCD model
__raw_writel(0xaaaaaaaa, S3C2410_GPDCON);
__raw_writel(0xffffffff, S3C2410_GPDUP);
// __raw_writel(3, S3C2410_LCDINTMSK); // MASK LCD Sub Interrupt
// __raw_writel(0, S3C2410_TPAL); // Disable Temp Palette
// __raw_writel(0, S3C2410_LPCSEL); // Disable LPC3600
}

Then add following sentense before the “return 0” of static int s3c2410fb_init_registers(struct fb_info *info):

s3c2440fb_init_ltv350qv();

Now recompile the kernel, you will see framebuffer OK.

Comment all of the function content of drivers/char/vt.c blank_screen_t(unsigned long dummy), this will disable the LCD’s display from closing.

Directly boot from tftp

Avoiding flash into the nand.


>tftp 30000000 uImage
dm9000 i/o: 0x18000300, id: 0x90000a46 
> bootm