blob: d183d8bc248074c26f14c12e3240732224f49a5d [file] [log] [blame]
/*
* drivers/amlogic/media/vout/lcd/lcd_extern/i2c_anx6345.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/amlogic/i2c-amlogic.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/amlogic/media/vout/lcd/lcd_extern.h>
#include "lcd_extern.h"
#include "i2c_anx6345.h"
static struct lcd_extern_config_s *ext_config;
static struct aml_lcd_extern_i2c_dev_s *i2c0_dev;
static struct aml_lcd_extern_i2c_dev_s *i2c1_dev;
static unsigned int edp_tx_lane = 1;
#define LCD_EXTERN_NAME "lcd_anx6345"
#define LCD_EXTERN_I2C_ADDR (0x38) /* 7bit address */
#define LCD_EXTERN_I2C_ADDR2 (0x39) /* 7bit address */
#define LCD_EXTERN_I2C_BUS LCD_EXTERN_I2C_BUS_2
static int SP_TX_Write_Reg(unsigned char addr, unsigned char reg,
unsigned char data)
{
struct i2c_client *client = aml_anx6345_70_client;
unsigned char buff[2];
int ret;
buff[0] = reg;
buff[1] = data;
if (addr == 0x70)
client = i2c0_dev->client;
else if (addr == 0x72)
client = i2c1_dev->client;
if (client == NULL) {
EXTERR("%s: invalid i2c client\n", __func__);
return -1;
}
ret = lcd_extern_i2c_write(client, buff, 1);
return ret;
}
static int SP_TX_Read_Reg(unsigned char addr, unsigned char reg,
unsigned char *data)
{
struct i2c_client *client = aml_anx6345_70_client;
int ret;
*data = reg;
if (addr == 0x70)
client = i2c0_dev->client;
else if (addr == 0x72)
client = i2c1_dev->client;
if (client == NULL) {
EXTERR("%s: invalid i2c client\n", __func__);
return -1;
}
ret = lcd_extern_i2c_read(client, data, 1);
return ret;
}
static int SP_TX_Wait_AUX_Finished(void)
{
unsigned char c;
unsigned char cCnt;
cCnt = 0;
SP_TX_Read_Reg(0x70, SP_TX_AUX_STATUS, &c);
while (c & 0x10) {/* aux busy */
cCnt++;
SP_TX_Read_Reg(0x70, SP_TX_AUX_STATUS, &c);
if (cCnt > 100)
return -1; /* aux fail */
}
return 0; /* aux ok */
}
static int SP_TX_AUX_DPCDRead_Bytes(unsigned char addrh, unsigned char addrm,
unsigned char addrl, unsigned char cCount, unsigned char *pBuf)
{
unsigned char c, i;
/* clr buffer */
SP_TX_Write_Reg(0x70, SP_TX_BUF_DATA_COUNT_REG, 0x80);
/* set read cmd and count */
SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG, (((cCount-1) << 4) | 0x09));
/* set aux address15:0 */
SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_7_0_REG, addrl);
SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_15_8_REG, addrm);
/* set address19:16 and enable aux */
SP_TX_Read_Reg(0x70, SP_TX_AUX_ADDR_19_16_REG, &c);
SP_TX_Write_Reg(0x70, SP_TX_AUX_ADDR_19_16_REG, ((c & 0xf0) | addrh));
/* Enable Aux */
SP_TX_Read_Reg(0x70, SP_TX_AUX_CTRL_REG2, &c);
SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG2, (c | 0x01));
mdelay(5);
if (SP_TX_Wait_AUX_Finished())
return -1;
for (i = 0; i < cCount; i++) {
SP_TX_Read_Reg(0x70, SP_TX_BUF_DATA_0_REG+i, &c);
*(pBuf+i) = c;
if (i >= MAX_BUF_CNT)
break;
}
return 0; /* aux ok */
}
static int lcd_extern_power_on(void)
{
unsigned int lane_num;
unsigned int link_rate;
unsigned char bits;
unsigned char device_id;
unsigned char temp;
unsigned char temp1;
unsigned int count = 0;
unsigned int count1 = 0;
lcd_extern_pinmux_set(1);
lane_num = edp_tx_lane; /* 1 lane */
link_rate = VAL_EDP_TX_LINK_BW_SET_270; /* 2.7G */
bits = 0; /* 0x00: 6bit; 0x10:8bit */
SP_TX_Write_Reg(0x72, 0x05, 0x00);
SP_TX_Read_Reg(0x72, 0x01, &device_id);
if (device_id == 0xaa) {
EXTPR("ANX6345 Chip found\n\n");
} else {
EXTERR("ANX6345 Chip not found\n\n");
return -1;
}
temp = device_id;
/* if aux read fail, do h/w reset */
while (SP_TX_AUX_DPCDRead_Bytes(0x00, 0x00, 0x00, 1, &temp1) &&
(count < 200)) {
/* read fail, h/w reset */
SP_TX_Write_Reg(0x72, 0x06, 0x01);
SP_TX_Write_Reg(0x72, 0x06, 0x00);
SP_TX_Write_Reg(0x72, 0x05, 0x00);
mdelay(10);
count++;
}
/* software reset */
SP_TX_Read_Reg(0x72, SP_TX_RST_CTRL_REG, &temp);
SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL_REG, (temp | SP_TX_RST_SW_RST));
SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL_REG, (temp & ~SP_TX_RST_SW_RST));
/* EDID address for AUX access */
SP_TX_Write_Reg(0x70, SP_TX_EXTRA_ADDR_REG, 0x50);
/* disable HDCP polling mode. */
SP_TX_Write_Reg(0x70, SP_TX_HDCP_CTRL, 0x00);
/* Enable HDCP polling mode. */
/* SP_TX_Write_Reg(0x70, SP_TX_HDCP_CTRL, 0x02); */
/* enable M value read out */
SP_TX_Write_Reg(0x70, SP_TX_LINK_DEBUG_REG, 0x30);
/* SP_TX_Read_Reg(0x70, SP_TX_DEBUG_REG1, &temp); */
SP_TX_Write_Reg(0x70, SP_TX_DEBUG_REG1, 0x00); /*disable polling HPD */
SP_TX_Read_Reg(0x70, SP_TX_HDCP_CONTROL_0_REG, &temp);
/* set KSV valid */
SP_TX_Write_Reg(0x70, SP_TX_HDCP_CONTROL_0_REG, (temp | 0x03));
SP_TX_Read_Reg(0x70, SP_TX_AUX_CTRL_REG2, &temp);
/* set double AUX output */
SP_TX_Write_Reg(0x70, SP_TX_AUX_CTRL_REG2, (temp | 0x08));
/* unmask pll change int */
SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK1, 0xbf);
SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK2, 0xff);/* mask all int */
SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK3, 0xff);/* mask all int */
SP_TX_Write_Reg(0x72, SP_COMMON_INT_MASK4, 0xff);/* mask all int */
/* reset AUX */
SP_TX_Read_Reg(0x72, SP_TX_RST_CTRL2_REG, &temp);
SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL2_REG, temp | SP_TX_AUX_RST);
SP_TX_Write_Reg(0x72, SP_TX_RST_CTRL2_REG, temp & (~SP_TX_AUX_RST));
/* Chip initialization */
SP_TX_Write_Reg(0x70, SP_TX_SYS_CTRL1_REG, 0x00);
mdelay(10);
SP_TX_Write_Reg(0x72, SP_TX_VID_CTRL2_REG, bits);
/* ANX6345 chip analog setting */
SP_TX_Write_Reg(0x70, SP_TX_PLL_CTRL_REG, 0x00);/* UPDATE: 0x07->0x00 */
/* ANX chip analog setting */
/* UPDATE: 0xF0->0X70 */
/* SP_TX_Write_Reg(0x72, ANALOG_DEBUG_REG1, 0x70); */
SP_TX_Write_Reg(0x70, SP_TX_LINK_DEBUG_REG, 0x30);
/* force HPD */
SP_TX_Write_Reg(0x70, SP_TX_SYS_CTRL3_REG, 0x30);
/* enable ssc function */
SP_TX_Write_Reg(0x70, 0xA7, 0x00); /* disable SSC first */
SP_TX_Write_Reg(0x70, 0xD0, 0x5f); /* ssc d 0.4%, f0/4 mode */
SP_TX_Write_Reg(0x70, 0xD1, 0x00);
SP_TX_Write_Reg(0x70, 0xD2, 0x75); /* ctrl_th */
SP_TX_Read_Reg(0x70, 0xA7, &temp);
SP_TX_Write_Reg(0x70, 0xA7, (temp | 0x10)); /* enable SSC */
SP_TX_Read_Reg(0x72, 0x07, &temp); /* reset SSC */
SP_TX_Write_Reg(0x72, 0x07, (temp | 0x80));
SP_TX_Write_Reg(0x72, 0x07, (temp & (~0x80)));
/* Select 2.7G */
/* 2.7g:0x0a; 1.62g:0x06 */
SP_TX_Write_Reg(0x70, SP_TX_LINK_BW_SET_REG, link_rate);
/* Select 2 lanes */
SP_TX_Write_Reg(0x70, 0xa1, lane_num);
SP_TX_Write_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG,
SP_TX_LINK_TRAINING_CTRL_EN);
mdelay(5);
SP_TX_Read_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG, &temp);
/* UPDATE: FROM 0X01 TO 0X80 */
while ((temp & 0x80) != 0) {
/* debug_puts("Waiting...\n"); */
mdelay(5);
count1++;
if (count1 > 100) {
EXTERR("ANX6345 Link training fail\n");
break;
}
SP_TX_Read_Reg(0x70, SP_TX_LINK_TRAINING_CTRL_REG, &temp);
}
SP_TX_Write_Reg(0x72, 0x12, 0x2c);
SP_TX_Write_Reg(0x72, 0x13, 0x06);
SP_TX_Write_Reg(0x72, 0x14, 0x00);
SP_TX_Write_Reg(0x72, 0x15, 0x06);
SP_TX_Write_Reg(0x72, 0x16, 0x02);
SP_TX_Write_Reg(0x72, 0x17, 0x04);
SP_TX_Write_Reg(0x72, 0x18, 0x26);
SP_TX_Write_Reg(0x72, 0x19, 0x50);
SP_TX_Write_Reg(0x72, 0x1a, 0x04);
SP_TX_Write_Reg(0x72, 0x1b, 0x00);
SP_TX_Write_Reg(0x72, 0x1c, 0x04);
SP_TX_Write_Reg(0x72, 0x1d, 0x18);
SP_TX_Write_Reg(0x72, 0x1e, 0x00);
SP_TX_Write_Reg(0x72, 0x1f, 0x10);
SP_TX_Write_Reg(0x72, 0x20, 0x00);
SP_TX_Write_Reg(0x72, 0x21, 0x28);
SP_TX_Write_Reg(0x72, 0x11, 0x03);
/* enable BIST. In normal mode, don't need to config this reg
* if want to open BIST, must setting
* right dat 0x11-0x21 base lcd timing.
*/
/* SP_TX_Write_Reg(0x72, 0x0b, 0x09); //colorbar:08,graystep:09 */
SP_TX_Write_Reg(0x72, 0x08, 0x81); /* SDR:0x81;DDR:0x8f */
/* force HPD and stream valid */
SP_TX_Write_Reg(0x70, 0x82, 0x33);
EXTPR("%s\n", __func__);
return 0;
}
static int lcd_extern_power_off(void)
{
int ret = 0;
lcd_extern_pinmux_set(0);
return ret;
}
static int lcd_extern_driver_update(struct aml_lcd_extern_driver_s *ext_drv)
{
if (ext_drv == NULL) {
EXTERR("%s: driver is null\n", LCD_EXTERN_NAME);
return -1;
}
ext_drv->power_on = lcd_extern_power_on;
ext_drv->power_off = lcd_extern_power_off;
return 0;
}
int aml_lcd_extern_i2c_anx6345_probe(struct aml_lcd_extern_driver_s *ext_drv)
{
int ret = 0;
ext_config = ext_drv->config;
i2c0_dev = lcd_extern_get_i2c_device(ext_config->i2c_addr);
if (i2c0_dev == NULL) {
EXTERR("invalid i2c0 device\n");
return -1;
}
EXTPR("get i2c0 device: %s, addr 0x%02x OK\n",
i2c0_dev->name, i2c0_dev->client->addr);
i2c1_dev = lcd_extern_get_i2c_device(ext_config->i2c_addr2);
if (i2c1_dev == NULL) {
EXTERR("invalid i2c1 device\n");
i2c0_dev = NULL;
return -1;
}
EXTPR("get i2c1 device: %s, addr 0x%02x OK\n",
i2c1_dev->name, i2c1_dev->client->addr);
ret = lcd_extern_driver_update(ext_drv);
if (lcd_debug_print_flag)
EXTPR("%s: %d\n", __func__, ret);
return ret;
}
int aml_lcd_extern_i2c_anx6345_remove(void)
{
i2c0_dev = NULL;
i2c1_dev = NULL;
ext_config = NULL;
return 0;
}