Linux 3.0 HDMI驱动机制详解

1. 版本背景

  • 发布时间:2011年7月21日
  • HDMI支持状态:基础HDMI支持,主要通过显卡驱动实现,CEC/HDCP支持有限
  • 关键里程碑
    • DRM/KMS框架初步稳定
    • Intel i915驱动HDMI基础支持
    • HDMI音频初步集成
    • EDID解析框架完善

2. 子系统架构

整体架构

复制代码
  +---------------------+
  |     用户空间        |
  |  DRM API (libdrm)   |
  |  ALSA HDMI音频     |
  +----------+----------+
             |
  +----------v----------+
  |     内核空间        |
  |  DRM核心子系统     |
  |  + HDMI控制器     |
  +----------+----------+
             |
  +----------v----------+
  |  显卡特定HDMI驱动  |
  |  (i915/radeon/etc) |
  +----------+----------+
             |
  +----------v----------+
  |     HDMI物理层     |
  +---------------------+

核心组件

  • DRM框架drivers/gpu/drm/ 目录下核心实现
  • 显卡驱动drivers/gpu/drm/i915/ (Intel), drivers/gpu/drm/radeon/ (AMD)
  • HDMI音频sound/pci/hda/ 下的HDMI音频实现
  • EDID处理drivers/gpu/drm/drm_edid.c

3. 源码深度解析

HDMI连接检测

复制代码
// drivers/gpu/drm/i915/intel_hdmi.c
static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector, bool force)
{
    struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
    uint32_t stat = I915_READ(HDMI_STATUS);

    if (stat & HDMI_DDC_DONE) {
        /* 检测EDID */
        if (intel_hdmi->has_hdmi_sink)
            return connector_status_connected;
    }

    return connector_status_disconnected;
}
  • 关键函数
    • detect():连接状态检测
    • mode_valid():模式验证
    • commit():配置HDMI输出
    • dpms():电源管理

HDMI音频初始化

复制代码
// sound/pci/hda/patch_hdmi.c
static int generic_hdmi_build_pcms(struct hda_codec *codec)
{
    struct hdmi_spec *spec = codec->spec;
    struct hda_pcm *pcm;

    pcm = &spec->pcm_rec[0];
    pcm->name = "HDMI 0";
    pcm->stream[SNDRV_PCM_STREAM_PLAYBACK] = &hdmi_pcm_analog_playback;

    return 0;
}

static int patch_generic_hdmi(struct hda_codec *codec)
{
    codec->spec = kzalloc(sizeof(*spec), GFP_KERNEL);
    spec->ops.pin_get_eld = generic_pin_get_eld;
    spec->ops.pin_setup_infoframe = setup_channel_mapping;

    return 0;
}
  • 关键流程
    • ELD (EDID Like Data)获取
    • 通道映射配置
    • 音频信息帧设置

4. 设备树配置详解

注意:Linux 3.0时期设备树主要用于ARM平台,x86平台HDMI配置主要通过BIOS和驱动内部配置,无标准设备树支持。

ARM平台设备树示例 (OMAP4)

复制代码
&hdmi {
    status = "okay";
    ti,pll-ctrl = <0x00000100>;
    ti,phy-sel = <0x00000000>;

    port {
        hdmi_out: endpoint {
            remote-endpoint = <&dpi_in>;
        };
    };
};

&dpi {
    status = "okay";
    port {
        dpi_in: endpoint {
            remote-endpoint = <&hdmi_out>;
        };
    };
};
  • 关键属性
    • ti,pll-ctrl:PLL控制寄存器值
    • ti,phy-sel:PHY选择配置

5. EDID处理机制

EDID解析流程

复制代码
// drivers/gpu/drm/drm_edid.c
struct edid *drm_get_edid(struct drm_connector *connector,
                        struct i2c_adapter *adapter)
{
    struct edid *edid;
    int i;

    for (i = 0; i < 3; i++) {
        edid = drm_do_get_edid(connector, adapter);
        if (edid)
            return edid;
        msleep(10);
    }

    return NULL;
}

static struct edid *drm_do_get_edid(...)
{
    /* 尝试标准EDID块(128字节) */
    if (!drm_edid_block_valid(raw_edid, 0, false, NULL))
        return NULL;

    /* 检查扩展块 */
    if (raw_edid[0x7e] > 0)
        drm_do_get_edid_block(...);

    return (struct edid *)raw_edid;
}
  • 关键步骤
    • I2C读取EDID数据
    • 验证EDID块有效性
    • 处理扩展EDID块
    • 提取显示模式信息

6. CEC/HDCP实现

CEC基础支持

注意:Linux 3.0时期CEC支持非常有限,主要通过实验性驱动实现

复制代码
// drivers/media/rc/cec.c (实验性)
static int cec_transmit(struct cec_adapter *adap, u8 attempts,
                       u8 signal_free_time, const struct cec_msg *msg)
{
    struct cec_dev *dev = adap->priv;
    int ret;

    mutex_lock(&dev->lock);
    ret = dev->ops->transmit(dev, msg);
    mutex_unlock(&dev->lock);

    return ret;
}
  • 限制
    • 仅支持基本CEC命令
    • 无标准框架,各厂商实现差异大
    • 需要特定硬件支持

HDCP支持

注意:Linux 3.0时期HDCP支持非常有限,主要通过专有驱动实现,开源实现不完整

7. 性能调优指南

HDMI输出调优

  • 内核参数

    复制代码
    # 调整EDID读取重试次数
    echo 5 > /sys/module/drm/parameters/edid_retry_count
    
    # 禁用特定显示模式
    echo "1920x1080" > /sys/class/drm/card0-HDMI-A-1/modes

音频调优

复制代码
# 设置HDMI音频采样率
echo "plug:hdmi:CARD=PCH,DEV=0 rate=48000" > ~/.asoundrc

# 查看音频设备信息
cat /proc/asound/card*/eld#

8. 版本差异对比

特性 Linux 2.6.39 Linux 3.0
DRM/KMS架构 初步整合 更稳定
HDMI音频 基础支持 完整框架
EDID处理 简单解析 完整支持
CEC支持 实验性
HDCP支持 专有驱动

9. 实际硬件适配案例

Intel Sandy Bridge HDMI适配

硬件平台:Intel Core i5-2500K + HD 3000 GPU

关键配置

复制代码
// drivers/gpu/drm/i915/intel_hdmi.c
static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
    .reset = intel_encoder_reset,
    .destroy = intel_encoder_destroy,
};

static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
    .dpms = drm_helper_connector_dpms,
    .fill_modes = drm_helper_probe_single_connector_modes,
    .set_property = intel_hdmi_set_property,
    .detect = intel_hdmi_detect,
};

static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
    .mode_valid = intel_hdmi_mode_valid,
    .best_encoder = intel_best_encoder,
};

EDID问题解决

复制代码
# 强制使用特定EDID
cp /lib/firmware/edid/known.bin /sys/class/drm/card0-HDMI-A-1/edid

10. 常见问题与解决方案

1. 无HDMI音频输出

  • 可能原因

    • ALSA配置不正确
    • 音频驱动未加载
    • EDID中未报告音频能力
  • 解决方案

    复制代码
    # 检查音频设备
    aplay -l
    
    # 设置默认音频设备
    echo "defaults.pcm.card 1" >> /etc/asound.conf

2. 高分辨率支持问题

  • 可能原因

    • EDID未正确报告支持的分辨率
    • 显卡驱动限制
  • 解决方案

    复制代码
    # 手动添加显示模式
    xrandr --newmode "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
    xrandr --addmode HDMI-1 1920x1080_60.00

11. 参考资料