rk30 hdmi:
authorZheng Yang <zhengyang@rock-chips.com>
Thu, 10 May 2012 03:05:35 +0000 (11:05 +0800)
committerZheng Yang <zhengyang@rock-chips.com>
Thu, 10 May 2012 03:05:35 +0000 (11:05 +0800)
1. Support video pixel data color space convertion.
2. Output video color mode accrording EDID: When sink declares support
YCbCr, hdmi should send YCbCr pixel data. Otherwise, hdmi should send
RGB pixel data.

drivers/video/rockchip/hdmi/rk30_hdmi_hw.c
drivers/video/rockchip/hdmi/rk30_hdmi_hw.h
drivers/video/rockchip/hdmi/rk30_hdmi_task.c

index 0c3bf081272a64c84d12e842b1a3251a4cdeafb6..9c75c3031c415df79ed41b406e0fa935e7a3014e 100755 (executable)
@@ -197,39 +197,158 @@ static void rk30_hdmi_config_phy(unsigned char vic)
 
 static void rk30_hdmi_config_avi(unsigned char vic, unsigned char output_color)
 {
-       int i;
+       int i, clolorimetry, aspect_ratio;
        char info[SIZE_AVI_INFOFRAME];
        
-       HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
-       HDMIWrReg(CONTROL_PACKET_HB0, 0x82);
-       HDMIWrReg(CONTROL_PACKET_HB1, 0x2);
-       HDMIWrReg(CONTROL_PACKET_HB2, 0x0D);
        memset(info, 0, SIZE_AVI_INFOFRAME);
+       HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+       info[0] = 0x82;
+       info[1] = 0x02;
+       info[2] = 0x0D; 
+       info[3] = info[0] + info[1] + info[2];
+
+       if(output_color == VIDEO_OUTPUT_YCBCR444)       
+               info[4] = (AVI_COLOR_MODE_YCBCR444 << 5);
+       else if(output_color == VIDEO_OUTPUT_YCBCR422)
+               info[4] = (AVI_COLOR_MODE_YCBCR422 << 5);
+       else
+               info[4] = (AVI_COLOR_MODE_RGB << 5);
+       info[4] |= (1 << 4);    //Enable active format data bits is present in info[2]
+       
+       switch(vic)
+       {
+               case HDMI_720x480i_60Hz_4_3:
+               case HDMI_720x576i_50Hz_4_3:
+               case HDMI_720x480p_60Hz_4_3:
+               case HDMI_720x576p_50Hz_4_3:                            
+                       aspect_ratio = AVI_CODED_FRAME_ASPECT_4_3;
+                       clolorimetry = AVI_COLORIMETRY_SMPTE_170M;
+                       break;
+               case HDMI_720x480i_60Hz_16_9:
+               case HDMI_720x576i_50Hz_16_9:
+               case HDMI_720x480p_60Hz_16_9:
+               case HDMI_720x576p_50Hz_16_9:
+                       aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9;
+                       clolorimetry = AVI_COLORIMETRY_SMPTE_170M;
+                       break;
+               default:
+                       aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9;
+                       clolorimetry = AVI_COLORIMETRY_ITU709;
+       }
+
+       if(output_color == VIDEO_OUTPUT_RGB444)
+               clolorimetry = AVI_COLORIMETRY_NO_DATA;
        
-       info[1] = (AVI_COLOR_MODE_RGB << 5);
-       info[2] = (AVI_COLORIMETRY_NO_DATA << 6) | (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
-       info[3] = 0;
-       info[4] = vic;
-       info[5] = 0;
+       info[5] = (clolorimetry << 6) | (aspect_ratio << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
+       info[6] = 0;
+       info[7] = vic;
+       info[8] = 0;
 
        // Calculate AVI InfoFrame ChecKsum
-       info[0] = 0x82 + 0x02 +0x0D;
-       for (i = 1; i < SIZE_AVI_INFOFRAME; i++)
+       for (i = 4; i < SIZE_AVI_INFOFRAME; i++)
        {
-       info[0] += info[i];
+       info[3] += info[i];
        }
-       info[0] = 0x100 - info[0];
+       info[3] = 0x100 - info[3];
        
        for(i = 0; i < SIZE_AVI_INFOFRAME; i++)
-               HDMIWrReg(CONTROL_PACKET_PB_ADDR + i*4, info[i]);
+               HDMIWrReg(CONTROL_PACKET_HB0 + i*4, info[i]);
 }
+static char coeff_csc[][24] = {
+               //G                     B                       R                       Bias
+       {       //CSC_RGB_0_255_TO_ITU601_16_235
+               0x11, 0xb6, 0x02, 0x0b, 0x10, 0x55, 0x00, 0x80,         //Cr
+               0x02, 0x59, 0x01, 0x32, 0x00, 0x75, 0x00, 0x10,         //Y
+               0x11, 0x5b, 0x10, 0xb0, 0x02, 0x0b, 0x00, 0x80,         //Cb
+       },
+       {       //CSC_RGB_0_255_TO_ITU709_16_235
+               0x11, 0xdb, 0x02, 0x0b, 0x10, 0x30, 0x00, 0x80,         //Cr
+               0x02, 0xdc, 0x00, 0xda, 0x00, 0x4a, 0x00, 0x10,         //Y
+               0x11, 0x93, 0x10, 0x78, 0x02, 0x0b, 0x00, 0x80,         //Cb
+       },
+               //Y                     Cr                      Cb                      Bias
+       {       //CSC_ITU601_16_235_TO_RGB_16_235
+               0x04, 0x00, 0x05, 0x7c, 0x00, 0x00, 0x02, 0xaf,         //R
+               0x04, 0x00, 0x12, 0xcb, 0x11, 0x58, 0x00, 0x84,         //G
+               0x04, 0x00, 0x00, 0x00, 0x06, 0xee, 0x02, 0xde,         //B
+       },
+       {       //CSC_ITU709_16_235_TO_RGB_16_235
+               0x04, 0x00, 0x06, 0x29, 0x00, 0x00, 0x02, 0xc5,         //R
+               0x04, 0x00, 0x11, 0xd6, 0x10, 0xbb, 0x00, 0x52,         //G
+               0x04, 0x00, 0x00, 0x00, 0x07, 0x44, 0x02, 0xe8,         //B
+       },
+       {       //CSC_ITU601_16_235_TO_RGB_0_255
+               0x04, 0xa8, 0x05, 0x7c, 0x00, 0x00, 0x02, 0xc2,         //R
+               0x04, 0xa8, 0x12, 0xcb, 0x11, 0x58, 0x00, 0x72,         //G
+               0x04, 0xa8, 0x00, 0x00, 0x06, 0xee, 0x02, 0xf0,         //B
+       },
+       {       //CSC_ITU709_16_235_TO_RGB_0_255
+               0x04, 0xa8, 0x06, 0x29, 0x00, 0x00, 0x02, 0xd8,         //R
+               0x04, 0xa8, 0x11, 0xd6, 0x10, 0xbb, 0x00, 0x40,         //G
+               0x04, 0xa8, 0x00, 0x00, 0x07, 0x44, 0x02, 0xfb,         //B
+       },
+       
+};
 
-int rk30_hdmi_config_video(int vic, int output_color, int output_mode)
+static void rk30_hdmi_config_csc(struct rk30_hdmi_video_para *vpara)
+{
+       int i, temp, mode;
+       char *coeff = NULL;
+       
+       if( ((vpara->input_color == VIDEO_INPUT_COLOR_RGB) && (vpara->output_color == VIDEO_OUTPUT_RGB444)) ||
+               ((vpara->input_color == VIDEO_INPUT_COLOR_YCBCR) && (vpara->output_color != VIDEO_OUTPUT_RGB444) ))
+       {
+               HDMIWrReg(CSC_CONFIG1, v_CSC_MODE(CSC_MODE_AUTO) | v_CSC_BRSWAP_DIABLE(1));
+               return;
+       }
+       
+       switch(vpara->vic)
+       {
+               case HDMI_720x480i_60Hz_4_3:
+               case HDMI_720x576i_50Hz_4_3:
+               case HDMI_720x480p_60Hz_4_3:
+               case HDMI_720x576p_50Hz_4_3:
+               case HDMI_720x480i_60Hz_16_9:
+               case HDMI_720x576i_50Hz_16_9:
+               case HDMI_720x480p_60Hz_16_9:
+               case HDMI_720x576p_50Hz_16_9:
+                       if(vpara->input_color == VIDEO_INPUT_COLOR_RGB)
+                               mode = CSC_RGB_0_255_TO_ITU601_16_235;
+                       else if(vpara->output_mode == OUTPUT_HDMI)
+                               mode = CSC_ITU601_16_235_TO_RGB_16_235;
+                       else
+                               mode = CSC_ITU601_16_235_TO_RGB_0_255;
+                       break;
+               default:
+                       if(vpara->input_color == VIDEO_INPUT_COLOR_RGB)
+                               mode = CSC_RGB_0_255_TO_ITU709_16_235;
+                       else if(vpara->output_mode == OUTPUT_HDMI)
+                               mode = CSC_ITU709_16_235_TO_RGB_16_235;
+                       else
+                               mode = CSC_ITU709_16_235_TO_RGB_0_255;
+                       break;
+       }
+       
+       coeff = coeff_csc[mode];
+       
+       HDMIMskReg(temp, CSC_CONFIG1, m_CSC_MODE, v_CSC_MODE(CSC_MODE_MANUAL));
+       
+       for(i = 0; i < 24; i++)
+               HDMIWrReg(CSC_PARA_C0_H + i*4, coeff[i]);
+               
+       HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(1));
+}
+
+//int rk30_hdmi_config_video(int vic, int output_color, int output_mode)
+int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara)
 {
        int value;
        struct fb_videomode *mode;
        
        hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__);
+       if(vpara == NULL)
+               return -1;
        
        if(hdmi->pwr_mode == PWR_SAVE_MODE_E)
                rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D);
@@ -238,18 +357,20 @@ int rk30_hdmi_config_video(int vic, int output_color, int output_mode)
                
        // Input video mode is RGB24bit, Data enable signal from external
        HDMIMskReg(value, AV_CTRL1, m_INPUT_VIDEO_MODE | m_DE_SIGNAL_SELECT, \
-               v_INPUT_VIDEO_MODE(VIDEO_INPUT_RGB_YCBCR_444) | EXTERNAL_DE)    
+               v_INPUT_VIDEO_MODE(vpara->input_mode) | EXTERNAL_DE)    
        HDMIMskReg(value, VIDEO_CTRL1, m_VIDEO_OUTPUT_MODE | m_VIDEO_INPUT_DEPTH | m_VIDEO_INPUT_COLOR_MODE, \
-               v_VIDEO_OUTPUT_MODE(output_color) | v_VIDEO_INPUT_DEPTH(VIDEO_INPUT_DEPTH_8BIT) | VIDEO_INPUT_COLOR_RGB)
-       
+               v_VIDEO_OUTPUT_MODE(vpara->output_color) | v_VIDEO_INPUT_DEPTH(VIDEO_INPUT_DEPTH_8BIT) | vpara->input_color)
+       HDMIWrReg(DEEP_COLOR_MODE, 0x20);
+       // color space convert
+       rk30_hdmi_config_csc(vpara);
        // Set HDMI Mode
-       HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(output_mode));
+       HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(vpara->output_mode));
 
        // Set ext video
-       mode = (struct fb_videomode *)hdmi_vic_to_videomode(vic);
+       mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic);
        if(mode == NULL)
        {
-               hdmi_dbg(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vic);
+               hdmi_dbg(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vpara->vic);
                return -ENOENT;
        }
        value = v_EXT_VIDEO_ENABLE(1) | v_INTERLACE(mode->vmode);
@@ -287,14 +408,15 @@ int rk30_hdmi_config_video(int vic, int output_color, int output_mode)
        value = mode->vsync_len;
        HDMIWrReg(EXT_VIDEO_PARA_VSYNCWIDTH, value & 0xFF);
        
-       if(output_mode == OUTPUT_HDMI) {
-               rk30_hdmi_config_avi(vic, output_mode);
+       if(vpara->output_mode == OUTPUT_HDMI) {
+               rk30_hdmi_config_avi(vpara->vic, vpara->output_color);
                hdmi_dbg(hdmi->dev, "[%s] sucess output HDMI.\n", __FUNCTION__);
        }
        else {
                hdmi_dbg(hdmi->dev, "[%s] sucess output DVI.\n", __FUNCTION__); 
        }
-       rk30_hdmi_config_phy(vic);
+       
+       rk30_hdmi_config_phy(vpara->vic);
        rk30_hdmi_control_output(0);
        return 0;
 }
index df2c258f01066c20bcad46a9de35d5779ff8c78e..968614f9b9b0605960269aafc73dcf24fd2d9a1d 100755 (executable)
@@ -78,6 +78,11 @@ enum {
 #define m_DE_SIGNAL_SELECT                     (1 << 0)
 
 /* HDMI_AV_CTRL2 */
+#define AV_CTRL2       0xec
+#define m_CSC_ENABLE                           (1 << 0)
+#define v_CSC_ENABLE(n)                                (n)
+
+/* HDMI_VIDEO_CTRL1 */
 #define VIDEO_CTRL1    0x58
 enum {
        VIDEO_OUTPUT_RGB444 = 0,
@@ -116,6 +121,56 @@ enum{
 #define TMDS_CLOCK_MODE_MASK   0x3 << 6
 #define TMDS_CLOCK_MODE(n)             (n) << 6
 
+#define CSC_PARA_C0_H  0x60
+#define CSC_PARA_C0_L  0x64
+#define CSC_PARA_C1_H  0x68
+#define CSC_PARA_C1_L  0x6c
+#define CSC_PARA_C2_H  0x70
+#define CSC_PARA_C2_L  0x74
+#define CSC_PARA_C3_H  0x78
+#define CSC_PARA_C3_L  0x7c
+#define CSC_PARA_C4_H  0x80
+#define CSC_PARA_C4_L  0x84
+#define CSC_PARA_C5_H  0x88
+#define CSC_PARA_C5_L  0x8c
+#define CSC_PARA_C6_H  0x90
+#define CSC_PARA_C6_L  0x94
+#define CSC_PARA_C7_H  0x98
+#define CSC_PARA_C7_L  0x9c
+#define CSC_PARA_C8_H  0xa0
+#define CSC_PARA_C8_L  0xa4
+#define CSC_PARA_C9_H  0xa8
+#define CSC_PARA_C10_H 0xac
+#define CSC_PARA_C10_L 0xb4
+#define CSC_PARA_C11_H 0xb8
+#define CSC_PARA_C11_L 0xbc
+
+#define CSC_CONFIG1            0x34c
+#define m_CSC_MODE                     (1 << 7)
+#define m_CSC_COEF_MODE        (0xF << 3)      //Only used in auto csc mode
+#define m_CSC_STATUS           (1 << 2)
+#define m_CSC_VID_SELECT       (1 << 1)
+#define m_CSC_BRSWAP_DIABLE    (1)
+
+enum {
+       CSC_MODE_MANUAL = 0,
+       CSC_MODE_AUTO
+};
+#define v_CSC_MODE(n)                  (n << 7)
+enum {
+       COE_SDTV_LIMITED_RANGE = 0x08,
+       COE_SDTV_FULL_RANGE = 0x04,
+       COE_HDTV_60Hz = 0x2,
+       COE_HDTV_50Hz = 0x1
+};
+#define v_CSC_COE_MODE(n)              (n << 3)
+enum {
+       CSC_INPUT_VID_5_19 = 0,
+       CSC_INPUT_VID_28_29
+};
+#define v_CSC_VID_SELECT(n)            (n << 1)
+#define v_CSC_BRSWAP_DIABLE(n) (n)
+
 /* VIDEO_SETTING2 */
 #define VIDEO_SETTING2 0x114
 #define m_UNMUTE                                       (1 << 7)
@@ -139,7 +194,7 @@ enum {
 #define CONTROL_PACKET_HB1                     0x184
 #define CONTROL_PACKET_HB2                     0x188
 #define CONTROL_PACKET_PB_ADDR         0x18c
-#define SIZE_AVI_INFOFRAME                     0xe             // 14 bytes
+#define SIZE_AVI_INFOFRAME                     0x11    // 17 bytes
 #define SIZE_AUDIO_INFOFRAME           0x0F    // 15 bytes
 enum {
        AVI_COLOR_MODE_RGB = 0,
@@ -288,10 +343,29 @@ enum {
        temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \
        __raw_writel(temp | ( (val) & (msk) ),  hdmi->regbase + addr); 
 
+/* RK30 HDMI Video Configure Parameters */
+struct rk30_hdmi_video_para {
+       int vic;
+       int input_mode;         //input video data interface
+       int input_color;        //input video color mode
+       int output_mode;        //output hdmi or dvi
+       int output_color;       //output video color mode
+};
+
+/* Color Space Convertion Mode */
+enum {
+       CSC_RGB_0_255_TO_ITU601_16_235 = 0,     //RGB 0-255 input to YCbCr 16-235 output according BT601
+       CSC_RGB_0_255_TO_ITU709_16_235,         //RGB 0-255 input to YCbCr 16-235 output accroding BT709
+       CSC_ITU601_16_235_TO_RGB_16_235,        //YCbCr 16-235 input to RGB 16-235 output according BT601
+       CSC_ITU709_16_235_TO_RGB_16_235,        //YCbCr 16-235 input to RGB 16-235 output according BT709
+       CSC_ITU601_16_235_TO_RGB_0_255,         //YCbCr 16-235 input to RGB 0-255 output according BT601
+       CSC_ITU709_16_235_TO_RGB_0_255          //YCbCr 16-235 input to RGB 0-255 output according BT709
+};
+
 extern int rk30_hdmi_detect_hotplug(void);
 extern int rk30_hdmi_read_edid(int block, unsigned char *buff);
 extern int rk30_hdmi_removed(void);
-extern int rk30_hdmi_config_video(int vic, int output_color, int output_mode);
+extern int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara);
 extern int rk30_hdmi_config_audio(struct hdmi_audio *audio);
 extern void rk30_hdmi_control_output(int enable);
 #endif
\ No newline at end of file
index 03a9b63a80f046effd183420ff5762a7e8e65628..b4c9c2c37e7771aff9402208103d49a6e7c3a9d3 100755 (executable)
@@ -162,6 +162,7 @@ void hdmi_work(struct work_struct *work)
 {
        int hotplug, state_last;
        int rc = HDMI_ERROR_SUCESS, trytimes = 0;
+       struct rk30_hdmi_video_para video;
        
        mutex_lock(&work_mutex);
        /* Process hdmi command */
@@ -230,7 +231,17 @@ void hdmi_work(struct work_struct *work)
                                break;
                        case CONFIG_VIDEO:
                                hdmi->display = HDMI_DISABLE;
-                               rc = rk30_hdmi_config_video(hdmi->vic, VIDEO_OUTPUT_RGB444, hdmi->edid.sink_hdmi);                      
+                               video.vic = hdmi->vic;
+                               video.input_mode = VIDEO_INPUT_RGB_YCBCR_444;
+                               video.input_color = VIDEO_INPUT_COLOR_RGB;//VIDEO_INPUT_COLOR_YCBCR
+                               video.output_mode = hdmi->edid.sink_hdmi;
+                               if(hdmi->edid.ycbcr444)
+                                       video.output_color = VIDEO_OUTPUT_YCBCR444;
+                               else if(hdmi->edid.ycbcr422)
+                                       video.output_color = VIDEO_OUTPUT_YCBCR422;
+                               else
+                                       video.output_color = VIDEO_OUTPUT_RGB444;
+                               rc = rk30_hdmi_config_video(&video);
                                if(rc == HDMI_ERROR_SUCESS)
                                {
                                        if(hdmi->edid.sink_hdmi)