blob: a580e32b1fba19265461ce3d423dd7e08dc09c27 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
*
* Copyright (C) 2019 Amlogic, Inc. All rights reserved.
*
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#ifdef CONFIG_AMLOGIC_VPU
#include <linux/amlogic/media/vpu/vpu.h>
#endif
#include <linux/amlogic/media/vout/lcd/lcd_vout.h>
#include "edp_tx.h"
#include "lcd_tablet.h"
#include "../lcd_reg.h"
#include "../lcd_clk_config.h"
#include "../lcd_common.h"
#define EDP_TX_AUX_REQ_TIMEOUT 1000
#define EDP_TX_AUX_REQ_INTERVAL 1
#define EDP_AUX_RETRY_CNT 5
#define EDP_AUX_TIMEOUT 1000
#define EDP_AUX_INTERVAL 200
static int dptx_aux_check(struct aml_lcd_drv_s *pdrv)
{
if (dptx_reg_read(pdrv, EDP_TX_TRANSMITTER_OUTPUT_ENABLE))
return 0;
LCDERR("[%d]: %s: dptx is not enabled\n", pdrv->index, __func__);
return -1;
}
static void dptx_aux_request(struct aml_lcd_drv_s *pdrv, struct dptx_aux_req_s *req)
{
unsigned int state, timeout = 0;
int i = 0;
timeout = 0;
while (timeout++ < EDP_TX_AUX_REQ_TIMEOUT) {
state = dptx_reg_getb(pdrv, EDP_TX_AUX_STATE, 1, 1);
if (state == 0)
break;
lcd_delay_us(EDP_TX_AUX_REQ_INTERVAL);
};
dptx_reg_write(pdrv, EDP_TX_AUX_ADDRESS, req->address);
/*submit data only for write commands*/
if (req->cmd_state == 0) {
for (i = 0; i < req->byte_cnt; i++)
dptx_reg_write(pdrv, EDP_TX_AUX_WRITE_FIFO, req->data[i]);
}
/*submit the command and the data size*/
dptx_reg_write(pdrv, EDP_TX_AUX_COMMAND,
((req->cmd_code << 8) | ((req->byte_cnt - 1) & 0xf)));
}
static int dptx_aux_submit_cmd(struct aml_lcd_drv_s *pdrv, struct dptx_aux_req_s *req)
{
unsigned int status = 0, reply = 0;
unsigned int retry_cnt = 0, timeout = 0;
char str[8];
if (dptx_aux_check(pdrv))
return -1;
if (req->cmd_state)
sprintf(str, "read");
else
sprintf(str, "write");
dptx_aux_submit_cmd_retry:
dptx_aux_request(pdrv, req);
timeout = 0;
while (timeout++ < EDP_AUX_TIMEOUT) {
lcd_delay_us(EDP_AUX_INTERVAL);
reply = 0;
status = dptx_reg_read(pdrv, EDP_TX_AUX_TRANSFER_STATUS);
if (status & (1 << 0)) {
reply = dptx_reg_read(pdrv, EDP_TX_AUX_REPLY_CODE);
if (reply == DPTX_AUX_REPLY_CODE_ACK)
return 0;
if (reply & DPTX_AUX_REPLY_CODE_DEFER) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: edp aux %s addr 0x%x Defer!\n",
pdrv->index, str, req->address);
}
}
if (reply & DPTX_AUX_REPLY_CODE_NACK) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: edp aux %s addr 0x%x NACK!\n",
pdrv->index, str, req->address);
}
//return -1;
}
if (reply & DPTX_AUX_REPLY_CODE_I2C_DEFER) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: edp aux i2c %s addr 0x%x Defer!\n",
pdrv->index, str, req->address);
}
}
if (reply & DPTX_AUX_REPLY_CODE_I2C_NACK) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: edp aux i2c %s addr 0x%x NACK!\n",
pdrv->index, str, req->address);
}
//return -1;
}
break;
}
if (status & (1 << 3)) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
LCDPR("[%d]: edp aux %s addr 0x%x Error!\n",
pdrv->index, str, req->address);
}
break;
}
}
if (retry_cnt++ < EDP_AUX_RETRY_CNT) {
lcd_delay_us(EDP_AUX_INTERVAL);
LCDPR("[%d]: edp aux %s addr 0x%x timeout, status 0x%x, reply 0x%x, retry %d\n",
pdrv->index, str, req->address, status, reply, retry_cnt);
goto dptx_aux_submit_cmd_retry;
}
LCDPR("[%d]: edp aux %s addr 0x%x failed\n", pdrv->index, str, req->address);
return -1;
}
static int dptx_aux_write(struct aml_lcd_drv_s *pdrv, unsigned int addr,
unsigned int len, unsigned char *buf)
{
struct dptx_aux_req_s aux_req;
int ret;
aux_req.cmd_code = DPTX_AUX_CMD_WRITE;
aux_req.cmd_state = 0;
aux_req.address = addr;
aux_req.byte_cnt = len;
aux_req.data = buf;
ret = dptx_aux_submit_cmd(pdrv, &aux_req);
return ret;
}
static int dptx_aux_read(struct aml_lcd_drv_s *pdrv, unsigned int addr,
unsigned int len, unsigned char *buf)
{
struct dptx_aux_req_s aux_req;
int i, ret;
aux_req.cmd_code = DPTX_AUX_CMD_READ;
aux_req.cmd_state = 0;
aux_req.address = addr;
aux_req.byte_cnt = len;
aux_req.data = buf;
ret = dptx_aux_submit_cmd(pdrv, &aux_req);
if (ret)
return -1;
for (i = 0; i < len; i++)
buf[i] = (unsigned char)(dptx_reg_read(pdrv, EDP_TX_AUX_REPLY_DATA));
return 0;
}
static int dptx_aux_i2c_read(struct aml_lcd_drv_s *pdrv, unsigned int dev_addr,
unsigned int reg_addr, unsigned int len,
unsigned char *buf)
{
struct dptx_aux_req_s aux_req;
unsigned char aux_data[4];
unsigned int n = 0, reply_count = 0;
int i, ret;
len = (len > 16) ? 16 : len; /*cap the byte count*/
aux_data[0] = reg_addr;
aux_data[1] = 0x00;
/*send the dev_addr write*/
aux_req.cmd_code = DPTX_AUX_CMD_I2C_WRITE_MOT;
aux_req.cmd_state = 0;
aux_req.address = dev_addr;
aux_req.byte_cnt = 1;
aux_req.data = aux_data;
ret = dptx_aux_submit_cmd(pdrv, &aux_req);
if (ret)
return -1;
/*submit the read command to hardware*/
aux_req.cmd_code = DPTX_AUX_CMD_I2C_READ;
aux_req.cmd_state = 1;
aux_req.address = dev_addr;
aux_req.byte_cnt = len;
while (n < len) {
ret = dptx_aux_submit_cmd(pdrv, &aux_req);
if (ret)
return -1;
reply_count = dptx_reg_read(pdrv, EDP_TX_AUX_REPLY_DATA_COUNT);
for (i = 0; i < reply_count; i++) {
buf[n] = dptx_reg_read(pdrv, EDP_TX_AUX_REPLY_DATA);
n++;
}
aux_req.byte_cnt -= reply_count;
/*increment the address for the next transaction*/
aux_data[0] += reply_count;
}
return 0;
}
static void dptx_edid_print(struct dptx_edid_s *edp_edid)
{
pr_info("Manufacturer ID: %s\n"
"Product ID: 0x%04x\n"
"Product SN: 0x%08x\n"
"Week: %d\n"
"Year: %d\n"
"EDID Version: %04x\n",
edp_edid->manufacturer_id,
edp_edid->product_id,
edp_edid->product_sn,
edp_edid->week,
edp_edid->year,
edp_edid->version);
if (edp_edid->string_flag & (1 << 0))
pr_info("Monitor Name: %s\n", edp_edid->name);
if (edp_edid->string_flag & (1 << 1))
pr_info("Monitor AScii String: %s\n", edp_edid->asc_string);
if (edp_edid->string_flag & (1 << 2))
pr_info("Monitor SN: %s\n", edp_edid->serial_num);
pr_info("Detail Timing:\n"
" Pixel Clock: %d.%dMHz\n"
" H Active: %d\n"
" H Blank: %d\n"
" V Active: %d\n"
" V Blank: %d\n"
" H FP: %d\n"
" H PW: %d\n"
" V FP: %d\n"
" V PW: %d\n"
" H Size: %dmm\n"
" V Size: %dmm\n"
" H Border: %d\n"
" V Border: %d\n"
" Hsync Pol: %d\n"
" Vsync Pol: %d\n",
edp_edid->preferred_timing.pclk / 1000000,
(edp_edid->preferred_timing.pclk % 1000000) / 1000,
edp_edid->preferred_timing.h_active,
edp_edid->preferred_timing.h_blank,
edp_edid->preferred_timing.v_active,
edp_edid->preferred_timing.v_blank,
edp_edid->preferred_timing.h_fp,
edp_edid->preferred_timing.h_pw,
edp_edid->preferred_timing.v_fp,
edp_edid->preferred_timing.v_pw,
edp_edid->preferred_timing.h_size,
edp_edid->preferred_timing.v_size,
edp_edid->preferred_timing.h_border,
edp_edid->preferred_timing.v_border,
(edp_edid->preferred_timing.timing_ctrl >> 1) & 0x1,
(edp_edid->preferred_timing.timing_ctrl >> 2) & 0x1);
}
static int dptx_edid_valid_check(unsigned char *edid_buf)
{
unsigned char header[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
unsigned int checksum = 0;
int i;
if (memcmp(edid_buf, header, 8)) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDERR("%s: invalid EDID header\n", __func__);
return -1;
}
for (i = 0; i < 128; i++)
checksum += edid_buf[i];
if ((checksum & 0xff)) {
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
LCDERR("%s: EDID checksum Wrong\n", __func__);
return -1;
}
return 0;
}
static void dptx_edid_parase(unsigned char *edid_buf, struct dptx_edid_s *edp_edid)
{
struct dptx_edid_range_limit_s *range;
struct dptx_edid_timing_s *timing;
unsigned int temp;
int i, j;
range = &edp_edid->range_limit;
timing = &edp_edid->preferred_timing;
temp = ((edid_buf[8] << 8) | edid_buf[9]);
for (i = 0; i < 3; i++)
edp_edid->manufacturer_id[i] = (((temp >> ((2 - i) * 5)) & 0x1f) - 1) + 'A';
edp_edid->manufacturer_id[3] = '\0';
temp = ((edid_buf[11] << 8) | edid_buf[10]);
edp_edid->product_id = temp;
temp = ((edid_buf[12] << 24) | (edid_buf[13] << 16) | (edid_buf[14] << 8) | edid_buf[15]);
edp_edid->product_sn = temp;
edp_edid->week = edid_buf[16];
edp_edid->year = 1990 + edid_buf[17];
temp = ((edid_buf[18] << 8) | edid_buf[19]);
edp_edid->version = temp;
edp_edid->string_flag = 0;
for (i = 0; i < 4; i++) {
j = 54 + i * 18;
if ((edid_buf[j] + edid_buf[j + 1]) == 0) {
if ((edid_buf[j + 2] + edid_buf[j + 4]) == 0) {
switch (edid_buf[j + 3]) {
case 0xfc: //monitor name
memcpy(edp_edid->name, &edid_buf[j + 5], 13);
edp_edid->name[13] = '\0';
edp_edid->string_flag |= (1 << 0);
break;
case 0xfd: //monitor range limits
range->min_vfreq = edid_buf[j + 5];
range->max_v_freq = edid_buf[j + 6];
range->min_hfreq = edid_buf[j + 7];
range->max_hfreq = edid_buf[j + 8];
range->max_pclk = edid_buf[j + 9];
range->GTF_ctrl = ((edid_buf[j + 11] << 8) |
edid_buf[j + 10]);
range->GTF_start_hfreq = edid_buf[j + 12] * 2000;
range->GTF_C = edid_buf[j + 13] / 2;
range->GTF_M = ((edid_buf[j + 15] << 8) |
edid_buf[j + 14]);
range->GTF_K = edid_buf[j + 16];
range->GTF_J = edid_buf[j + 17] / 2;
break;
case 0xfe: //ascii string
memcpy(edp_edid->asc_string, &edid_buf[j + 5], 13);
edp_edid->asc_string[13] = '\0';
edp_edid->string_flag |= (1 << 1);
break;
case 0xff: //monitor serial num
memcpy(edp_edid->serial_num, &edid_buf[j + 5], 13);
edp_edid->serial_num[13] = '\0';
edp_edid->string_flag |= (1 << 2);
break;
default:
break;
}
}
} else {//detail timing
temp = ((edid_buf[j + 1] << 8) | (edid_buf[j])) * 10000;
timing->pclk = temp;
temp = ((((edid_buf[j + 4] >> 4) & 0xf) << 8) | edid_buf[j + 2]);
timing->h_active = temp;
temp = ((((edid_buf[j + 4] >> 0) & 0xf) << 8) | edid_buf[j + 3]);
timing->h_blank = temp;
temp = ((((edid_buf[j + 7] >> 4) & 0xf) << 8) | edid_buf[j + 5]);
timing->v_active = temp;
temp = ((((edid_buf[j + 7] >> 0) & 0xf) << 8) | edid_buf[j + 6]);
timing->v_blank = temp;
temp = ((((edid_buf[j + 11] >> 6) & 0x3) << 8) | edid_buf[j + 8]);
timing->h_fp = temp;
temp = ((((edid_buf[j + 11] >> 4) & 0x3) << 8) | edid_buf[j + 9]);
timing->h_pw = temp;
temp = ((((edid_buf[j + 11] >> 2) & 0x3) << 4) |
((edid_buf[j + 10] >> 4) & 0xf));
timing->v_fp = temp;
temp = ((((edid_buf[j + 11] >> 0) & 0x3) << 4) |
((edid_buf[j + 10] >> 0) & 0xf));
timing->v_pw = temp;
temp = ((((edid_buf[j + 14] >> 4) & 0xf) << 8) | edid_buf[j + 12]);
timing->h_size = temp;
temp = ((((edid_buf[j + 14] >> 0) & 0xf) << 8) | edid_buf[j + 13]);
timing->v_size = temp;
timing->h_border = edid_buf[j + 15];
timing->v_border = edid_buf[j + 16];
timing->timing_ctrl = edid_buf[j + 17];
}
}
edp_edid->ext_flag = edid_buf[126];
edp_edid->checksum = edid_buf[127];
}
static int dptx_read_edid(struct aml_lcd_drv_s *pdrv, unsigned char *edid_buf)
{
int i, ret;
if (!edid_buf) {
LCDERR("[%d]: %s: edid buf is null\n", pdrv->index, __func__);
return -1;
}
for (i = 0; i < 128; i += 16) {
ret = dptx_aux_i2c_read(pdrv, 0x50, i, 16, &edid_buf[i]);
if (ret)
break;
}
return ret;
}
static void edp_edid_timing_update(struct aml_lcd_drv_s *pdrv, struct dptx_edid_s *edp_edid)
{
struct lcd_config_s *pconf = &pdrv->config;
struct dptx_edid_timing_s *timing = &edp_edid->preferred_timing;
unsigned int sync_duration;
lcd_vout_notify_mode_change_pre(pdrv);
pconf->basic.h_active = timing->h_active;
pconf->basic.v_active = timing->v_active;
pconf->basic.h_period = timing->h_active + timing->h_blank;
pconf->basic.v_period = timing->v_active + timing->v_blank;
pconf->timing.lcd_clk = timing->pclk;
pconf->timing.lcd_clk_dft = pconf->timing.lcd_clk;
sync_duration = timing->pclk / pconf->basic.h_period;
sync_duration = sync_duration * 100 / pconf->basic.v_period;
pconf->timing.sync_duration_num = sync_duration;
pconf->timing.sync_duration_den = 100;
pconf->timing.hsync_width = timing->h_pw;
pconf->timing.hsync_bp = timing->h_blank - timing->h_fp - timing->h_pw;
pconf->timing.hsync_pol = (timing->timing_ctrl >> 1) & 0x1;
pconf->timing.vsync_width = timing->v_pw;
pconf->timing.vsync_bp = timing->v_blank - timing->v_fp - timing->v_pw;
pconf->timing.vsync_pol = (timing->timing_ctrl >> 2) & 0x1;
pconf->basic.screen_width = timing->h_size;
pconf->basic.screen_height = timing->v_size;
lcd_timing_init_config(pdrv);
lcd_tablet_config_update(pdrv);
lcd_tablet_config_post_update(pdrv);
#ifdef CONFIG_AMLOGIC_VPU
vpu_dev_clk_request(pdrv->lcd_vpu_dev, pdrv->config.timing.lcd_clk);
#endif
lcd_set_clk(pdrv);
lcd_set_venc_timing(pdrv);
lcd_vinfo_update(pdrv);
}
int dptx_edid_dump(struct aml_lcd_drv_s *pdrv)
{
struct dptx_edid_s edp_edid;
unsigned char *edid_buf;
char *str;
int i, n, retry_cnt = 0, ret;
edid_buf = kcalloc(128, sizeof(unsigned char), GFP_KERNEL);
if (!edid_buf)
return -1;
dptx_edid_dump_retry:
ret = dptx_read_edid(pdrv, edid_buf);
if (ret) {
if (retry_cnt++ > DPTX_EDID_READ_RETRY_MAX) {
LCDERR("[%d]: %s: failed to read EDID from sink\n",
pdrv->index, __func__);
goto dptx_edid_dump_err;
}
memset(edid_buf, 0, 128 * sizeof(unsigned char));
goto dptx_edid_dump_retry;
}
ret = dptx_edid_valid_check(edid_buf);
if (ret) {
if (retry_cnt++ > DPTX_EDID_READ_RETRY_MAX) {
LCDERR("[%d]: %s: edid data check error\n",
pdrv->index, __func__);
goto dptx_edid_dump_err;
}
memset(edid_buf, 0, 128 * sizeof(unsigned char));
goto dptx_edid_dump_retry;
}
str = kcalloc(400, sizeof(char), GFP_KERNEL);
if (str) {
LCDPR("[%d]: EDID Raw data:\n", pdrv->index);
n = 0;
for (i = 0; i < 128; i++) {
n += sprintf(str + n, " %02x", edid_buf[i]);
if (i % 16 == 15)
n += sprintf(str + n, "\n");
}
pr_info("%s\n", str);
kfree(str);
}
dptx_edid_parase(edid_buf, &edp_edid);
dptx_edid_print(&edp_edid);
kfree(edid_buf);
return 0;
dptx_edid_dump_err:
kfree(edid_buf);
return -1;
}
int dptx_edid_timing_probe(struct aml_lcd_drv_s *pdrv)
{
struct edp_config_s *edp_cfg;
unsigned char *edid_buf;
struct dptx_edid_s edp_edid;
char *str;
int i, n, retry_cnt = 0, ret;
edp_cfg = &pdrv->config.control.edp_cfg;
edid_buf = edp_cfg->edid_data;
if (edp_cfg->edid_en == 0)
return 0;
if ((edp_cfg->edid_state & EDP_EDID_STATE_LOAD) == 0) {
dptx_edid_timing_probe_retry:
memset(edid_buf, 0, 128 * sizeof(unsigned char));
ret = dptx_read_edid(pdrv, edid_buf);
if (ret) {
if (retry_cnt++ > DPTX_EDID_READ_RETRY_MAX) {
LCDERR("[%d]: %s: failed to read EDID from sink, retry %d\n",
pdrv->index, __func__, retry_cnt);
goto dptx_edid_timing_probe_err;
}
goto dptx_edid_timing_probe_retry;
}
ret = dptx_edid_valid_check(edid_buf);
if (ret) {
if (retry_cnt++ > DPTX_EDID_READ_RETRY_MAX) {
LCDERR("[%d]: %s: edid data check error, retry %d\n",
pdrv->index, __func__, retry_cnt);
goto dptx_edid_timing_probe_err;
}
goto dptx_edid_timing_probe_retry;
}
edp_cfg->edid_state |= EDP_EDID_STATE_LOAD;
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL) {
str = kcalloc(400, sizeof(char), GFP_KERNEL);
if (str) {
LCDPR("[%d]: EDID Raw data:\n", pdrv->index);
n = 0;
for (i = 0; i < 128; i++) {
if (i % 16 == 0)
n += sprintf(str + n, "\n");
n += sprintf(str + n, " %02x", edid_buf[i]);
}
pr_info("%s\n", str);
kfree(str);
}
}
}
if ((edp_cfg->edid_state & EDP_EDID_STATE_APPLY) == 0) {
if (edp_cfg->edid_retry_cnt++ > EDP_EDID_RETRY_MAX)
return -1;
dptx_edid_parase(edid_buf, &edp_edid);
if (lcd_debug_print_flag & LCD_DBG_PR_NORMAL)
dptx_edid_print(&edp_edid);
edp_edid_timing_update(pdrv, &edp_edid);
edp_cfg->edid_state |= EDP_EDID_STATE_APPLY;
}
LCDPR("%s ok\n", __func__);
return 0;
dptx_edid_timing_probe_err:
LCDPR("%s failed\n", __func__);
return -1;
}
static void dptx_link_fast_training(struct aml_lcd_drv_s *pdrv)
{
int index = pdrv->index;
unsigned char p_data = 0;
int ret;
/* disable scrambling */
dptx_reg_write(pdrv, EDP_TX_SCRAMBLING_DISABLE, 0x1);
/* set training pattern 1 */
dptx_reg_write(pdrv, EDP_TX_TRAINING_PATTERN_SET, 0x1);
p_data = 0x21;
ret = dptx_aux_write(pdrv, EDP_DPCD_TRAINING_PATTERN_SET, 1, &p_data);
if (ret)
LCDERR("[%d]: edp training pattern 1 failed.....\n", index);
lcd_delay_us(10);
/* set training pattern 2 */
dptx_reg_write(pdrv, EDP_TX_TRAINING_PATTERN_SET, 0x2);
p_data = 0x22;
ret = dptx_aux_write(pdrv, EDP_DPCD_TRAINING_PATTERN_SET, 1, &p_data);
if (ret)
LCDERR("[%d]: edp training pattern 2 failed.....\n", index);
lcd_delay_us(10);
/* set training pattern 3 */
dptx_reg_write(pdrv, EDP_TX_TRAINING_PATTERN_SET, 0x3);
p_data = 0x23;
ret = dptx_aux_write(pdrv, EDP_DPCD_TRAINING_PATTERN_SET, 1, &p_data);
if (ret)
LCDERR("[%d]: edp training pattern 3 failed.....\n", index);
lcd_delay_us(10);
/* disable the training pattern */
p_data = 0x20;
ret = dptx_aux_write(pdrv, EDP_DPCD_TRAINING_PATTERN_SET, 1, &p_data);
if (ret)
LCDERR("[%d]: edp training pattern off failed.....\n", index);
dptx_reg_write(pdrv, EDP_TX_TRAINING_PATTERN_SET, 0x0);
}
void dptx_dpcd_dump(struct aml_lcd_drv_s *pdrv)
{
int index = pdrv->index;
unsigned char p_data[12];
int ret, i;
if (index > 1) {
LCDERR("[%d]: %s: invalid drv_index\n", index, __func__);
return;
}
memset(p_data, 0, 12);
LCDPR("[%d]: edp DPCD link status:\n", index);
ret = dptx_aux_read(pdrv, 0x100, 8, p_data);
if (ret == 0) {
for (i = 0; i < 8; i++)
pr_info("0x%04x: 0x%02x\n", (0x100 + i), p_data[i]);
pr_info("\n");
}
memset(p_data, 0, 12);
LCDPR("[%d]: edp DPCD training status:\n", index);
ret = dptx_aux_read(pdrv, 0x200, 12, p_data);
if (ret == 0) {
for (i = 0; i < 12; i++)
pr_info("0x%04x: 0x%02x\n", (0x200 + i), p_data[i]);
pr_info("\n");
}
}
static void dptx_set_msa(struct aml_lcd_drv_s *pdrv)
{
struct lcd_config_s *pconf = &pdrv->config;
unsigned int hactive, vactive, htotal, vtotal, hsw, hbp, vsw, vbp;
unsigned int bpc, data_per_lane, misc0_data, bit_depth, sync_mode;
unsigned int m_vid; /*pclk/1000 */
unsigned int n_vid; /*162000, 270000, 540000 */
unsigned int ppc = 1; /* 1 pix per clock pix0 only */
unsigned int cfmt = 0; /* RGB */
hactive = pconf->basic.h_active;
vactive = pconf->basic.v_active;
htotal = pconf->basic.h_period;
vtotal = pconf->basic.v_period;
hsw = pconf->timing.hsync_width;
hbp = pconf->timing.hsync_bp;
vsw = pconf->timing.vsync_width;
vbp = pconf->timing.vsync_bp;
m_vid = pconf->timing.lcd_clk / 1000;
switch (pconf->control.edp_cfg.link_rate) {
case 1: /* 2.7G */
n_vid = 270000;
break;
case 0: /* 1.62G */
default:
n_vid = 162000;
break;
}
/*6bit:0x0, 8bit:0x1, 10bit:0x2, 12bit:0x3 */
switch (pconf->basic.lcd_bits) {
case 6:
bit_depth = 0x0;
break;
case 8:
bit_depth = 0x1;
break;
case 10:
bit_depth = 0x2;
break;
default:
bit_depth = 0x7;
break;
}
bpc = pconf->basic.lcd_bits; /* bits per color */
sync_mode = pconf->control.edp_cfg.sync_clk_mode;
data_per_lane = ((hactive * bpc * 3) + 15) / 16 - 1;
/*bit[0] sync mode (1=sync 0=async) */
misc0_data = (cfmt << 1) | (sync_mode << 0);
misc0_data |= (bit_depth << 5);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_HTOTAL, htotal);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_VTOTAL, vtotal);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_POLARITY, (0 << 1) | (0 << 0));
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_HSWIDTH, hsw);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_VSWIDTH, vsw);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_HRES, hactive);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_VRES, vactive);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_HSTART, (hsw + hbp));
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_VSTART, (vsw + vbp));
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_MISC0, misc0_data);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_MISC1, 0x00000000);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_M_VID, m_vid); /*unit: 1kHz */
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_N_VID, n_vid); /*unit: 10kHz */
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_TRANSFER_UNIT_SIZE, 32);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_DATA_COUNT_PER_LANE,
data_per_lane);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_USER_PIXEL_WIDTH, ppc);
}
static void dptx_reset(struct aml_lcd_drv_s *pdrv)
{
unsigned int bit;
if (pdrv->index)
bit = 18;
else
bit = 17;
lcd_reset_setb(pdrv, RESETCTRL_RESET1_MASK, 0, bit, 1);
lcd_reset_setb(pdrv, RESETCTRL_RESET1_LEVEL, 0, bit, 1);
lcd_delay_us(1);
lcd_reset_setb(pdrv, RESETCTRL_RESET1_LEVEL, 1, bit, 1);
lcd_delay_us(1);
}
static void dptx_phy_reset(struct aml_lcd_drv_s *pdrv)
{
unsigned int bit;
if (pdrv->index)
bit = 20;
else
bit = 19;
lcd_reset_setb(pdrv, RESETCTRL_RESET1_MASK, 0, bit, 1);
lcd_reset_setb(pdrv, RESETCTRL_RESET1_LEVEL, 0, bit, 1);
lcd_delay_us(1);
lcd_reset_setb(pdrv, RESETCTRL_RESET1_LEVEL, 1, bit, 1);
lcd_delay_us(1);
}
static int dptx_wait_phy_ready(struct aml_lcd_drv_s *pdrv)
{
int index = pdrv->index;
unsigned int data = 0;
unsigned int done = 100;
do {
data = dptx_reg_read(pdrv, EDP_TX_PHY_STATUS);
if (done < 20) {
LCDPR("dptx%d wait phy ready: reg_val=0x%x, wait_count=%u\n",
index, data, (100 - done));
}
done--;
lcd_delay_us(100);
} while (((data & 0x7f) != 0x7f) && (done > 0));
if ((data & 0x7f) == 0x7f)
return 0;
LCDERR("[%d]: edp tx phy error!\n", index);
return -1;
}
#define EDP_HPD_TIMEOUT 1000
static void edp_tx_init(struct aml_lcd_drv_s *pdrv)
{
unsigned int hpd_state = 0;
unsigned char auxdata[2];
unsigned int offset;
int i, index, ret;
index = pdrv->index;
if (index > 1) {
LCDERR("%s: invalid drv_index %d\n", __func__, index);
return;
}
offset = pdrv->data->offset_venc_data[pdrv->index];
dptx_phy_reset(pdrv);
dptx_reset(pdrv);
mdelay(2);
lcd_vcbus_write(ENCL_VIDEO_EN + offset, 0);
/* Set Aux channel clk-div: 24MHz */
dptx_reg_write(pdrv, EDP_TX_AUX_CLOCK_DIVIDER, 24);
/* Enable the transmitter */
/* remove the reset on the PHY */
dptx_reg_write(pdrv, EDP_TX_PHY_RESET, 0);
dptx_wait_phy_ready(pdrv);
mdelay(2);
dptx_reg_write(pdrv, EDP_TX_TRANSMITTER_OUTPUT_ENABLE, 0x1);
i = 0;
while (i++ < EDP_HPD_TIMEOUT) {
hpd_state = dptx_reg_getb(pdrv, EDP_TX_AUX_STATE, 0, 1);
if (hpd_state)
break;
mdelay(2);
}
LCDPR("[%d]: edp HPD state: %d, i=%d\n", index, hpd_state, i);
dptx_edid_timing_probe(pdrv);
/* tx Link-rate and Lane_count */
dptx_reg_write(pdrv, EDP_TX_LINK_BW_SET, 0x0a); /* Link-rate */
dptx_reg_write(pdrv, EDP_TX_LINK_COUNT_SET, 0x02); /* Lanes */
/* sink Link-rate and Lane_count */
auxdata[0] = 0x0a; /* 2.7GHz //EDP_DPCD_LINK_BANDWIDTH_SET */
auxdata[1] = 2; /*EDP_DPCD_LANE_COUNT_SET */
ret = dptx_aux_write(pdrv, EDP_DPCD_LINK_BANDWIDTH_SET, 2, auxdata);
if (ret)
LCDERR("[%d]: edp sink set lane rate & count failed.....\n", index);
/* Power up link */
auxdata[0] = 0x1;
ret = dptx_aux_write(pdrv, EDP_DPCD_SET_POWER, 1, auxdata);
if (ret)
LCDERR("[%d]: edp sink power up link failed.....\n", index);
dptx_link_fast_training(pdrv);
/*dptx_dpcd_dump(pdrv); */
dptx_set_msa(pdrv);
lcd_vcbus_write(ENCL_VIDEO_EN + offset, 1);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_ENABLE, 0x1);
LCDPR("[%d]: edp enable main stream video\n", index);
}
static void edp_tx_disable(struct aml_lcd_drv_s *pdrv)
{
unsigned char auxdata;
int index, ret;
index = pdrv->index;
if (index > 1) {
LCDERR("%s: invalid drv_index %d\n", __func__, index);
return;
}
/* Power down link */
auxdata = 0x2;
ret = dptx_aux_write(pdrv, EDP_DPCD_SET_POWER, 1, &auxdata);
if (ret)
LCDERR("[%d]: edp sink power down link failed.....\n", index);
dptx_reg_write(pdrv, EDP_TX_MAIN_STREAM_ENABLE, 0x0);
LCDPR("[%d]: edp disable main stream video\n", index);
/* disable the transmitter */
dptx_reg_write(pdrv, EDP_TX_TRANSMITTER_OUTPUT_ENABLE, 0x0);
}
static void edp_power_init(int index)
{
#ifdef CONFIG_SECURE_POWER_CONTROL
/*#define PM_EDP0 48 */
/*#define PM_EDP1 49 */
if (index)
pwr_ctrl_psci_smc(PM_EDP1, 1);
else
pwr_ctrl_psci_smc(PM_EDP0, 1);
LCDPR("[%d]: edp power domain on\n", index);
#endif
}
void edp_tx_ctrl(struct aml_lcd_drv_s *pdrv, int flag)
{
if (flag) {
edp_power_init(pdrv->index);
edp_tx_init(pdrv);
} else {
edp_tx_disable(pdrv);
}
}