4、显示驱动
与云终端显示相关的硬件包括多层控制器(MLC)及显示控制器(DPC)。MLC对应的修改的程序是mlc.c、mlc.h、main.c,
DPC对应修改的程序为dpc_config.h、dpc_ioctl.h及main.c。修改主要涉及MLC顶层的初始设置、显示LOGO设置、及系统默认显示分辨率的设置、及与Pollux硬件相关的系统设置。对于DPC部分的修改主要为系统默认显示分辨率的设置及与其相关的控制信号HSYNC_SWIDTH、HSYNC_FRONT_PORCH、HSYNC_BACK_PORCH、HSYNC_ACTIVEHIGH、VSYNC_SWIDTH、VSYNC_FRONT_PORCH、DISPLAY_VID_PRI_VCLK_SOURCE、DPC_DESIRED_CLOCK_HZ等设置。
5、 I2C系统总线
参考系统使用了一条I2C总线,而Pollux系统使用使用了两条系统总线,一条连接到加密芯片、另一条连接到声音的i2S。所以修改驱动为识别及支持两条系统总线。以下为驱动程序。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define
I2C_CHANNEL CONFIG_I2C_LF1000_CHANNEL
#define LF1000_I2C_TIMEOUT 10
#define LF1000_I2C_RATE_HZ 100000
#ifdef CPU_LF1000
#define I2C_SCL0_PORT GPIO_PORT_A
#define I2C_SCL0_PIN 26
#define I2C_SCL0_FN GPIO_ALT1
#define I2C_SDA0_PORT GPIO_PORT_A
#define I2C_SDA0_PIN 27
#define I2C_SDA0_FN GPIO_ALT1
#define I2C_SCL1_PORT GPIO_PORT_A
#define I2C_SCL1_PIN 28
#define I2C_SCL1_FN GPIO_ALT1
#define I2C_SDA1_PORT GPIO_PORT_A
#define I2C_SDA1_PIN 29
#define I2C_SDA1_FN GPIO_ALT1
#endif
enum lf1000_i2c_state {
I2C_SEND_ADDR,
I2C_SEND_DATA,
I2C_SEND_DONE,
};
struct lf1000_i2c {
void __iomem *base;
int irq;
int div;
wait_queue_head_t wait;
int ready;
wait_queue_head_t bus_access;
int busy;
};
static struct lf1000_i2c dev[2] = {
{
.base = NULL,
.irq = -1,
.ready = 0,
.div = 16,
.busy = 0
},
{
.base = NULL,
.irq = -1,
.ready = 0,
.div = 16,
.busy = 0
}
};
static irqreturn_t lf1000_i2c_irq(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
int ch = pdev->id;
u32 tmp = ioread32(dev[ch].base+IRQ_PEND);
if(!(tmp &
(1<
return IRQ_NONE;
tmp |=
(1<
iowrite32(tmp, dev[ch].base+IRQ_PEND);
dev[ch].ready = 1;
wake_up_interruptible(&dev[ch].wait);
return IRQ_HANDLED;
}
static int lf1000_i2c_wait(struct i2c_adapter *adap)
{
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
int ret =
wait_event_interruptible_timeout(dev[ch].wait,
(dev[ch].ready),
LF1000_I2C_TIMEOUT);
if(unlikely(ret < 0))
printk(KERN_INFO "i2c:
interrupted\n");
else if(unlikely(!(dev[ch].ready)))
return -ETIMEDOUT;
dev[ch].ready = 0;
return 0;
}
static int i2c_bus_available(struct i2c_adapter *adap)
{
unsigned long flags;
int ret;
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
spin_lock_irqsave(&dev[ch].bus_access,
flags);
ret = !(dev[ch].busy);
spin_unlock_irqrestore(&dev[ch].bus_access,
flags);
return ret;
}
static void start_stop_condition(struct i2c_adapter *adap)
{
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
u32 tmp = ioread32(dev[ch].base+IRQ_PEND);
tmp |=
(1<
iowrite32(tmp, dev[ch].base+IRQ_PEND);
}
static void lf1000_i2c_clock(char en, struct i2c_adapter
*adap)
{
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
u32 tmp = ioread32(dev[ch].base+I2C_CLKENB);
en ? BIT_SET(tmp, 3) :BIT_CLR(tmp, 3);
iowrite32(tmp, dev[ch].base+I2C_CLKENB);
}
static void lf1000_i2c_hwinit(struct i2c_adapter *adap)
{
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
iowrite32(0, dev[ch].base+ICCR);
#ifdef CPU_MF2530F
iowrite32(0, dev.base+BURST_CTRL);
#endif
iowrite32((1<
dev[ch].base+ICCR);
iowrite32((1<
dev[ch].base+QCNT_MAX);
iowrite32(0x1010, dev[ch].base+ICSR);
start_stop_condition(adap); }
static int xfer_write(struct i2c_adapter *adap, unsigned char *buf,
int length)
{
u32 tmp;
int ret;
enum lf1000_i2c_state state =
I2C_SEND_ADDR;
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
tmp = (ioread32(dev[ch].base+ICSR)
& 0x10F0);
tmp |=
((1<
(1<
iowrite32(tmp, dev[ch].base+ICSR);
start_stop_condition(adap);
while(1) {
switch(state) {
case
I2C_SEND_ADDR:
ret =
lf1000_i2c_wait(adap);
if(ret !=
0)
goto
done_write;
tmp =
ioread32(dev[ch].base+ICSR);
if(tmp
& (1<
{
printk(KERN_INFO
"i2c: no ACK in xfer_write\n");
ret
= -EFAULT;
goto
done_write;
}
state =
I2C_SEND_DATA;
break;
case
I2C_SEND_DATA:
iowrite32(*buf++,
dev[ch].base+IDSR);
start_stop_condition(adap);
ret =
lf1000_i2c_wait(adap);
if(ret !=
0)
goto
done_write;
if(--length
<= 0)
state
= I2C_SEND_DONE;
break;
case
I2C_SEND_DONE:
ret =
0;
goto
done_write;
}
}
done_write:
tmp = (ioread32(dev[ch].base+ICSR)
& 0x1F0F);
tmp |=
((1<
iowrite32(tmp, dev[ch].base+ICSR);
start_stop_condition(adap);
iowrite32(0, dev[ch].base+ICSR);
return 0;
}
static int xfer_read(struct i2c_adapter *adap, unsigned char *buf,
int length)
{
u32 tmp;
int ret;
enum lf1000_i2c_state state =
I2C_SEND_ADDR;
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
tmp = (ioread32(dev[ch].base+ICSR)
& 0x1F0F);
tmp |=
((1<
(1<
iowrite32(tmp, dev[ch].base+ICSR);
start_stop_condition(adap);
while(1) {
switch(state) {
case
I2C_SEND_ADDR:
ret =
lf1000_i2c_wait(adap);
if(ret !=
0)
goto
done_read;
tmp =
ioread32(dev[ch].base+ICSR);
if(tmp
& (1<
{
printk(KERN_INFO
"i2c: no ACK in xfer_read\n");
ret
= -EFAULT;
goto
done_read;
}
state =
I2C_SEND_DATA;
break;
case
I2C_SEND_DATA:
*buf++ =
ioread32(dev[ch].base+IDSR);
start_stop_condition(adap);
ret =
lf1000_i2c_wait(adap);
if(ret !=
0)
goto
done_read;
if(--length
<= 0)
state
= I2C_SEND_DONE;
break;
case
I2C_SEND_DONE:
ret =
0;
goto
done_read;
}
}
done_read:
tmp = ioread32(dev[ch].base+ICSR);
tmp &= ~(0x1F0F);
tmp |=
((1<
iowrite32(tmp, dev[ch].base+ICSR);
start_stop_condition(adap);
iowrite32(0, dev[ch].base+ICSR);
return ret;
}
static int lf1000_xfer(struct i2c_adapter *adap, struct i2c_msg
*pmsg, int num)
{
int i, ret;
unsigned long flags;
struct platform_device *pdev =
to_platform_device(adap->dev.parent);
int ch =
pdev->id;
spin_lock_irqsave(&dev[ch].bus_access,
flags);
while(dev[ch].busy) {
spin_unlock_irqrestore(&dev[ch].bus_access,
flags);
if(wait_event_interruptible(dev[ch].bus_access,
i2c_bus_available(adap))) {
dev[ch].busy
= 0;
spin_unlock_irqrestore(&dev[ch].bus_access,
flags);
return
-ERESTARTSYS;
}
spin_lock_irqsave(&dev[ch].bus_access,
flags);
}
dev[ch].busy = 1;
spin_unlock_irqrestore(&dev[ch].bus_access,
flags);
lf1000_i2c_clock(1,adap);
for(i = 0; i < num; i++)
{
lf1000_i2c_hwinit(adap);
iowrite32(pmsg->addr
| ((pmsg->flags & I2C_M_RD) ? 1 :
0),
dev[ch].base+IDSR);
if(pmsg->len
&& pmsg->buf)
{
if(pmsg->flags
& I2C_M_RD)
ret
= xfer_read(adap, pmsg->buf,
pmsg->len);
else
ret
= xfer_write(adap, pmsg->buf,
pmsg->len);
if(ret !=
0)
goto
xfer_done;
}
pmsg++;
}
ret = i;
xfer_done:
lf1000_i2c_clock(0,adap);
spin_lock_irqsave(&dev[ch].bus_access,
flags);
dev[ch].busy = 0;
spin_unlock_irqrestore(&dev[ch].bus_access,
flags);
return ret;
}
static u32 lf1000_func(struct i2c_adapter *adapter)
{
return
I2C_FUNC_I2C;
}
static struct i2c_algorithm lf1000_algorithm = {
.master_xfer = lf1000_xfer,
.functionality = lf1000_func,
};
static int lf1000_i2c_probe(struct platform_device *pdev)
{
struct resource *res;
struct i2c_adapter *adapter;
int ret = 0;
unsigned int pclk_hz;
int ch =
pdev->id;
res = platform_get_resource(pdev,
IORESOURCE_MEM, I2C_CHANNEL);
if(!res) {
printk(KERN_ERR "i2c: failed to
get resource\n");
return -ENXIO;
}
if(!request_mem_region(res->start,
(res->end - res->start) + 1,
"lf1000_i2c")) {
printk(KERN_ERR "i2c: failed to
request_mem_region\n");
return -EBUSY;
}
dev[ch].base =
ioremap(res->start, (res->end -
res->start) + 1);
if(dev[ch].base == NULL) {
printk(KERN_ERR "i2c: failed to
ioremap\n");
ret = -ENOMEM;
goto fail_remap;
}
adapter = kzalloc(sizeof(struct i2c_adapter),
GFP_KERNEL);
if(adapter == NULL) {
printk(KERN_ERR "i2c: failed to
allocate interface\n");
ret = -ENOMEM;
goto fail_adapter;
}
sprintf(adapter->name,
"LF1000");
adapter->algo =
&lf1000_algorithm;
adapter->class =
I2C_CLASS_HWMON;
adapter->dev.parent =
&pdev->dev;
platform_set_drvdata(pdev, adapter);
dev[ch].irq = platform_get_irq(pdev,
I2C_CHANNEL);
if(dev[ch].irq < 0) {
printk(KERN_ERR "i2c: failed to
get an IRQ\n");
ret = dev[ch].irq;
goto fail_irq;
}
ret = request_irq(dev[ch].irq,
lf1000_i2c_irq,
SA_INTERRUPT|SA_SAMPLE_RANDOM,
"i2c", pdev);
if(ret) {
printk(KERN_ERR "i2c:
requesting IRQ failed\n" );
goto fail_irq;
}
init_waitqueue_head(&dev[ch].wait);
init_waitqueue_head(&dev[ch].bus_access);
pclk_hz =
get_pll_freq(PCLK_PLL)/BCLK_DIV/2;
dev[ch].div = lf1000_CalcDivider(pclk_hz/256,
LF1000_I2C_RATE_HZ);
if(dev[ch].div < 0) {
printk(KERN_ALERT "i2c: failed
to get divider, using 16\n");
dev[ch].div = 16;
}
else if(dev[ch].div > 16) {
printk(KERN_ALERT "i2c: divider
too high, using 16\n");
dev[ch].div = 16;
}
if (pclk_hz/256/dev[ch].div <
LF1000_I2C_RATE_HZ)
dev[ch].div--;
else if (pclk_hz/256/dev[ch].div >
1000*1000 )
printk("i2c: 2IC bus frequency too high\n");
printk("I2C bus %d frequency set to %d KHz,
address at %x IRQ %d\n", ch,
pclk_hz/256/dev[ch].div/1000, res->start,
dev[ch].irq);
if(ch == 0 ) {
#ifdef I2C_SCL0_PORT
gpio_configure_pin(I2C_SCL0_PORT, I2C_SCL0_PIN,
I2C_SCL0_FN, 1, 0, 0);
#endif
#ifdef I2C_SDA0_PORT
gpio_configure_pin(I2C_SDA0_PORT, I2C_SDA0_PIN,
I2C_SDA0_FN, 1, 0, 0);
#endif
}
else if (ch == 1) {
#ifdef I2C_SCL1_PORT
gpio_configure_pin(I2C_SCL1_PORT, I2C_SCL1_PIN,
I2C_SCL1_FN, 1, 0, 0);
#endif
#ifdef I2C_SDA1_PORT
gpio_configure_pin(I2C_SDA1_PORT, I2C_SDA1_PIN,
I2C_SDA1_FN, 1, 0, 0);
#endif
}
lf1000_i2c_hwinit(adapter);
lf1000_i2c_clock(1,adapter);
ret = i2c_add_adapter(adapter);
if(ret != 0) {
printk(KERN_ERR "i2c: failed to
add adapter\n");
goto fail_register;
}
printk("I2C adapter %s registered\n",
adapter->dev.bus_id);
return 0;
fail_register:
lf1000_i2c_clock(0,adapter);
platform_set_drvdata(pdev, NULL);
kfree(adapter);
fail_irq:
free_irq(dev[ch].irq, NULL);
dev[ch].irq = -1;
fail_adapter:
iounmap(dev[ch].base);
fail_remap:
release_mem_region(res->start,
(res->end - res->start) + 1);
return ret;
}
static int lf1000_i2c_remove(struct platform_device *pdev)
{
struct i2c_adapter *adapter =
platform_get_drvdata(pdev);
struct resource *res;
int ret;
int ch = pdev->id;
if(adapter != NULL) {
lf1000_i2c_clock(0,adapter);
ret =
i2c_del_adapter(adapter);
platform_set_drvdata(pdev,
NULL);
res =
platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(dev[ch].base);
release_mem_region(res->start,
(res->end - res->start) + 1);
return ret;
}
return 0;
}
#ifdef CONFIG_PM
static int lf1000_i2c_suspend(struct platform_device *pdev,
pm_message_t mesg)
{
struct i2c_adapter *adap =
i2c_get_adapter(pdev->id);
lf1000_i2c_clock(0, adap);
return 0;
}
static int lf1000_i2c_resume(struct platform_device *pdev)
{
struct i2c_adapter *adap =
i2c_get_adapter(pdev->id);
lf1000_i2c_clock(1, adap);
return 0;
}
#else
#define lf1000_i2c_suspend NULL
#define lf1000_i2c_resume NULL
#endif
static struct platform_driver lf1000_i2c_driver = {
.probe = lf1000_i2c_probe,
.remove = lf1000_i2c_remove,
.suspend = lf1000_i2c_suspend,
.resume = lf1000_i2c_resume,
.driver = {
.name = "lf1000-i2c",
.owner =
THIS_MODULE,
},
};
static int __init lf1000_i2c_init(void)
{
return
platform_driver_register(&lf1000_i2c_driver);
}
static void __exit lf1000_i2c_exit(void)
{
return
platform_driver_unregister(&lf1000_i2c_driver);
}
module_init(lf1000_i2c_init);
module_exit(lf1000_i2c_exit);
MODULE_AUTHOR("Andrey Yurovsky");
MODULE_DESCRIPTION("I2C driver for LF1000");
MODULE_LICENSE("GPL");