/* * drivers/video/tegra/dc/sn65dsi86_dsi2edp.c * * Copyright (c) 2013, NVIDIA Corporation. * * Author: * Bibek Basu * * 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 #include #include #include #include #include #include #include #include "dc_priv.h" #include "sn65dsi86_dsi2edp.h" #include "dsi.h" static struct tegra_dc_dsi2edp_data *sn65dsi86_dsi2edp; static struct i2c_client *sn65dsi86_i2c_client; enum i2c_transfer_type { I2C_WRITE, I2C_READ, }; static inline int sn65dsi86_reg_write(struct tegra_dc_dsi2edp_data *dsi2edp, unsigned int addr, unsigned int val) { return regmap_write(dsi2edp->regmap, addr, val); } static inline void sn65dsi86_reg_read(struct tegra_dc_dsi2edp_data *dsi2edp, unsigned int addr, unsigned int *val) { regmap_read(dsi2edp->regmap, addr, val); } static const struct regmap_config sn65dsi86_regmap_config = { .reg_bits = 8, .val_bits = 8, }; static int sn65dsi86_dsi2edp_init(struct tegra_dc_dsi_data *dsi) { int err = 0; if (sn65dsi86_dsi2edp) { tegra_dsi_set_outdata(dsi, sn65dsi86_dsi2edp); return err; } sn65dsi86_dsi2edp = devm_kzalloc(&dsi->dc->ndev->dev, sizeof(*sn65dsi86_dsi2edp), GFP_KERNEL); if (!sn65dsi86_dsi2edp) return -ENOMEM; sn65dsi86_dsi2edp->dsi = dsi; sn65dsi86_dsi2edp->client_i2c = sn65dsi86_i2c_client; sn65dsi86_dsi2edp->regmap = devm_regmap_init_i2c(sn65dsi86_i2c_client, &sn65dsi86_regmap_config); if (IS_ERR(sn65dsi86_dsi2edp->regmap)) { err = PTR_ERR(sn65dsi86_dsi2edp->regmap); dev_err(&dsi->dc->ndev->dev, "sn65dsi86_dsi2edp: regmap init failed\n"); goto fail; } sn65dsi86_dsi2edp->mode = &dsi->dc->mode; tegra_dsi_set_outdata(dsi, sn65dsi86_dsi2edp); mutex_init(&sn65dsi86_dsi2edp->lock); fail: return err; } static void sn65dsi86_dsi2edp_destroy(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2edp_data *dsi2edp = tegra_dsi_get_outdata(dsi); if (!dsi2edp) return; sn65dsi86_dsi2edp = NULL; mutex_destroy(&dsi2edp->lock); } static void sn65dsi86_dsi2edp_enable(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2edp_data *dsi2edp = tegra_dsi_get_outdata(dsi); unsigned val = 0; if (dsi2edp && dsi2edp->dsi2edp_enabled) return; mutex_lock(&dsi2edp->lock); /* REFCLK 19.2MHz */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_PLL_REFCLK_CFG, 0x02); usleep_range(10000, 12000); /* Single 4 DSI lanes */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_DSI_CFG1, 0x26); usleep_range(10000, 12000); /* DSI CLK FREQ 422.5MHz */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_DSI_CHA_CLK_RANGE, 0x55); usleep_range(10000, 12000); sn65dsi86_reg_read(dsi2edp, SN65DSI86_DSI_CHA_CLK_RANGE, &val); sn65dsi86_reg_read(dsi2edp, SN65DSI86_DSI_CHA_CLK_RANGE, &val); /* enhanced framing */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_FRAMING_CFG, 0x04); usleep_range(10000, 12000); /* Pre0dB 2 lanes no SSC */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_DP_SSC_CFG, 0x20); usleep_range(10000, 12000); /* L0mV HBR */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_DP_CFG, 0x80); usleep_range(10000, 12000); /* PLL ENABLE */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_PLL_EN, 0x01); usleep_range(10000, 12000); /* DP_PLL_LOCK */ sn65dsi86_reg_read(dsi2edp, SN65DSI86_PLL_REFCLK_CFG, &val); usleep_range(10000, 12000); /* POST2 0dB */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_TRAINING_CFG, 0x00); usleep_range(10000, 12000); /* Semi-Auto TRAIN */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_ML_TX_MODE, 0x0a); usleep_range(10000, 12000); /* ADDR 0x96 CFR */ sn65dsi86_reg_read(dsi2edp, SN65DSI86_ML_TX_MODE, &val); msleep(20); /* CHA_ACTIVE_LINE_LENGTH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_VIDEO_CHA_LINE_LOW, 0x80); sn65dsi86_reg_write(dsi2edp, SN65DSI86_VIDEO_CHA_LINE_HIGH, 0x07); usleep_range(10000, 12000); /* CHA_VERTICAL_DISPLAY_SIZE */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_VERT_DISP_SIZE_LOW, 0x38); sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_VERT_DISP_SIZE_HIGH, 0x04); usleep_range(10000, 12000); /* CHA_HSYNC_PULSE_WIDTH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_HSYNC_PULSE_WIDTH_LOW, 0x10); sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_HSYNC_PULSE_WIDTH_HIGH, 0x80); usleep_range(10000, 12000); /* CHA_VSYNC_PULSE_WIDTH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_VSYNC_PULSE_WIDTH_LOW, 0x0e); sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_VSYNC_PULSE_WIDTH_HIGH, 0x80); usleep_range(10000, 12000); /* CHA_HORIZONTAL_BACK_PORCH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_HORIZONTAL_BACK_PORCH, 0x98); usleep_range(10000, 12000); /* CHA_VERTICAL_BACK_PORCH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_VERTICAL_BACK_PORCH, 0x13); usleep_range(10000, 12000); /* CHA_HORIZONTAL_FRONT_PORCH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_HORIZONTAL_FRONT_PORCH, 0x10); usleep_range(10000, 12000); /* CHA_VERTICAL_FRONT_PORCH */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_CHA_VERTICAL_FRONT_PORCH, 0x03); usleep_range(10000, 12000); /* DP-18BPP Enable */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_DP_18BPP_EN, 0x00); msleep(100); /* COLOR BAR */ /* sn65dsi86_reg_write(dsi2edp, SN65DSI86_COLOR_BAR_CFG, 0x10);*/ /* enhanced framing and Vstream enable */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_FRAMING_CFG, 0x0c); dsi2edp->dsi2edp_enabled = true; mutex_unlock(&dsi2edp->lock); } static void sn65dsi86_dsi2edp_disable(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2edp_data *dsi2edp = tegra_dsi_get_outdata(dsi); mutex_lock(&dsi2edp->lock); /* enhanced framing and Vstream disable */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_FRAMING_CFG, 0x04); dsi2edp->dsi2edp_enabled = false; mutex_unlock(&dsi2edp->lock); } #ifdef CONFIG_PM static void sn65dsi86_dsi2edp_suspend(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2edp_data *dsi2edp = tegra_dsi_get_outdata(dsi); mutex_lock(&dsi2edp->lock); /* configure GPIO1 for suspend */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_GPIO_CTRL_CFG, 0x02); dsi2edp->dsi2edp_enabled = false; mutex_unlock(&dsi2edp->lock); } static void sn65dsi86_dsi2edp_resume(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2edp_data *dsi2edp = tegra_dsi_get_outdata(dsi); mutex_lock(&dsi2edp->lock); /* disable configure GPIO1 for suspend */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_GPIO_CTRL_CFG, 0x00); /* enhanced framing and Vstream enable */ sn65dsi86_reg_write(dsi2edp, SN65DSI86_FRAMING_CFG, 0x0c); dsi2edp->dsi2edp_enabled = true; mutex_unlock(&dsi2edp->lock); } #endif struct tegra_dsi_out_ops tegra_dsi2edp_ops = { .init = sn65dsi86_dsi2edp_init, .destroy = sn65dsi86_dsi2edp_destroy, .enable = sn65dsi86_dsi2edp_enable, .disable = sn65dsi86_dsi2edp_disable, #ifdef CONFIG_PM .suspend = sn65dsi86_dsi2edp_suspend, .resume = sn65dsi86_dsi2edp_resume, #endif }; static int sn65dsi86_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { sn65dsi86_i2c_client = client; return 0; } static int sn65dsi86_i2c_remove(struct i2c_client *client) { sn65dsi86_i2c_client = NULL; return 0; } static const struct i2c_device_id sn65dsi86_id_table[] = { {"sn65dsi86_dsi2edp", 0}, {}, }; static struct i2c_driver sn65dsi86_i2c_drv = { .driver = { .name = "sn65dsi86_dsi2edp", .owner = THIS_MODULE, }, .probe = sn65dsi86_i2c_probe, .remove = sn65dsi86_i2c_remove, .id_table = sn65dsi86_id_table, }; static int __init sn65dsi86_i2c_client_init(void) { int err = 0; err = i2c_add_driver(&sn65dsi86_i2c_drv); if (err) pr_err("sn65dsi86_dsi2edp: Failed to add i2c client driver\n"); return err; } static void __exit sn65dsi86_i2c_client_exit(void) { i2c_del_driver(&sn65dsi86_i2c_drv); } subsys_initcall(sn65dsi86_i2c_client_init); module_exit(sn65dsi86_i2c_client_exit); MODULE_AUTHOR("Bibek Basu "); MODULE_DESCRIPTION(" TI SN65DSI86 dsi bridge to edp"); MODULE_LICENSE("GPL");