diff options
author | ankishore <ankishore@nvidia.com> | 2011-04-15 11:46:51 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:45:01 -0800 |
commit | 5228c9aadeaab46ad238decea6d5dea737e1095f (patch) | |
tree | 674c8e90cbd8dece2ae24546c3edb7b39ccc122a /drivers/video/tegra | |
parent | 179961e8fe89510a2cf52ea20c0a9f891234ae14 (diff) |
video: dsi: tegra: Read from dsi panel
Support to read from dsi panel even with dc stream on.
Original-Change-Id: I4ecbeded43d3e8424d66f64321f902c540163427
Reviewed-on: http://git-master/r/27465
Reviewed-by: Animesh Kishore <ankishore@nvidia.com>
Tested-by: Animesh Kishore <ankishore@nvidia.com>
Reviewed-by: Venkata Nageswara Penumarty <vpenumarty@nvidia.com>
Reviewed-by: Hanumanth Venkateswa Moganty <vmoganty@nvidia.com>
Original-Change-Id: I68c59bb0d8f8761102e10f130029ffce827cdadb
Rebase-Id: R6b3498b0cb1b9608ca457ab9869d97ad46bea4f9
Diffstat (limited to 'drivers/video/tegra')
-rwxr-xr-x | drivers/video/tegra/dc/dsi.c | 329 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dsi.h | 9 |
2 files changed, 298 insertions, 40 deletions
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index c82e4039a146..d2b8cdf214ba 100755 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -66,6 +66,9 @@ #define DSI_CLK_BURST_NONE_BURST 0x1 #define DSI_CLK_BURST_BURST_MODE 0x2 +#define DSI_DC_STREAM_DISABLE 0x0 +#define DSI_DC_STREAM_ENABLE 0x1 + struct dsi_status { unsigned init:2; @@ -77,6 +80,8 @@ struct dsi_status { unsigned clk_out:2; unsigned clk_mode:2; unsigned clk_burst:2; + + unsigned dc_stream:1; }; /* source of video data */ @@ -701,6 +706,8 @@ static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc, tegra_dc_writel(dc, 0, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + + dsi->status.dc_stream = DSI_DC_STREAM_DISABLE; } void tegra_dsi_stop_dc_stream_at_frame_end(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) @@ -737,6 +744,7 @@ static void tegra_dsi_start_dc_stream(struct tegra_dc *dc, struct tegra_dc_dsi_data *dsi) { u32 val; + tegra_dc_writel(dc, DSI_ENABLE, DC_DISP_DISP_WIN_OPTIONS); /* TODO: clean up */ @@ -766,6 +774,8 @@ static void tegra_dsi_start_dc_stream(struct tegra_dc *dc, tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); } + + dsi->status.dc_stream = DSI_DC_STREAM_ENABLE; } static void tegra_dsi_set_dc_clk(struct tegra_dc *dc, @@ -857,7 +867,7 @@ static void tegra_dsi_hs_clk_out_disable(struct tegra_dc *dc, { u32 val; - if (dsi->status.driven == DSI_DRIVEN_MODE_DC) + if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) tegra_dsi_stop_dc_stream(dc, dsi); val = tegra_dsi_readl(dsi, DSI_CONTROL); @@ -965,7 +975,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc, } tegra_gpio_enable(TEGRA_GPIO_PJ1); - if (dsi->status.driven == DSI_DRIVEN_MODE_DC) + if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) tegra_dsi_stop_dc_stream(dc, dsi); /* Initializing DSI registers */ @@ -993,6 +1003,7 @@ static int tegra_dsi_init_hw(struct tegra_dc *dc, dsi->status.clk_out = DSI_PHYCLK_OUT_DIS; dsi->status.clk_mode = DSI_PHYCLK_NOT_INIT; dsi->status.clk_burst = DSI_CLK_BURST_NOT_INIT; + dsi->status.dc_stream = DSI_DC_STREAM_DISABLE; fail: return err; } @@ -1010,8 +1021,8 @@ static int tegra_dsi_set_to_lp_mode(struct tegra_dc *dc, if (dsi->status.lphs == DSI_LPHS_IN_LP_MODE) goto success; - if (dsi->status.driven == DSI_DRIVEN_MODE_DC) - tegra_dsi_stop_dc_stream(dc, dsi); + if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) + tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); /* disable/enable hs clock according to enable_hs_clock_on_lp_cmd_mode */ if ((dsi->status.clk_out == DSI_PHYCLK_OUT_EN) && @@ -1046,8 +1057,8 @@ static int tegra_dsi_set_to_hs_mode(struct tegra_dc *dc, goto fail; } - if (dsi->status.driven == DSI_DRIVEN_MODE_DC) - tegra_dsi_stop_dc_stream(dc, dsi); + if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) + tegra_dsi_stop_dc_stream_at_frame_end(dc, dsi); if ((dsi->status.clk_out == DSI_PHYCLK_OUT_EN) && (!dsi->info.enable_hs_clock_on_lp_cmd_mode)) @@ -1117,11 +1128,45 @@ fail: return status; } -static int tegra_dsi_read_data(struct tegra_dc *dc, - struct tegra_dc_dsi_data *dsi) +static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi, + u8* pdata, u8 data_id, u16 data_len) { - /* TODO: implement DSI read */ - return ENXIO; + u8 virtual_channel; + u8 *pval; + u32 val; + int err; + + err = 0; + + virtual_channel = dsi->info.virtual_channel << DSI_VIR_CHANNEL_BIT_POSITION; + + /* always use hw for ecc */ + val = (virtual_channel | data_id) << 0 | + data_len << 8; + tegra_dsi_writel(dsi, val, DSI_WR_DATA); + + /* if pdata != NULL, pkt type is long pkt */ + if (pdata != NULL) { + while (data_len) { + if (data_len >= 4) { + val = ((u32*) pdata)[0]; + data_len -= 4; + pdata += 4; + } else { + val = 0; + pval = (u8*) &val; + do + *pval++ = *pdata++; + while(--data_len); + } + tegra_dsi_writel(dsi, val, DSI_WR_DATA); + } + } + + if (!tegra_dsi_host_trigger(dsi)) + err = -EIO; + + return err; } static int tegra_dsi_write_data(struct tegra_dc *dc, @@ -1131,9 +1176,7 @@ static int tegra_dsi_write_data(struct tegra_dc *dc, bool switch_back_to_hs_mode; bool switch_back_to_dc_mode; u32 val; - u8 *pval; int err; - u8 virtua_channel; err = 0; switch_back_to_hs_mode = false; @@ -1165,33 +1208,8 @@ static int tegra_dsi_write_data(struct tegra_dc *dc, } } - virtua_channel = dsi->info.virtual_channel << DSI_VIR_CHANNEL_BIT_POSITION; + err = _tegra_dsi_write_data(dsi, pdata, data_id, data_len); - /* always use hw for ecc */ - val = (virtua_channel | data_id) << 0 | - data_len << 8; - tegra_dsi_writel(dsi, val, DSI_WR_DATA); - - /* if pdata != NULL, pkt type is long pkt */ - if (pdata != NULL) { - while (data_len) { - if (data_len >= 4) { - val = ((u32*) pdata)[0]; - data_len -= 4; - pdata += 4; - } else { - val = 0; - pval = (u8*) &val; - do - *pval++ = *pdata++; - while(--data_len); - } - tegra_dsi_writel(dsi, val, DSI_WR_DATA); - } - } - - if (!tegra_dsi_host_trigger(dsi)) - err = -EIO; if (switch_back_to_dc_mode) dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_DC; @@ -1227,6 +1245,237 @@ static int tegra_dsi_init_panel(struct tegra_dc *dc, return err; } +static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) +{ + u32 val; + u32 poll_time; + int err; + + poll_time = 0; + err = 0; + + val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); + val |= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); + tegra_dsi_writel(dsi, val, DSI_HOST_DSI_CONTROL); + + while (poll_time < DSI_STATUS_POLLING_DURATION_USEC) { + val = tegra_dsi_readl(dsi, DSI_HOST_DSI_CONTROL); + val &= DSI_HOST_DSI_CONTROL_IMM_BTA(TEGRA_DSI_ENABLE); + if (!val) + break; + udelay(DSI_STATUS_POLLING_DELAY_USEC); + poll_time += DSI_STATUS_POLLING_DELAY_USEC; + } + if (poll_time > DSI_STATUS_POLLING_DURATION_USEC) + err = -EBUSY; + + return err; +} + +static void tegra_dsi_read_fifo(struct tegra_dc *dc, + struct tegra_dc_dsi_data *dsi, + u32 rd_fifo_cnt, + u8 *read_fifo) +{ + u32 val; + u32 i; + + /* Read data from FIFO */ + for (i = 0; i < rd_fifo_cnt; i++) { + val = tegra_dsi_readl(dsi, DSI_RD_DATA); + printk(KERN_INFO "Read data[%d]: 0x%x\n", i, val); + memcpy(read_fifo, &val, 4); + read_fifo += 4; + } + + /* Make sure all the data is read from the FIFO */ + val = tegra_dsi_readl(dsi, DSI_STATUS); + val &= DSI_STATUS_RD_FIFO_COUNT(0x1f); + if (val) + dev_err(&dc->ndev->dev, "DSI FIFO_RD_CNT not zero" + " even after reading FIFO_RD_CNT words from read fifo\n"); +} + +static int tegra_dsi_parse_read_response(struct tegra_dc *dc, + u32 rd_fifo_cnt, u8 *read_fifo) +{ + int err; + u32 payload_size; + + payload_size = 0; + err = 0; + + printk(KERN_INFO "escape sequence[0x%x]\n", read_fifo[0]); + switch (read_fifo[4] & 0xff) { + case GEN_LONG_RD_RES: + /* Fall through */ + case DCS_LONG_RD_RES: + payload_size = (read_fifo[5] | + (read_fifo[6] << 8)) & 0xFFFF; + printk(KERN_INFO "Long read response Packet\n" + "payload_size[0x%x]\n", payload_size); + break; + case GEN_1_BYTE_SHORT_RD_RES: + /* Fall through */ + case DCS_1_BYTE_SHORT_RD_RES: + payload_size = 1; + printk(KERN_INFO "Short read response Packet\n" + "payload_size[0x%x]\n", payload_size); + break; + case GEN_2_BYTE_SHORT_RD_RES: + /* Fall through */ + case DCS_2_BYTE_SHORT_RD_RES: + payload_size = 2; + printk(KERN_INFO "Short read response Packet\n" + "payload_size[0x%x]\n", payload_size); + break; + case ACK_ERR_RES: + payload_size = 2; + printk(KERN_INFO "Acknowledge error report response\n" + "Packet payload_size[0x%x]\n", payload_size); + break; + default: + /*reading from RD_FIFO_COUNT*/ + printk(KERN_INFO "Invalid read response payload_size\n"); + err = -EINVAL; + break; + } + return err; +} + +static int tegra_dsi_read_data(struct tegra_dc *dc, + struct tegra_dc_dsi_data *dsi, + u32 max_ret_payload_size, + u32 panel_reg_addr, u8 *read_data) +{ + u32 val; + int err; + u32 poll_time; + u32 rd_fifo_cnt; + bool switch_back_to_hs_mode; + bool restart_dc_stream; + bool switch_back_to_dc_mode; + + err = 0; + switch_back_to_hs_mode = false; + restart_dc_stream = false; + switch_back_to_dc_mode = false; + + if ((dsi->status.init != DSI_MODULE_INIT) || + (dsi->status.lphs == DSI_LPHS_NOT_INIT) || + (dsi->status.driven == DSI_DRIVEN_MODE_NOT_INIT)) { + err = -EPERM; + goto fail; + } + + val = tegra_dsi_readl(dsi, DSI_STATUS); + val &= DSI_STATUS_RD_FIFO_COUNT(0x1f); + if (val) { + err = -EBUSY; + dev_err(&dc->ndev->dev, "DSI fifo count not zero\n"); + goto fail; + } + + if (!tegra_dsi_is_controller_idle(dsi)) { + err = -EBUSY; + dev_err(&dc->ndev->dev, "DSI trigger bit is already set\n"); + goto fail; + } + + if (dsi->status.lphs == DSI_LPHS_IN_HS_MODE) { + if (dsi->status.driven == DSI_DRIVEN_MODE_DC) { + if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) + restart_dc_stream = true; + dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_HOST; + switch_back_to_dc_mode = true; + if (dsi->info.hs_cmd_mode_supported) { + err = tegra_dsi_set_to_hs_mode(dc, dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to go to HS mode host driven\n"); + goto fail; + } + } + } + if (!dsi->info.hs_cmd_mode_supported) { + err = tegra_dsi_set_to_lp_mode(dc, dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI failed to go to LP mode\n"); + goto fail; + } + switch_back_to_hs_mode = true; + } + } + + /* Set max return payload size in words */ + err = _tegra_dsi_write_data(dsi, NULL, + dsi_command_max_return_pkt_size, + max_ret_payload_size); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI write failed\n"); + goto fail; + } + + /* DCS to read given panel register */ + err = _tegra_dsi_write_data(dsi, NULL, + dsi_command_dcs_read_with_no_params, + panel_reg_addr); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI write failed\n"); + goto fail; + } + + err = tegra_dsi_bta(dsi); + if (err < 0) { + dev_err(&dc->ndev->dev, + "DSI IMM BTA timeout\n"); + goto fail; + } + + poll_time = 0; + while (poll_time < DSI_DELAY_FOR_READ_FIFO) { + mdelay(1); + val = tegra_dsi_readl(dsi, DSI_STATUS); + rd_fifo_cnt = val & DSI_STATUS_RD_FIFO_COUNT(0x1f); + if (rd_fifo_cnt << 2 > DSI_READ_FIFO_DEPTH) + dev_err(&dc->ndev->dev, + "DSI RD_FIFO_CNT is greater than RD_FIFO_DEPTH\n"); + break; + poll_time++; + } + + if (rd_fifo_cnt == 0) { + dev_info(&dc->ndev->dev, + "DSI RD_FIFO_CNT is zero\n"); + err = -EINVAL; + goto fail; + } + + if (val & DSI_STATUS_LB_UNDERFLOW(0x1) || + val & DSI_STATUS_LB_OVERFLOW(0x1)) { + dev_err(&dc->ndev->dev, + "DSI overflow/underflow error\n"); + err = -EINVAL; + goto fail; + } + + tegra_dsi_read_fifo(dc, dsi, rd_fifo_cnt, read_data); + + err = tegra_dsi_parse_read_response(dc, rd_fifo_cnt, read_data); +fail: + if (switch_back_to_dc_mode) + dsi->driven_mode = TEGRA_DSI_DRIVEN_BY_DC; + if (switch_back_to_dc_mode || switch_back_to_hs_mode) + tegra_dsi_set_to_hs_mode(dc, dsi); + if (restart_dc_stream) + tegra_dsi_start_dc_stream(dc, dsi); + + return err; +} + static void tegra_dc_dsi_enable(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); @@ -1456,7 +1705,7 @@ static void tegra_dc_dsi_destroy(struct tegra_dc *dc) kfree(dsi->info.dsi_init_cmd); /* Disable dc stream*/ - if(dsi->status.driven == DSI_DRIVEN_MODE_DC) + if(dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) tegra_dsi_stop_dc_stream(dc, dsi); /* Disable dsi phy clock*/ @@ -1484,7 +1733,7 @@ static void tegra_dc_dsi_disable(struct tegra_dc *dc) mutex_lock(&dsi->lock); - if (dsi->status.driven == DSI_DRIVEN_MODE_DC) + if (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE) tegra_dsi_stop_dc_stream(dc, dsi); if (dsi->status.clk_out == DSI_PHYCLK_OUT_EN) diff --git a/drivers/video/tegra/dc/dsi.h b/drivers/video/tegra/dc/dsi.h index dbbb357319f9..cbfb2d56178e 100644 --- a/drivers/video/tegra/dc/dsi.h +++ b/drivers/video/tegra/dc/dsi.h @@ -41,6 +41,15 @@ enum{ #define DSI_MAX_COMMAND_DELAY_USEC 250000 #define DSI_COMMAND_DELAY_STEPS_USEC 10 +/* DSI return packet types */ +#define GEN_LONG_RD_RES 0x1A +#define DCS_LONG_RD_RES 0x1C +#define GEN_1_BYTE_SHORT_RD_RES 0x11 +#define DCS_1_BYTE_SHORT_RD_RES 0x21 +#define GEN_2_BYTE_SHORT_RD_RES 0x12 +#define DCS_2_BYTE_SHORT_RD_RES 0x22 +#define ACK_ERR_RES 0x02 + /* End of Transmit command for HS mode */ #define DSI_CMD_HS_EOT_PACKAGE 0x000F0F08 |