This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

HOW TO: Output VGA from DaVinci using THS8200 and VPIF

Other Parts Discussed in Thread: THS8200, CDCE949, CLOCKPRO

 

I don't know if this was the correct way to do it, but I seem to have it fully working now.  There were many issues.

My custom board is derived from the DM6467, and includes the THS8200.  I have it producing VGA 640x480 60Hz output that I can see on a computer monitor.  I figured it out based on my own research, as well as a quick comment from Brijesh Jadav80 on 06-15-2011 8:17AM at http://e2e.ti.com/support/embedded/f/354/p/116736/414361.aspx#414361 .  Otherwise no help from the forum was forthcoming in a timely manner :(

Below are the major areas that I had to modify to get this working.

APPLICATION PROGRAM:

I had wanted to use an RGB mode, but VPIF doesn't support it.  More specifically, neither ColorSpace_RGB565 nor ColorSpace_RGB888 are supported.   I tentatively got working a mode where the THS8200 was expecting 16-bit RGB 4:4:4 (which is RGB565 encoding), but the DaVinci was thinking it was sending YUV422.  To cope with this, my application program faked it by stuffing the RGB565 data into Y and CbCr buffer locations, per the implicit map that the THS8200 uses as seen in Figure 4-3 about 16-bit RGB 4:4:4 format.

HOWEVER, I soon realized the better method was to configure the THS8200 for 20-bit YCbCr 4:2:2, which directly receives the application data in ColorSpace_YUV422PSEMI format.  In this format, my old test code that previously sent NTSC out from an ADV7171 worked all the same in setting up test buffers of video data.

CLOCKING:

Modified git/arch/arm/mach-davinci/cdce949.c to produce a 25.175MHz pixel clock that my circuit delivers from the CDCE949 to the DM6467 VPIF_CLKIN2 signal.  This required a few code changes:

 

static struct cdce_freq_table freq_table[] =  {

TABLE(13500),
TABLE(16875),
TABLE(25175), /*added by HgF*/
TABLE(27000),
TABLE(54000),
TABLE(81000),
TABLE(74250),
TABLE(148000),

};

And corresponding value referenced by TABLE macro.  Note that the values in this table were ignored by the code!  The table entry just needs to exist, regardless of value!!!
static const struct cdce_cfg_val freq_25175 = { /*added by HgF*/
.reg_val = {4, 0x00, 0x40, 0x02, 0x08}, /*these values NOT updated because NEVER used!*/
};
Then, down in cdce_set_rate(), where the cdce949 register values were being hard coded rather than derived from the table, I added more hard coding to match.  Note that my circuit is reduced a little and uses ONLY the Y1 output of the CDCE949.  It doesn't use the other outputs.  If you use them and need them configured, then you may need slightly different register values.  You won't find TI Pro Clock software anywhere!  Instead, find TI ClockPro from http://www.ti.com/product/cdce949#toolssoftware (valid as of writing this post).  I only set the non-reserved values, making sure to set everything that elsewhere in the code might have gotten set to something else by some other configuration.
} else if (rate == 25175) { /*added by HgF*/
// Values from TI Clock Pro 1.0 Software
// Turn on ONLY single Y1 timer output at 25.175Mhz.  All other timers turn off to save power
/*HgF*/printk(KERN_CRIT "cdce949.c configure 25.175MHz BEGIN...\n");
err |= i2c_smbus_write_byte_data(client, 0x01 | 0x80, 0x00);
err |= i2c_smbus_write_byte_data(client, 0x02 | 0x80, 0xB4);
err |= i2c_smbus_write_byte_data(client, 0x03 | 0x80, 0x08);
err |= i2c_smbus_write_byte_data(client, 0x05 | 0x80, 0x50);
err |= i2c_smbus_write_byte_data(client, 0x06 | 0x80, 0x40);
err |= i2c_smbus_write_byte_data(client, 0x13 | 0x80, 0x00);
err |= i2c_smbus_write_byte_data(client, 0x14 | 0x80, 0x6D);
err |= i2c_smbus_write_byte_data(client, 0x15 | 0x80, 0x02);
err |= i2c_smbus_write_byte_data(client, 0x16 | 0x80, 0x00);
err |= i2c_smbus_write_byte_data(client, 0x17 | 0x80, 0x00);
err |= i2c_smbus_write_byte_data(client, 0x18 | 0x80, 0xBC);
err |= i2c_smbus_write_byte_data(client, 0x19 | 0x80, 0xDA);
err |= i2c_smbus_write_byte_data(client, 0x1A | 0x80, 0x9B);
err |= i2c_smbus_write_byte_data(client, 0x1B | 0x80, 0xAB);
err |= i2c_smbus_write_byte_data(client, 0x1C | 0x80, 0x00);
err |= i2c_smbus_write_byte_data(client, 0x1D | 0x80, 0x40);
err |= i2c_smbus_write_byte_data(client, 0x1E | 0x80, 0x02);
err |= i2c_smbus_write_byte_data(client, 0x1F | 0x80, 0x08);
/*HgF*/printk(KERN_CRIT "cdce949.c configure 25.175MHz ...END\n");
}

 

DMAI MODIFICATIONS:

The file dvsdk/dvsdk_3_10_00_11/dmai_2_10_00_05/packages/ti/sdo/dmai/linux/Display_v4l2.c has an array v4l2_std_id_stds[] that maps the video standard numbers used by DMAI to the video standards used by VPIF.  VideoStd_VGA is already defined somewhere in DMAI as being a 4, but the  v4l2_std_id_stds[] array has a zero in that position.  I changed that 0 to match the #define I would soon define in git/include/media/davinci/videohd.h:

 

static v4l2_std_id stds[VideoStd_COUNT] = { 0, 0, 0, 0, V4L2_STD_VGA640x480_60/*added by HgF, was 0*/, V4L2_STD_NTSC,
    V4L2_STD_PAL, V4L2_STD_525P_60, V4L2_STD_625P_50, V4L2_STD_720P_60, 
    V4L2_STD_720P_50, 0, V4L2_STD_1080I_60, V4L2_STD_1080I_50, 
    0, 0, 0, V4L2_STD_1080P_60, V4L2_STD_1080P_50 };

 

VPIF MODIFICATIONS:

I had to modify git/include/media/davinci/videohd.h to define a standard for VGA.  I did it by adding a new line below the TV standards:

#define V4L2_STD_VGA640x480_60       ((v4l2_std_id)(0x0100000000000000ULL)) /*added by HgF*/

Then, in git/drivers/media/video/davinci/vpif_display.c I had to add a new entry to the array struct vpif_channel_config_params ch_params[].  This array is searched for standard ID's such as the new one I added for V4L2_STD_VGA640x480_60.  I think the entries can be in any order.  I added my new one to the bottom (end).  I also broke out some comments:

 

{ // Added by HgF

"VGA 640x480 60Hz", 
640, /*width*/
480, /*height*/
60, /*Hz*/
1, /*progressive*/
0, /* VPIF parameter, 0=y/c not muxed, use 2 channels.  1=y/c muxed, use 1 channel.*/

* These are VPIF parameters.  See VPIF Users Guide Figures 4, 5, 39 and Tables 6-9, etc */
/* Since this array entry for VGA is progressive, Figure VPIF Users Guide Figure 5 is the one to use */
/* Useful VGA timing diagram posted by MBedder at http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=102783 */
/*   Horizontal Timing: 8 pixels front porch, 96 pixels horiz sync, 40 pixels back porch, 8 pixels left border, 640 pixels video 8 pixels right border; 800 pixels total */
/*   Vertical Timing: 2 lines front porch, 2 lines vert sync, 25 lines back porch, 8 lines top border, 480 lines video, 8 lines bottom border; 525 lines total */
/* Regarding eav2sav & sav2sav:  For NTSC, 27MHz data div by 29.97 fps yields 900900 data bits/frame.  Div by 525 lines yields 1716 bits/line... */
/*   Where px = pixel or bit time; eav2sav=268px, sav2eav=1440px, eav=4px, sav=4px; Therefore eav+eav2sav+sav+sav2eav = eav2sav+sav2eav+8 = 1716, which matchines
16 bits/line above */
/* Calculate eav2sav & sav2eav for VGA, in case VPIF uses it.  800px/line, 640 px/line, therefore blanking=160px, less eav+sav leaves eav2sav=152px, also sav2eav=800-8-
v2sav=640, yep*/
152, /* eav2sav. length of eav 2 sav */
640, /* sav2eav. length of sav 2 eav */
/* VPIF doc doesn't describe physical vertical sync signal very well.  Let's assume first field starts at beginning of vertical sync pulse. */
/*   Therefore, active video starts after 2 lines of vert sync, 25 lines back porch, 8 lines top border; which totals 35. Thus active video starts on line 36*/
/*   Then field=frame continues for 480 lines, such that first line of lower blanking is at line 516. */
/*   This is then followed by 8 lines bottom border, 2 lines front porch, which is 10 lines.  So last line is 516+10-1 = 525, yep */
1, /* L1. first line of upper blanking, top field, always 1. */
28, /* L3. first line of active video, top field. */
508, /* L5. first line of lower blanking, top field. */
0, /* L7. first line of upper blanking, bottom field, not used if progressive. */
0, /* L9. first line of active video, bottom field, not used if progressive. */
0, /* L11. first line of lower blanking, bottom field, not used if progressive. */
525, /* vsize. number of lines per frame, equals last line of bottom field (equals last line of top and only field if progressive) */
0, /* capture_format */
0, /* no vbi */
0, /* hd_sd */
V4L2_STD_VGA640x480_60, 
25175000 /* clk_rate ??? can't confirm this with scope */

} // Added by HgF

Note, however, that I thought L3=28 and L5=508 would control the vertical position of the active video relative to the sync.  Those values were tried experimentally.  However, changing them had no impact on the image position on my VGA monitor.  Instead, changes to the THS8200.c driver were necessary.  So, I don't think these values matter.  However, you still need internal consistency.  For example, if L5 is greater than vsize, which I had at one point, streaming gets hung.  I think this is because the first lien of lower blanking (L5), which corresponds to the line after active video, never happens, given that vsize is exhausted first.  So watch out for this one.  Just set up what values you think are correct, even though they might be ignored.  IF YOU KNOW BETTER, PLEASE INFORM ME ON THIS THREAD.  THANKS.
In the same vpif_display.c file, I also had to upgrade the list of handled standards:
#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50 |\
V4L2_STD_1080P_60 | V4L2_STD_720P_60|\
V4L2_STD_720P_50 | V4L2_STD_1080I_60 |\
V4L2_STD_1080I_50 | V4L2_STD_525P_60 |\
V4L2_STD_625P_50 | V4L2_STD_VGA640x480_60/*added by HgF*/)
THS8200 MODIFICATIONS:

 

Finally, over in the git/drivers/media/video/ths8200.c I had to add new array entry to struct ths8200_mode_info mode_info[] in order to support the VGA mode.  I also embedded a lot of comments to help me figure out what in the world was going on:

/* VGA Modes - added by HgF */
{

V4L2_STD_VGA640x480_60, /* "std". Standard added by HgF, for doing VGA at 640x480 pixels, 60Hz frames, VESA compatible */
#if 0
THS8200_INTF_16BIT_RGB, /* "input_intf".  Interface 16-Bit RGB 4:4:4 Data Format (THS8200 datasheet Figure 4-3) */
#else
THS8200_INTF_20BIT_YUV422, /* "input_intf".  Interface 20-bit YCbCr 4:2:2 Data Format (THS8200 datasheet Figure 4-2) */
#endif
THS8200_VESA_MASTER /* "mode".  THS8200 originates timing.  In THS8200_VESA_SLAVE mode, THS8200 uses input sync signals for timing */,
/* See http://www.epanorama.net/faq/vga2rgb/calc.html and bottom of page 38 of http://www.ti.com/lit/an/spraan0/spraan0.pdf */
/* See http://e2e.ti.com/support/data_converters/videoconverters/f/376/t/113249.aspx */
525, /* "frame_size".  SEE BELOW "THS8200 REGISTER VALUES FOR 640x480 VGA" */
525, /* "field_size". SEE BELOW "THS8200 REGISTER VALUES FOR 640x480 VGA" */
800, /* "pixels_per_line". SEE BELOW "THS8200 REGISTER VALUES FOR 640x480 VGA" */
0, /* "hs_in_delay". SEE BELOW "THS8200 REGISTER VALUES FOR 640x480 VGA" */
0x2C, /* "negative_hsync_width" "dtg1_spec_a" ???????????*/
0x2C, /* "positive_hsync_width" "dtg1_spec_c" ???????????*/
0x84, /* "hfront_porch" "dtg1_spec_d" ????????????*/
0x58, /* "hback_porch" "dtg1_spec_b" ????????????*/
0xC0, /* "vfront_porch" "dtg1_spec_e", was 0xC0 but should perhaps be 0x8A*/
0x00, /* "pulse_duration" "dtg1_spec_h" ???????????*/
0x00, /* "full_line_pulse_duration" "dtg1_spec_i" ???????????*/
0x58, /* "vback_porch" "dtg1_spec_k" ??????????????*/
0x00, /* "dtg_spec_k1" ???????????????*/
0 /* "vs_in_delay". SEE BELOW "THS8200 REGISTER VALUES FOR 640x480 VGA" */

}

/* Useful VGA timing diagram posted by MBedder at http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=102783 */
/*   Horizontal Timing: 8 pixels front porch, 96 pixels horiz sync, 40 pixels back porch, 8 pixels left border, 640 pixels video 8 pixels right border; 800 pixels total */
/*   Vertical Timing: 2 lines front porch, 2 lines vert sync, 25 lines back porch, 8 lines top border, 480 lines video, 8 lines bottom border; 525 lines total */

/*
THS8200 REGISTER VALUES FOR 640x480 VGA

From http://e2e.ti.com/support/data_converters/videoconverters/f/376/t/113249.aspx
Compare to datasheet definitions of sub-address and value, including around page 63 of datasheet.

If the FID input pin is pulled or driven low for progressive discrete sync formats, use REG36h=80h when FID polarity is set to low polarity in REG 82h.
(MHB/HHS has FID pin pulled to 3.3V via 10K resistor.  For VGA Master mode, FID pin unused.  I believe REG36h=00h is correct.)

Sub-address; value
0x03; 0xC1
0x19; 0x03
0x1C; 0x70
0x34; 0x03 *combined with reg below makes dtg1_total_pixels=800*
0x35; 0x20
0x36; 0x00 *see FID above*
0x37; 0x01
0x38; 0x87 *dtg1_mode register dtg1_on=0x80, dtg1_pass_through=0x10 (not set in this case), dtg1_mode=0x07 (VESA Slave Mode)*
0x39; 0x22 *combined with two regs below makes dtg1_frame_size=525 and dtg1_field_size=525*
0x3A; 0x0D
0x3B; 0x0D
0x3C; 0x80
0x4A; 0x8C
0x4B; 0x44
0x4C; 0x00
0x4D; 0x00
0x4E; 0x00
0x4F; 0xC0
0x70; 0x60
0x71; 0x00
0x72; 0x01
0x73; 0x03
0x74; 0x00
0x75; 0x01
0x76; 0x00
0x77; 0x07
0x78; 0xFF
0x79; 0x00 *combined with reg below makes dtg2_hs_in_dly=0*
0x7A; 0x00
0x7B; 0x00 *combined with reg below makes dtg2_vs_in_dly=0*
0x7C; 0x00
0x82; 0x43
*/

This did cause VGA format to come out, but the horizontal and vertical syncs were in the wrong place.  Changing the dtg1_spec_e above seemed to have no effect.  Instead, getting a little weary, I inserted the following code rather than upgrading the whole table definition.  It seemed that I needed to change dtg2_vdly1 and dtg2_hdly.  I added code to the ths8200_setstd() function, near the end and just before the THS8200_CHIP_CTL twiddle, that I've repeated below for location reference.

 

if (V4L2_STD_VGA640x480_60 == mode_info[i].std) { /* added by HgF */

int vdly1 = 11;
int hdly = 68;
#define THS8200_DTG2_VDLY1_LSB_MASK     (0xFF)
#define THS8200_DTG2_VDLY1_MSB_MASK    (0x07)
#define THS8200_DTG2_HDLY_LSB_MASK      (0xFF)
#define THS8200_DTG2_HDLY_MSB_MASK    (0x1F)

printk(KERN_CRIT "{HgF ths8200_setstd()} V4L2_STD_VGA640x480_60 explicitly recognized, vdly1=%d, hdly=%d\n", (int)vdly1, (int)hdly); /* added by HgF */

err |= ths8200_write(sd, THS8200_DTG2_VDLY1_LSB,
        (vdly1 & THS8200_DTG2_VDLY1_LSB_MASK));
value = ths8200_read(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB);
value &= 0xF8;
value |= ((vdly1 >> 8) & THS8200_DTG2_VDLY1_MSB_MASK);
err |= ths8200_write(sd, THS8200_DTG2_VLENGTH1_MSB_VDLY1_MSB, value);

err |= ths8200_write(sd, THS8200_DTG2_HLENGTH_HDLY_LSB, /*FYI, THS8200_DTG2_HLENGTH_HDLY_LSB inconsistently named.  Should be THS8200_DTG2_HDLY_LSB */
        (hdly & THS8200_DTG2_HDLY_LSB_MASK));
value = ths8200_read(sd, THS8200_DTG2_HLENGTH_LSB_HDLY_MSB);
value &= 0xE0;
value |= ((hdly >> 8) & THS8200_DTG2_HDLY_MSB_MASK);
 err |= ths8200_write(sd, THS8200_DTG2_HLENGTH_LSB_HDLY_MSB, value);

}

ths8200_write(sd, THS8200_CHIP_CTL, 0x00);
mdelay(10);
ths8200_write(sd, THS8200_CHIP_CTL, 0x01);

THAT SHOULD BE ALL:

I might have missed something, but I think I got it all.  With all those changes, my splash application (derived from decode demo) can now display a fixed image on a VGA monitor, going from the DaVinci DM6467 through the VPIF and THS8200.

-Helmut

  • I later discovered I forgot color space conversion.  I thought DMAI was already doing it, but it wasn't.  

    Surprisingly,

    1. if you sent out YCbCr to the THS8200 in 16-bit YCbCr 4:2:2 format, 
    2. which uses only the GY and BCb input ports
    3. while the RCr input port is grounded like the DM64667 EVM, 
    4. yet you leave csc_bypass on like DMAI does, which means the csc is not being used

    then color space conversion doesn't occur.  As a result, the THS8200 sends out to your R, G, B outputs intended to be separate component signals for VGA, what came in on GY, BCb and RCr.  In other words, your intended Y goes straight to G.  Therefore, your luma appears reasonable on the VGA output, it's just all green tinted.  Then, your CbCr muxed on BCb gets all sent to B.  I couldn't see much impact of that.  Then, your RCr that's grounded sends zeroes to R.  All in all, I didn't think it was a color space conversion error, but some kind of other problem.  I'll know better next time!

    Anyway, I added csc and now it all works correctly.  Below is the extra code, with some #define lines included to help locate the position of this code relative to my last post.

     

    #define THS8200_DTG2_VDLY1_LSB_MASK     (0xFF)
    #define THS8200_DTG2_VDLY1_MSB_MASK    (0x07)
    #define THS8200_DTG2_HDLY_LSB_MASK      (0xFF)
    #define THS8200_DTG2_HDLY_MSB_MASK    (0x1F)

    // added by HgF - Need YCbCr to RGB conversion.  Use HDTV example from THS8200 datasheet on pages 25 and 26.
    // Program color conversion matrix.  Packed register values from THS8200 datasheet table on page 26 (2nd of 2 tables), section 4.4 Color Space Conversion (CSC)
    err |= ths8200_write(sd, THS8200_CSC_R11, 0x81); // 1000 0001
    err |= ths8200_write(sd, THS8200_CSC_R12, 0xD5); // 1101 0101
    err |= ths8200_write(sd, THS8200_CSC_R21, 0x00); // 0000 0000
    err |= ths8200_write(sd, THS8200_CSC_R22, 0x00); // 0000 0000
    err |= ths8200_write(sd, THS8200_CSC_R31, 0x06); // 0000 0110
    err |= ths8200_write(sd, THS8200_CSC_R32, 0x29); // 0010 1001
    err |= ths8200_write(sd, THS8200_CSC_G11, 0x04); // 0000 0100
    err |= ths8200_write(sd, THS8200_CSC_G12, 0x00); // 0000 0000
    err |= ths8200_write(sd, THS8200_CSC_G21, 0x04); // 0000 0100
    err |= ths8200_write(sd, THS8200_CSC_G22, 0x80); // 1000 0000
    err |= ths8200_write(sd, THS8200_CSC_G31, 0x04); // 0000 0100
    err |= ths8200_write(sd, THS8200_CSC_G32, 0x00); // 0000 0000
    err |= ths8200_write(sd, THS8200_CSC_B11, 0x80); // 1000 0000
    err |= ths8200_write(sd, THS8200_CSC_B12, 0xBB); // 1011 1011
    err |= ths8200_write(sd, THS8200_CSC_B21, 0x07); // 0000 0111
    err |= ths8200_write(sd, THS8200_CSC_B22, 0x42); // 0100 0010
    err |= ths8200_write(sd, THS8200_CSC_B31, 0x00); // 0000 0000
    err |= ths8200_write(sd, THS8200_CSC_B32, 0x00); // 0000 0000
    err |= ths8200_write(sd, THS8200_CSC_OFFS1, 0x14); // 0001 0100
    err |= ths8200_write(sd, THS8200_CSC_OFFS12, 0xAE); // 1010 1110
    err |= ths8200_write(sd, THS8200_CSC_OFFS23, 0x8B); // 1000 1011
    err |= ths8200_write(sd, THS8200_CSC_OFFS3, 0x15); // was 0001 0100 => now 0001 0101 // This line turns off CSC bypass, thus inserting CSC.  Turn on c_uof_cntl with bit 0x01, otherwise bright blue blooms into magenta.
    printk(KERN_CRIT "{HgF ths8200_initialize()} CSC CONFIGURED AND INSERTED (not bypassed)\n"); /* added by HgF */

     

  • This is a wonderful post! Kudos to you!

    I am sure it helps tons of other people.

    -Manju

  • Hi What a wonderful post 

    i am using dvsdk _3_10_00_19 fo dm355

    My driver is compiling but not initializing from i2c prob function

    Please help me as soon as possible

    Best Regrads

    Tejas Dave