From fdf669e16c492c38dba87263755e0e0077d6f0bd Mon Sep 17 00:00:00 2001 From: Anders Bauer Date: Fri, 4 Nov 2011 17:05:28 +0100 Subject: video: mcde: add DSI video mode support This patch adds support for DSI video mode. ST-Ericsson Linux next: - ST-Ericsson ID: 365705 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I103bed18f814e85e48bc687b6b08b4829be347a6 Signed-off-by: Anders Bauer Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36517 Reviewed-by: QABUILD Reviewed-by: Jimmy RUBIN Reviewed-by: Per PERSSON --- drivers/video/mcde/mcde_display.c | 23 +++- drivers/video/mcde/mcde_hw.c | 282 +++++++++++++++++++++++++++++++++++++- include/video/mcde.h | 20 +++ 3 files changed, 321 insertions(+), 4 deletions(-) diff --git a/drivers/video/mcde/mcde_display.c b/drivers/video/mcde/mcde_display.c index 28409166c7d..a461b36c9b6 100644 --- a/drivers/video/mcde/mcde_display.c +++ b/drivers/video/mcde/mcde_display.c @@ -122,7 +122,28 @@ static inline int mcde_display_try_video_mode_default( struct mcde_display_device *ddev, struct mcde_video_mode *video_mode) { - /* TODO Check if inside native_xres and native_yres */ + /* + * DSI video mode: + * This function is intended for configuring supported video mode(s). + * Overload it into the panel driver file and set up blanking + * intervals and pixel clock according to below recommendations. + * + * vertical blanking parameters vbp, vfp, vsw are given in lines + * horizontal blanking parameters hbp, hfp, hsw are given in pixels + * + * video_mode->pixclock is the time between two pixels (in picoseconds) + * The source of the pixel clock is DSI PLL and it shall be set to + * meet the requirement + * + * non-burst mode: + * pixel clock (Hz) = (VACT+VBP+VFP+VSA) * (HACT+HBP+HFP+HSA) * + * framerate * bpp / num_data_lanes + * + * burst mode: + * pixel clock (Hz) > (VACT+VBP+VFP+VSA) * (HACT+HBP+HFP+HSA) * + * framerate * bpp / num_data_lanes * 1.1 + * (1.1 is a 10% margin needed for burst mode calculations) + */ return 0; } diff --git a/drivers/video/mcde/mcde_hw.c b/drivers/video/mcde/mcde_hw.c index 720b94db30f..0baffc246d7 100644 --- a/drivers/video/mcde/mcde_hw.c +++ b/drivers/video/mcde/mcde_hw.c @@ -921,6 +921,87 @@ static int wait_for_vcmp(struct mcde_chnl_state *chnl) return ret; } +static void get_vid_operating_mode(const struct mcde_port *port, + bool *burst_mode, bool *sync_is_pulse, bool *tvg_enable) +{ + switch (port->phy.dsi.vid_mode) { + case NON_BURST_MODE_WITH_SYNC_EVENT: + *burst_mode = false; + *sync_is_pulse = false; + *tvg_enable = false; + break; + case NON_BURST_MODE_WITH_SYNC_EVENT_TVG_ENABLED: + *burst_mode = false; + *sync_is_pulse = false; + *tvg_enable = true; + break; + case BURST_MODE_WITH_SYNC_EVENT: + *burst_mode = true; + *sync_is_pulse = false; + *tvg_enable = false; + break; + case BURST_MODE_WITH_SYNC_PULSE: + *burst_mode = true; + *sync_is_pulse = true; + *tvg_enable = false; + break; + default: + dev_err(&mcde_dev->dev, "Unsupported video mode"); + break; + } +} + +static void update_vid_static_registers(const struct mcde_port *port) +{ + u8 link = port->link; + bool burst_mode, sync_is_pulse, tvg_enable; + + get_vid_operating_mode(port, &burst_mode, &sync_is_pulse, &tvg_enable); + + /* burst mode or non-burst mode */ + dsi_wfld(link, DSI_VID_MAIN_CTL, BURST_MODE, burst_mode); + + /* sync is pulse or event */ + dsi_wfld(link, DSI_VID_MAIN_CTL, SYNC_PULSE_ACTIVE, sync_is_pulse); + dsi_wfld(link, DSI_VID_MAIN_CTL, SYNC_PULSE_HORIZONTAL, sync_is_pulse); + + /* disable video stream when using TVG */ + if (tvg_enable) { + dsi_wfld(link, DSI_MCTL_MAIN_EN, IF1_EN, false); + dsi_wfld(link, DSI_MCTL_MAIN_EN, IF2_EN, false); + } + + /* + * behavior during blanking time + * 00: NULL packet 1x:LP 01:blanking-packet + */ + dsi_wfld(link, DSI_VID_MAIN_CTL, REG_BLKLINE_MODE, 1); + + /* + * behavior during eol + * 00: NULL packet 1x:LP 01:blanking-packet + */ + dsi_wfld(link, DSI_VID_MAIN_CTL, REG_BLKEOL_MODE, 2); + + /* time to perform LP->HS on D-PHY */ + dsi_wfld(link, DSI_VID_DPHY_TIME, REG_WAKEUP_TIME, + port->phy.dsi.vid_wakeup_time); + + /* + * video stream starts on VSYNC packet + * and stops at the end of a frame + */ + dsi_wfld(link, DSI_VID_MAIN_CTL, VID_ID, port->phy.dsi.virt_id); + dsi_wfld(link, DSI_VID_MAIN_CTL, START_MODE, 0); + dsi_wfld(link, DSI_VID_MAIN_CTL, STOP_MODE, 0); + + /* 1: if1 in video mode, 0: if1 in command mode */ + dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, IF1_MODE, 1); + + /* 1: enables the link, 0: disables the link */ + dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, VID_EN, 1); +} + static int update_channel_static_registers(struct mcde_chnl_state *chnl) { const struct mcde_port *port = &chnl->port; @@ -1081,7 +1162,9 @@ static int update_channel_static_registers(struct mcde_chnl_state *chnl) MCDE_DSIVID0CONF0_BYTE_SWAP(false) | MCDE_DSIVID0CONF0_DCSVID_NOTGEN(true)); - if (port->mode == MCDE_PORTMODE_CMD) { + if (port->mode == MCDE_PORTMODE_VID) { + update_vid_static_registers(port); + } else { if (port->ifc == 0) dsi_wfld(port->link, DSI_CMD_MODE_CTL, IF1_ID, port->phy.dsi.virt_id); @@ -1529,6 +1612,192 @@ static u32 get_pkt_div(u32 disp_ppl, return 1; } +static void update_vid_horizontal_blanking(struct mcde_port *port, + struct mcde_video_mode *vmode, bool sync_is_pulse, u8 bpp) +{ + int hfp, hbp, hsa; + u8 link = port->link; + + /* + * vmode->hfp, vmode->hbp and vmode->hsw are given in pixels + * and must be re-calculated into bytes + * + * 6 + 2 is HFP header + checksum + */ + hfp = vmode->hfp * bpp - 6 - 2; + if (sync_is_pulse) { + /* + * 6 is HBP header + checksum + * 4 is RGB header + checksum + */ + hbp = vmode->hbp * bpp - 4 - 6; + /* + * 6 is HBP header + checksum + * 4 is HSW packet bytes + * 4 is RGB header + checksum + */ + hsa = vmode->hsw * bpp - 4 - 4 - 6; + } else { + /* + * 6 is HBP header + checksum + * 4 is HSW packet bytes + * 4 is RGB header + checksum + */ + hbp = (vmode->hbp + vmode->hsw) * bpp - 4 - 4 - 6; + /* HSA is not considered in this mode and set to 0 */ + hsa = 0; + } + if (hfp < 0) { + hfp = 0; + dev_warn(&mcde_dev->dev, + "%s: negative calc for hfp, set to 0\n", __func__); + } + if (hbp < 0) { + hbp = 0; + dev_warn(&mcde_dev->dev, + "%s: negative calc for hbp, set to 0\n", __func__); + } + if (hsa < 0) { + hsa = 0; + dev_warn(&mcde_dev->dev, + "%s: negative calc for hsa, set to 0\n", __func__); + } + + dsi_wfld(link, DSI_VID_HSIZE1, HFP_LENGTH, hfp); + dsi_wfld(link, DSI_VID_HSIZE1, HBP_LENGTH, hbp); + dsi_wfld(link, DSI_VID_HSIZE1, HSA_LENGTH, hsa); +} + +static void update_vid_frame_parameters(struct mcde_port *port, + struct mcde_video_mode *vmode, u8 bpp) +{ + u8 link = port->link; + bool burst_mode, sync_is_pulse, tvg_enable; + u32 hs_byte_clk, pck_len, blkline_pck, line_duration; + u32 blkeol_pck, blkeol_duration; + u8 pixel_mode; + u8 rgb_header; + + get_vid_operating_mode(port, &burst_mode, &sync_is_pulse, &tvg_enable); + + dsi_wfld(link, DSI_VID_VSIZE, VFP_LENGTH, vmode->vfp); + dsi_wfld(link, DSI_VID_VSIZE, VBP_LENGTH, vmode->vbp); + dsi_wfld(link, DSI_VID_VSIZE, VSA_LENGTH, vmode->vsw); + update_vid_horizontal_blanking(port, vmode, sync_is_pulse, bpp); + + dsi_wfld(link, DSI_VID_VSIZE, VACT_LENGTH, vmode->yres); + dsi_wfld(link, DSI_VID_HSIZE2, RGB_SIZE, vmode->xres * bpp); + + /* + * The rgb_header identifies the pixel stream format, + * as described in the MIPI DSI Specification: + * + * 0x0E: Packed pixel stream, 16-bit RGB, 565 format + * 0x1E: Packed pixel stream, 18-bit RGB, 666 format + * 0x2E: Loosely Packed pixel stream, 18-bit RGB, 666 format + * 0x3E: Packed pixel stream, 24-bit RGB, 888 format + */ + switch (port->pixel_format) { + case MCDE_PORTPIXFMT_DSI_16BPP: + pixel_mode = 0; + rgb_header = 0x0E; + break; + case MCDE_PORTPIXFMT_DSI_18BPP: + pixel_mode = 2; + rgb_header = 0x2E; + break; + case MCDE_PORTPIXFMT_DSI_18BPP_PACKED: + pixel_mode = 1; + rgb_header = 0x1E; + break; + case MCDE_PORTPIXFMT_DSI_24BPP: + pixel_mode = 3; + rgb_header = 0x3E; + break; + default: + pixel_mode = 3; + rgb_header = 0x3E; + dev_warn(&mcde_dev->dev, + "%s: invalid pixel format %d\n", + __func__, port->pixel_format); + break; + } + + dsi_wfld(link, DSI_VID_MAIN_CTL, VID_PIXEL_MODE, pixel_mode); + dsi_wfld(link, DSI_VID_MAIN_CTL, HEADER, rgb_header); + + if (tvg_enable) { + /* + * with these settings, expect to see 64 pixels wide + * red and green vertical stripes on the screen when + * tvg_enable = 1 + */ + dsi_wfld(link, DSI_MCTL_MAIN_DATA_CTL, TVG_SEL, 1); + + dsi_wfld(link, DSI_TVG_CTL, TVG_STRIPE_SIZE, 6); + dsi_wfld(link, DSI_TVG_CTL, TVG_MODE, 2); + dsi_wfld(link, DSI_TVG_CTL, TVG_STOPMODE, 2); + dsi_wfld(link, DSI_TVG_CTL, TVG_RUN, 1); + + dsi_wfld(link, DSI_TVG_IMG_SIZE, TVG_NBLINE, vmode->yres); + dsi_wfld(link, DSI_TVG_IMG_SIZE, TVG_LINE_SIZE, + vmode->xres * bpp); + + dsi_wfld(link, DSI_TVG_COLOR1, COL1_BLUE, 0); + dsi_wfld(link, DSI_TVG_COLOR1, COL1_GREEN, 0); + dsi_wfld(link, DSI_TVG_COLOR1, COL1_RED, 0xFF); + + dsi_wfld(link, DSI_TVG_COLOR2, COL2_BLUE, 0); + dsi_wfld(link, DSI_TVG_COLOR2, COL2_GREEN, 0xFF); + dsi_wfld(link, DSI_TVG_COLOR2, COL2_RED, 0); + } + + /* + * vid->pixclock is the time between two pixels (in picoseconds) + * + * hs_byte_clk is the amount of transferred bytes per lane and + * second (in MHz) + */ + hs_byte_clk = 1000000 / vmode->pixclock / 8; + pck_len = 1000000 * hs_byte_clk / port->refresh_rate / + (vmode->vsw + vmode->vbp + vmode->yres + vmode->vfp) * + port->phy.dsi.num_data_lanes; + + /* + * 6 is header + checksum, header = 4 bytes, checksum = 2 bytes + * 4 is short packet for vsync/hsync + */ + if (sync_is_pulse) + blkline_pck = pck_len - vmode->hsw - 6; + else + blkline_pck = pck_len - 4 - 6; + + line_duration = (blkline_pck + 6) / port->phy.dsi.num_data_lanes; + blkeol_pck = pck_len - + (vmode->hsw + vmode->hbp + vmode->xres + vmode->hfp) * bpp - 6; + blkeol_duration = (blkeol_pck + 6) / port->phy.dsi.num_data_lanes; + + if (sync_is_pulse) + dsi_wfld(link, DSI_VID_BLKSIZE2, BLKLINE_PULSE_PCK, + blkline_pck); + else + dsi_wfld(link, DSI_VID_BLKSIZE1, BLKLINE_EVENT_PCK, + blkline_pck); + dsi_wfld(link, DSI_VID_DPHY_TIME, REG_LINE_DURATION, line_duration); + if (burst_mode) { + dsi_wfld(link, DSI_VID_BLKSIZE1, BLKEOL_PCK, blkeol_pck); + dsi_wfld(link, DSI_VID_PCK_TIME, BLKEOL_DURATION, + blkeol_duration); + dsi_wfld(link, DSI_VID_VCA_SETTING1, MAX_BURST_LIMIT, + blkeol_pck - 6); + dsi_wfld(link, DSI_VID_VCA_SETTING2, EXACT_BURST_LIMIT, + blkeol_pck); + } + if (sync_is_pulse) + dsi_wfld(link, DSI_VID_VCA_SETTING2, MAX_LINE_LIMIT, + blkline_pck - 6); +} + void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs, struct mcde_port *port, enum mcde_fifo fifo, struct mcde_video_mode *video_mode) @@ -1699,8 +1968,11 @@ void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs, fidx * MCDE_DSIVID0CONF0_GROUPOFFSET, (temp & ~MCDE_DSIVID0CONF0_PACKING_MASK) | MCDE_DSIVID0CONF0_PACKING(regs->dsipacking)); - /* 1==CMD8 */ - packet = ((screen_ppl / pkt_div * regs->bpp) >> 3) + 1; + /* no extra command byte in video mode */ + if (port->mode == MCDE_PORTMODE_CMD) + packet = ((screen_ppl / pkt_div * regs->bpp) >> 3) + 1; + else + packet = ((screen_ppl / pkt_div * regs->bpp) >> 3); mcde_wreg(MCDE_DSIVID0FRAME + fidx * MCDE_DSIVID0FRAME_GROUPOFFSET, MCDE_DSIVID0FRAME_FRAME(packet * pkt_div * screen_lpf)); @@ -1721,6 +1993,10 @@ void update_channel_registers(enum mcde_chnl chnl_id, struct chnl_regs *regs, fidx * MCDE_DSIVID0DELAY1_GROUPOFFSET, MCDE_DSIVID0DELAY1_TEREQDEL(0) | MCDE_DSIVID0DELAY1_FRAMESTARTDEL(0)); + + if (port->mode == MCDE_PORTMODE_VID) + update_vid_frame_parameters(port, video_mode, + regs->bpp / 8); } else if (port->type == MCDE_PORTTYPE_DPI && !port->phy.dpi.tv_mode) { /* DPI LCD Mode */ diff --git a/include/video/mcde.h b/include/video/mcde.h index ae456743827..8e1c772fdaa 100644 --- a/include/video/mcde.h +++ b/include/video/mcde.h @@ -91,6 +91,15 @@ struct mcde_col_transform { u16 offset[3]; }; +/* DSI video mode */ +enum mcde_dsi_vid_mode { + NON_BURST_MODE_WITH_SYNC_EVENT = 0, + /* enables tvg, test video generator */ + NON_BURST_MODE_WITH_SYNC_EVENT_TVG_ENABLED = 1, + BURST_MODE_WITH_SYNC_EVENT = 2, + BURST_MODE_WITH_SYNC_PULSE = 3, +}; + #define MCDE_PORT_DPI_NO_CLOCK_DIV 0 #define DPI_ACT_HIGH_ALL 0 /* all signals are active high */ @@ -105,6 +114,7 @@ struct mcde_port { enum mcde_port_type type; enum mcde_port_mode mode; enum mcde_port_pix_fmt pixel_format; + u8 refresh_rate; /* display refresh rate given in Hz */ u8 ifc; u8 link; enum mcde_sync_src sync_src; @@ -118,6 +128,16 @@ struct mcde_port { bool clk_cont; bool host_eot_gen; + /* DSI video mode operating modes */ + enum mcde_dsi_vid_mode vid_mode; + + /* + * wakeup_time is the time to perform + * LP->HS on D-PHY. Given in clock + * cycles of byte clock frequency. + */ + u32 vid_wakeup_time; + /* DSI data lanes are swapped if true */ bool data_lanes_swap; } dsi; -- cgit v1.2.3