diff options
author | Animesh Kishore <ankishore@nvidia.com> | 2012-07-11 17:55:28 +0530 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-07-19 00:52:30 -0700 |
commit | 4c93dd529897db120a5b8151e04598ebc864f0d2 (patch) | |
tree | 32b593829093c68bff2eae391fdff1643332303f /drivers/video | |
parent | 871f9eb32a6717f595340d95a56c6530f7b1798a (diff) |
video: tegra: dsi: Implement dcs video + host
Send host commands to panel during vertical blanking
of a frame. Implementation is generic enough to
handle both long and short packets.
Bug 1009863
Change-Id: I9a80641df2d8b67eb3649d220c028543b246a5f3
Signed-off-by: Animesh Kishore <ankishore@nvidia.com>
Reviewed-on: http://git-master/r/114990
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/tegra/dc/dsi.c | 208 |
1 files changed, 145 insertions, 63 deletions
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 7ee9375f58f1..ba8a451a4b4f 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -2081,7 +2081,6 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi, u8 *pdata, u8 data_id, u16 data_len) { u8 virtual_channel; - u8 *pval; u32 val; int err; @@ -2104,10 +2103,9 @@ static int _tegra_dsi_write_data(struct tegra_dc_dsi_data *dsi, pdata += 4; } else { val = 0; - pval = (u8 *) &val; - do - *pval++ = *pdata++; - while (--data_len); + memcpy(&val, pdata, data_len); + pdata += data_len; + data_len = 0; } tegra_dsi_writel(dsi, val, DSI_WR_DATA); } @@ -2129,6 +2127,9 @@ int tegra_dsi_write_data(struct tegra_dc *dc, tegra_dc_io_start(dc); + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + init_status = tegra_dsi_prepare_host_transmission( dc, dsi, DSI_LP_OP_WRITE); if (IS_ERR_OR_NULL(init_status)) { @@ -2174,7 +2175,7 @@ static int tegra_dsi_send_panel_cmd(struct tegra_dc *dc, return err; } -static u8 get_8bit_ecc(u32 header) +static u8 tegra_dsi_ecc(u32 header) { char ecc_parity[24] = { 0x07, 0x0b, 0x0d, 0x0e, 0x13, 0x15, 0x16, 0x19, @@ -2191,75 +2192,156 @@ static u8 get_8bit_ecc(u32 header) return ecc_byte; } -/* This function is written to send DCS short write (1 parameter) only. - * This means the cmd will contain only 1 byte of index and 1 byte of value. - * The data type ID is fixed at 0x15 and the ECC is calculated based on the - * data in pdata. - * The command will be sent by hardware every frame. - * pdata should contain both the index + value for each cmd. - * data_len will be the total number of bytes in pdata. - */ -int tegra_dsi_send_panel_short_cmd(struct tegra_dc *dc, u8 *pdata, u8 data_len) +static u16 tegra_dsi_cs(char *pdata, u16 data_len) { - u8 ecc8bits = 0, data_len_orig = 0; - u32 val = 0, pkthdr = 0; - int err = 0, count = 0; - struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); + u16 byte_cnt; + u8 bit_cnt; + char curr_byte; + u16 crc = 0xFFFF; + u16 poly = 0x8408; - if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) - tegra_dc_host_resume(dc); + if (data_len > 0) { + for (byte_cnt = 0; byte_cnt < data_len; byte_cnt++) { + curr_byte = pdata[byte_cnt]; + for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) { + if (((crc & 0x0001 ) ^ + (curr_byte & 0x0001)) > 0) + crc = ((crc >> 1) & 0x7FFF) ^ poly; + else + crc = (crc >> 1) & 0x7FFF; - data_len_orig = data_len; - if (pdata != NULL) { - while (data_len) { - if (data_len >= 2) { - pkthdr = (CMD_SHORTW | - (((u16 *)pdata)[0]) << 8 | 0x00 << 24); - ecc8bits = get_8bit_ecc(pkthdr); - val = (pkthdr | (ecc8bits << 24)); - data_len -= 2; - pdata += 2; - count++; + curr_byte = (curr_byte >> 1 ) & 0x7F; } - switch (count) { - case 1: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0); - break; - case 2: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_1); - break; - case 3: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_2); - break; - case 4: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_3); - break; - case 5: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_4); - break; - case 6: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_5); - break; - case 7: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_6); - break; - case 8: - tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_7); - break; - default: - err = 1; - break; + } + } + return crc; +} + +static int tegra_dsi_dcs_pkt_seq_ctrl_init(struct tegra_dc_dsi_data *dsi, + struct tegra_dsi_cmd *cmd) +{ + u8 virtual_channel; + u32 val; + u16 data_len = cmd->sp_len_dly.data_len; + u8 seq_ctrl_reg = 0; + + virtual_channel = dsi->info.virtual_channel << + DSI_VIR_CHANNEL_BIT_POSITION; + + val = (virtual_channel | cmd->data_id) << 0 | + data_len << 8; + + val |= tegra_dsi_ecc(val) << 24; + + tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0 + seq_ctrl_reg++); + + /* if pdata != NULL, pkt type is long pkt */ + if (cmd->pdata != NULL) { + u8 *pdata; + u8 *pdata_mem; + /* allocate memory for pdata + 2 bytes checksum */ + pdata_mem = kzalloc(sizeof(u8) * data_len + 2, GFP_KERNEL); + if (!pdata_mem) { + dev_err(&dsi->dc->ndev->dev, "dsi: memory err\n"); + tegra_dsi_soft_reset(dsi); + return -ENOMEM; + } + + memcpy(pdata_mem, cmd->pdata, data_len); + pdata = pdata_mem; + *((u16 *)(pdata + data_len)) = tegra_dsi_cs(pdata, data_len); + + /* data_len = length of pdata + 2 byte checksum */ + data_len += 2; + + while (data_len) { + if (data_len >= 4) { + val = ((u32 *) pdata)[0]; + data_len -= 4; + pdata += 4; + } else { + val = 0; + memcpy(&val, pdata, data_len); + pdata += data_len; + data_len = 0; } + tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_DATA_0 + + seq_ctrl_reg++); } + kfree(pdata_mem); + } + + return 0; +} + +int tegra_dsi_start_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi, + struct tegra_dsi_cmd *cmd) +{ +#define PKT_HEADER_LEN_BYTE 4 +#define CHECKSUM_LEN_BYTE 2 + + int err = 0; + u32 val; + u16 tot_pkt_len = PKT_HEADER_LEN_BYTE; + struct tegra_dc *dc = dsi->dc; + + if (cmd->cmd_type != TEGRA_DSI_PACKET_CMD) + return -EINVAL; + + mutex_lock(&dsi->lock); + tegra_dc_io_start(dc); + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + + err = tegra_dsi_dcs_pkt_seq_ctrl_init(dsi, cmd); + if (err < 0) { + dev_err(&dsi->dc->ndev->dev, + "dsi: dcs pkt seq ctrl init failed\n"); + goto fail; + } + + if (cmd->pdata) { + u16 data_len = cmd->sp_len_dly.data_len; + tot_pkt_len += data_len + CHECKSUM_LEN_BYTE; } - val = DSI_INIT_SEQ_CONTROL_DSI_FRAME_INIT_BYTE_COUNT(data_len_orig * 2) - | DSI_INIT_SEQ_CONTROL_DSI_SEND_INIT_SEQUENCE(1); + val = DSI_INIT_SEQ_CONTROL_DSI_FRAME_INIT_BYTE_COUNT(tot_pkt_len) | + DSI_INIT_SEQ_CONTROL_DSI_SEND_INIT_SEQUENCE( + TEGRA_DSI_ENABLE); tegra_dsi_writel(dsi, val, DSI_INIT_SEQ_CONTROL); +fail: + tegra_dc_io_end(dc); + mutex_unlock(&dsi->lock); return err; + +#undef PKT_HEADER_LEN_BYTE +#undef CHECKSUM_LEN_BYTE +} +EXPORT_SYMBOL(tegra_dsi_start_host_cmd_v_blank_dcs); + +void tegra_dsi_stop_host_cmd_v_blank_dcs(struct tegra_dc_dsi_data * dsi) +{ + struct tegra_dc *dc = dsi->dc; + u32 cnt; + + mutex_lock(&dsi->lock); + tegra_dc_io_start(dc); + + if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) + tegra_dc_host_resume(dc); + + tegra_dsi_writel(dsi, TEGRA_DSI_DISABLE, DSI_INIT_SEQ_CONTROL); + + /* clear seq data registers */ + for (cnt = 0; cnt < 8; cnt++) + tegra_dsi_writel(dsi, 0, DSI_INIT_SEQ_DATA_0 + cnt); + + tegra_dc_io_end(dc); + mutex_unlock(&dsi->lock); } -EXPORT_SYMBOL(tegra_dsi_send_panel_short_cmd); +EXPORT_SYMBOL(tegra_dsi_stop_host_cmd_v_blank_dcs); static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi) { |