找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6334|回复: 0
打印 上一主题 下一主题
收起左侧

xxx芯片厂商hdmi分辨率识别补丁分析散笔

[复制链接]
跳转到指定楼层
楼主
ID:82083 发表于 2015-6-6 02:51 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 qinqin 于 2015-6-6 02:52 编辑

好久没写过笔记了,这次难得遇到那么全的补丁,从上层直通底层,又碰巧在跟踪接口方面是我的弱项,所以不分析一下太对不住自己了。。。

分析方法由上到下,从接口往底层跟。

接口的使用方法:
import android.view.DisplayManagerAw;
final DisplayManagerAw mDisplayManager = (DisplayManagerAw) mContext.getSystemService(Context.DISPLAY_SERVICE_AW);
int hdmiMode
hdmiMode = mDisplayManager.getSuitableHdmiNativeMode();
返回的hdmiMode是frameworks/base/core/java/android/view/DisplayManagerAw.java  对应的模式,比如 5对应 720P_60HZ, 10对应1080P_60HZ


android/frameworks/base/core/java/android/view/DisplayManagerAw.java
public int getSuitableHdmiNativeMode()
{

int displayno = 0;
int i;

try
{ // 从最高分辨率遍历所有Hdmi分辨率,测试哪一项是支持的。
for (i = DISPLAY_TVFORMAT_1080P_60HZ; i >= DISPLAY_TVFORMAT_480I; i--)
{
if (mService.isSupportHdmiNativeMode(i) == 1) // 这一步是关键
return i;
}
return  -1;
}
catch (RemoteException ex)
{
return -1;
}
}

mService.isSupportHdmiNativeMode(i) 是 mService 里面的方法,所以找mService的类。
private IDisplayManagerAw mService; ---> import android.view.IDisplayManagerAw;
---> android\frameworks\base\core\java\android\view\IDisplayManagerAw.aidl
关键代码:
interface IDisplayManagerAw
{
// ... 省略
int isSupportHdmiNativeMode(int mode);
}


对应:android\frameworks\base\services\java\com\android\serv\DisplayManagerServiceAw.java
public class DisplayManagerServiceAw extends IDisplayManagerAw.Stub
{
    private static final String TAG = "DisplayManagerServiceAw";
    // 声明本地方法,明显是JNI方法
    private native int  nativeIsSupportHdmiNativeMode(int mode);
    // ... 省略
    public int isSupportHdmiNativeMode(int mode)
    {
        return nativeIsSupportHdmiNativeMode(mode);
    }
}
去JNI目录看看:
android\frameworks\base\services/jni/com_android_server_DisplayManagerServiceAw.cpp

namespace android
{   
    // ... 省略代码
// 这里是具体实现
    static jint isSupportHdmiNativeMode_native(JNIEnv *env, jobject clazz,int mode)
    {   // Client ???  发的是这个命令 DISPLAY_CMD_ISSUPPORTHDMINATIVEMODE
        return  SurfaceComposerClient::setDisplayProp(DISPLAY_CMD_ISSUPPORTHDMINATIVEMODE,mode,0,0);
    }

    static JNINativeMethod method_table[] = {
        // ... 省略其他映射
// nativeIsSupportHdmiNativeMode() 实际调用的是 isSupportHdmiNativeMode_native()
{ "nativeIsSupportHdmiNativeMode", "(I)I", (void*)isSupportHdmiNativeMode_native },
    };

    int register_android_server_DisplayManagerServiceAw(JNIEnv *env)
    {// 注意这里
    jclass clazz = env->FindClass("com/android/server/DisplayManagerServiceAw");

        if (clazz == NULL)
        {
            ALOGE("Can't find com/android/server/DisplayManagerServiceAw");           
            return -1;
        }
        // 注意这里,与 DisplayManagerServiceAw 有对应关系
        return jniRegisterNativeMethods(env, "com/android/server/DisplayManagerServiceAw",
                method_table, NELEM(method_table));
    }
};

看接收命令处实现了什么:
android/framework/native/include/ui/DisplayCommand.h
#define   DISPLAY_CMD_ISSUPPORTHDMINATIVEMODE           30

看其实现:android/framework/native/services/surfaceflinger/DisplayDispatcher.cpp
int DisplayDispatcher::setDispProp(int cmd,int param0,int param1,int param2)
{
switch(cmd)
{
// ... 省略
case DISPLAY_CMD_ISSUPPORTHDMINATIVEMODE:
return mDevice->issupporthdminativemode(mDevice,param0);
}
}
mDevice下的方法 issupporthdminativemode(),找 mDevice 对象
display_device_t*    mDevice; 是在 DisplayDispatcher.h 中定义。
display_device_t 结构体在 android/framework/native/include/hardware/display.h
struct display_device_t
{
// ... 省略
/*判断某种hdmi制式是否被电视本地支持*/
    int (*setdisplayareapercent)(struct display_device_t *dev, int displayno,int percent);
// ... 省略
};
是一个函数指针,看其指向了哪里:android/framework/native/include/hardware/display.cpp
android/device/softwinner/wing-common/hardware/libhardware/display/display.cpp

static int open_display(const struct hw_module_t* module, const char* name,
        struct hw_device_t** device)
{
// ... 代码省略
ctx->device.issupporthdminativemode = display_issupporthdminativemode;
// ... 代码省略
    return status;
}

// 实现的地方
int display_issupporthdminativemode(struct display_device_t *dev,int mode)
{
    struct display_context_t* ctx = (struct display_context_t*)dev;

    if(ctx)
    {
        if(ctx->mFD_disp)
        {
            unsigned long args[4];

            args[0] = 0;
    // 这里通过最开始传进来的 DISPLAY_TVFORMAT_1080P_60HZ ...
    // 实际上是查表 g_tv_para[]  mode 跟 g_tv_para[ i].mode 比较。
    // 将得到的 g_tv_para[ i].driver_mode  通过 ioctl 传递给驱动
            args[1] = display_get_driver_tv_format(mode);
    // 这里是往设备驱动发送 DISP_CMD_HDMI_NATIVE_SUPPORT_MODE 命令
            if(ioctl(ctx->mFD_disp,DISP_CMD_HDMI_NATIVE_SUPPORT_MODE,args))
            {
                return 1;
            }
        }
    }

    return  0;
}
// 这里就是它的实现,从代码上很容易看到是在查表
static int display_get_driver_tv_format(int format)
{
    unsigned int i = 0;

    for(i=0; i<sizeof(g_tv_para)/sizeof(struct tv_para_t); i++)
    {
        if(g_tv_para[ i].mode == format)
        {
            return g_tv_para[ i].driver_mode;
        }
    }

    return -1;
}
// 这里就是这张表的样子
struct tv_para_t
{
    int type;// bit3:cvbs, bit2:ypbpr, bit1:vga, bit0:hdmi
    int mode;
    int width;
    int height;
    int valid_width;
    int valid_height;
    int driver_mode;
};
static struct tv_para_t g_tv_para[]=
{
    {8, DISPLAY_TVFORMAT_NTSC,             720,    480,    690,    450,    DISP_TV_MOD_NTSC},
    {8, DISPLAY_TVFORMAT_PAL,              720,    576,    690,    546,    DISP_TV_MOD_PAL},
    {8, DISPLAY_TVFORMAT_PAL_M,            720,    576,    690,    546,    DISP_TV_MOD_PAL_M},
    {8, DISPLAY_TVFORMAT_PAL_NC,           720,    576,    690,    546,    DISP_TV_MOD_PAL_NC},

    {5, DISPLAY_TVFORMAT_480I,             720,    480,    690,    450,    DISP_TV_MOD_480I},
    {5, DISPLAY_TVFORMAT_576I,             720,    576,    690,    546,    DISP_TV_MOD_576I},
    {5, DISPLAY_TVFORMAT_480P,             720,    480,    690,    450,    DISP_TV_MOD_480P},
    {5, DISPLAY_TVFORMAT_576P,             720,    576,    690,    546,    DISP_TV_MOD_576P},
    {5, DISPLAY_TVFORMAT_720P_50HZ,        1280,   720,    1220,   690,    DISP_TV_MOD_720P_50HZ},
    {5, DISPLAY_TVFORMAT_720P_60HZ,        1280,   720,    1220,   690,    DISP_TV_MOD_720P_60HZ},
    {5, DISPLAY_TVFORMAT_1080I_50HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080I_50HZ},
    {5, DISPLAY_TVFORMAT_1080I_60HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080I_60HZ},
    {1, DISPLAY_TVFORMAT_1080P_24HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080P_24HZ},
    {5, DISPLAY_TVFORMAT_1080P_50HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080P_50HZ},
    {5, DISPLAY_TVFORMAT_1080P_60HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080P_60HZ},
    {1, DISPLAY_TVFORMAT_1080P_25HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080P_25HZ},
    {1, DISPLAY_TVFORMAT_1080P_30HZ,       1920,   1080,   1880,   1040,   DISP_TV_MOD_1080P_30HZ},

    {1, DISPLAY_TVFORMAT_1080P_24HZ_3D_FP, 1920,   1080,   1920,   1080,   DISP_TV_MOD_1080P_24HZ_3D_FP},
    {1, DISPLAY_TVFORMAT_720P_50HZ_3D_FP,  1280,   720,    1280,   720,    DISP_TV_MOD_720P_50HZ_3D_FP},
    {1, DISPLAY_TVFORMAT_720P_60HZ_3D_FP,  1280,   720,    1280,   720,    DISP_TV_MOD_720P_60HZ_3D_FP},

    {2, DISPLAY_VGA_H1680_V1050,           1668,   1050,   1668,   1050,   DISP_VGA_H1680_V1050},
    {2, DISPLAY_VGA_H1440_V900,            1440,   900,    1440,   900,    DISP_VGA_H1440_V900},
    {2, DISPLAY_VGA_H1360_V768,            1360,   768,    1360,   768,    DISP_VGA_H1360_V768},
    {2, DISPLAY_VGA_H1280_V1024,           1280,   1024,   1280,   1024,   DISP_VGA_H1280_V1024},
    {2, DISPLAY_VGA_H1024_V768,            1024,   768,    1204,   768,    DISP_VGA_H1024_V768},
    {2, DISPLAY_VGA_H800_V600,             800,    600,    800,    600,    DISP_VGA_H800_V600},
    {2, DISPLAY_VGA_H640_V480,             640,    480,    640,    480,    DISP_VGA_H640_V480},
    {2, DISPLAY_VGA_H1440_V900_RB,         1440,   900,    1440,   900,    DISP_VGA_H1440_V900},
    {2, DISPLAY_VGA_H1920_V1080,           1920,   1080,   1920,   1080,   DISP_VGA_H1920_V1080},
    {2, DISPLAY_VGA_H1280_V720,            1280,   720,    1280,   720,    DISP_VGA_H1280_V720},
};

// 继续分析 先看 DISP_CMD_HDMI_NATIVE_SUPPORT_MODE 命令的定义
android/device/softwinner/common/hardware/include/drv_display.h
typedef enum tag_DISP_CMD
{   
// ... 省略
//----hdmi----
    DISP_CMD_HDMI_ON             = 0x1c0,
    DISP_CMD_HDMI_OFF            = 0x1c1,
    DISP_CMD_HDMI_SET_MODE       = 0x1c2,
    DISP_CMD_HDMI_GET_MODE       = 0x1c3,
    DISP_CMD_HDMI_SUPPORT_MODE   = 0x1c4,
    DISP_CMD_HDMI_GET_HPD_STATUS = 0x1c5,
    DISP_CMD_HDMI_SET_SRC        = 0x1c6,
    DISP_CMD_HDMI_NATIVE_SUPPORT_MODE = 0x1c7,  <----
// ... 省略
}

接下来分析Linux 设备驱动接到这个命令之后会怎么处理。
lichee/linux-3.4/include/linux/drv_display.h
typedef enum tag_DISP_CMD
{
    // ... 省略
    //----hdmi----
    DISP_CMD_HDMI_ON                        = 0x1c0,
    DISP_CMD_HDMI_OFF                       = 0x1c1,
    DISP_CMD_HDMI_SET_MODE                  = 0x1c2,
    DISP_CMD_HDMI_GET_MODE                  = 0x1c3,
    DISP_CMD_HDMI_SUPPORT_MODE              = 0x1c4,
    DISP_CMD_HDMI_GET_HPD_STATUS            = 0x1c5,
    DISP_CMD_HDMI_SET_SRC                   = 0x1c6,
    DISP_CMD_HDMI_NATIVE_SUPPORT_MODE = 0x1c7, <----

}__disp_cmd_t;

看看接收到这个命令之后做了些什么: 这里是驱动程序
linux-3.4/drivers/video/sun7i/disp/dev_disp.c
long disp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
// ... 省略一些代码
        switch(cmd)
        {
// ... 省略一大波 case 代码
    // 这里调用了 BSP_disp_hdmi_check_native_support_mode()
    case DISP_CMD_HDMI_NATIVE_SUPPORT_MODE:
ret = BSP_disp_hdmi_check_native_support_mode(ubuffer[0], ubuffer[1]);
break;

// ... 省略一大波 case 代码
default:
break;
        }

return ret;
}
定义在 linux-3.4\drivers\video\sun7i\disp\de_bsp\de\disp_display.h
extern __s32 BSP_disp_hdmi_check_native_support_mode(__u32 sel, __u8  mode);

看看 BSP_disp_hdmi_check_native_support_mode() 是怎么实现的
linux-3.4/drivers/video/sun7i/disp/de_bsp/de/disp_hdmi.c
__s32 BSP_disp_hdmi_check_native_support_mode(__u32 sel, __u8 mode)
{
    if(gdisp.init_para.hdmi_native_mode_support)
    {
// 如果该函数指针有被赋值则调用 尼玛... 又来一次...
        return gdisp.init_para.hdmi_native_mode_support(mode);
    }
    else
    {
        DE_WRN("hdmi_native_mode_support is NULL\n");
        return DIS_NOT_SUPPORT;
    }
}
gdisp的结构体定义在这:linux-3.4\drivers\video\sun7i\disp\de_bsp\de\disp_display.h
typedef struct
{
    __disp_bsp_init_para    init_para;//para from driver
// ... 省略
}__disp_dev_t;

extern __disp_dev_t gdisp;

__disp_bsp_init_para 结构体定义
typedef struct
{
// ... 省略
// 这里新增了一个成员
__s32 (*hdmi_native_mode_support)(__disp_tv_mode_t mode);   
}__disp_bsp_init_para;

看 gdisp.init_para 初始化的地方:linux-3.4/drivers/video/sun7i/disp/de_bsp/de/disp_hdmi.c
__s32 BSP_disp_set_hdmi_func(__disp_hdmi_func * func)
{
// ... 省略代码
// ... 无语了,又来...
gdisp.init_para.hdmi_native_mode_support = func->hdmi_native_mode_support;
    return DIS_SUCCESS;
}

hdmi_native_mode_support()是 fun的成员,先看看 fun 的结构 __disp_hdmi_func 这个结构体。
lichee/linux-3.4/include/linux/drv_display.h
typedef struct
{
    // ... 省略
// 新增的
__s32 (*hdmi_native_mode_support)(__disp_tv_mode_t mode);
}__disp_hdmi_func;

好了,接着看谁调用了这个 BSP_disp_set_hdmi_func(),往上跟,看func到底指向了哪个函数
linux-3.4\drivers\video\sun7i\disp\Dev_disp.c
__s32 disp_set_hdmi_func(__disp_hdmi_func * func)
{
        BSP_disp_set_hdmi_func(func);
        return 0;
}

看谁调用了 disp_set_hdmi_func()
linux-3.4\drivers\video\sun7i\hdmi\drv_hdmi.c
__s32 Hdmi_init(void)
{
// ... 省略代码
// 终于找到了,在这里赋值
disp_func.hdmi_native_mode_support = Hdmi_native_mode_support;
// 在这里调用了 disp_set_hdmi_func()
disp_set_hdmi_func(&disp_func);

    return 0;
}

看看 Hdmi_native_mode_support() 的实现
__s32 Hdmi_native_mode_support(__disp_tv_mode_t mode)
{
__u32 hdmi_mode;

switch(mode)
{
case DISP_TV_MOD_480I:
hdmi_mode = HDMI1440_480I;
break;

case DISP_TV_MOD_576I:
hdmi_mode = HDMI1440_576I;
break;

case DISP_TV_MOD_480P:
hdmi_mode = HDMI480P;
break;

case DISP_TV_MOD_576P:
hdmi_mode = HDMI576P;
break;  

case DISP_TV_MOD_720P_50HZ:
hdmi_mode = HDMI720P_50;
break;

case DISP_TV_MOD_720P_60HZ:
hdmi_mode = HDMI720P_60;
break;

case DISP_TV_MOD_1080I_50HZ:
hdmi_mode = HDMI1080I_50;
break;

case DISP_TV_MOD_1080I_60HZ:
hdmi_mode = HDMI1080I_60;
break;         

case DISP_TV_MOD_1080P_24HZ:
hdmi_mode = HDMI1080P_24;
break;   

case DISP_TV_MOD_1080P_50HZ:
hdmi_mode = HDMI1080P_50;
break;

case DISP_TV_MOD_1080P_60HZ:
hdmi_mode = HDMI1080P_60;
break;  

case DISP_TV_MOD_1080P_25HZ:
hdmi_mode = HDMI1080P_25;
break;  

case DISP_TV_MOD_1080P_30HZ:
hdmi_mode = HDMI1080P_30;
break;  

case DISP_TV_MOD_1080P_24HZ_3D_FP:
hdmi_mode = HDMI1080P_24_3D_FP;
break;

case DISP_TV_MOD_720P_50HZ_3D_FP:
hdmi_mode = HDMI720P_50_3D_FP;
break;

case DISP_TV_MOD_720P_60HZ_3D_FP:
hdmi_mode = HDMI720P_60_3D_FP;
break;

default:
hdmi_mode = HDMI720P_50;
break;
}
// 竟然。。。。。。
return Hdmi_hal_native_mode_support(hdmi_mode);
}

继续分析 Hdmi_hal_native_mode_support()

声明在此:
linux-3.4\drivers\video\sun7i\hdmi/hdmi_hal.h
extern __s32 Hdmi_hal_native_mode_support(__u32 mode);

实现在此:
linux-3.4\drivers\video\sun7i\hdmi\aw\hdmi_hal.c
__s32 Hdmi_hal_native_mode_support(__u32 mode)
{
    if(Hpd_Check() == 0)
    {
        return 0;
    }
    else
    {
int counter = 1000;
// hdmi_state < 5 ???  应该是等待 接收 电视中传过来的数据 里面包含有分辨率这些信息
        while(hdmi_state < HDMI_State_Wait_Video_config && counter--)
        {
            hdmi_delay_ms(1);
        }
// 这里实际上是查询上层传递下来的分辨率是否被支持
        return Device_Native_Support_VIC[mode];
    }
}

先看看数组的定义:
linux-3.4\drivers/video/sun7i/hdmi/aw/hdmi_core.h
extern __u8Device_Native_Support_VIC[512];
linux-3.4\drivers/video/sun7i/hdmi/aw/hdmi_core.c
__u8 Device_Native_Support_VIC[512];

赋值在这里:
linux-3.4\drivers\video\sun7i\hdmi\aw\hdmi_edid.c
// 看样子这里还不是关键
__s32 Parse_VideoData_Block(__u8 *pbuf,__u8 size)
{
int i=0;
while(i<size)
{
Device_Support_VIC[pbuf[ i] &0x7f] = 1;
if(pbuf[ i] &0x80)
{
   __inf("Parse_VideoData_Block: VIC %d(native) support\n", pbuf[ i]&0x7f);
   // 这里是记录对应的电视机上支持哪些分辨率 1 表示支持
   Device_Native_Support_VIC[pbuf[ i] &0x7f] = 1;
}
else
{
   __inf("Parse_VideoData_Block: VIC %d support\n", pbuf[ i]);
}
i++;
}
return 0;
}

通过搜索,看看哪个地方调用了该函数:
linux-3.4\drivers\video\sun7i\hdmi\aw\hdmi_edid.c
这个函数涉及到芯片的HDMI控制器,比较复杂,要是真的深入分析的话,估计要花上好一段时间.
__s32 ParseEDID(void)
{
    //collect the EDID ucdata of segment 0
    __u8 BlockCount ;
    __u32 i,offset ;

    __inf("ParseEDID\n");

    memset(Device_Support_VIC,0,sizeof(Device_Support_VIC));
// 这里清空了该数组,也就是说,默认全为 0
    memset(Device_Native_Support_VIC,0,sizeof(Device_Native_Support_VIC));
    memset(EDID_Buf,0,sizeof(EDID_Buf));
    isHDMI = 0;
    YCbCr444_Support = 0;
DDC_Init(); // 初始化DDC 、使能、复位DDC、设置DDC时钟

    GetEDIDData(0, EDID_Buf);// 读取数据
// 校验数据
if( EDID_CheckSum(0, EDID_Buf) != 0)
{
return 0;
}
// 校验数据头
if( EDID_Header_Check(EDID_Buf)!= 0)
{
return 0;
}
// 检查版本
if( EDID_Version_Check(EDID_Buf)!= 0)
{
return 0;
}
Parse_DTD_Block(EDID_Buf + 0x36);

Parse_DTD_Block(EDID_Buf + 0x48);

    BlockCount = EDID_Buf[0x7E];

    if( BlockCount > 0 )
    {
    if ( BlockCount > 4 )
    {
        BlockCount = 4 ;
    }
// 读取所有数据块
    for( i = 1 ; i <= BlockCount ; i++ )
    {
        GetEDIDData(i, EDID_Buf) ;  
        if( EDID_CheckSum(i, EDID_Buf)!= 0)
        {
        return 0;
        }

if((EDID_Buf[0x80*i+0]==2)/*&&(EDID_Buf[0x80*i+1]==1)*/)
{
if( (EDID_Buf[0x80*i+1]>=1))
{
if(EDID_Buf[0x80*i+3]&0x20)
{
YCbCr444_Support = 1;
__inf("device support YCbCr44 output\n");
if(rgb_only == 1)
{
__inf("rgb only test!\n");
YCbCr444_Support = 0;
}
}
}

offset = EDID_Buf[0x80*i+2];
if(offset > 4)//deal with reserved data block
{
__u8 bsum = 4;
while(bsum < offset)
{
__u8 tag = EDID_Buf[0x80*i+bsum]>>5;
__u8 len = EDID_Buf[0x80*i+bsum]&0x1f;
if( (len >0) && ((bsum + len + 1) > offset) )
{
    __inf("len or bsum size error\n");
return 0;
}else
{
if( tag == 1)//ADB
{
Parse_AudioData_Block(EDID_Buf+0x80*i+bsum+1,len);
}
else if( tag == 2)//VDB
{// 记录下来
Parse_VideoData_Block(EDID_Buf+0x80*i+bsum+1,len);
}
else if( tag == 3)//vendor specific
{
Parse_HDMI_VSDB(EDID_Buf+0x80*i+bsum+1,len);
}
}

bsum += (len +1);
}

}else
{
__inf("no data in reserved block%d\n",i);
}

if(offset >= 4)//deal with 18-byte timing block
{
while(offset < (0x80-18))
{
Parse_DTD_Block(EDID_Buf + 0x80*i + offset);
offset += 18;
}
}else
{
__inf("no datail timing in block%d\n",i);
}
}

    }
    }

    return 0 ;
}

最后总结:
当HDMI接入时,会通过HDMI控制器得到电视的一些信息并且保存起来,而 getSuitableHdmiNativeMode() 这个接口
实际上只是在已被保存下来的信息中查询得到该电视最高支持的分辨率。
        不得不吐槽下,为了实现这个功能,绕来绕去的。。。
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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