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

307 lines
8.1 KiB
C

/*
* dsi2lvds.c: dsi 2 lvds controller interface.
*
* Copyright (c) 2012-2018, NVIDIA CORPORATION. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/i2c.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/export.h>
#include "dc.h"
#include "dc_priv.h"
#include "dsi2lvds.h"
#include "dsi.h"
static struct tegra_dc_dsi2lvds_data *dsi2lvds;
enum i2c_transfer_type {
I2C_WRITE,
I2C_READ,
};
#define DSI2LVDS_TEGRA_I2C_BUS 3
#define DSI2LVDS_REG_VAL(addr, val) {(addr), (val)}
static u8 dsi2lvds_config_clk[][2] = {
DSI2LVDS_REG_VAL(0x0d, 0x00), /* pLL disable */
DSI2LVDS_REG_VAL(0x0a, 0x00), /* configure PLL */
DSI2LVDS_REG_VAL(0x0b, 0x00), /* configure PLL */
DSI2LVDS_REG_VAL(0x0d, 0x01), /* pLL enable */
};
static u8 dsi2lvds_config_dsi[][2] = {
DSI2LVDS_REG_VAL(0x10, 0x80), /* default left right ganged mode */
DSI2LVDS_REG_VAL(0x12, 0x08), /* channel A clk range */
DSI2LVDS_REG_VAL(0x13, 0x08), /* channel B clk range */
};
static u8 dsi2lvds_config_lvds[][2] = {
DSI2LVDS_REG_VAL(0x18, 0x7f),
};
static u8 dsi2lvds_config_video[][2] = {
DSI2LVDS_REG_VAL(0x20, 0x40), /* horizontal pixels on dsi channel A */
DSI2LVDS_REG_VAL(0x21, 0x01),
DSI2LVDS_REG_VAL(0x22, 0x40), /* horizontal pixels on dsi channel B */
DSI2LVDS_REG_VAL(0x23, 0x01),
DSI2LVDS_REG_VAL(0x24, 0xe0), /* vertical pixels on lvds channel A */
DSI2LVDS_REG_VAL(0x25, 0x01),
DSI2LVDS_REG_VAL(0x26, 0x00), /* vertical pixels on lvds channel B */
DSI2LVDS_REG_VAL(0x27, 0x00),
DSI2LVDS_REG_VAL(0x28, 0x40), /* Pixel clk delay from dsi to */
DSI2LVDS_REG_VAL(0x29, 0x01), /* lvds channel A */
DSI2LVDS_REG_VAL(0x2a, 0x10), /* Pixel clk delay from dsi to */
DSI2LVDS_REG_VAL(0x2b, 0x00), /* lvds channel B */
DSI2LVDS_REG_VAL(0x2c, 0x40), /* hsync width channel A */
DSI2LVDS_REG_VAL(0x2d, 0x00),
DSI2LVDS_REG_VAL(0x2e, 0x40), /* hsync width channel B */
DSI2LVDS_REG_VAL(0x2f, 0x00),
DSI2LVDS_REG_VAL(0x30, 0x02), /* vsync width channel A */
DSI2LVDS_REG_VAL(0x31, 0x00),
DSI2LVDS_REG_VAL(0x32, 0x00), /* vsync width channel B */
DSI2LVDS_REG_VAL(0x33, 0x00),
DSI2LVDS_REG_VAL(0x34, 0x10), /* h back porch channel A */
DSI2LVDS_REG_VAL(0x35, 0x00), /* h back porch channel B */
DSI2LVDS_REG_VAL(0x36, 0x21), /* v back porch channel A */
DSI2LVDS_REG_VAL(0x37, 0x00), /* v back porch channel B */
DSI2LVDS_REG_VAL(0x38, 0x10), /* h front porch channel A */
DSI2LVDS_REG_VAL(0x39, 0x00), /* h front porch channel B */
DSI2LVDS_REG_VAL(0x3a, 0x0a), /* v front porch channel A */
DSI2LVDS_REG_VAL(0x3b, 0x00), /* v front porch channel B */
DSI2LVDS_REG_VAL(0x3c, 0x00), /* channel A/B test pattern */
};
static u8 dsi2lvds_soft_reset[][2] = {
DSI2LVDS_REG_VAL(0x09, 0x01),
};
static struct i2c_driver tegra_dsi2lvds_i2c_slave_driver = {
.driver = {
.name = "dsi2lvds_bridge",
},
};
static struct i2c_client *init_i2c_slave(struct tegra_dc_dsi_data *dsi)
{
struct i2c_adapter *adapter;
struct i2c_client *client;
struct i2c_board_info p_data = {
.type = "dsi2lvds_bridge",
.addr = 0x2D,
};
int bus = DSI2LVDS_TEGRA_I2C_BUS;
int err = 0;
adapter = i2c_get_adapter(bus);
if (!adapter) {
dev_err(&dsi->dc->ndev->dev,
"dsi2lvds: can't get adpater for bus %d\n", bus);
err = -EBUSY;
goto err;
}
client = i2c_new_device(adapter, &p_data);
i2c_put_adapter(adapter);
if (!client) {
dev_err(&dsi->dc->ndev->dev,
"dsi2lvds: can't add i2c slave device\n");
err = -EBUSY;
goto err;
}
err = i2c_add_driver(&tegra_dsi2lvds_i2c_slave_driver);
if (err) {
dev_err(&dsi->dc->ndev->dev,
"dsi2lvds: can't add i2c slave driver\n");
goto err_free;
}
return client;
err:
return ERR_PTR(err);
err_free:
i2c_unregister_device(client);
return ERR_PTR(err);
}
static int tegra_dsi2lvds_init(struct tegra_dc_dsi_data *dsi)
{
int err = 0;
if (dsi2lvds) {
tegra_dsi_set_outdata(dsi, dsi2lvds);
return err;
}
dsi2lvds = devm_kzalloc(&dsi->dc->ndev->dev, sizeof(*dsi2lvds), GFP_KERNEL);
if (!dsi2lvds)
return -ENOMEM;
dsi2lvds->client_i2c = init_i2c_slave(dsi);
if (IS_ERR_OR_NULL(dsi2lvds->client_i2c)) {
dev_err(&dsi->dc->ndev->dev,
"dsi2lvds: i2c slave setup failure\n");
}
dsi2lvds->dsi = dsi;
tegra_dsi_set_outdata(dsi, dsi2lvds);
mutex_init(&dsi2lvds->lock);
return err;
}
static void tegra_dsi2lvds_destroy(struct tegra_dc_dsi_data *dsi)
{
struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi);
if (!dsi2lvds)
return;
mutex_lock(&dsi2lvds->lock);
i2c_del_driver(&tegra_dsi2lvds_i2c_slave_driver);
i2c_unregister_device(dsi2lvds->client_i2c);
mutex_unlock(&dsi2lvds->lock);
mutex_destroy(&dsi2lvds->lock);
kfree(dsi2lvds);
}
static int dsi2lvds_i2c_transfer(struct tegra_dc_dsi2lvds_data *dsi2lvds,
u8 transfers[][2], u32 no_of_tranfers,
enum i2c_transfer_type type)
{
struct i2c_msg *i2c_msg_transfer;
struct i2c_client *client = dsi2lvds->client_i2c;
int err = 0;
u32 cnt = 0;
i2c_msg_transfer = kzalloc
(no_of_tranfers * sizeof(*i2c_msg_transfer), GFP_KERNEL);
if (!i2c_msg_transfer)
return -ENOMEM;
for (cnt = 0; cnt < no_of_tranfers; cnt++) {
i2c_msg_transfer[cnt].addr = client->addr;
i2c_msg_transfer[cnt].flags = type;
i2c_msg_transfer[cnt].len = 2;
i2c_msg_transfer[cnt].buf = transfers[cnt];
}
for (cnt = 0; cnt < no_of_tranfers; cnt++) {
err = i2c_transfer(client->adapter, &i2c_msg_transfer[cnt], 1);
if (err < 0) {
dev_err(&dsi2lvds->dsi->dc->ndev->dev,
"dsi2lvds: i2c write failed\n");
break;
}
msleep(10);
}
kfree(i2c_msg_transfer);
return err;
}
static void tegra_dsi2lvds_enable(struct tegra_dc_dsi_data *dsi)
{
struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi);
int err = 0;
if (dsi2lvds && dsi2lvds->dsi2lvds_enabled)
return;
mutex_lock(&dsi2lvds->lock);
err = dsi2lvds_i2c_transfer(dsi2lvds, dsi2lvds_soft_reset,
ARRAY_SIZE(dsi2lvds_soft_reset), I2C_WRITE);
if (err < 0) {
dev_err(&dsi->dc->ndev->dev, "dsi2lvds: Soft reset failed\n");
goto fail;
}
err = dsi2lvds_i2c_transfer(dsi2lvds, dsi2lvds_config_clk,
ARRAY_SIZE(dsi2lvds_config_clk), I2C_WRITE);
if (err < 0) {
dev_err(&dsi->dc->ndev->dev, "dsi2lvds: Init clk failed\n");
goto fail;
}
if (dsi2lvds->dsi->info.ganged_type ==
TEGRA_DSI_GANGED_SYMMETRIC_EVEN_ODD) {
u32 cnt;
for (cnt = 0;
cnt < ARRAY_SIZE(dsi2lvds_config_dsi); cnt++) {
if (dsi2lvds_config_dsi[cnt][0] == 0x10)
/* select odd even ganged mode */
dsi2lvds_config_dsi[cnt][1] &= ~(1 << 7);
}
}
err = dsi2lvds_i2c_transfer(dsi2lvds, dsi2lvds_config_dsi,
ARRAY_SIZE(dsi2lvds_config_dsi), I2C_WRITE);
if (err < 0) {
dev_err(&dsi->dc->ndev->dev, "dsi2lvds: Init dsi failed\n");
goto fail;
}
err = dsi2lvds_i2c_transfer(dsi2lvds, dsi2lvds_config_lvds,
ARRAY_SIZE(dsi2lvds_config_lvds), I2C_WRITE);
if (err < 0) {
dev_err(&dsi->dc->ndev->dev, "dsi2lvds: Init lvds failed\n");
goto fail;
}
err = dsi2lvds_i2c_transfer(dsi2lvds, dsi2lvds_config_video,
ARRAY_SIZE(dsi2lvds_config_video), I2C_WRITE);
if (err < 0) {
dev_err(&dsi->dc->ndev->dev, "dsi2lvds: Init video failed\n");
goto fail;
}
dsi2lvds->dsi2lvds_enabled = true;
fail:
mutex_unlock(&dsi2lvds->lock);
}
static void tegra_dsi2lvds_disable(struct tegra_dc_dsi_data *dsi)
{
/* To be done */
}
#ifdef CONFIG_PM
static void tegra_dsi2lvds_suspend(struct tegra_dc_dsi_data *dsi)
{
dsi2lvds->dsi2lvds_enabled = false;
/* To be done */
}
static void tegra_dsi2lvds_resume(struct tegra_dc_dsi_data *dsi)
{
/* To be done */
}
#endif
struct tegra_dsi_out_ops tegra_dsi2lvds_ops = {
.init = tegra_dsi2lvds_init,
.destroy = tegra_dsi2lvds_destroy,
.enable = tegra_dsi2lvds_enable,
.disable = tegra_dsi2lvds_disable,
#ifdef CONFIG_PM
.suspend = tegra_dsi2lvds_suspend,
.resume = tegra_dsi2lvds_resume,
#endif
};