tegrakernel/kernel/nvidia/drivers/video/tegra/dc/bridge/hdmi2dsi_tc358870.c

1491 lines
39 KiB
C
Raw Normal View History

2022-02-16 09:13:02 -06:00
/*
* tc358870.c: HDMI to DSI bridge driver.
*
* Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/debugfs.h>
#include "panel/board-panel.h"
#include "dc.h"
#include "dc_priv.h"
#include "hdmi2dsi_tc358870.h"
#include "hdmi2dsi_tc358870_regs.h"
#include "edid.h"
#include "hdmi2.0.h"
static struct tc358870_state *gstate[MAX_BRIDGE_INSTANCES];
static int greset_gpio[MAX_BRIDGE_INSTANCES];
/* To get parent (struct tc358870_state) of memeber (i2c_client *) */
static inline struct tc358870_state *to_state(struct i2c_client *client)
{
return container_of(&client, struct tc358870_state, i2c_client);
}
/* --------------- I2C --------------- */
static void i2c_rd(struct tc358870_state *state, u16 reg, u8 *values, u32 n)
{
struct i2c_client *client = state->i2c_client;
int err;
u8 buf[REG_LEN] = { reg >> 8, reg & 0xff };
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = REG_LEN,
.buf = buf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = n,
.buf = values,
},
};
err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (err != ARRAY_SIZE(msgs)) {
dev_err(&client->dev,
"%s: reading register 0x%x from 0x%x failed\n",
__func__, reg, client->addr);
}
dev_dbg(&client->dev, "I2C read %d bytes from 0x%04X = 0x%02X\n",
n, reg, values[0]);
}
static void i2c_wr(struct tc358870_state *state, u16 reg, u8 *values, u32 n)
{
struct i2c_client *client = state->i2c_client;
int err, i;
struct i2c_msg msg;
u8 data[REG_LEN + n];
msg.addr = client->addr;
msg.buf = data;
msg.len = REG_LEN + n;
msg.flags = 0;
data[0] = reg >> 8;
data[1] = reg & 0xff;
for (i = 0; i < n; i++)
data[REG_LEN + i] = values[i];
err = i2c_transfer(client->adapter, &msg, 1);
if (err != 1) {
dev_err(&client->dev,
"%s: writing register 0x%x from 0x%x failed\n",
__func__, reg, client->addr);
return;
}
dev_dbg(&client->dev, "I2C write %d bytes in 0x%04X = 0x%02X\n",
n, reg, data[REG_LEN]);
}
static u8 i2c_rd8(struct tc358870_state *state, u16 reg)
{
u8 val;
i2c_rd(state, reg, &val, 1);
return val;
}
static void i2c_wr8(struct tc358870_state *state, u16 reg, u8 val)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s reg = 0x%04x val = 0x%04x",
__func__, reg, val);
i2c_wr(state, reg, &val, 1);
}
static void i2c_wr8_and_or(struct tc358870_state *state, u16 reg,
u8 mask, u8 val)
{
i2c_wr8(state, reg, (i2c_rd8(state, reg) & mask) | val);
}
static u16 i2c_rd16(struct tc358870_state *state, u16 reg)
{
u16 val;
i2c_rd(state, reg, (u8 *)&val, 2);
return val;
}
static void i2c_wr16(struct tc358870_state *state, u16 reg, u16 val)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s reg = 0x%04x val = 0x%04x",
__func__, reg, val);
i2c_wr(state, reg, (u8 *)&val, 2);
}
static u32 i2c_rd32(struct tc358870_state *state, u16 reg)
{
u32 val;
i2c_rd(state, reg, (u8 *)&val, 4);
return val;
}
static void i2c_wr32(struct tc358870_state *state, u16 reg, u32 val)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s reg = 0x%04x val = 0x%08x",
__func__, reg, val);
i2c_wr(state, reg, (u8 *)&val, 4);
}
static void i2c_wr32_and_or(struct tc358870_state *state, u32 reg,
u32 mask, u32 val)
{
i2c_wr32(state, reg, (i2c_rd32(state, reg) & mask) | val);
}
static inline bool is_hdmi(struct tc358870_state *state)
{
return i2c_rd8(state, SYS_STATUS) & MASK_S_HDMI;
}
/* Ctl set */
static void tc358870_set_ctl(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr16(state, CONFCTL0, MASK_AUTOINDEX);
i2c_wr16(state, SYSCTL, MASK_RESET_ALL);
i2c_wr16(state, SYSCTL, MASK_NORMAL);
i2c_wr16(state, CONFCTL1, MASK_INIT);
}
/* Split control */
static void tc358870_split_control(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr16(state, SPLITTX0_CTRL, MASK_SP_EN_SPLIT);
i2c_wr16(state, SPLITTX0_SPLIT, MASK_SPLIT_TXHW_AUTO);
i2c_wr16(state, SPLITTX1_CTRL, MASK_SP_EN_SPLIT);
}
/* HDMI Phy */
static void tc358870_hdmi_phy(struct tc358870_state *state, bool enable)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s: enable = %d\n", __func__, enable);
if (enable) {
i2c_wr8(state, PHY_CTL, MASK_POWERCTL | MASK_48_MHZ);
i2c_wr8_and_or(state, PHY_ENB, ~MASK_ENABLE_PHY,
MASK_ENABLE_PHY);
i2c_wr8_and_or(state, EQ_BYPS, ~DISABLE_BYPS, DISABLE_BYPS);
i2c_wr8(state, APPL_CTL, MASK_APLL_CPCTL | MASK_APLL_ON);
i2c_wr8(state, DDCIO_CTL, MASK_DDC_PWR_ON);
} else {
i2c_wr8(state, PHY_CTL, MASK_48_MHZ);
i2c_wr8(state, APPL_CTL, MASK_APLL_CPCTL);
i2c_wr8_and_or(state, PHY_ENB, ~MASK_ENABLE_PHY, 0x0);
}
}
/* HDMI Clock */
static void tc358870_hdmi_clock(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
struct tc358870_platform_data *pdata = &state->pdata;
u32 lock_ref_freq;
u16 sys_freq;
u16 csc_freq;
dev_dbg(&client->dev, "%s:\n", __func__);
/* The refclk should be between 40 and 50 Mhz */
WARN_ON((pdata->refclk_hz < MIN_REFCLK) ||
(pdata->refclk_hz > MAX_REFCLK));
/* System clock is set as clock frequency divided by 10000 */
sys_freq = pdata->refclk_hz / SYS_FREQ_DIVIDER;
i2c_wr16(state, SYS_FREQ0, sys_freq);
/* Audio system frequency is set as clock frequency divided by 100 */
lock_ref_freq = pdata->refclk_hz / LOCK_FREQ_DIVIDER;
i2c_wr8(state, LOCK_REF_FREQA, lock_ref_freq & MASK_LOCK_REF_FREQA);
i2c_wr16(state, LOCK_REF_FREQB, lock_ref_freq >> 8);
/* Audio PLL setting */
i2c_wr8(state, NCO_F0_MOD, MASK_NCO_F0_MOD_REG);
/* CSC clock is set as clock frequency divided by 10000 */
csc_freq = pdata->refclk_hz / CSC_FREQ_DIVIDER;
i2c_wr16(state, SCLK_CSC0, csc_freq);
}
/* HDMI interrupt */
static void tc358870_hdmi_interrupt(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr8(state, MISC_INT, 0xFF);
i2c_wr8(state, MISC_INTM, ~MASK_SYNC_CHG);
i2c_wr16(state, INT_STATUS, MASK_INT_STATUS_MASK_ALL);
i2c_wr16(state, INT_MASK, MASK_INT_MASK_MASK_ALL);
}
/* Video out format */
static void tc358870_set_vout(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr8(state, VOUT_SYNC0, M3_VSIZE_INIT | MASK_MODE_2);
}
/* HDMI System */
static void tc358870_set_hdmi_system(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr8(state, DDC_CTL, MASK_DDC5V_MODE_100MS);
i2c_wr8(state, HPD_CTL, MASK_HPD_CTL0);
}
/* HDMI Source start access */
static void tc358870_hdmi_start_access(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr8(state, INIT_END, MASK_INIT_END);
}
/* HDMI Audio refclk */
static void tc358870_hdmi_audio_refclk(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
struct tc358870_platform_data *pdata = &state->pdata;
u32 nco_48;
u32 nco_44;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr8(state, FORCE_MUTE, MASK_MUTE_OFF);
i2c_wr8(state, AUTO_CMD0, MASK_AUTO_MUTE7 | MASK_AUTO_MUTE6 |
MASK_AUTO_MUTE5 | MASK_AUTO_MUTE4 |
MASK_AUTO_MUTE1 | MASK_AUTO_MUTE0);
i2c_wr8(state, AUTO_CMD1, MASK_AUTO_MUTE9);
i2c_wr8(state, AUTO_CMD2, MASK_AUTO_PLAY3 | MASK_AUTO_PLAY2);
i2c_wr8(state, BUFINIT_START, SET_BUFINIT_START_MS(500));
i2c_wr8(state, FS_MUTE, MASK_FS_UNMUTE_ALL);
i2c_wr8(state, SDO_MODE1, MASK_SDO_FMT_I2S);
/* For 48 kHz, 6.144 MHz * 2^28 = 1649267442 */
nco_48 = NCO_48_CALC / (pdata->refclk_hz / MHZ);
i2c_wr32(state, NCO_48F0A, nco_48);
/* For 44 kHz, 5.644 MHz * 2^28 = 1515264462 */
nco_44 = NCO_44_CALC / (pdata->refclk_hz / MHZ);
i2c_wr32(state, NCO_44F0A, nco_44);
i2c_wr8(state, HDMIAUDIO_MODE, MASK_NORMAL_AUDIO);
}
/* HDMI end of RX init */
static void tc358870_hdmi_end_rxinit(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr16(state, CONFCTL0, MASK_VTX0EN | MASK_VTX1EN | MASK_AUTOINDEX |
MASK_AUDOUTSEL_I2S | MASK_ABUFEN);
i2c_wr16(state, CONFCTL1, MASK_TX_OUT_FMT_RGB888);
}
/* Command transmission after video */
static void tc358870_transmission_after_video(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr32(state, DSITX0_BASE_ADDR + MODECONF,
MASK_VSYNC_POL_SW | MASK_HSYNC_POL_SW);
i2c_wr32(state, DSITX1_BASE_ADDR + MODECONF,
MASK_VSYNC_POL_SW | MASK_HSYNC_POL_SW);
}
/* EDID */
static void tc358870_set_edid(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
/* EDID should be set from DT, if not, do it here. */
}
static void tc358870_set_pll(struct tc358870_state *state,
enum tc358870_dsi_port port)
{
struct tc358870_platform_data *pdata = &state->pdata;
struct i2c_client *client = state->i2c_client;
u16 base_addr;
u16 pll_frs;
u32 pllconf;
u32 hsck;
dev_dbg(&client->dev, "%s:\n", __func__);
WARN_ON((pdata->dsi_port <= DSI_TX_NONE) ||
(pdata->dsi_port > DSI_TX_BOTH));
if (pdata->dsi_port == DSI_TX_NONE) {
dev_err(&client->dev, "%s: No DSI port defined!\n", __func__);
return;
}
base_addr = (port == DSI_TX_0) ? DSITX0_BASE_ADDR :
DSITX1_BASE_ADDR;
pllconf = SET_PLL_PRD(pdata->pll_prd) |
SET_PLL_FBD(pdata->pll_fbd);
hsck = (pdata->refclk_hz / pdata->pll_prd) *
pdata->pll_fbd;
if (hsck > MHZ_500)
pll_frs = 0x0;
else if (hsck > MHZ_250)
pll_frs = 0x1;
else if (hsck > MHZ_125)
pll_frs = 0x2;
else
pll_frs = 0x3;
dev_dbg(&client->dev, "%s: Updating PLL clock of DSI TX%d\n",
__func__, port-1);
i2c_wr32(state, base_addr + PLLCONF, pllconf | SET_PLL_FRS(pll_frs));
}
static void tc358870_set_dsi(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
struct tc358870_platform_data *pdata = &state->pdata;
struct panel_out *pout = &state->pout;
unsigned lanes = pout->n_data_lanes / 2;
enum tc358870_dsi_port port;
u16 base_addr;
dev_dbg(&client->dev, "%s:\n", __func__);
for (port = DSI_TX_0; port <= DSI_TX_1; port++) {
base_addr = (port == DSI_TX_0) ? DSITX0_BASE_ADDR :
DSITX1_BASE_ADDR;
if (pdata->dsi_port != DSI_TX_BOTH &&
pdata->dsi_port != port) {
dev_info(&client->dev,
"%s: Disabling DSI TX%d\n", __func__, port - 1);
/* Disable DSI lanes (high Z) */
i2c_wr32_and_or(state, base_addr + LANEEN,
~(MASK_CLANEEN), 0);
continue;
}
dev_dbg(&client->dev, "%s: Enabling DSI TX%d\n",
__func__, port - 1);
/* 0x0108 */
i2c_wr32(state, base_addr + DSITX_CLKEN, MASK_DSITX_EN);
/* 0x010C */
i2c_wr32(state, base_addr + PPI_CLKEN, MASK_HSTXCLKEN);
/* 0x02A0 */
i2c_wr32(state, base_addr + MIPI_CLKEN, MASK_MP_ENABLE);
tc358870_set_pll(state, port);
usleep_range(10000, 11000);
/* 0x02A0 */
i2c_wr32(state, base_addr + MIPI_CLKEN,
MASK_MP_ENABLE | MASK_MP_CKEN);
/* 0x0118 */
i2c_wr32(state, base_addr + LANEEN,
(lanes & MASK_LANES) | MASK_CLANEEN);
/* 0x0120 */
i2c_wr32(state, base_addr + LINEINITCNT, pdata->lineinitcnt);
/* 0x0124 */
i2c_wr32(state, base_addr + HSTOCNT, MASK_ZERO_32);
/* 0x0128 */
i2c_wr32(state, base_addr + FUNCEN,
MASK_VH_DLY_EN | MASK_IND_MODE_SEL_REG);
/* 0x0130 */
i2c_wr32(state, base_addr + DSI_TATO_COUNT,
MASK_DSI_TATO_COUNT_INIT);
/* 0x0134 */
i2c_wr32(state, base_addr + DSI_PRESP_BTA_COUNT,
MASK_DSI_PRESP_BTA_COUNT_INIT);
/* 0x0138 */
i2c_wr32(state, base_addr + DSI_PRESP_LPR_COUNT,
MASK_DSI_PRESP_LPR_COUNT_INIT);
/* 0x013C */
i2c_wr32(state, base_addr + DSI_PRESP_LPW_COUNT,
DSI_PRESP_LPW_COUNT);
/* 0x0140 */
i2c_wr32(state, base_addr + DSI_PRESP_HSR_COUNT,
DSI_PRESP_LPW_COUNT);
/* 0x0144 */
i2c_wr32(state, base_addr + DSI_PRESP_HSW_COUNT,
DSI_PRESP_LPW_COUNT);
/* 0x0148 */
i2c_wr32(state, base_addr + DSI_PR_TO_COUNT,
MASK_DSI_PR_TO_COUNT_INIT);
/* 0x014C */
i2c_wr32(state, base_addr + DSI_LRX_H_TO_COUNT,
MASK_DSI_LRX_H_TO_COUNT_INIT);
/* 0x0150 */
i2c_wr32(state, base_addr + FUNCMODE,
MASK_EO_TP_EN | MASK_CRC_DIS | MASK_ECC_DIS);
/* 0x0154 */
i2c_wr32(state, base_addr + DSIRX_VC_ENABLE, MASK_RXVC0_EN);
/* 0x0158 */
i2c_wr32(state, base_addr + IND_TO_COUNT,
MASK_INT_TO_COUNT_INIT);
/* 0x0168 */
i2c_wr32(state, base_addr + DSI_HSYNC_STOP_COUNT,
MASK_INIT_HSYNC_STOP_COUNT);
/* 0x0170 */
i2c_wr32(state, base_addr + APF_VDELAY_CNT,
MASK_INIT_APF_DELAY);
/* 0x017C */
i2c_wr32(state, base_addr + DSITX_MODE,
MASK_BLANKPKT_EN | MASK_DSITX_MODE_EVENT);
/* 0x018C */
i2c_wr32(state, base_addr + DSI_HSYNC_WIDTH,
MASK_HSYNC_WIDTH_INIT);
/* 0x0190 */
i2c_wr32(state, base_addr + DSI_HBPR, MASK_HBPR_INIT);
/* 0x01A4 */
i2c_wr32(state, base_addr + DSI_RX_STATE_INT_MASK,
MASK_ZERO_32);
/* 0x01C0 */
i2c_wr32(state, base_addr + DSI_LPRX_THRESH_COUNT,
MASK_LPRX_THRESH_COUNT_INIT);
/* 0x0214 */
i2c_wr32(state, base_addr + APP_SIDE_ERR_INT_MASK,
MASK_ZERO_32);
/* 0x021C */
i2c_wr32(state, base_addr + DSI_RX_ERR_INT_MASK,
MASK_ERR_REPORT_7);
/* 0x0224 */
i2c_wr32(state, base_addr + DSI_LPTX_INT_MASK, MASK_ZERO_32);
/* 0x0254 */
i2c_wr32(state, base_addr + LPTXTIMECNT, pdata->lptxtimecnt);
/* 0x0258 */
i2c_wr32(state, base_addr + TCLK_HEADERCNT,
pdata->tclk_headercnt);
/* 0x025C */
i2c_wr32(state, base_addr + TCLK_TRAILCNT,
pdata->tclk_trailcnt);
/* 0x0260 */
i2c_wr32(state, base_addr + THS_HEADERCNT,
pdata->ths_headercnt);
/* 0x0264 */
i2c_wr32(state, base_addr + TWAKEUP, pdata->twakeup);
/* 0x0268 */
i2c_wr32(state, base_addr + TCLK_POSTCNT, pdata->tclk_postcnt);
/* 0x026C */
i2c_wr32(state, base_addr + THS_TRAILCNT, pdata->ths_trailcnt);
/* 0x0270 */
i2c_wr32(state, base_addr + HSTXVREGCNT, pdata->hstxvregcnt);
/* 0x0274 */
i2c_wr32(state, base_addr + HSTXVREGEN,
((lanes > 0) ? MASK_CLM_HSTXVREGEN : 0x0) |
((lanes > 0) ? MASK_D0M_HSTXVREGEN : 0x0) |
((lanes > 1) ? MASK_D1M_HSTXVREGEN : 0x0) |
((lanes > 2) ? MASK_D2M_HSTXVREGEN : 0x0) |
((lanes > 3) ? MASK_D3M_HSTXVREGEN : 0x0));
/* 0x0278 */
i2c_wr32(state, base_addr + PPI_DSI_BTA_COUNT, pdata->btacnt);
/* 0x027C */
i2c_wr32(state, base_addr + PPI_DPHYTX_ADJUST,
MASK_LPTX_25_OUT_CUR);
/* 0x011C */
i2c_wr32(state, base_addr + DSITX_START, MASK_DSITX_START);
}
}
/* Panel functions */
static int dcs_panel_command(struct tc358870_state *state,
struct tegra_dsi_cmd *cmd, u32 n_cmd)
{
struct i2c_client *client = state->i2c_client;
struct tegra_dsi_cmd *cur_cmd = NULL;
int err = 0;
u32 index = 0;
u16 data0 = 0, data1, len = 0;
u8 *pdata = NULL;
u16 status = 0;
dev_dbg(&client->dev, "%s n_cmd is %d\n", __func__, n_cmd);
if (cmd == NULL || n_cmd == 0) {
dev_err(&client->dev, "%s: cmd is empty\n", __func__);
err = -EINVAL;
return err;
}
for (index = 0; index < n_cmd; index++) {
cur_cmd = &cmd[index];
status = i2c_rd16(state, DCSCMD_ST);
dev_dbg(&client->dev, "dcs status = 0x%04x index = %d\n",
status, index);
if (cur_cmd->cmd_type == TEGRA_DSI_DELAY_MS) {
/* DSI Delay Command in milliseconds */
usleep_range(cur_cmd->sp_len_dly.delay_ms * 1000,
(cur_cmd->sp_len_dly.delay_ms * 1000) + 500);
} else if (cur_cmd->cmd_type == TEGRA_DSI_DELAY_US) {
/* DSI Delay Command in microseconds */
usleep_range(cur_cmd->sp_len_dly.delay_us,
(cur_cmd->sp_len_dly.delay_us) + 100);
} else if (cur_cmd->cmd_type == TEGRA_DSI_PACKET_CMD) {
/* DSI Packet Command */
if (cur_cmd->data_id == DSI_DCS_WRITE_0_PARAM) {
/* DSI_DCS_WRITE_0_PARAM */
data0 = cur_cmd->sp_len_dly.sp.data0 &
DATA0_MASK;
i2c_wr16(state, DCSCMD_Q,
DSI_DCS_WRITE_0_PARAM);
i2c_wr16(state, DCSCMD_Q, data0);
dev_dbg(&client->dev, "0x%04x 0x%04x\n",
DSI_DCS_WRITE_0_PARAM, data0);
} else if (cur_cmd->data_id == DSI_DCS_WRITE_1_PARAM ||
cur_cmd->data_id ==
DSI_GENERIC_SHORT_WRITE_2_PARAMS) {
/* DSI_DCS_WRITE_1_PARAM */
data0 = cur_cmd->sp_len_dly.sp.data0 &
DATA0_MASK;
data1 = (cur_cmd->sp_len_dly.sp.data1 << 8) &
DATA1_MASK;
i2c_wr16(state, DCSCMD_Q, cur_cmd->data_id);
i2c_wr16(state, DCSCMD_Q, data1 | data0);
dev_dbg(&client->dev, "0x%04x 0x%04x\n",
cur_cmd->data_id, data1 | data0);
} else if (cur_cmd->data_id == DSI_DCS_LONG_WRITE) {
/* DSI_DCS_LONG_WRITE */
len = cur_cmd->sp_len_dly.data_len;
pdata = cur_cmd->pdata;
if (len == 0 && pdata == NULL) {
/* Short packet with no payload */
i2c_wr16(state, DCSCMD_Q,
DSI_DCS_LONG_WRITE);
i2c_wr16(state, DCSCMD_Q, data0);
} else {
i2c_wr16(state, DCSCMD_Q,
LONG_PKT_MASK | DSI_DCS_LONG_WRITE);
while (len) {
if (len >= 2) {
data0 =
((u16 *) pdata)[0];
len -= 2;
pdata += 2;
} else {
data0 =
((u8 *) pdata)[0] &
DATA0_MASK;
pdata += len;
len = 0;
}
i2c_wr16(state, DCSCMD_Q,
data0);
dev_dbg(&client->dev,
"0x%04x 0x%04x\n",
LONG_PKT_MASK |
DSI_DCS_LONG_WRITE,
data0);
}
}
}
}
}
return err;
}
/* DCS commands post transmission */
static int dcs_post_transmission(struct tc358870_state *state)
{
int err = 0;
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
err = dcs_panel_command(state, state->pout.dsi_postvideo_cmd,
state->pout.n_postvideo_cmd);
return err;
}
static int dcs_enable(struct tc358870_state *state)
{
int err = 0;
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
i2c_wr16(state, DCSCMD_SEL, MASK_CMD_SEL_BOTH);
i2c_wr32(state, DSITX0_BASE_ADDR + MODECONF,
MASK_VSYNC_POL_SW | MASK_HSYNC_POL_SW | MASK_INDMODE);
i2c_wr32(state, DSITX1_BASE_ADDR + MODECONF,
MASK_VSYNC_POL_SW | MASK_HSYNC_POL_SW | MASK_INDMODE);
err = dcs_panel_command(state, state->pout.dsi_init_cmd,
state->pout.n_init_cmd);
return err;
}
static int dcs_disable(struct tc358870_state *state)
{
int err = 0;
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
err = dcs_panel_command(state, state->pout.dsi_suspend_cmd,
state->pout.n_suspend_cmd);
return err;
}
#ifdef CONFIG_OF
static struct tegra_dsi_cmd *parse_cmd_dt(struct i2c_client *client,
const struct device_node *node,
struct property *prop,
u32 n_cmd)
{
struct tegra_dsi_cmd *dsi_cmd = NULL;
dsi_cmd = dsi_parse_cmd_dt(&client->dev, node, prop, n_cmd);
return dsi_cmd;
}
static bool panel_parse_dt(struct panel_out *pout,
struct i2c_client *client)
{
struct device_node *np_panel = NULL;
u32 temp;
bool ret = true;
np_panel = of_get_next_child(client->dev.of_node, NULL);
if (!of_property_read_u32(np_panel,
"nvidia,dsi-n-data-lanes", &temp)) {
pout->n_data_lanes = (u8)temp;
dev_dbg(&client->dev, "n data lanes %d\n", pout->n_data_lanes);
}
if (!of_property_read_u32(np_panel,
"nvidia,dsi-pixel-format", &temp)) {
pout->pixel_format = (u8)temp;
dev_dbg(&client->dev, "pixel format %d\n", pout->pixel_format);
}
if (!of_property_read_u32(np_panel,
"nvidia,dsi-refresh-rate", &temp)) {
pout->refresh_rate = (u8)temp;
dev_dbg(&client->dev, "refresh rate %d\n", pout->refresh_rate);
}
if (!of_property_read_u32(np_panel,
"nvidia,dsi-n-init-cmd", &temp)) {
pout->n_init_cmd = (u16)temp;
dev_dbg(&client->dev, "n init cmd %d\n", pout->n_init_cmd);
}
pout->dsi_init_cmd = parse_cmd_dt(client, np_panel,
of_find_property(np_panel, "nvidia,dsi-init-cmd", NULL),
pout->n_init_cmd);
if (pout->n_init_cmd &&
IS_ERR_OR_NULL(pout->dsi_init_cmd)) {
dev_err(&client->dev, "dsi: copy init cmd from dt failed\n");
ret = false;
goto parse_dsi_settings_fail;
};
if (!of_property_read_u32(np_panel,
"nvidia,dsi-n-postvideo-cmd", &temp)) {
pout->n_postvideo_cmd = (u16)temp;
dev_dbg(&client->dev, "n postvideo cmd %d\n",
pout->n_postvideo_cmd);
}
pout->dsi_postvideo_cmd = parse_cmd_dt(client, np_panel,
of_find_property(np_panel,
"nvidia,dsi-postvideo-cmd", NULL),
pout->n_postvideo_cmd);
if (pout->n_postvideo_cmd &&
IS_ERR_OR_NULL(pout->dsi_postvideo_cmd)) {
dev_err(&client->dev, "dsi: copy init cmd from dt failed\n");
ret = false;
goto parse_dsi_settings_fail;
};
if (!of_property_read_u32(np_panel,
"nvidia,dsi-n-suspend-cmd", &temp)) {
pout->n_suspend_cmd = (u16)temp;
dev_dbg(&client->dev, "n suspend cmd %d\n",
pout->n_suspend_cmd);
}
pout->dsi_suspend_cmd = parse_cmd_dt(client, np_panel,
of_find_property(np_panel,
"nvidia,dsi-suspend-cmd", NULL),
pout->n_suspend_cmd);
if (pout->n_suspend_cmd &&
IS_ERR_OR_NULL(pout->dsi_suspend_cmd)) {
dev_err(&client->dev, "dsi: copy suspend cmd from dt failed\n");
ret = false;
goto parse_dsi_settings_fail;
};
parse_dsi_settings_fail:
of_node_put(np_panel);
return ret;
}
static bool tc358870_parse_dt(struct tc358870_platform_data *pdata,
struct i2c_client *client)
{
struct device_node *node = client->dev.of_node;
const u32 *property;
dev_dbg(&client->dev, "Device Tree Parameters:\n");
pdata->reset_gpio = of_get_named_gpio(node, "reset-gpios", 0);
if (pdata->reset_gpio == 0)
return false;
dev_dbg(&client->dev, "reset_gpio = %d\n", pdata->reset_gpio);
property = of_get_property(node, "bridge-instance", NULL);
if (property == NULL)
return false;
pdata->bridge_instance = be32_to_cpup(property);
dev_dbg(&client->dev, "bridge_instance = %d\n",
pdata->bridge_instance);
property = of_get_property(node, "refclk_hz", NULL);
if (property == NULL)
return false;
pdata->refclk_hz = be32_to_cpup(property);
dev_dbg(&client->dev, "refclk_hz = %d\n", be32_to_cpup(property));
property = of_get_property(node, "ddc5v_delay", NULL);
if (property == NULL)
return false;
pdata->ddc5v_delay = be32_to_cpup(property);
if (pdata->ddc5v_delay > DDC5V_DELAY_MAX)
pdata->ddc5v_delay = DDC5V_DELAY_MAX;
dev_dbg(&client->dev, "ddc5v_delay = %d ms\n",
50 * pdata->ddc5v_delay);
property = of_get_property(node, "enable_hdcp", NULL);
if (property == NULL)
return false;
pdata->enable_hdcp = be32_to_cpup(property);
dev_dbg(&client->dev, "enable_hdcp = %d\n", be32_to_cpup(property));
property = of_get_property(node, "dsi_port", NULL);
if (property == NULL)
return false;
pdata->dsi_port = be32_to_cpup(property);
dev_dbg(&client->dev, "dsi_port = %d\n", be32_to_cpup(property));
property = of_get_property(node, "lineinitcnt", NULL);
if (property == NULL)
return false;
pdata->lineinitcnt = be32_to_cpup(property);
dev_dbg(&client->dev, "lineinitcnt = %d\n", be32_to_cpup(property));
property = of_get_property(node, "lptxtimecnt", NULL);
if (property == NULL)
return false;
pdata->lptxtimecnt = be32_to_cpup(property);
dev_dbg(&client->dev, "lptxtimecnt = %d\n", be32_to_cpup(property));
property = of_get_property(node, "tclk_headercnt", NULL);
if (property == NULL)
return false;
pdata->tclk_headercnt = be32_to_cpup(property);
dev_dbg(&client->dev, "tclk_headercnt = %d\n",
be32_to_cpup(property));
property = of_get_property(node, "tclk_trailcnt", NULL);
if (property == NULL)
return false;
pdata->tclk_trailcnt = be32_to_cpup(property);
dev_dbg(&client->dev, "tclk_trailcnt = %d\n",
be32_to_cpup(property));
property = of_get_property(node, "ths_headercnt", NULL);
if (property == NULL)
return false;
pdata->ths_headercnt = be32_to_cpup(property);
dev_dbg(&client->dev, "ths_headercnt = %d\n",
be32_to_cpup(property));
property = of_get_property(node, "twakeup", NULL);
if (property == NULL)
return false;
pdata->twakeup = be32_to_cpup(property);
dev_dbg(&client->dev, "twakeup = %d\n", be32_to_cpup(property));
property = of_get_property(node, "tclk_postcnt", NULL);
if (property == NULL)
return false;
pdata->tclk_postcnt = be32_to_cpup(property);
dev_dbg(&client->dev, "tclk_postcnt = %d\n",
be32_to_cpup(property));
property = of_get_property(node, "ths_trailcnt", NULL);
if (property == NULL)
return false;
pdata->ths_trailcnt = be32_to_cpup(property);
dev_dbg(&client->dev, "ths_trailcnt = %d\n",
be32_to_cpup(property));
property = of_get_property(node, "hstxvregcnt", NULL);
if (property == NULL)
return false;
pdata->hstxvregcnt = be32_to_cpup(property);
dev_dbg(&client->dev, "hstxvregcnt = %d\n", be32_to_cpup(property));
property = of_get_property(node, "pll_prd", NULL);
if (property == NULL)
return false;
pdata->pll_prd = be32_to_cpup(property);
dev_dbg(&client->dev, "pll_prd = %d\n", be32_to_cpup(property));
property = of_get_property(node, "pll_fbd", NULL);
if (property == NULL)
return false;
pdata->pll_fbd = be32_to_cpup(property);
dev_dbg(&client->dev, "pll_fbd = %d\n", be32_to_cpup(property));
return true;
}
#endif
static int tc358870_pwr_init(struct tc358870_platform_data *pdata,
struct i2c_client *client)
{
int err = 0;
pdata->iovdd = regulator_get(&client->dev, "vdd-boe-1v8");
if (IS_ERR_OR_NULL(pdata->iovdd)) {
dev_err(&client->dev, "cannot get regulator vdd-boe-1v8\n");
err = PTR_ERR(pdata->iovdd);
pdata->iovdd = NULL;
goto fail;
}
pdata->dvdd = regulator_get(&client->dev, "vdd-boe-1v2");
if (IS_ERR_OR_NULL(pdata->dvdd)) {
dev_err(&client->dev, "cannot get regulator vdd-boe-1v2\n");
err = PTR_ERR(pdata->dvdd);
pdata->dvdd = NULL;
goto dvdd_fail;
}
if (pdata->dvdd) {
err = regulator_enable(pdata->dvdd);
if (err < 0) {
dev_err(&client->dev,
"cannot enable vdd-boe-1v8 %d\n", err);
return -EINVAL;
}
}
if (pdata->iovdd) {
err = regulator_enable(pdata->iovdd);
if (err < 0) {
dev_err(&client->dev,
"cannot enable vdd-boe-1v2 %d\n", err);
return -EINVAL;
}
}
return err;
dvdd_fail:
if (pdata->iovdd) {
regulator_put(pdata->iovdd);
pdata->iovdd = NULL;
}
fail:
return err;
}
static int tc358870_verify_chipid(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
u16 cid = 0;
cid = i2c_rd16(state, CHIPID_ADDR);
if (cid != TC358870_CHIPID) {
dev_err(&client->dev, "Invalid chip ID 0x%04X\n", cid);
return -ENODEV;
}
dev_dbg(&client->dev, "TC358870 ChipID 0x%02x, Revision 0x%02x\n",
(cid & MASK_CHIPID) >> 8, cid & MASK_REVID);
return 0;
}
static int hdmi2dsi_tc358870_en_gpio(struct tc358870_state *state,
bool enable)
{
int err = 0;
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "Setting reset-gpio (gpio 0x%04X)\n",
state->pdata.reset_gpio);
err = gpio_is_valid(state->pdata.reset_gpio);
if (!err) {
dev_err(&client->dev, "reset GPIO is invalid!\n");
return err;
}
if (enable)
err = gpio_direction_output(state->pdata.reset_gpio,
GPIOF_OUT_INIT_HIGH);
else
err = gpio_direction_output(state->pdata.reset_gpio,
GPIOF_OUT_INIT_LOW);
if (err < 0) {
dev_err(&client->dev, "Failed to set reset GPIO 0x%04X: %d\n",
state->pdata.reset_gpio, err);
return err;
}
return err;
}
static void tc358870_initial_setup(struct tc358870_state *state)
{
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
tc358870_set_ctl(state);
tc358870_set_dsi(state);
tc358870_split_control(state);
tc358870_hdmi_phy(state, true);
tc358870_hdmi_clock(state);
tc358870_hdmi_interrupt(state);
tc358870_set_edid(state);
tc358870_set_vout(state);
tc358870_set_hdmi_system(state);
tc358870_hdmi_audio_refclk(state);
state->enabled = true;
state->power_down = false;
}
static int hdmi2dsi_tc358870_init(struct tegra_hdmi *hdmi)
{
int sor_num = 0;
int err = 0;
/* map the right bridge instance to hdmi instance.
* hdmi is identified by the sor_num and the bridge is
* identified by the bridge instance parsed from bridge DT
*/
struct tc358870_state *state = gstate[sor_num];
struct i2c_client *client = NULL;
if (hdmi == NULL) {
pr_err("hdmi is NULL %s\n", __func__);
return 0;
}
if (hdmi->dc->out_ops && hdmi->dc->out_ops->get_connector_instance)
sor_num = hdmi->dc->out_ops->get_connector_instance(hdmi->dc);
state = gstate[sor_num];
if (state == NULL) {
pr_err("state is NULL from the global %s\n", __func__);
return -ENODEV;
}
client = state->i2c_client;
state->hdmi = hdmi;
state->mode = &hdmi->dc->mode;
tegra_hdmi_set_outdata(hdmi, state);
tc358870_initial_setup(state);
return err;
}
static void hdmi2dsi_tc358870_destroy(struct tegra_hdmi *hdmi)
{
struct tc358870_state *state = tegra_hdmi_get_outdata(hdmi);
if (!state)
return;
hdmi2dsi_tc358870_en_gpio(state, false);
mutex_destroy(&state->lock);
}
static void hdmi2dsi_tc358870_enable(struct tegra_hdmi *hdmi)
{
struct tc358870_state *state = tegra_hdmi_get_outdata(hdmi);
struct i2c_client *client = NULL;
if (state && state->enabled) {
state->enabled = false;
return;
}
if (state)
client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
mutex_lock(&state->lock);
hdmi2dsi_tc358870_en_gpio(state, true);
if (state->power_down == true) {
tc358870_set_ctl(state);
tc358870_set_dsi(state);
tc358870_hdmi_phy(state, true);
}
i2c_wr8(state, VI_MUTE, MASK_AUTO_MUTE);
if (state->out_ops && state->out_ops->enable)
state->out_ops->enable(&state->i2c_client->dev);
dcs_enable(state);
tc358870_hdmi_start_access(state);
tc358870_hdmi_end_rxinit(state);
tc358870_transmission_after_video(state);
msleep(1000);
dcs_post_transmission(state);
state->enabled = true;
mutex_unlock(&state->lock);
}
static void hdmi2dsi_tc358870_disable(struct tegra_hdmi *hdmi)
{
struct tc358870_state *state = tegra_hdmi_get_outdata(hdmi);
struct i2c_client *client = state->i2c_client;
dev_dbg(&client->dev, "%s:\n", __func__);
mutex_lock(&state->lock);
if (state->enabled) {
/* Suspend sequence for the panel.
* Set display off, enter sleep mode, etc... (panel specific)
*/
dcs_disable(state);
/* Disable panel regulator, reset pin */
if (state->out_ops && state->out_ops->disable)
state->out_ops->disable(&state->i2c_client->dev);
/* Disable video TX 0/1 enable */
i2c_wr16(state, CONFCTL0, MASK_AUTOINDEX);
/* Disable MIPI PLL/clock enable */
i2c_wr32(state, DSITX0_BASE_ADDR + MIPI_CLKEN, 0x00000000);
i2c_wr32(state, DSITX1_BASE_ADDR + MIPI_CLKEN, 0x00000000);
/* Power down HDMI phy */
tc358870_hdmi_phy(state, false);
state->enabled = false;
state->power_down = true;
}
mutex_unlock(&state->lock);
}
#ifdef CONFIG_PM
static void hdmi2dsi_tc358870_suspend(struct tegra_hdmi *hdmi)
{
hdmi2dsi_tc358870_disable(hdmi);
}
static void hdmi2dsi_tc358870_resume(struct tegra_hdmi *hdmi)
{
hdmi2dsi_tc358870_enable(hdmi);
}
#endif
struct tegra_hdmi_out_ops tegra_hdmi2dsi_ops = {
.init = hdmi2dsi_tc358870_init,
.destroy = hdmi2dsi_tc358870_destroy,
.enable = hdmi2dsi_tc358870_enable,
.disable = hdmi2dsi_tc358870_disable,
#ifdef CONFIG_PM
.resume = hdmi2dsi_tc358870_resume,
.suspend = hdmi2dsi_tc358870_suspend,
#endif /* CONFIG_PM */
};
/* Global variables for debugfs */
static u16 d_bridge_id;
static u16 d_reg_addr;
static u16 read_size;
static u32 d_reg_value;
/* debugfs - read register */
static int tc358870_hdmi2dsi_regs_show(struct seq_file *s, void *data)
{
u32 value;
struct tc358870_state *state = gstate[d_bridge_id];
mutex_lock(&state->lock);
if (read_size == 8) {
value = i2c_rd8(state, d_reg_addr);
seq_printf(s, "reg = 0x%04x value = 0x%02x\n",
d_reg_addr, (u8)value);
} else if (read_size == 16) {
value = i2c_rd16(state, d_reg_addr);
seq_printf(s, "reg = 0x%04x value = 0x%04x\n",
d_reg_addr, (u16)value);
} else if (read_size == 32) {
value = i2c_rd32(state, d_reg_addr);
seq_printf(s, "reg = 0x%04x value = 0x%08x\n",
d_reg_addr, value);
}
mutex_unlock(&state->lock);
return 0;
}
/* debugfs - write register */
static int tc358870_hdmi2dsi_reg_write(struct seq_file *s, void *data)
{
u32 value;
struct tc358870_state *state = gstate[d_bridge_id];
mutex_lock(&state->lock);
if (read_size == 8) {
i2c_wr8(state, d_reg_addr, (u8) d_reg_value);
value = i2c_rd8(state, d_reg_addr);
seq_printf(s, "reg = 0x%04x value = 0x%02x\n",
d_reg_addr, (u8)value);
} else if (read_size == 16) {
i2c_wr16(state, d_reg_addr, (u16) d_reg_value);
value = i2c_rd16(state, d_reg_addr);
seq_printf(s, "reg = 0x%04x value = 0x%04x\n",
d_reg_addr, (u16)value);
} else if (read_size == 32) {
i2c_wr32(state, d_reg_addr, (u32) d_reg_value);
value = i2c_rd32(state, d_reg_addr);
seq_printf(s, "reg = 0x%04x value = 0x%08x\n",
d_reg_addr, value);
}
mutex_unlock(&state->lock);
return 0;
}
static int tc358870_hdmi2dsi_reg_read_open(struct inode *inode,
struct file *file)
{
return single_open(file, tc358870_hdmi2dsi_regs_show, inode->i_private);
}
static int tc358870_hdmi2dsi_reg_write_open(struct inode *inode,
struct file *file)
{
return single_open(file, tc358870_hdmi2dsi_reg_write, inode->i_private);
}
static const struct file_operations reg_read_fops = {
.open = tc358870_hdmi2dsi_reg_read_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations reg_write_fops = {
.open = tc358870_hdmi2dsi_reg_write_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void tc358870_hdmi2dsi_remove_debugfs(struct tc358870_state *state)
{
debugfs_remove_recursive(state->debugdir);
};
static int tc358870_hdmi2dsi_create_debugfs(struct tc358870_state *state)
{
struct dentry *pentry = NULL;
int ret = 0;
state->debugdir = debugfs_create_dir("tc358870_hdmi2dsi", NULL);
if (!state->debugdir) {
ret = -ENOTDIR;
goto err;
}
pentry = debugfs_create_u16("bridge-id", 0644,
state->debugdir, &d_bridge_id);
pentry = debugfs_create_u16("addr", 0644,
state->debugdir, &d_reg_addr);
pentry = debugfs_create_u16("read_size", 0644,
state->debugdir, &read_size);
pentry = debugfs_create_u32("value", 0644,
state->debugdir, &d_reg_value);
pentry = debugfs_create_file("show", 0644,
state->debugdir, state, &reg_read_fops);
pentry = debugfs_create_file("set", 0644,
state->debugdir, state, &reg_write_fops);
if (pentry == NULL) {
ret = ENOENT;
goto err;
}
return ret;
err:
tc358870_hdmi2dsi_remove_debugfs(state);
dev_err(&state->i2c_client->dev, "Failed to create debugfs\n");
return ret;
}
/* i2c driver ops */
static int tc358870_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct tc358870_state *state;
struct device_node *panel_node;
int i = 0;
int err = 0;
bool reset = true;
state = devm_kzalloc(&client->dev,
sizeof(struct tc358870_state), GFP_KERNEL);
if (!state)
return -ENOMEM;
if (&client->dev.of_node) {
if (!tc358870_parse_dt(&state->pdata, client)) {
dev_err(&client->dev,
"Couldn't parse bridge device tree\n");
devm_kfree(&client->dev, state);
return -ENODEV;
}
if (!panel_parse_dt(&state->pout, client)) {
dev_err(&client->dev,
"Couldn't parse panel device tree\n");
devm_kfree(&client->dev, state);
return -ENODEV;
}
err = tc358870_pwr_init(&state->pdata, client);
if (err) {
dev_err(&client->dev,
"Couldn't power init %d\n", err);
devm_kfree(&client->dev, state);
return -ENODEV;
}
} else {
if (!client->dev.platform_data) {
dev_err(&client->dev, "No platform data!\n");
devm_kfree(&client->dev, state);
return -ENODEV;
}
state->pdata = *(struct tc358870_platform_data *)
client->dev.platform_data;
}
gstate[state->pdata.bridge_instance] = state;
greset_gpio[state->pdata.bridge_instance] = state->pdata.reset_gpio;
state->i2c_client = client;
/* The panel node is the child node of the bridge */
panel_node = of_get_next_child(client->dev.of_node, NULL);
if (panel_node) {
/*Get panel_ops for the panel_node */
state->out_ops = tegra_dc_get_panel_ops(panel_node);
if (!state->out_ops) {
dev_err(&client->dev, "No panel ops found\n");
devm_kfree(&client->dev, state);
return -ENODATA;
}
} else {
dev_err(&client->dev, "No panel node\n");
devm_kfree(&client->dev, state);
return -ENODEV;
}
/* Check if the gpio is already requested, for cases where
* multiple bridge instance share the same gpio. If so, skip request
*/
for (i = 0; i < MAX_BRIDGE_INSTANCES; i++) {
if (i == state->pdata.bridge_instance)
continue;
if ((gstate[i]) &&
(gstate[i]->pdata.reset_gpio ==
state->pdata.reset_gpio))
reset = false;
}
if (reset) {
err = devm_gpio_request_one(&client->dev,
state->pdata.reset_gpio,
GPIOF_OUT_INIT_HIGH, "tc358870-reset");
if (err) {
dev_err(&client->dev,
"Failed to request Reset GPIO 0x%04X: %d\n",
state->pdata.reset_gpio, err);
devm_kfree(&client->dev, state);
return err;
}
hdmi2dsi_tc358870_en_gpio(state, true);
}
i2c_set_clientdata(client, state);
/* Verify chip ID */
err = tc358870_verify_chipid(state);
if (err)
return err;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
devm_kfree(&client->dev, state);
return -EIO;
}
dev_dbg(&client->dev, "Chip found @ 7h%02X (%s)\n",
client->addr, client->adapter->name);
mutex_init(&state->lock);
dev_info(&client->dev, "%s found @ 7h%02X (%s)\n", client->name,
client->addr, client->adapter->name);
tc358870_hdmi2dsi_create_debugfs(state);
return err;
}
static int tc358870_remove(struct i2c_client *client)
{
struct tc358870_state *state = to_state(client);
tc358870_hdmi2dsi_remove_debugfs(state);
devm_kfree(&client->dev, state);
return 0;
}
static const struct i2c_device_id tc358870_id[] = {
{ "tc358870", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tc358870_id);
#ifdef CONFIG_OF
static const struct of_device_id tc358870_of_table[] = {
{ .compatible = "toshiba,tc358870" },
{ }
};
MODULE_DEVICE_TABLE(of, tc358870_of_table);
#endif
static struct i2c_driver tc358870_driver = {
.driver = {
.of_match_table = of_match_ptr(tc358870_of_table),
.name = "tc358870",
.owner = THIS_MODULE,
},
.probe = tc358870_probe,
.remove = tc358870_remove,
.id_table = tc358870_id,
};
module_i2c_driver(tc358870_driver);
MODULE_DESCRIPTION("Driver for Toshiba TC358870 HDMI to DSI Bridge");
MODULE_AUTHOR("Ishwary Balaji Gururajan (igururajan@nvidia.com)");
MODULE_LICENSE("GPL v2");