qcom Display and I2C In Lk(一)
本文主要讲述如何在在qcom平台的LK中移植Display 以及I2c驱动:
- Display
- I2C
I2C
1.確保 lk/platform/msm_shared/i2c_qup.c 已经被编译了,如果没有需要加入到编译选项里(同目录的rule.mk)
2.i2c_qup.c为我们提供了i2c的初始化接口:qup_blsp_i2c_init,以及i2c的read/write接口:qup_i2c_xfer
3.正常情况下qup_i2c_xfer接口不需要更改,但在qup_blsp_i2c_init里需要对我们的特定i2c进行移植
struct qup_i2c_dev *qup_blsp_i2c_init(uint8_t blsp_id, uint8_t qup_id, uint32_t clk_freq, uint32_t src_clk_freq)
{
struct qup_i2c_dev *dev;
if (dev_addr != NULL) {
return dev_addr;
}
dev = malloc(sizeof(struct qup_i2c_dev));
if (!dev) {
return NULL;
}
dev = memset(dev, 0, sizeof(struct qup_i2c_dev));
/* Platform uses BLSP */
dev->qup_irq = BLSP_QUP_IRQ(blsp_id, qup_id);
dev->qup_base = BLSP_QUP_BASE(blsp_id, qup_id);
/* This must be done for qup_i2c_interrupt to work. */
dev_addr = dev;
/* Initialize the GPIO for BLSP i2c */
gpio_config_blsp_i2c(blsp_id, qup_id);
clock_config_blsp_i2c(blsp_id, qup_id);
qup_i2c_sec_init(dev, clk_freq, src_clk_freq);
return dev;
}
3.1 查看datasheet赋值正确的qup_irq和qup_base.
3.2 配置i2c所用的GPIO,配置成i2c func.platform/msmxxxx/gpio.c
void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
{
if(blsp_id == BLSP_ID_1) {
switch (qup_id) {
case QUP_ID_3:
/* configure I2C SDA gpio */
gpio_tlmm_config(14, 3, GPIO_OUTPUT, GPIO_NO_PULL,
GPIO_8MA, GPIO_DISABLE);
/* configure I2C SCL gpio */
gpio_tlmm_config(15, 3, GPIO_OUTPUT, GPIO_NO_PULL,
GPIO_8MA, GPIO_DISABLE);
break;
default:
dprintf(CRITICAL, "Incorrect QUP id %d\n",qup_id);
ASSERT(0);
};
} else {
dprintf(CRITICAL, "Incorrect BLSP id %d\n",blsp_id);
ASSERT(0);
}
}
3.3 配置时钟,这里根据时钟名去搜索,并使能.platform/msmxxxx/acpuclock.c
void clock_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
{
uint8_t ret = 0;
char clk_name[64];
struct clk *qup_clk;
if((blsp_id != BLSP_ID_1) || ((qup_id != QUP_ID_1) && (qup_id != QUP_ID_3))) {
dprintf(CRITICAL, "Incorrect BLSP-%d or QUP-%d configuration\n", blsp_id, qup_id);
ASSERT(0);
}
if (qup_id == QUP_ID_1) {
snprintf(clk_name, sizeof(clk_name), "blsp1_qup2_ahb_iface_clk");
}
else if (qup_id == QUP_ID_3) {
snprintf(clk_name, sizeof(clk_name), "blsp1_qup4_ahb_iface_clk");
}
ret = clk_get_set_enable(clk_name, 0 , 1);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s clock\n", clk_name);
return;
}
if (qup_id == QUP_ID_1) {
snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup2_i2c_apps_clk");
}
else if (qup_id == QUP_ID_3) {
snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup4_i2c_apps_clk");
}
qup_clk = clk_get(clk_name);
if (!qup_clk) {
dprintf(CRITICAL, "Failed to get %s\n", clk_name);
return;
}
ret = clk_enable(qup_clk);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s\n", clk_name);
return;
}
}
3.3.1在platform/msmxxxx/msmxxxx-clock.c 中配置刚刚使用到的时钟,在msm_clocks_8952添加入口以便被查询到
static struct clk_lookup msm_clocks_8952[] =
{
CLK_LOOKUP("sdc1_iface_clk", gcc_sdcc1_ahb_clk.c),
CLK_LOOKUP("sdc1_core_clk", gcc_sdcc1_apps_clk.c),
CLK_LOOKUP("sdc2_iface_clk", gcc_sdcc2_ahb_clk.c),
CLK_LOOKUP("sdc2_core_clk", gcc_sdcc2_apps_clk.c),
CLK_LOOKUP("uart2_iface_clk", gcc_blsp1_ahb_clk.c),
CLK_LOOKUP("uart2_core_clk", gcc_blsp1_uart2_apps_clk.c),
CLK_LOOKUP("usb_iface_clk", gcc_usb_hs_ahb_clk.c),
CLK_LOOKUP("usb_core_clk", gcc_usb_hs_system_clk.c),
CLK_LOOKUP("mdp_ahb_clk", mdp_ahb_clk.c),
CLK_LOOKUP("mdss_esc0_clk", mdss_esc0_clk.c),
CLK_LOOKUP("mdss_esc1_clk", mdss_esc1_clk.c),
CLK_LOOKUP("mdss_axi_clk", mdss_axi_clk.c),
CLK_LOOKUP("mdss_vsync_clk", mdss_vsync_clk.c),
CLK_LOOKUP("mdss_mdp_clk_src", mdss_mdp_clk_src.c),
CLK_LOOKUP("mdss_mdp_clk", mdss_mdp_clk.c),
CLK_LOOKUP("ce1_ahb_clk", gcc_ce1_ahb_clk.c),
CLK_LOOKUP("ce1_axi_clk", gcc_ce1_axi_clk.c),
CLK_LOOKUP("ce1_core_clk", gcc_ce1_clk.c),
CLK_LOOKUP("ce1_src_clk", ce1_clk_src.c),
//add for i2c
CLK_LOOKUP("blsp1_qup4_ahb_iface_clk", gcc_blsp1_ahb_clk.c),
CLK_LOOKUP("gcc_blsp1_qup4_i2c_apps_clk_src", gcc_blsp1_qup4_i2c_apps_clk_src.c),
CLK_LOOKUP("gcc_blsp1_qup4_i2c_apps_clk", gcc_blsp1_qup4_i2c_apps_clk.c),
};
3.3.2 配置刚添加的结构体,gcc_blsp1_ahb_clk、gcc_blsp1_qup4_i2c_apps_clk_src、gcc_blsp1_qup4_i2c_apps_clk. 如果没有定义需要添加
static struct clk_freq_tbl ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk[] = {
F( 96000, cxo, 10, 1, 2),
F( 4800000, cxo, 4, 0, 0),
F( 9600000, cxo, 2, 0, 0),
F( 16000000, gpll0, 10, 1, 5),
F( 19200000, gpll0, 1, 0, 0),
F( 25000000, gpll0, 16, 1, 2),
F( 50000000, gpll0, 16, 0, 0),
F_END
};
static struct rcg_clk gcc_blsp1_qup4_i2c_apps_clk_src =
{
.cmd_reg = (uint32_t *) GCC_BLSP1_QUP4_CMD_RCGR,
//.cfg_reg = (uint32_t *) GCC_BLSP1_QUP4_CFG_RCGR,
.set_rate = clock_lib2_rcg_set_rate_hid,
.freq_tbl = ftbl_gcc_blsp1_2_qup1_4_i2c_apps_clk,
.current_freq = &rcg_dummy_freq,
.c = {
.dbg_name = "gcc_blsp1_qup4_i2c_apps_clk_src",
.ops = &clk_ops_rcg,
},
};
static struct branch_clk gcc_blsp1_qup4_i2c_apps_clk = {
.cbcr_reg = (uint32_t *) GCC_BLSP1_QUP4_APPS_CBCR,
.parent = &gcc_blsp1_qup4_i2c_apps_clk_src.c,
.c = {
.dbg_name = "gcc_blsp1_qup4_i2c_apps_clk",
.ops = &clk_ops_branch,
},
};
4.封装i2c的读写方法
int dlp_i2c_read(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len)
{
if (!buf)
return ERR_INVALID_ARGS;
if(!dlp_i2c_dev){
return ERR_NOT_VALID;
}
struct i2c_msg rd_buf[] = {
{addr, I2C_M_WR, 1, ®},
{addr, I2C_M_RD, len, buf}
};
int err = qup_i2c_xfer(dlp_i2c_dev, rd_buf, 2);
if (err < 0) {
dprintf(CRITICAL, "Read reg failed\n");
return err;
}else{
dprintf(CRITICAL, "dlp_i2c_read_ext success \n");
}
return NO_ERROR;
}
int dlp_i2c_write(uint8_t addr, uint8_t* val,int len)
{
int err;
int num;
struct i2c_msg msg[1];
unsigned char data[9];
int i;
if (!val)
return ERR_INVALID_ARGS;
if(!dlp_i2c_dev){
dprintf(CRITICAL, "--dlp_i2c_write\n");
return ERR_NOT_VALID;
}
msg->addr = addr;
msg->flags = 0;
msg->len = len ;
msg->buf = data;
for(i=0;i<len;i++)
data[i]=val[i];
num=0;
while(num<5){
num++;
err = qup_i2c_xfer(dlp_i2c_dev, msg, 1);
//err = i2c_transfer(client->adapter, msg, 1);
if(err>=0)
break;
mdelay(50);
}
if (err >= 0){
dprintf(CRITICAL, "dlp_i2c_write_reg: success \n");
return 0;
}else{
dprintf(CRITICAL, "dlp_i2c_write_reg reg failed\n");
}
return err;
}