HDMI: High Definition Multimedia Interface,高清多媒体接口,是一种全数字化视频和声音发送接口,可以发送未压缩的音频及视频信号。HDMI有4种类型的接口,分别为 type A, type B, type C和type D,下面我们来看一下这4种接口的定义:

Pin type A type B type C type D
1 TMDS Data2+ TMDS Data2+ TMDS Data2 Shield Hot Plug Detect
2 TMDS Data2 Shield TMDS Data2 Shield TMDS Data2+ Utility
3 TMDS Data2– TMDS Data2– TMDS Data2– TMDS Data2+
4 TMDS Data1+ TMDS Data1+ TMDS Data1 Shield TMDS Data2 Shield
5 TMDS Data1 Shield TMDS Data1 Shield TMDS Data1+ TMDS Data2-
6 TMDS Data1– TMDS Data1– TMDS Data1– TMDS Data1+
7 TMDS Data0+ TMDS Data0+ TMDS Data0 Shield TMDS Data1 Shield
8 TMDS Data0 Shield TMDS Data0 Shield TMDS Data0+ TMDS Data1-
9 TMDS Data0– TMDS Data0– TMDS Data0– TMDS Data0+
10 TMDS Clock+ TMDS Clock+ TMDS Clock Shield TMDS Data0 Shield
11 TMDS Clock Shield TMDS Clock Shield TMDS Clock+ TMDS Data0-
12 TMDS Clock– TMDS Clock– TMDS Clock– TMDS Clock+
13 CEC TMDS Data5+ DDC/CEC Ground TMDS Clock Shield
14 Reserved(N.C. on device) TMDS Data5 Shield CEC TMDS Clock-
15 SCL TMDS Data5- SCL CEC
16 SDA TMDS Data4+ SDA DDC/CEC Ground
17 DDC/CEC Ground TMDS Data4 Shield Reserved(N.C. on device) SCL
18 +5V Power TMDS Data4- +5V Power SDA
19 Hot Plug Detect TMDS Data3+ Hot Plug Detect +5V Power
20 ---- TMDS Data3 Shield ---- ----
21 ---- TMDS Data3- ---- ----
22 ---- CEC ---- ----
23 ---- Reserved(N.C. on device) ---- ----
24 ---- Reserved(N.C. on device) ---- ----
25 ---- SCL ---- ----
26 ---- SDA ---- ----
27 ---- DDC/CEC Ground ---- ----
28 ---- +5V Power ---- ----
29 ---- Hot Plug Detect ---- ----

这里我们用的是type A的接口,电路图如下:

从上面可以看出HDMI采用差分数据的形式进行数据传输,有3组数据差分和一组时钟差分,类似的形式比如CAN, USB等都是通过差分的形式进行数据交互的,采用这种形式的好处是抗干扰能力强,而且速度快,比较新的HDMI 2.1最大传输速率可以达到42.6G bit/s,我们这里用到的全志的CPU是HDMI 1.4,理论上最大支持速率到8.16G bit/s,这个其实已经很快了,allwinner官方的datasheet上说可以支持30 fps的4K画质播放.下边是不同HDMI版本的区别:

接下来我们分析一下HDMI的硬件框架,上面的电路图上pin脚可以分为4部分:

第一部分:4组差分线,数据交互等工作.
第二部分:一组I2C总线,这里称为DDC,主要进行CPU和显示设备之间HDCP数据密钥交换和获取EDID的接口.
第三部分:CEC线,在HDMI上有多个显示器时,如果显示器都支持CEC,那么一个遥控就可以控制其它设备.
第四部分:HHPD线,热插拔检测引脚,驱动程序中会根据这个引脚的变化做拔插相关工作.

上面还有两个概念需要说一下:
HDCP: 高带宽数据内容保护,目的是对数据加密,保护知识产权.
EDID: 包含显示器及其性能参数.

allwinner H3上有集成HDMI控制器,大概的一个框架图如下:

从上面框架可以看出,支持HDMI和TV两种形式,datasheet上了解到,这款芯片支持同时输出HDMI和TV,这个的TV指的是CVBS,那么,我们可以通过这个功能实现双屏,说到双屏,这里简单提一下,双屏主要分为3种显示形式: 双屏同显, 双屏异显,双屏合显,当然,单屏也可以实现类似效果,这里只说一下双屏,实现双屏的形式有很多种方式,比如DSI + HDMI, DSI + CVBS,DSI + SPI,...有很多形式,当然,最好的方法其实是两个屏幕用同一组接口,通过片选或者地址选择,双屏同显看起来比较容易,双屏在公交车上也有用到,公交车的广告机屏1播放传媒视频,屏2播放PPT式广告,手机上比如努比亚,ZTE等都有推出双屏的手机.

好了,回到正题,继续说HDMI,接下来说一下软件部分:

本次使用的是Android进行验证,源码方面是7.0, Android N的代码,kernel使用的是Linux-4.4,和上次camera那一节使用的Linux-3.4代码上做了一些升级,HDMI这一部分还是有很大变动的,无论是HDMI专有部分还是通用部分,都进行了代码优化和变动,不过大致的框架还是差不多的,代码方面依然可以去参考Linux-3.4的,这里贴一下代码路径:

Linux-3.4在线看代码: https://github.com/orangepi-xunlong/orangepi_h3_linux

Android N全部压缩源代码: https://pan.baidu.com/s/1o9HIBii#list/path=%2F

这里简单说一下全志平台Android N的编译步骤,首先从百度云下载7.0的全部压缩包H3-sdk7.0-2017-11-03.tar.gzaa到*.gzan,然后在Ubuntu执行:

cat H3-sdk7.0-2017-11-03.tar.gza* > H3-Android7.0.tar.gz
tar -xvf H3-Android7.0.tar.gz

就可以解压得到Android和kernel源码,然后根据我之前写的编译环境搭建,安装以下Android源码的编译依赖,不过要注意,Android 7.0需要openjdk-8-jdk,不是6或者7,如果安装错了,则编译报错,如果有安装多个openjdk则根据我之前写的编译环境搭建,做一下切换即可.安装完编译依赖环境之后,上述所说的解压文件会得到两个文件夹,一个是android,另一个是lichee,我们进入lichee,做如下配置:

phoebus@Linux:~/xshark/Android/AndroidN/lichee$ ./build.sh config
Welcome to mkscript setup progress
All available chips:0. sun50iw1p11. sun50iw2p12. sun50iw6p13. sun8iw11p14. sun8iw12p15. sun8iw6p16. sun8iw7p17. sun8iw8p18. sun9iw1p1
Choice: 6
All available platforms:0. android1. dragonboard2. linux3. camdroid
Choice: 0
All available business:0. dolphin1. secure2. karaok
Choice: 0
LICHEE_BUSINESS=dolphin
using kernel 'linux-3.10':
All available kernel:0. linux-4.4
Choice: 0

然后会自动编译,如果没有自动编译,则直接运行./build.sh即可,等待编译完成,然后,进入android目录执行:

source ./build/envsetup.sh
lunch 28
extract-bsp
make -j8 && pack

因为我用的是H3,所以选择lunch 28,其它CPU根据实际情况选择编译对应版本的Android.下面主要说一下HDMI驱动的实现.在全志平台,video子系统这部分可以分为两部分,一部分是通用代码,另一部分是具体控制器专用驱动.这里用的H3只集成了HDMI和CVBS,所以目录结构如下:

通用部分代码量还是不少的,这里暂时主题是具体驱动实现,所以,暂时不讨论通用部分,CVBS使用的很少了,这里也先不说,重点说一下HDMI目录,目录结构如下:

可以看出来sun8iw11和sun50iw1工程,全志只是提供了动态库,并没有给出源码,应该是这部分涉及到一些全志的特色技术,但是不得不说,全志这种做法已经违反了GPL协议,GNU组织没有告全志,可能是觉得这个vendor应该太小了,来一趟还不够油钱,用<<惠子相梁>>形容一下.

因为这里用的是H3,代号是sun8iw7p1,这里正好这部分是开源的,所以动态库,对本次没有影响.我们先来看一下,这个目录的makefile:

obj-$(CONFIG_HDMI_DISP2_SUNXI) += hdmi.ohdmi-y := drv_hdmi.o hdmi_core.o hdmi_edid.o
ifeq ($(CONFIG_ARM64),y)
$(shell cp $(obj)/libhdmi_sun50iw1 $(obj)/libhdmi_sun50iw1.a)
hdmi-$(CONFIG_ARCH_SUN50IW1P1) += libhdmi_sun50iw1.a
hdmi-$(CONFIG_ARCH_SUN50IW2P1) += libhdmi_sun50iw1.a
endififeq ($(CONFIG_ARM),y)
$(shell cp $(obj)/libhdmi_sun8iw11 $(obj)/libhdmi_sun8iw11.a)
hdmi-$(CONFIG_ARCH_SUN50IW1P1) += libhdmi_sun8iw11.a
hdmi-$(CONFIG_ARCH_SUN50IW2P1) += libhdmi_sun8iw11.a
hdmi-$(CONFIG_ARCH_SUN8IW11)   += libhdmi_sun8iw11.a
hdmi-$(CONFIG_ARCH_SUN8IW12) += hdmi_bsp_sun8iw12.o
hdmi-$(CONFIG_ARCH_SUN8IW7) += hdmi_bsp_sun8iw7.o
endif

可以看出,这个目录的文件编译之后,会链接成一个hdmi.o,这个文件包括:drv_hdmi.c, hdmi_core.c, hdmi_edid.c,然后这里是sun8iw7p1,所以,还会包含:hdmi_bsp_sun8iw7.c,这就是HDMI功能实现的驱动代码,代码量不是很多,3K多行,它们的组织关系是, hdmi_bsp_sun8iw7.c和hdmi_edid.c为hdmi_core.c提供API, hdmi_core.c为drv_hdmi.c提供API,可以看出,主要实现就是在drv_hdmi.c中,这个文件是重点, 然后,需要我们实现的是hdmi_bsp_sun8iw7.c,不过这个全志已经实现了,所以,这里主要就是两个文件是我们比较关注的.因为时间比较紧,所以没有画这个驱动的框架图,这里先直接贴一下这两个文件的代码:

hdmi_bsp_sun8iw7.c

/** Allwinner SoCs hdmi driver.** Copyright (C) 2017 Allwinner.** This file is licensed under the terms of the GNU General Public* License version 2.  This program is licensed "as is" without any* warranty of any kind, whether express or implied.*/#include "hdmi_bsp.h"
#include "hdmi_core.h"static unsigned int hdmi_base_addr;
static unsigned int hdmi_version;
/*static struct audio_para glb_audio;*/
static unsigned int tmp_rcal_100, tmp_rcal_200;
static unsigned int rcal_flag;#define get_bvalue(addr) (*((volatile unsigned char *)(addr)))
#define put_bvalue(addr, v) \(*((volatile unsigned char *)(addr)) = (unsigned char)(v))
#define get_wvalue(addr) (*((volatile unsigned long *)(addr)))
#define put_wvalue(addr, v) \(*((volatile unsigned long *)(addr)) = (unsigned long)(v))static int hdmi_phy_set(struct video_para *video);struct para_tab
{unsigned int para[19];
};struct pcm_sf
{unsigned int sf;unsigned char cs_sf;
};static struct para_tab ptbl[] = {{{6, 1, 1, 1, 5, 3, 0, 1, 4, 0, 0, 160, 20, 38, 124, 240, 22, 0, 0}},{{21, 11, 1, 1, 5, 3, 1, 1, 2, 0, 0, 160, 32, 24, 126, 32, 24, 0, 0}},{{2, 11, 0, 0, 2, 6, 1, 0, 9, 0, 0, 208, 138, 16, 62, 224, 45, 0, 0}},{{17, 11, 0, 0, 2, 5, 2, 0, 5, 0, 0, 208, 144, 12, 64, 64, 49, 0, 0}},{{19, 4, 0, 96, 5, 5, 2, 2, 5, 1, 0, 0, 188, 184, 40, 208, 30, 1, 1}},{{4, 4, 0, 96, 5, 5, 2, 1, 5, 0, 0, 0, 114, 110, 40, 208, 30, 1, 1}},{{20, 4, 0, 97, 7, 5, 4, 2, 2, 2, 0, 128, 208, 16, 44, 56, 22, 1, 1}},{{5, 4, 0, 97, 7, 5, 4, 1, 2, 0, 0, 128, 24, 88, 44, 56, 22, 1, 1}},{{31, 2, 0, 96, 7, 5, 4, 2, 4, 2, 0, 128, 208, 16, 44, 56, 45, 1, 1}},{{16, 2, 0, 96, 7, 5, 4, 1, 4, 0, 0, 128, 24, 88, 44, 56, 45, 1, 1}},{{32, 4, 0, 96, 7, 5, 4, 3, 4, 2, 0, 128, 62, 126, 44, 56, 45, 1, 1}},{{33, 4, 0, 0, 7, 5, 4, 2, 4, 2, 0, 128, 208, 16, 44, 56, 45, 1, 1}},{{34, 4, 0, 0, 7, 5, 4, 1, 4, 0, 0, 128, 24, 88, 44, 56, 45, 1, 1}},{{160, 2, 0, 96, 7, 5, 8, 3, 4, 2, 0, 128, 62, 126, 44, 157, 45, 1, 1}},{{147, 2, 0, 96, 5, 5, 5, 2, 5, 1, 0, 0, 188, 184, 40, 190, 30, 1, 1}},{{132, 2, 0, 96, 5, 5, 5, 1, 5, 0, 0, 0, 114, 110, 40, 160, 30, 1, 1}},{{257, 1, 0, 96, 15, 10, 8, 2, 8, 0, 0, 0, 48, 176, 88, 112, 90, 1, 1}},{{258, 1, 0, 96, 15, 10, 8, 5, 8, 4, 0, 0, 160, 32, 88, 112, 90, 1, 1}},{{259, 1, 0, 96, 15, 10, 8, 6, 8, 4, 0, 0, 124, 252, 88, 112, 90, 1, 1}},{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
};static unsigned char ca_table[64] = {0x00, 0x11, 0x01, 0x13, 0x02, 0x31, 0x03, 0x33, 0x04, 0x15, 0x05,0x17, 0x06, 0x35, 0x07, 0x37, 0x08, 0x55, 0x09, 0x57, 0x0a, 0x75,0x0b, 0x77, 0x0c, 0x5d, 0x0d, 0x5f, 0x0e, 0x7d, 0x0f, 0x7f, 0x10,0xdd, 0x11, 0xdf, 0x12, 0xfd, 0x13, 0xff, 0x14, 0x99, 0x15, 0x9b,0x16, 0xb9, 0x17, 0xbb, 0x18, 0x9d, 0x19, 0x9f, 0x1a, 0xbd, 0x1b,0xbf, 0x1c, 0xdd, 0x1d, 0xdf, 0x1e, 0xfd, 0x1f, 0xff,
};static struct pcm_sf sf[10] = {{22050, 0x04},{44100, 0x00},{88200, 0x08},{176400, 0x0c},{24000, 0x06},{48000, 0x02},{96000, 0x0a},{192000, 0x0e},{32000, 0x03},{768000, 0x09},
};static unsigned int n_table[21] = {32000, 3072, 4096, 44100, 4704, 6272, 88200,9408, 12544, 176400, 18816, 25088, 48000, 5120,6144, 96000, 10240, 12288, 192000, 20480, 24576,
};/* byte write */
static void hdmi_write(unsigned int addr, unsigned char data)
{put_bvalue(hdmi_base_addr + addr, data);
}/* word write */
static void hdmi_writel(unsigned int addr, unsigned int data)
{put_wvalue(hdmi_base_addr + addr, data);
}/* byte read */
static unsigned char hdmi_read(unsigned int addr)
{return get_bvalue(hdmi_base_addr + addr);
}/* word read */
static unsigned int hdmi_readl(unsigned int addr)
{return get_wvalue(hdmi_base_addr + addr);
}/* hdmi us delay */
static void hdmi_udelay(unsigned long us)
{hdmi_delay_us(us);
}static void hdmi_phy_init(struct video_para *video)
{unsigned int to_cnt;unsigned int tmp;hdmi_writel(0x10020, 0);hdmi_writel(0x10020, (1 << 0));hdmi_udelay(5);hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 16));hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 1));hdmi_udelay(10);hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 2));hdmi_udelay(5);hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 3));hdmi_udelay(40);hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 19));hdmi_udelay(100);hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 18));hdmi_writel(0x10020, hdmi_readl(0x10020) | (7 << 4));to_cnt = 10;while (1){if ((hdmi_readl(0x10038) & 0x80) == 0x80)break;hdmi_udelay(200);to_cnt--;if (to_cnt == 0) {pr_warn("%s, timeout\n", __func__);break;}}hdmi_writel(0x10020, hdmi_readl(0x10020) | (0xf << 8));/*hdmi_writel(0x10020,hdmi_readl(0x10020)&(~(1<<19)));*/hdmi_writel(0x10020, hdmi_readl(0x10020) | (1 << 7));/*hdmi_writel(0x10020,hdmi_readl(0x10020)|(0xf<<12));*/hdmi_writel(0x1002c, 0x39dc5040);hdmi_writel(0x10030, 0x80084343);hdmi_udelay(10000);hdmi_writel(0x10034, 0x00000001);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0x02000000);hdmi_udelay(100000);tmp = hdmi_readl(0x10038);tmp_rcal_100 = (tmp & 0x3f) >> 1;tmp_rcal_200 = (tmp & 0x3f) >> 2;rcal_flag = 1;hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0xC0000000);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | ((tmp & 0x1f800) >> 11));hdmi_writel(0x10020, 0x01FF0F7F);hdmi_writel(0x10024, 0x80639000);hdmi_writel(0x10028, 0x0F81C405);
}static unsigned int get_vid(unsigned int id)
{unsigned int i, count;count = sizeof(ptbl) / sizeof(struct para_tab) - 1;for (i = 0; i < count; i++){if (id == ptbl[i].para[0])return i;}ptbl[i].para[0] = id;return i;
}static int hdmi_phy_set(struct video_para *video)
{unsigned int id;unsigned int count;unsigned int tmp;unsigned int div;count = sizeof(ptbl) / sizeof(struct para_tab);if (rcal_flag == 0)hdmi_phy_init(video);id = get_vid(video->vic);if (id == (count - 1))div = video->clk_div - 1;elsediv = ptbl[id].para[1] - 1;div &= 0xf;hdmi_writel(0x10020, hdmi_readl(0x10020) & (~0xf000));switch (ptbl[id].para[1]){case 1:if (hdmi_version == 0)hdmi_writel(0x1002c, 0x31dc5fc0);elsehdmi_writel(0x1002c, 0x30dc5fc0);hdmi_writel(0x10030, 0x800863C0 | div);hdmi_udelay(10000);hdmi_writel(0x10034, 0x00000001);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0x02000000);hdmi_udelay(200000);tmp = hdmi_readl(0x10038);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0xC0000000);if (((tmp & 0x1f800) >> 11) < 0x3d)hdmi_writel(0x1002c, hdmi_readl(0x1002c) | (((tmp & 0x1f800) >> 11) + 2));elsehdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0x3f);hdmi_udelay(100000);hdmi_writel(0x10020, 0x01FFFF7F);hdmi_writel(0x10024, 0x8063b000);hdmi_writel(0x10028, 0x0F8246B5);break;case 2:hdmi_writel(0x1002c, 0x39dc5040);hdmi_writel(0x10030, 0x80084380 | div);hdmi_udelay(10000);hdmi_writel(0x10034, 0x00000001);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0x02000000);hdmi_udelay(100000);tmp = hdmi_readl(0x10038);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0xC0000000);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | ((tmp & 0x1f800) >> 11));hdmi_writel(0x10020, 0x01FFFF7F);hdmi_writel(0x10024, 0x8063a800);hdmi_writel(0x10028, 0x0F81C485);break;case 4:hdmi_writel(0x1002c, 0x39dc5040);hdmi_writel(0x10030, 0x80084340 | div);hdmi_udelay(10000);hdmi_writel(0x10034, 0x00000001);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0x02000000);hdmi_udelay(100000);tmp = hdmi_readl(0x10038);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0xC0000000);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | ((tmp & 0x1f800) >> 11));hdmi_writel(0x10020, 0x11FFFF7F);hdmi_writel(0x10024, 0x80623000 | tmp_rcal_200);hdmi_writel(0x10028, 0x0F814385);break;case 11:hdmi_writel(0x1002c, 0x39dc5040);hdmi_writel(0x10030, 0x80084300 | div);hdmi_udelay(10000);hdmi_writel(0x10034, 0x00000001);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0x02000000);hdmi_udelay(100000);tmp = hdmi_readl(0x10038);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | 0xC0000000);hdmi_writel(0x1002c, hdmi_readl(0x1002c) | ((tmp & 0x1f800) >> 11));hdmi_writel(0x10020, 0x11FFFF7F);hdmi_writel(0x10024, 0x80623000 | tmp_rcal_200);hdmi_writel(0x10028, 0x0F80C285);break;default:return -1;}return 0;
}void bsp_hdmi_set_version(unsigned int version)
{hdmi_version = version;
}void bsp_hdmi_set_addr(uintptr_t base_addr)
{hdmi_base_addr = base_addr;rcal_flag = 0;
}void bsp_hdmi_inner_init(void)
{hdmi_write(0x10010, 0x45);hdmi_write(0x10011, 0x45);hdmi_write(0x10012, 0x52);hdmi_write(0x10013, 0x54);hdmi_write(0x8080, 0x00);hdmi_udelay(1);hdmi_write(0xF01F, 0x00);hdmi_write(0x8403, 0xff);hdmi_write(0x904C, 0xff);hdmi_write(0x904E, 0xff);hdmi_write(0xD04C, 0xff);hdmi_write(0x8250, 0xff);hdmi_write(0x8A50, 0xff);hdmi_write(0x8272, 0xff);hdmi_write(0x40C0, 0xff);hdmi_write(0x86F0, 0xff);hdmi_write(0x0EE3, 0xff);hdmi_write(0x8EE2, 0xff);hdmi_write(0xA049, 0xf0);hdmi_write(0xB045, 0x1e);hdmi_write(0x00C1, 0x00);hdmi_write(0x00C1, 0x03);hdmi_write(0x00C0, 0x00);hdmi_write(0x40C1, 0x10);hdmi_write(0x0081, 0xfd);hdmi_write(0x0081, 0x00);hdmi_write(0x0081, 0xfd);hdmi_write(0x0010, 0xff);hdmi_write(0x0011, 0xff);hdmi_write(0x8010, 0xff);hdmi_write(0x8011, 0xff);hdmi_write(0x0013, 0xff);hdmi_write(0x8012, 0xff);hdmi_write(0x8013, 0xff);
}void bsp_hdmi_init(void)
{struct video_para vpara;vpara.vic = 17;hdmi_phy_init(&vpara);bsp_hdmi_inner_init();
}void bsp_hdmi_set_video_en(unsigned char enable)
{if (enable)hdmi_writel(0x10020, hdmi_readl(0x10020) | (0xf << 12));elsehdmi_writel(0x10020, hdmi_readl(0x10020) & (~(0xf << 12)));
}int bsp_hdmi_video_get_div(unsigned int pixel_clk)
{int div = 1;if (pixel_clk > 148500000)div = 1;else if (pixel_clk > 74250000)div = 2;else if (pixel_clk > 27000000)div = 4;elsediv = 11;return div;
}int bsp_hdmi_video(struct video_para *video)
{unsigned int count;unsigned int id = get_vid(video->vic);switch (video->vic){case 2:case 6:case 17:case 21:video->csc = BT601;break;default:video->csc = BT709;break;}count = sizeof(ptbl) / sizeof(struct para_tab);if (id == count - 1) {ptbl[id].para[1] = bsp_hdmi_video_get_div(video->pixel_clk);ptbl[id].para[2] = video->pixel_repeat;ptbl[id].para[3] = ((video->hor_sync_polarity & 1) << 5) |((video->ver_sync_polarity & 1) << 6) |(video->b_interlace & 1);ptbl[id].para[4] = video->x_res / 256;ptbl[id].para[5] = video->ver_sync_time;ptbl[id].para[6] = video->y_res / 256;ptbl[id].para[7] = (video->hor_total_time - video->x_res) / 256;ptbl[id].para[8] = video->ver_front_porch;ptbl[id].para[9] = video->hor_front_porch / 256;ptbl[id].para[10] = video->hor_sync_time / 256;ptbl[id].para[11] = video->x_res % 256;ptbl[id].para[12] = (video->hor_total_time - video->x_res) % 256;ptbl[id].para[13] = video->hor_front_porch % 256;ptbl[id].para[14] = video->hor_sync_time % 256;ptbl[id].para[15] = video->y_res % 256;ptbl[id].para[16] = video->ver_total_time - video->y_res;ptbl[id].para[17] = 1;if (video->x_res <= 736 && video->y_res <= 576)video->csc = BT601;elsevideo->csc = BT709;}if (hdmi_phy_set(video) != 0)return -1;bsp_hdmi_inner_init();hdmi_write(0x0840, 0x01);hdmi_write(0x4845, 0x00);hdmi_write(0x0040, ptbl[id].para[3] | 0x10);hdmi_write(0x10001, ((ptbl[id].para[3] < 96) ? 0x03 : 0x00));hdmi_write(0x8040, ptbl[id].para[4]);hdmi_write(0x4043, ptbl[id].para[5]);hdmi_write(0x8042, ptbl[id].para[6]);hdmi_write(0x0042, ptbl[id].para[7]);hdmi_write(0x4042, ptbl[id].para[8]);hdmi_write(0x4041, ptbl[id].para[9]);hdmi_write(0xC041, ptbl[id].para[10]);hdmi_write(0x0041, ptbl[id].para[11]);hdmi_write(0x8041, ptbl[id].para[12]);hdmi_write(0x4040, ptbl[id].para[13]);hdmi_write(0xC040, ptbl[id].para[14]);hdmi_write(0x0043, ptbl[id].para[15]);hdmi_write(0x8043, ptbl[id].para[16]);hdmi_write(0x0045, 0x0c);hdmi_write(0x8044, 0x20);hdmi_write(0x8045, 0x01);hdmi_write(0x0046, 0x0b);hdmi_write(0x0047, 0x16);hdmi_write(0x8046, 0x21);hdmi_write(0x3048, ptbl[id].para[2] ? 0x21 : 0x10);hdmi_write(0x0401, ptbl[id].para[2] ? 0x01 : 0x00);hdmi_write(0x8400, 0x07);hdmi_write(0x8401, 0x00);hdmi_write(0x0402, 0x47);hdmi_write(0x0800, 0x01);hdmi_write(0x0801, 0x07);hdmi_write(0x8800, 0x00);hdmi_write(0x8801, 0x00);hdmi_write(0x0802, 0x00);hdmi_write(0x0803, 0x00);hdmi_write(0x8802, 0x00);hdmi_write(0x8803, 0x00);if (video->is_hdmi){hdmi_write(0xB045, 0x08);hdmi_write(0x2045, 0x00);hdmi_write(0x2044, 0x0c);hdmi_write(0x6041, 0x03);hdmi_write(0xA044, ((ptbl[id].para[0] & 0x100) == 0x100)? 0x20: (((ptbl[id].para[0] & 0x80) == 0x80) ? 0x40 : 0x00));hdmi_write(0xA045, ((ptbl[id].para[0] & 0x100) == 0x100)? (ptbl[id].para[0] & 0x7f): 0x00);hdmi_write(0x2046, 0x00);hdmi_write(0x3046, 0x01);hdmi_write(0x3047, 0x11);hdmi_write(0x4044, 0x00);hdmi_write(0x0052, 0x00);hdmi_write(0x8051, 0x11);hdmi_write(0x10010, 0x45);hdmi_write(0x10011, 0x45);hdmi_write(0x10012, 0x52);hdmi_write(0x10013, 0x54);hdmi_write(0x0040, hdmi_read(0x0040) | 0x08);hdmi_write(0x10010, 0x52);hdmi_write(0x10011, 0x54);hdmi_write(0x10012, 0x41);hdmi_write(0x10013, 0x57);hdmi_write(0x4045, video->is_yuv ? 0x02 : 0x00);if (ptbl[id].para[17] == 0)hdmi_write(0xC044, (video->csc << 6) | 0x18);else if (ptbl[id].para[17] == 1)hdmi_write(0xC044, (video->csc << 6) | 0x28);elsehdmi_write(0xC044, (video->csc << 6) | 0x08);hdmi_write(0xC045, video->is_yuv ? 0x00 : 0x04);hdmi_write(0x4046, ((ptbl[id].para[0] & 0x100) == 0x100)? 0x00: (ptbl[id].para[0] & 0x7f));}if (video->is_hcts){hdmi_write(0x00C0, video->is_hdmi ? 0x91 : 0x90);hdmi_write(0x00C1, 0x05);hdmi_write(0x40C1, (ptbl[id].para[3] < 96) ? 0x10 : 0x1a);hdmi_write(0x80C2, 0xff);hdmi_write(0x40C0, 0xfd);hdmi_write(0xC0C0, 0x40);hdmi_write(0x00C1, 0x04);hdmi_write(0x10010, 0x45);hdmi_write(0x10011, 0x45);hdmi_write(0x10012, 0x52);hdmi_write(0x10013, 0x54);hdmi_write(0x0040, hdmi_read(0x0040) | 0x80);hdmi_write(0x00C0, video->is_hdmi ? 0x95 : 0x94);hdmi_write(0x10010, 0x52);hdmi_write(0x10011, 0x54);hdmi_write(0x10012, 0x41);hdmi_write(0x10013, 0x57);}hdmi_write(0x0082, 0x00);hdmi_write(0x0081, 0x00);hdmi_write(0x0840, 0x00);return 0;
}int bsp_hdmi_audio(struct audio_para *audio)
{unsigned int i;unsigned int n;unsigned int count;unsigned id = get_vid(audio->vic);count = sizeof(ptbl) / sizeof(struct para_tab);hdmi_write(0xA049, (audio->ch_num > 2) ? 0xf1 : 0xf0);for (i = 0; i < 64; i += 2) {if (audio->ca == ca_table[i]) {hdmi_write(0x204B, ~ca_table[i + 1]);break;}}hdmi_write(0xA04A, 0x00);hdmi_write(0xA04B, 0x30);hdmi_write(0x6048, 0x00);hdmi_write(0x6049, 0x01);hdmi_write(0xE048, 0x42);hdmi_write(0xE049, 0x86);hdmi_write(0x604A, 0x31);hdmi_write(0x604B, 0x75);hdmi_write(0xE04A, 0x00 | 0x01);for (i = 0; i < 10; i += 1) {if (audio->sample_rate == sf[i].sf) {hdmi_write(0xE04A, 0x00 | sf[i].cs_sf);break;}}hdmi_write(0xE04B, 0x00 | (audio->sample_bit == 16)? 0x02: ((audio->sample_bit == 24) ? 0xb : 0x0));hdmi_write(0x0251, audio->sample_bit);n = 6272;/*cts = 0;*/for (i = 0; i < 21; i += 3) {if (audio->sample_rate == n_table[i]) {if ((id != count - 1) && (ptbl[id].para[1] == 1))n = n_table[i + 1];elsen = n_table[i + 2];/*cts = (n / 128) * (glb_video.tmds_clk / 100) /*//*(audio->sample_rate / 100);*/break;}}hdmi_write(0x0A40, n);hdmi_write(0x0A41, n >> 8);hdmi_write(0x8A40, n >> 16);hdmi_write(0x0A43, 0x00);hdmi_write(0x8A42, 0x04);hdmi_write(0xA049, (audio->ch_num > 2) ? 0x01 : 0x00);if (audio->type == PCM)hdmi_write(0x2043, (audio->ch_num - 1) * 16);elsehdmi_write(0x2043, 0x00);hdmi_write(0xA042, 0x00);hdmi_write(0xA043, audio->ca);hdmi_write(0x6040, 0x00);if (audio->type == PCM) {hdmi_write(0x8251, 0x00);} else if ((audio->type == DTS_HD) || (audio->type == MAT)) {hdmi_write(0x8251, 0x03);hdmi_write(0x0251, 0x15);hdmi_write(0xA043, 0);} else {hdmi_write(0x8251, 0x02);hdmi_write(0x0251, 0x15);hdmi_write(0xA043, 0);}hdmi_write(0x0250, 0x00);hdmi_write(0x0081, 0x08);hdmi_write(0x8080, 0xf7);hdmi_udelay(100);hdmi_write(0x0250, 0xaf);hdmi_udelay(100);hdmi_write(0x0081, 0x00);return 0;
}/* hdmi_edid调用, ddc-->i2c */
int bsp_hdmi_ddc_read(char cmd, char pointer, char offset, int nbyte, char *pbuf)
{unsigned char off = offset;unsigned int to_cnt;int ret = 0;hdmi_write(0x10010, 0x45);hdmi_write(0x10011, 0x45);hdmi_write(0x10012, 0x52);hdmi_write(0x10013, 0x54);hdmi_write(0x4EE1, 0x00);to_cnt = 50;while ((hdmi_read(0x4EE1) & 0x01) != 0x01){hdmi_udelay(10);to_cnt--; /* wait for 500us for timeout*/if (to_cnt == 0){pr_warn("ddc rst timeout\n");break;}}hdmi_write(0x8EE3, 0x05);hdmi_write(0x0EE3, 0x08);hdmi_write(0x4EE2, 0xd8);hdmi_write(0xCEE2, 0xfe);to_cnt = 10;while (nbyte > 0){to_cnt = 10;hdmi_write(0x0EE0, 0xa0 >> 1);hdmi_write(0x0EE1, off);hdmi_write(0x4EE0, 0x60 >> 1);hdmi_write(0xCEE0, pointer);hdmi_write(0x0EE2, 0x02);while (1){to_cnt--; /* wait for 10ms for timeout*/if (to_cnt == 0) {pr_warn("ddc read timeout, byte cnt = %d\n",nbyte);break;}if ((hdmi_read(0x0013) & 0x02) == 0x02) {hdmi_write(0x0013, hdmi_read(0x0013) & 0x02);*pbuf++ = hdmi_read(0x8EE1);break;} else if ((hdmi_read(0x0013) & 0x01) == 0x01) {hdmi_write(0x0013, hdmi_read(0x0013) & 0x01);ret = -1;break;}hdmi_udelay(1000);}nbyte--;off++;}hdmi_write(0x10010, 0x52);hdmi_write(0x10011, 0x54);hdmi_write(0x10012, 0x41);hdmi_write(0x10013, 0x57);return ret;
}unsigned int bsp_hdmi_get_hpd(void)
{unsigned int ret = 0;hdmi_write(0x10010, 0x45);hdmi_write(0x10011, 0x45);hdmi_write(0x10012, 0x52);hdmi_write(0x10013, 0x54);if (hdmi_readl(0x10038) & 0x80000)ret = 1;elseret = 0;hdmi_write(0x10010, 0x52);hdmi_write(0x10011, 0x54);hdmi_write(0x10012, 0x41);hdmi_write(0x10013, 0x57);return ret;
}void bsp_hdmi_standby(void)
{hdmi_write(0x10020, 0x07);hdmi_write(0x1002c, 0x00);
}void bsp_hdmi_hrst(void)
{hdmi_write(0x00C1, 0x04);hdmi_write(0x0081, 0x40);
}void bsp_hdmi_hdl(void)
{
}int bsp_hdmi_hdcp_err_check(void)
{int ret = 0;hdmi_write(0x10010, 0x45);hdmi_write(0x10011, 0x45);hdmi_write(0x10012, 0x52);hdmi_write(0x10013, 0x54);if ((hdmi_read(0x80c0) & 0xfe) != 0x40)hdmi_write(0x00c1, hdmi_read(0x00c1) & 0xfe);hdmi_write(0x10010, 0x52);hdmi_write(0x10011, 0x54);hdmi_write(0x10012, 0x41);hdmi_write(0x10013, 0x57);return ret;
}int bsp_hdmi_cec_get_simple_msg(unsigned char *msg)
{return 0;
}int bsp_hdmi_cec_send(char *buf, unsigned char bytes)
{return 0;
}void bsp_hdmi_cec_free_time_set(unsigned char value)
{
}int bsp_hdmi_set_func(hdmi_bsp_func *func)
{return 0;
}

drv_hdmi.c

/** Allwinner SoCs hdmi driver.** Copyright (C) 2016 Allwinner.** This file is licensed under the terms of the GNU General Public* License version 2.  This program is licensed "as is" without any* warranty of any kind, whether express or implied.*/#include "drv_hdmi_i.h"
#include "hdmi_core.h"
#include "../disp/disp_sys_intf.h"
#include "../disp/dev_disp.h"
#include <linux/regulator/consumer.h>
#include <linux/clk-provider.h>
#include <linux/clk/sunxi.h>
#include <linux/sunxi-clk-prepare.h>
#if defined(CONFIG_EXTCON)
#include <linux/extcon.h>
#endif
#include <linux/sunxi-sid.h>static u32 io_enable_count;static struct semaphore *run_sem;
static struct task_struct *HDMI_task;
static struct task_struct *cec_task;
static bool hdmi_cec_support;
static char hdmi_power[25];
static char hdmi_io_regulator[25];
static bool hdmi_power_used;
static bool hdmi_io_regulator_used;
static bool hdmi_used;
static bool boot_hdmi;
#if defined(CONFIG_COMMON_CLK)
static struct clk *hdmi_clk;
static struct clk *hdmi_ddc_clk;
static struct clk *hdmi_clk_parent;
#if defined(CONFIG_ARCH_SUN8IW12)
static struct clk *hdmi_cec_clk;
#endif
#endif
static u32 power_enable_count;
static u32 io_regulator_enable_count;
static u32 clk_enable_count;
static struct mutex mlock;
#if defined(CONFIG_SND_SUNXI_SOC_SUNXI_HDMIAUDIO)
static bool audio_enable;
#endif
static bool b_hdmi_suspend;
static bool b_hdmi_suspend_pre;static struct cdev *my_cdev;
static dev_t devid;
static struct class *hdmi_class;
hdmi_info_t ghdmi;void hdmi_delay_ms(unsigned long ms)
{u32 timeout = ms*HZ/1000;set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(timeout);
}void hdmi_delay_us(unsigned long us)
{udelay(us);
}unsigned int hdmi_get_soc_version(void)
{unsigned int version = 0;
#if defined(CONFIG_ARCH_SUN8IW7)
#if defined(SUN8IW7P1_REV_A) || defined(SUN8IW7P2_REV_B)unsigned int chip_ver = sunxi_get_soc_ver();switch (chip_ver) {case SUN8IW7P1_REV_A:case SUN8IW7P2_REV_A:version = 0;break;case SUN8IW7P1_REV_B:case SUN8IW7P2_REV_B:version = 1;}
#elseversion = 1;
#endif /*endif  SUN8IW7P1_REV_A*/
#endifreturn version;
}static int hdmi_parse_io_config(void)
{disp_sys_pin_set_state("hdmi", DISP_PIN_STATE_ACTIVE);return 0;
}static int hdmi_io_config(u32 bon)
{return disp_sys_pin_set_state("hdmi", (bon == 1) ? DISP_PIN_STATE_ACTIVE : DISP_PIN_STATE_SLEEP);
}#if defined(CONFIG_COMMON_CLK)
static int hdmi_clk_enable(void)
{int ret = 0;if (hdmi_clk)ret = clk_prepare_enable(hdmi_clk);if (ret != 0)return ret;if (hdmi_ddc_clk)ret = clk_prepare_enable(hdmi_ddc_clk);#if defined(CONFIG_ARCH_SUN8IW12)if (hdmi_cec_clk)ret = clk_prepare_enable(hdmi_cec_clk);
#endif /*endif CONFIG_ARCH_SUN8IW12 */if (ret != 0)clk_disable(hdmi_clk);return ret;
}static int hdmi_clk_disable(void)
{if (hdmi_clk)clk_disable(hdmi_clk);if (hdmi_ddc_clk)clk_disable(hdmi_ddc_clk);#if defined(CONFIG_ARCH_SUN8IW12)if (hdmi_cec_clk)clk_disable(hdmi_cec_clk);
#endif /*endif CONFIG_ARCH_SUN8IW12 */return 0;
}static int hdmi_clk_config(u32 vic)
{int index = 0;index = hdmi_core_get_video_info(vic);if (hdmi_clk)clk_set_rate(hdmi_clk, video_timing[index].pixel_clk);return 0;
}
#else
static int hdmi_clk_enable(void){}
static int hdmi_clk_disable(void){}
static int hdmi_clk_config(u32 vic) {}
#endif/* hdmi_clk_enable_prepare - prepare for hdmi enable* if there is some other clk will affect hdmi module,* should enable these clk before enable hdmi*/
int hdmi_clk_enable_prepare(void)
{int ret = 0;pr_warn("%s()L%d\n", __func__, __LINE__);if (hdmi_clk)ret = sunxi_clk_enable_prepare(hdmi_clk);if (ret != 0)return ret;return ret;
}/* hdmi_clk_disable_prepare - prepare for hdmi disable* if there is some other clk will affect hdmi module,* should disable these clk after disable hdmi*/
int hdmi_clk_disable_prepare(void)
{pr_warn("%s()L%d\n", __func__, __LINE__);if (hdmi_clk)sunxi_clk_disable_prepare(hdmi_clk);return 0;
}unsigned int hdmi_clk_get_div(void)
{unsigned long rate = 1, rate_parent = 1;unsigned int div = 4;if (!hdmi_clk || !hdmi_clk_parent) {pr_warn("%s, get clk div fail\n", __func__);goto exit;}if (hdmi_clk)rate = clk_get_rate(hdmi_clk);if (hdmi_clk_parent)rate_parent = clk_get_rate(hdmi_clk_parent);if (rate != 0)div = rate_parent / rate;elsepr_warn("%s, hdmi clk rate is ZERO!\n", __func__);exit:return div;
}#ifdef CONFIG_AW_AXP
static int hdmi_power_enable(char *name)
{struct regulator *regu = NULL;int ret = -1;regu = regulator_get(NULL, name);if (IS_ERR(regu)) {pr_err("%s: some error happen, fail to get regulator %s\n",__func__, name);goto exit;}/* enalbe regulator */ret = regulator_enable(regu);if (ret != 0) {pr_err("%s: some error happen, fail to enable regulator %s!\n",__func__, name);goto exit1;} else {hdmi_inf("suceess to enable regulator %s!\n", name);}exit1:/* put regulater, when module exit */regulator_put(regu);
exit:return ret;
}static int hdmi_power_disable(char *name)
{struct regulator *regu = NULL;int ret = 0;regu = regulator_get(NULL, name);if (IS_ERR(regu)) {hdmi_wrn("%s: some error happen, fail to get regulator %s\n",__func__, name);goto exit;}/* disalbe regulator */ret = regulator_disable(regu);if (ret != 0) {hdmi_wrn("%s: some error happen, fail to disable regulator %s!\n",__func__, name);goto exit1;} else {hdmi_inf("suceess to disable regulator %s!\n", name);}exit1:/* put regulater, when module exit */regulator_put(regu);
exit:return ret;
}
#else
static int hdmi_power_enable(char *name) {return 0; }
static int hdmi_power_disable(char *name) {return 0; }
#endifstatic s32 hdmi_enable(void)
{hdmi_inf("[hdmi_enable]\n");mutex_lock(&mlock);if (ghdmi.bopen != 1) {hdmi_clk_config(ghdmi.mode);hdmi_core_set_video_enable(1);ghdmi.bopen = 1;}mutex_unlock(&mlock);return 0;
}static s32 hdmi_disable(void)
{hdmi_inf("[hdmi_disable]\n");mutex_lock(&mlock);if (ghdmi.bopen != 0) {hdmi_core_set_video_enable(0);ghdmi.bopen = 0;}mutex_unlock(&mlock);return 0;
}static struct disp_hdmi_mode hdmi_mode_tbl[] = {{DISP_TV_MOD_480I,                HDMI1440_480I,     },{DISP_TV_MOD_576I,                HDMI1440_576I,     },{DISP_TV_MOD_480P,                HDMI480P,          },{DISP_TV_MOD_576P,                HDMI576P,          },{DISP_TV_MOD_720P_50HZ,           HDMI720P_50,       },{DISP_TV_MOD_720P_60HZ,           HDMI720P_60,       },{DISP_TV_MOD_1080I_50HZ,          HDMI1080I_50,      },{DISP_TV_MOD_1080I_60HZ,          HDMI1080I_60,      },{DISP_TV_MOD_1080P_24HZ,          HDMI1080P_24,      },{DISP_TV_MOD_1080P_50HZ,          HDMI1080P_50,      },{DISP_TV_MOD_1080P_60HZ,          HDMI1080P_60,      },{DISP_TV_MOD_1080P_25HZ,          HDMI1080P_25,      },{DISP_TV_MOD_1080P_30HZ,          HDMI1080P_30,      },{DISP_TV_MOD_1080P_24HZ_3D_FP,    HDMI1080P_24_3D_FP,},{DISP_TV_MOD_720P_50HZ_3D_FP,     HDMI720P_50_3D_FP, },{DISP_TV_MOD_720P_60HZ_3D_FP,     HDMI720P_60_3D_FP, },{DISP_TV_MOD_3840_2160P_30HZ,     HDMI3840_2160P_30, },{DISP_TV_MOD_3840_2160P_25HZ,     HDMI3840_2160P_25, },{DISP_TV_MOD_3840_2160P_24HZ,     HDMI3840_2160P_24, },{DISP_TV_MOD_4096_2160P_24HZ,     HDMI4096_2160P_24, },
};static u32 hdmi_get_vic(u32 mode)
{u32 hdmi_mode = DISP_TV_MOD_720P_50HZ;u32 i;bool find = false;for (i = 0; i < sizeof(hdmi_mode_tbl)/sizeof(struct disp_hdmi_mode);i++) {if (hdmi_mode_tbl[i].mode == mode) {hdmi_mode = hdmi_mode_tbl[i].hdmi_mode;find = true;break;}}if (false == find)pr_warn("[HDMI]can't find vic for mode(%d)\n", mode);return hdmi_mode;
}static s32 hdmi_set_display_mode(u32 mode)
{u32 hdmi_mode;u32 i;bool find = false;hdmi_inf("[hdmi_set_display_mode],mode:%d\n", mode);for (i = 0; i < sizeof(hdmi_mode_tbl)/sizeof(struct disp_hdmi_mode);i++) {if (hdmi_mode_tbl[i].mode == (enum disp_tv_mode)mode) {hdmi_mode = hdmi_mode_tbl[i].hdmi_mode;find = true;break;}}if (find) {ghdmi.mode = hdmi_mode;return hdmi_core_set_video_mode(hdmi_mode);}hdmi_wrn("unsupported video mode %d when set display mode\n", mode);return -1;}#if defined(CONFIG_SND_SUNXI_SOC_SUNXI_HDMIAUDIO)
static s32 hdmi_audio_enable(u8 mode, u8 channel)
{hdmi_inf("[hdmi_audio_enable],mode:%d,ch:%d\n", mode, channel);mutex_lock(&mlock);audio_enable = mode;mutex_unlock(&mlock);return hdmi_core_set_audio_enable(audio_enable);
}static s32 hdmi_set_audio_para(hdmi_audio_t *audio_para)
{hdmi_inf("[hdmi_set_audio_para]\n");return hdmi_core_audio_config(audio_para);
}
#endifstatic s32 hdmi_mode_support(u32 mode)
{u32 hdmi_mode;u32 i;bool find = false;for (i = 0; i < sizeof(hdmi_mode_tbl)/sizeof(struct disp_hdmi_mode);i++) {if (hdmi_mode_tbl[i].mode == (enum disp_tv_mode)mode) {hdmi_mode = hdmi_mode_tbl[i].hdmi_mode;find = true;break;}}if (find)return hdmi_core_mode_support(hdmi_mode);elsereturn 0;
}static s32 hdmi_get_HPD_status(void)
{return hdmi_core_hpd_check();
}static s32 hdmi_get_hdcp_enable(void)
{return hdmi_core_get_hdcp_enable();
}static s32 hdmi_get_video_timming_info(struct disp_video_timings **video_info)
{struct disp_video_timings *info;int ret = -1;int i, list_num;info = video_timing;list_num = hdmi_core_get_list_num();for (i = 0; i < list_num; i++) {if (info->vic == ghdmi.mode) {*video_info = info;ret = 0;break;}info++;}return ret;
}static s32 hdmi_get_input_csc(void)
{return hdmi_core_get_csc_type();
}static int hdmi_run_thread(void *parg)
{while (1) {if (kthread_should_stop())break;mutex_lock(&mlock);if (false == b_hdmi_suspend) {/* normal state */b_hdmi_suspend_pre = b_hdmi_suspend;mutex_unlock(&mlock);hdmi_core_loop();if (false == b_hdmi_suspend) {if (hdmi_get_hdcp_enable() == 1)hdmi_delay_ms(100);elsehdmi_delay_ms(200);}} else {/* suspend state */if (false == b_hdmi_suspend_pre) {/* first time after enter suspend state *//* hdmi_core_enter_lp(); */}b_hdmi_suspend_pre = b_hdmi_suspend;mutex_unlock(&mlock);}}return 0;
}void cec_msg_sent(char *buf)
{char *envp[2];envp[0] = buf;envp[1] = NULL;kobject_uevent_env(&ghdmi.dev->kobj, KOBJ_CHANGE, envp);
}#define CEC_BUF_SIZE 32
static int cec_thread(void *parg)
{int ret = 0;char buf[CEC_BUF_SIZE];unsigned char msg;while (1) {if (kthread_should_stop())break;mutex_lock(&mlock);ret = -1;if (false == b_hdmi_suspend)ret = hdmi_core_cec_get_simple_msg(&msg);mutex_unlock(&mlock);if (ret == 0) {memset(buf, 0, CEC_BUF_SIZE);snprintf(buf, sizeof(buf), "CEC_MSG=0x%x", msg);cec_msg_sent(buf);}hdmi_delay_ms(10);}return 0;
}#if defined(CONFIG_EXTCON)
static const unsigned int hdmi_cable[] = {EXTCON_DISP_HDMI,EXTCON_NONE,
};
static struct extcon_dev extcon_hdmi = {.name = "hdmi",
};/* s32 disp_set_hdmi_detect(bool hpd); */
static void hdmi_report_hpd_work(struct work_struct *work)
{if (hdmi_get_HPD_status()) {extcon_set_state(&extcon_hdmi, STATUE_OPEN);disp_set_hdmi_detect(1);hdmi_inf("switch_set_state 1\n");} else {extcon_set_state(&extcon_hdmi, STATUE_CLOSE);disp_set_hdmi_detect(0);hdmi_inf("switch_set_state 0\n");}
}s32 hdmi_hpd_state(u32 state)
{if (state == 0)extcon_set_state(&extcon_hdmi, STATUE_CLOSE);elseextcon_set_state(&extcon_hdmi, STATUE_OPEN);return 0;
}
#else
static void hdmi_report_hpd_work(struct work_struct *work)
{
}s32 hdmi_hpd_state(u32 state)
{return 0;
}
#endif
/*** hdmi_hpd_report - report hdmi hot plug state to user space* @hotplug: 0: hdmi plug out;   1:hdmi plug in** always return success.*/
s32 hdmi_hpd_event(void)
{schedule_work(&ghdmi.hpd_work);return 0;
}static s32 hdmi_suspend(void)
{hdmi_core_update_detect_time(0);if (hdmi_used && (false == b_hdmi_suspend)) {if (HDMI_task) {kthread_stop(HDMI_task);HDMI_task = NULL;}if (hdmi_cec_support && cec_task) {kthread_stop(cec_task);cec_task = NULL;}mutex_lock(&mlock);b_hdmi_suspend = true;hdmi_core_enter_lp();if (clk_enable_count != 0) {hdmi_clk_disable();clk_enable_count--;}if (io_enable_count != 0) {hdmi_io_config(0);io_enable_count--;}if ((hdmi_power_used) && (power_enable_count != 0)) {hdmi_power_disable(hdmi_power);power_enable_count--;}if (hdmi_io_regulator_used && io_regulator_enable_count) {hdmi_power_disable(hdmi_io_regulator);--io_regulator_enable_count;}mutex_unlock(&mlock);pr_info("[HDMI]hdmi suspend\n");}return 0;
}static s32 hdmi_resume(void)
{int ret;mutex_lock(&mlock);if (hdmi_used && (true == b_hdmi_suspend)) {/* normal state */if (clk_enable_count == 0) {ret = hdmi_clk_enable();if (ret == 0)clk_enable_count++;else {pr_warn("fail to enable hdmi's clock\n");goto exit;}}if (hdmi_io_regulator_used && !io_regulator_enable_count) {hdmi_power_enable(hdmi_io_regulator);++io_regulator_enable_count;}if ((hdmi_power_used) && (power_enable_count == 0)) {hdmi_power_enable(hdmi_power);power_enable_count++;}if (io_enable_count == 0) {hdmi_io_config(1);io_enable_count++;}/* first time after exit suspend state */hdmi_core_exit_lp();HDMI_task = kthread_create(hdmi_run_thread, (void *)0, "hdmi proc");if (IS_ERR(HDMI_task)) {s32 err = 0;pr_warn("Unable to start kernel thread %s.\n\n", "hdmi proc");err = PTR_ERR(HDMI_task);HDMI_task = NULL;} elsewake_up_process(HDMI_task);if (hdmi_cec_support) {cec_task = kthread_create(cec_thread, (void *)0, "cec proc");if (IS_ERR(cec_task)) {s32 err = 0;pr_warn("Unable to start kernel thread %s.\n\n", "cec proc");err = PTR_ERR(cec_task);cec_task = NULL;} elsewake_up_process(cec_task);}pr_info("[HDMI]hdmi resume\n");}exit:mutex_unlock(&mlock);hdmi_core_update_detect_time(200);/* 200ms */b_hdmi_suspend = false;return  0;
}#if defined(CONFIG_SND_SUNXI_SOC_SUNXI_HDMIAUDIO)
extern void audio_set_hdmi_func(__audio_hdmi_func *hdmi_func);
#endif
/* extern s32 disp_set_hdmi_func(struct disp_device_func *func); */
/* extern unsigned int disp_boot_para_parse(const char *name); */s32 hdmi_init(struct platform_device *pdev)
{
#if defined(CONFIG_SND_SUNXI_SOC_SUNXI_HDMIAUDIO)__audio_hdmi_func audio_func;
#if defined(CONFIG_SND_SUNXI_SOC_AUDIOHUB_INTERFACE)__audio_hdmi_func audio_func_muti;
#endif
#endifunsigned int value, output_type0, output_mode0;unsigned int output_type1, output_mode1;struct disp_device_func disp_func;int ret = 0;uintptr_t reg_base;hdmi_used = 0;b_hdmi_suspend_pre = b_hdmi_suspend = false;hdmi_power_used = 0;hdmi_used = 1;/*  parse boot para */value = disp_boot_para_parse("boot_disp");output_type0 = (value >> 8) & 0xff;output_mode0 = (value) & 0xff;output_type1 = (value >> 24) & 0xff;output_mode1 = (value >> 16) & 0xff;if ((output_type0 == DISP_OUTPUT_TYPE_HDMI) ||(output_type1 == DISP_OUTPUT_TYPE_HDMI)) {boot_hdmi = true;ghdmi.bopen = 1;ghdmi.mode = (output_type0 == DISP_OUTPUT_TYPE_HDMI) ? output_mode0 : output_mode1;ghdmi.mode = hdmi_get_vic(ghdmi.mode);}/* iomap */reg_base = (uintptr_t __force)of_iomap(pdev->dev.of_node, 0);if (reg_base == 0) {dev_err(&pdev->dev, "unable to map hdmi registers\n");ret = -EINVAL;goto err_iomap;}hdmi_core_set_base_addr(reg_base);/* get clk */hdmi_clk = of_clk_get(pdev->dev.of_node, 0);if (IS_ERR(hdmi_clk)) {dev_err(&pdev->dev, "fail to get clk for hdmi\n");goto err_clk_get;}hdmi_clk_parent = clk_get_parent(hdmi_clk);if (IS_ERR(hdmi_clk_parent)) {dev_err(&pdev->dev, "fail to get clk parent for hdmi\n");goto err_power;}clk_enable_count = __clk_get_enable_count(hdmi_clk);hdmi_ddc_clk = of_clk_get(pdev->dev.of_node, 1);if (IS_ERR(hdmi_ddc_clk)) {dev_err(&pdev->dev, "fail to get clk for hdmi ddc\n");goto err_power;}#if defined(CONFIG_ARCH_SUN8IW12)hdmi_cec_clk = of_clk_get(pdev->dev.of_node, 2);if (IS_ERR_OR_NULL(hdmi_cec_clk)) {dev_err(&pdev->dev, "fail to get hdmi_cec_clk\n");goto err_power;}
#endif /*endif CONFIG_ARCH_SUN8IW12 *//* parse io config */hdmi_parse_io_config();mutex_init(&mlock);if (io_enable_count == 0) {hdmi_io_config(1);io_enable_count++;}mutex_lock(&mlock);if (clk_enable_count == 0) {hdmi_wrn("hdmi_clk_enable\n");ret = hdmi_clk_enable();clk_enable_count++;}mutex_unlock(&mlock);if (ret != 0) {clk_enable_count--;dev_err(&pdev->dev, "fail to enable hdmi clk\n");goto err_clk_enable;}INIT_WORK(&ghdmi.hpd_work, hdmi_report_hpd_work);#if defined(CONFIG_EXTCON)extcon_hdmi.supported_cable = hdmi_cable;extcon_dev_register_attr(&extcon_hdmi, &pdev->dev);
#endifret = disp_sys_script_get_item("hdmi", "hdmi_io_regulator",(int *)hdmi_io_regulator, 2);if (ret == 2) {mutex_lock(&mlock);ret = hdmi_power_enable(hdmi_io_regulator);mutex_unlock(&mlock);if (ret) {dev_err(&pdev->dev, "fail to enable hdmi io power %s\n", hdmi_io_regulator);} else {++io_regulator_enable_count;hdmi_io_regulator_used = 1;}}ret = disp_sys_script_get_item("hdmi", "hdmi_power", (int *)hdmi_power, 2);if (ret == 2) {hdmi_power_used = 1;if (hdmi_power_used) {pr_info("[HDMI] power %s\n", hdmi_power);mutex_lock(&mlock);ret = hdmi_power_enable(hdmi_power);power_enable_count++;mutex_unlock(&mlock);if (ret != 0) {power_enable_count--;dev_err(&pdev->dev,"fail to enable hdmi power %s\n", hdmi_power);goto err_power;}}}ret = disp_sys_script_get_item("hdmi", "hdmi_cts_compatibility", &value, 1);if (ret == 1)hdmi_core_set_cts_enable(value);ret = disp_sys_script_get_item("hdmi", "hdmi_hdcp_enable", &value, 1);if (ret == 1)hdmi_core_set_hdcp_enable(value);ret = disp_sys_script_get_item("hdmi", "hdmi_hpd_mask", &value, 1);if (ret == 1)hdmi_hpd_mask = value;ret = disp_sys_script_get_item("hdmi", "hdmi_cec_support", &value, 1);if ((ret == 1) && (value == 1))hdmi_cec_support = true;hdmi_core_cec_enable(hdmi_cec_support);pr_info("[HDMI] cec support = %d\n", hdmi_cec_support);hdmi_core_initial(boot_hdmi);run_sem = kmalloc(sizeof(struct semaphore), GFP_KERNEL | __GFP_ZERO);if (!run_sem) {dev_err(&pdev->dev, "fail to kmalloc memory for run_sem\n");goto err_sem;}sema_init((struct semaphore *)run_sem, 0);HDMI_task = kthread_create(hdmi_run_thread, (void *)0, "hdmi proc");if (IS_ERR(HDMI_task)) {s32 err = 0;dev_err(&pdev->dev, "Unable to start kernel thread %s.\n\n","hdmi proc");err = PTR_ERR(HDMI_task);HDMI_task = NULL;goto err_thread;}wake_up_process(HDMI_task);if (hdmi_cec_support) {cec_task = kthread_create(cec_thread, (void *)0, "cec proc");if (IS_ERR(cec_task)) {s32 err = 0;dev_err(&pdev->dev,"Unable to start kernel thread %s.\n\n","cec proc");err = PTR_ERR(cec_task);cec_task = NULL;goto err_thread;}wake_up_process(cec_task);}#if defined(CONFIG_SND_SUNXI_SOC_SUNXI_HDMIAUDIO)audio_func.hdmi_audio_enable = hdmi_audio_enable;audio_func.hdmi_set_audio_para = hdmi_set_audio_para;audio_set_hdmi_func(&audio_func);
#if defined(CONFIG_SND_SUNXI_SOC_AUDIOHUB_INTERFACE)audio_func_muti.hdmi_audio_enable = hdmi_audio_enable;audio_func_muti.hdmi_set_audio_para = hdmi_set_audio_para;audio_set_muti_hdmi_func(&audio_func_muti);
#endif
#endifmemset(&disp_func, 0, sizeof(struct disp_device_func));disp_func.enable = hdmi_enable;disp_func.disable = hdmi_disable;disp_func.set_mode = hdmi_set_display_mode;disp_func.mode_support = hdmi_mode_support;disp_func.get_HPD_status = hdmi_get_HPD_status;disp_func.get_input_csc = hdmi_get_input_csc;disp_func.get_video_timing_info = hdmi_get_video_timming_info;disp_func.suspend = hdmi_suspend;disp_func.resume = hdmi_resume;disp_set_hdmi_func(&disp_func);return 0;err_thread:kfree(run_sem);
err_sem:hdmi_power_disable(hdmi_power);
err_power:hdmi_clk_disable();
err_clk_enable:
err_clk_get:iounmap((char __iomem *)reg_base);
err_iomap:return -1;
}s32 hdmi_exit(void)
{if (hdmi_used) {hdmi_core_exit();kfree(run_sem);run_sem = NULL;if (HDMI_task) {kthread_stop(HDMI_task);HDMI_task = NULL;}if (hdmi_cec_support && cec_task) {kthread_stop(cec_task);cec_task = NULL;}if ((hdmi_power_used == 1) && (power_enable_count != 0))hdmi_power_disable(hdmi_power);if (hdmi_io_regulator_used && io_regulator_enable_count)hdmi_power_disable(hdmi_io_regulator);if (clk_enable_count != 0) {hdmi_clk_disable();clk_enable_count--;}}return 0;
}#ifndef CONFIG_OF
static struct resource hdmi_resource[1] = {[0] = {.start = 0x01c16000,.end   = 0x01c165ff,.flags = IORESOURCE_MEM,},
};static struct platform_device hdmi_device = {.name          = "hdmi",.id              = -1,.num_resources  = ARRAY_SIZE(hdmi_resource),.resource        = hdmi_resource,.dev           = {}
};
#else
static const struct of_device_id sunxi_hdmi_match[] = {{ .compatible = "allwinner,sunxi-hdmi", },{},
};
#endifstatic ssize_t
hdmi_debug_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "debug=%s\n", hdmi_print ? "on" : "off");
}static ssize_t
hdmi_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{if (count < 1)return -EINVAL;if (strncasecmp(buf, "on", 2) == 0 || strncasecmp(buf, "1", 1) == 0)hdmi_print = 1;else if (strncasecmp(buf, "off", 3) == 0 ||strncasecmp(buf, "0", 1) == 0)hdmi_print = 0;elsereturn -EINVAL;return count;
}#if defined(HDMI_ENABLE_DUMP_WRITE)static ssize_t
hdmi_dump_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{static long long val;long long reg, num, i = 0;unsigned char value_r[128];s32 ret = -1;ret = kstrtoll(buf, 0, &val);reg = (val >> 8);num = val & 0xff;pr_alert("\n");pr_alert("read:start add:0x%llx,count:0x%llx\n", reg, num);do {bsp_hdmi_read(reg, &value_r[i]);pr_alert("0x%llx: 0x%04x ", reg, value_r[i]);reg += 1;i++;if (i == num)pr_alert("\n");if (i % 4 == 0)pr_alert("\n");} while (i < num);return count;
}static ssize_t
hdmi_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{static long long val;long long reg;long long value_w;s32 ret = -1;ret = kstrtoll(buf, 0, &val);if (ret != 0) {pr_alert("convert string fail!\n");return 0;}reg = (val >> 16);value_w =  val & 0xFFFF;bsp_hdmi_write(reg, value_w);pr_alert("write 0x%llx to reg:0x%llx\n", value_w, reg);return count;
}static ssize_t
hdmi_dump_show(struct device *dev, struct device_attribute *attr, char *buf)
{pr_alert("echo reg|count > dump\n");pr_alert("eg read star address=0x0006,count 0x10:echo 0x610 > dump\n");pr_alert("eg read star address=0x2000,count 0x10:echo 0x200010 > dump\n");return 0;
}static ssize_t
hdmi_write_show(struct device *dev, struct device_attribute *attr, char *buf)
{pr_alert("echo reg|val > write\n");pr_alert("eg write value:0x13fe to address:0x0004 :echo 0x413fe > write\n");pr_alert("eg write value:0x6 to address:0x2000 :echo 0x20000006 > write\n");return 0;
}#endif /*endif HDMI_ENABLE_DUMP_WRITE */
static DEVICE_ATTR(debug, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_debug_show, hdmi_debug_store);#if defined(HDMI_ENABLE_DUMP_WRITE)
static DEVICE_ATTR(dump, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_dump_show, hdmi_dump_store);static DEVICE_ATTR(write, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_write_show, hdmi_write_store);
#endif/* s32 hdmi_hpd_state(u32 state); */
static ssize_t
hdmi_state_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "nothing\n");
}static ssize_t
hdmi_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{if (count < 1)return -EINVAL;if (strncasecmp(buf, "1", 1) == 0)hdmi_hpd_state(1);elsehdmi_hpd_state(0);return count;
}static DEVICE_ATTR(state, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_state_show, hdmi_state_store);static ssize_t
hdmi_rgb_only_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "rgb_only=%s\n", rgb_only ? "on" : "off");
}static ssize_t
hdmi_rgb_only_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{if (count < 1)return -EINVAL;if (strncasecmp(buf, "on", 2) == 0 || strncasecmp(buf, "1", 1) == 0)rgb_only = 1;else if (strncasecmp(buf, "off", 3) == 0 ||strncasecmp(buf, "0", 1) == 0)rgb_only = 0;elsereturn -EINVAL;return count;
}static DEVICE_ATTR(rgb_only, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_rgb_only_show, hdmi_rgb_only_store);static ssize_t
hdmi_hpd_mask_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "0x%x\n", hdmi_hpd_mask);
}static ssize_t
hdmi_hpd_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{int err;unsigned long val;if (count < 1)return -EINVAL;err = kstrtoul(buf, 16, &val);if (err) {pr_err("Invalid size\n");return err;}pr_info("val=0x%x\n", (u32)val);hdmi_hpd_mask = val;return count;
}static DEVICE_ATTR(hpd_mask, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_hpd_mask_show, hdmi_hpd_mask_store);static ssize_t
hdmi_edid_show(struct device *dev, struct device_attribute *attr, char *buf)
{void *pedid = (void *)hdmi_edid_get_data();memcpy(buf, pedid, HDMI_EDID_LEN);return HDMI_EDID_LEN;
}static ssize_t
hdmi_edid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{return count;
}static DEVICE_ATTR(edid, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_edid_show, hdmi_edid_store);static ssize_t
hdmi_hdcp_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "%d\n", hdmi_core_get_hdcp_enable());
}static ssize_t
hdmi_hdcp_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{if (count < 1)return -EINVAL;if (strncasecmp(buf, "1", 1) == 0) {if (hdmi_core_get_hdcp_enable() != 1)hdmi_core_set_hdcp_enable(1);} else {if (hdmi_core_get_hdcp_enable() != 0)hdmi_core_set_hdcp_enable(0);}return count;
}// /sys/.../hdcp_enable
static DEVICE_ATTR(hdcp_enable, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_hdcp_enable_show, hdmi_hdcp_enable_store);static int hdmi_probe(struct platform_device *pdev)
{hdmi_inf("hdmi_probe call\n");memset(&ghdmi, 0, sizeof(hdmi_info_t));ghdmi.dev = &pdev->dev;hdmi_init(pdev);return 0;
}static int hdmi_remove(struct platform_device *pdev)
{hdmi_inf("hdmi_remove call\n");hdmi_exit();return 0;
}static struct platform_driver hdmi_driver = {.probe  = hdmi_probe,.remove = hdmi_remove,.driver = {.name   = "hdmi",.owner  = THIS_MODULE,.of_match_table = sunxi_hdmi_match,},
};static int hdmi_open(struct inode *inode, struct file *file)
{return 0;
}static int hdmi_release(struct inode *inode, struct file *file)
{return 0;
}static ssize_t hdmi_read(struct file *file, char __user *buf,size_t count, loff_t *ppos)
{return -EINVAL;
}static ssize_t hdmi_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{return -EINVAL;
}static int hdmi_mmap(struct file *file, struct vm_area_struct *vma)
{return 0;
}static long hdmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{return 0;
}static const struct file_operations hdmi_fops = {.owner       = THIS_MODULE,.open        = hdmi_open,.release   = hdmi_release,.write      = hdmi_write,.read     = hdmi_read,.unlocked_ioctl    = hdmi_ioctl,.mmap     = hdmi_mmap,
};static struct attribute *hdmi_attributes[] = {&dev_attr_debug.attr,&dev_attr_state.attr,&dev_attr_rgb_only.attr,&dev_attr_hpd_mask.attr,&dev_attr_edid.attr,&dev_attr_hdcp_enable.attr,
#if defined(HDMI_ENABLE_DUMP_WRITE)&dev_attr_dump.attr,&dev_attr_write.attr,
#endifNULL
};static struct attribute_group hdmi_attribute_group = {.name = "attr",.attrs = hdmi_attributes
};static int __init hdmi_module_init(void)
{int ret = 0, err;alloc_chrdev_region(&devid, 0, 1, "hdmi");my_cdev = cdev_alloc();cdev_init(my_cdev, &hdmi_fops);my_cdev->owner = THIS_MODULE;err = cdev_add(my_cdev, devid, 1);if (err) {hdmi_wrn("cdev_add fail.\n");return -1;}hdmi_class = class_create(THIS_MODULE, "hdmi");if (IS_ERR(hdmi_class)) {hdmi_wrn("class_create fail\n");return -1;}ghdmi.dev = device_create(hdmi_class, NULL, devid, NULL, "hdmi");ret = sysfs_create_group(&ghdmi.dev->kobj, &hdmi_attribute_group);#ifndef CONFIG_OFret = platform_device_register(&hdmi_device);
#endifif (ret == 0)ret = platform_driver_register(&hdmi_driver);hdmi_inf("hdmi_module_init\n");return ret;
}static void __exit hdmi_module_exit(void)
{hdmi_inf("hdmi_module_exit\n");platform_driver_unregister(&hdmi_driver);
#ifndef CONFIG_OFplatform_device_unregister(&hdmi_device);
#endifclass_destroy(hdmi_class);cdev_del(my_cdev);
}late_initcall(hdmi_module_init);
module_exit(hdmi_module_exit);MODULE_AUTHOR("tyle");
MODULE_DESCRIPTION("hdmi driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:hdmi");

这次的驱动增加了sys fs节点的一些操作特性,这里还是简单说一下,sys fs是用户空间可以操作的,这下面的节点我们可以进行echo或者cat等操作,这里代码只要是如下:

(1):
ret = sysfs_create_group(&ghdmi.dev->kobj, &hdmi_attribute_group);(2):
static struct attribute_group hdmi_attribute_group = {.name = "attr",.attrs = hdmi_attributes
};(3):
static struct attribute *hdmi_attributes[] = {&dev_attr_debug.attr,&dev_attr_state.attr,&dev_attr_rgb_only.attr,&dev_attr_hpd_mask.attr,&dev_attr_edid.attr,&dev_attr_hdcp_enable.attr,
#if defined(HDMI_ENABLE_DUMP_WRITE)&dev_attr_dump.attr,&dev_attr_write.attr,
#endifNULL
};(4):
static ssize_t
hdmi_hdcp_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{return sprintf(buf, "%d\n", hdmi_core_get_hdcp_enable());
}static ssize_t
hdmi_hdcp_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{if (count < 1)return -EINVAL;if (strncasecmp(buf, "1", 1) == 0) {if (hdmi_core_get_hdcp_enable() != 1)hdmi_core_set_hdcp_enable(1);} else {if (hdmi_core_get_hdcp_enable() != 0)hdmi_core_set_hdcp_enable(0);}return count;
}// /sys/.../hdcp_enable
static DEVICE_ATTR(hdcp_enable, S_IRUGO|S_IWUSR|S_IWGRP, hdmi_hdcp_enable_show, hdmi_hdcp_enable_store);...

关于这部分,可以参考:https://blog.csdn.net/njuitjf/article/details/16849333

在一些时候,这些用户空间驱动程序节点是很有用处的,在实际的上层软件调用驱动时,这种方式也是有的,比如我们公司的ENGPC工程,就调用/sys/下面的节点大量使用.

就先说这么多吧,后续,如果有机会会进行一定补充.

㉔AW-H3 Linux驱动开发之HDMI驱动程序相关推荐

  1. Linux驱动开发之platform设备驱动实验【完整教程】

    为了方便驱动的编写,提高软件的重用性和跨平台性能,于是就提出了Linux驱动的分离和分层   驱动的分层,分层的目的时为了在不同的层处理不同的内容,最简单的驱动分层是input子系统负责管理所有跟输入 ...

  2. ㉓AW-H3 Linux驱动开发之mipi camera(CSI)驱动程序

    本次说一下mipi camera的驱动开发,平台用的是全志的H3芯片,项目代号:sun8iw7p1,这次使用运行在H3上面的Ubuntu进行验证的. Linux代码:https://github.co ...

  3. ⑭tiny4412 Linux驱动开发之cpufreq子系统驱动程序

    本次我们来说一下CPU动态调频子系统. 首先来看一下三星Exynos 4412的datasheet,如下: 上图就是Exynos 4412的时钟分布图,可以看到CPU的频率可以在1.4GHz~200M ...

  4. ㉕AW-A33 Linux驱动开发之audio子系统驱动程序

    在Linux源码里,Aduio这一部分现在是一个独立文件夹叫sound,在2.x的版本时,sound这个目录是在drivers里的,后来从这个里面剥离出来了,很多人不知道其中的原因,我也不知道,我们先 ...

  5. linux 串口驱动 4412,⑮tiny4412 Linux驱动开发之tty子系统(UART)驱动程序

    本次说一下tty子系统的驱动编程,因为UART相关的寄存器比较多,同时,应用比较广泛,所以本次的驱动程序量也不少,而且只是完成和特定CPU相关的一部分,通用的部分本次都没有涉及到.在写驱动之前,我们先 ...

  6. ⑨tiny4412 Linux驱动开发之1-wire子系统(DS18B20)驱动程序

    本来这次想做LCD背光灯的调节的,但是没有调通,时间很紧迫,就转向了其它东西,昨天调了一下DHT11,今天又调了一下DS18B20,还算有个安慰,本来是想用1-wire子系统做的,但是时间上有点紧,要 ...

  7. Linux驱动开发之DRM驱动

    作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 Linux DRM Gr ...

  8. linux驱动开发之spi-omap-100k.c源码分析

    代码分析 对于linux的驱动代码来说,我们要从后往前分析: /** OMAP7xx SPI 100k controller driver* Author: Fabrice Crohas <fc ...

  9. Linux驱动开发之IIC驱动实验【完整教程】

    本实验基于正点原子ALPHT开发板上的AP3216C作为实验开展对象 基础知识 1.IIC总线驱动   IIC总线驱动是对IIC硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在C ...

最新文章

  1. 矢量对比_「插画原画必学教程」ps入门——03 图层、位图矢量图、调色
  2. 全球100款大数据工具汇总(前50款)
  3. nodemailer 附件_如何使用Nodemailer发送带有附件的电子邮件。 Node.js
  4. Spring Boot——@ConfigurationProperties与@Value的区别
  5. 无法解析 uafxcw.lib_二级建造师《实务科目》推荐知识点习题,附中业网校答案解析...
  6. PostgreSQL备份恢复实现
  7. 中加学校计算机考试题,嘉应学院2009年计算机期末考试试题
  8. oracle分布式事物锁,ORA-02049:超时:分布式事务处理等待锁诊断-Oracle
  9. Ubuntu命令技巧
  10. 运行MINGW时遇到缺少.dll
  11. 谷歌地球(google earth)中文专业版官方下载
  12. 手机电源管理芯片一般在哪
  13. delete不起作用 nsis_Delete键为什么不起作用了?
  14. Python处理图片九宫格,炫酷朋友圈
  15. 在控制面板找不到程序的情况下,卸载流氓软件
  16. 吃鸡路线(map最短路径)
  17. 退休当月要干到月底吗_到退休年龄,是当月办理退休,还是提前一个月办理?...
  18. Unity项目-黑魂复刻(二)玩家控制器(跳跃)
  19. LeetCode Daily challenge - Course Schedule
  20. oracle 注入 nc,oracle注入,utl_http方法

热门文章

  1. cellpadding与cellspacing
  2. 批处理经典入门教程!!!
  3. Apache配置WebSocket代理
  4. 12个球找其中一个不同
  5. labelme生成的json文件批量转化为label图片
  6. linux jconsole命令,在Linux中的JConsole位置
  7. Arduino+红外避障模块
  8. 计算机考试操作步骤,医师资格计算机化考试操作步骤
  9. 用vue做轮播图 关于require的用法
  10. android 高德定位