九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4930|回复: 1
打印 上一主题 下一主题

x4412&ibox项目实战57-Linux HDMI驱动之I2C驱动实验

[复制链接]
跳转到指定楼层
楼主
发表于 2014-11-29 15:02:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
HDMI通过DDC驱动实现了一个标准的I2C驱动。相关驱动源码路径如下:
  1. kernel/arch/arm/mach-x4412.c
  2. kernel/drivers/media/video/samsung/tvout/hw_if/hdcp.c
复制代码
在mach-x4412.c中定义了HDMI对应I2C设备的名称和设备地址:
  1. static struct i2c_board_info i2c_devs2[] __initdata = {
  2. #ifdef CONFIG_VIDEO_TVOUT
  3.          {
  4.                    I2C_BOARD_INFO("s5p_ddc", (0x74 >> 1)),
  5.          },
  6. #endif
  7. };
复制代码
       smdk4x12_machine_init函数中,通过i2c_register_board_info函数注册该设备:
  1. s3c_i2c2_set_platdata(NULL);
  2. i2c_register_board_info(2, i2c_devs2, ARRAY_SIZE(i2c_devs2));
复制代码
       hdcp.c中,实际上包含两个驱动,一个是DDC驱动,另一个是HDCP驱动。在DDC驱动的s5p_ddc_init初始化函数中通过i2c_add_driver函数添加一个名为s5p_ddci2c驱动,很明显它和mach-x4412.c中添加的i2c设备名称一致,探测函数s5p_ddc_probe将会执行。该函数仅仅是将探测函数引入的i2c_client结构体赋值给一个全局结构体ddc_port,用于I2C读写函数。
       s5p_ddc_read函数用于I2C寄存器的批量读函数,其原型如下:
  1. static int s5p_ddc_read(u8 reg, int bytes, u8 *dest)
  2. {
  3.          struct i2c_client *i2c = ddc_port;
  4.          u8 addr = reg;
  5.          int ret, cnt = 0;

  6.          struct i2c_msg msg[] = {
  7.                    [0] = {
  8.                             .addr = i2c->addr,
  9.                             .flags = 0,//先写地址
  10.                             .len = 1,
  11.                             .buf = &addr
  12.                    },
  13.                    [1] = {
  14.                             .addr = i2c->addr,
  15.                             .flags = I2C_M_RD,//再读bytes个字节的长度到dest
  16.                             .len = bytes,
  17.                             .buf = dest
  18.                    }
  19.          };

  20.          do {
  21.                    if (!s5p_hdmi_reg_get_hpd_status() ||
  22.                        s5p_hdmi_ctrl_status() == false || on_stop_process)
  23.                             goto ddc_read_err;

  24.                    ret = i2c_transfer(i2c->adapter, msg, 2);//开始读

  25.                    if (ret < 0 || ret != 2)
  26.                             tvout_dbg("ddc : can't read data, retry %d\n", cnt);
  27.                    else//如果读成功,则退出while循环,否则读取DDC_RETRY_CNT次,直到成功为止
  28.                             break;

  29.                    if (hdcp_info.auth_status == FIRST_AUTHENTICATION_DONE
  30.                             || hdcp_info.auth_status == SECOND_AUTHENTICATION_DONE)
  31.                             goto ddc_read_err;

  32.                    msleep(DDC_DELAY);
  33.                    cnt++;
  34.          } while (cnt < DDC_RETRY_CNT);

  35.          if (cnt == DDC_RETRY_CNT)
  36.                    goto ddc_read_err;

  37.          tvout_dbg("ddc : read data ok\n");

  38.          return 0;
  39. ddc_read_err:
  40.          tvout_err("ddc : can't read data, timeout\n");
  41.          return -1;
  42. }
复制代码
       传入的形参reg为需要读取的寄存器群的首地址,bytes表示需要读取的寄存器的字节数,dest用于存储读取到的寄存器值。该函数将标准的I2C读写函数i2c_transfer封装起来,同时加入了读失败了反复读取的机制,其核心和前面的I2C读写并无多大区别。
       s5p_ddc_write函数用于批量写寄存器,其函数原型如下:
  1. static int s5p_ddc_write(u8 reg, int bytes, u8 *src)
  2. {
  3.          struct i2c_client *i2c = ddc_port;
  4.          u8 msg[bytes + 1];
  5.          int ret, cnt = 0;

  6.          msg[0] = reg;
  7.          memcpy(&msg[1], src, bytes);

  8.          do {
  9.                    if (!s5p_hdmi_reg_get_hpd_status() ||
  10.                        s5p_hdmi_ctrl_status() == false || on_stop_process)
  11.                             goto ddc_write_err;

  12.                    ret = i2c_master_send(i2c, msg, bytes + 1);//一次性写入bytes个字节的数据,在写入字节之前需要先写寄存器地址

  13.                    if (ret < 0 || ret < bytes + 1)
  14.                             tvout_dbg("ddc : can't write data, retry %d\n", cnt);
  15.                    else
  16.                             break;

  17.                    msleep(DDC_DELAY);
  18.                    cnt++;
  19.          } while (cnt < DDC_RETRY_CNT);

  20.          if (cnt == DDC_RETRY_CNT)
  21.                    goto ddc_write_err;

  22.          tvout_dbg("ddc : write data ok\n");
  23.          return 0;
  24. ddc_write_err:
  25.          tvout_err("ddc : can't write data, timeout\n");
  26.          return -1;
  27. }
复制代码
       同样,传入的形参reg为要写入的寄存器群的起始地址,bytes表示要写入的寄存器的字节数,src指向要写入的寄存器的值的地址。所不同的是,这里调用的是i2c_master_send函数,而不是i2c_transfer函数。其实i2c_master_send函数的本质也就是封装了i2c_transfer函数,其函数原型如下:
  1. int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
  2. {
  3.         int ret;
  4.         struct i2c_adapter *adap = client->adapter;
  5.         struct i2c_msg msg;

  6.         msg.addr = client->addr;
  7.         msg.flags = client->flags & I2C_M_TEN;
  8.         msg.len = count;
  9.         msg.buf = (char *)buf;

  10.         ret = i2c_transfer(adap, &msg, 1);

  11.         /* If everything went ok (i.e. 1 msg transmitted), return #bytes
  12.            transmitted, else error code. */
  13.         return (ret == 1) ? count : ret;
  14. }
复制代码
       它本质上还是和s5p_ddc_read函数调用了相同的I2C函数。这两个函数被包装起来给HDMIHDCP等驱动调用,完成HDMI的相关通讯。
回复

使用道具 举报

沙发
发表于 2014-12-8 11:59:07 | 只看该作者
严重顶 严重顶 严重顶
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-6-15 05:13 , Processed in 0.017647 second(s), 17 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表