/* * sn65dsi85_dsi2lvds.c: dsi to lvds sn65dsi85 controller driver. * * Copyright (c) 2013-2018, NVIDIA CORPORATION. All rights reserved. * * Author: * Tow Wang * * 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 #include /*copy_from_user*/ #include #include "dc.h" #include "dc_priv.h" #include "sn65dsi85_dsi2lvds.h" #include "dsi.h" /* TODO: support multiple instances */ static struct tegra_dc_dsi2lvds_data *sn65dsi85_dsi2lvds; static struct i2c_client *sn65dsi85_i2c_client; enum i2c_transfer_type { I2C_WRITE, I2C_READ, }; static inline int sn65dsi85_reg_write(struct tegra_dc_dsi2lvds_data *dsi2lvds, unsigned int addr, unsigned int val) { return regmap_write(dsi2lvds->regmap, addr, val); } static inline int sn65dsi85_reg_read(struct tegra_dc_dsi2lvds_data *dsi2lvds, unsigned int addr, unsigned int *val) { return regmap_read(dsi2lvds->regmap, addr, val); } static const struct regmap_config sn65dsi85_regmap_config = { .reg_bits = 8, .val_bits = 8, }; static void sn65dsi85_dsi2lvds_en_gpio( struct tegra_dc_dsi2lvds_data *dsi2lvds, bool enable) { if (!gpio_is_valid(dsi2lvds->en_gpio)) return; if (enable) { gpio_direction_output(dsi2lvds->en_gpio, !(dsi2lvds->en_gpio_flags & OF_GPIO_ACTIVE_LOW)); /* Wait >1ms for internal regulator to stabilize */ usleep_range(2000, 3000); } else gpio_direction_output(dsi2lvds->en_gpio, dsi2lvds->en_gpio_flags & OF_GPIO_ACTIVE_LOW); } static int sn65dsi85_dsi2lvds_init(struct tegra_dc_dsi_data *dsi) { int err = 0; struct tegra_dc_dsi2lvds_data *dsi2lvds = sn65dsi85_dsi2lvds; if (!sn65dsi85_dsi2lvds) return -ENODEV; dsi2lvds->dsi = dsi; tegra_dsi_set_outdata(dsi, dsi2lvds); if (gpio_is_valid(dsi2lvds->en_gpio)) { /* Request and set direction, but keep disabled until enable() */ err = devm_gpio_request_one(&sn65dsi85_i2c_client->dev, dsi2lvds->en_gpio, dsi2lvds->en_gpio_flags & OF_GPIO_ACTIVE_LOW, "dsi2lvds"); if (err) pr_err("err %d: dsi2lvds GPIO request failed\n", err); } else pr_err("err %d: dsi2lvds GPIO is invalid\n", err); return 0; } static void sn65dsi85_dsi2lvds_destroy(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi); if (!dsi2lvds) return; sn65dsi85_dsi2lvds_en_gpio(dsi2lvds, false); devm_gpio_free(&sn65dsi85_i2c_client->dev, dsi2lvds->en_gpio); devm_kfree(&sn65dsi85_i2c_client->dev, sn65dsi85_dsi2lvds); sn65dsi85_dsi2lvds = NULL; mutex_destroy(&dsi2lvds->lock); } /* Excerpt from SN65DSI85 spec, section 9.3: * init seq2: Assert the EN terminal * init seq3: Wait for 1 ms * init seq4: Initialize all CSR registers Before this function is called, DSI lanes must be in LP11. Enable DSI video after this function is called. */ static void sn65dsi85_dsi2lvds_enable(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi); if (dsi2lvds && dsi2lvds->dsi2lvds_enabled) return; mutex_lock(&dsi2lvds->lock); sn65dsi85_dsi2lvds_en_gpio(dsi2lvds, true); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_PLL_REFCLK_CFG, dsi2lvds->pll_refclk_cfg); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_DIVIDER_MULTIPLIER, dsi2lvds->dsi_clk_div_mult); /* LVDS clk = (DSI HS clk * mult) / div */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_DSI_CFG1, dsi2lvds->dsi_cfg1); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_DSI_CHA_CLK_RANGE, dsi2lvds->dsi_cha_clk_range); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_DSI_CHB_CLK_RANGE, dsi2lvds->dsi_chb_clk_range); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_LVDS_FORMAT, dsi2lvds->lvds_format); /* CHA_ACTIVE_LINE_LENGTH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_VIDEO_CHA_LINE_LOW, dsi2lvds->video_cha_line_low); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_VIDEO_CHA_LINE_HIGH, dsi2lvds->video_cha_line_high); /* CHB_ACTIVE_LINE_LENGTH */ if (dsi2lvds->video_chb_line_low >= 0) sn65dsi85_reg_write(dsi2lvds, SN65DSI85_VIDEO_CHB_LINE_LOW, dsi2lvds->video_chb_line_low); if (dsi2lvds->video_chb_line_high) sn65dsi85_reg_write(dsi2lvds, SN65DSI85_VIDEO_CHB_LINE_HIGH, dsi2lvds->video_chb_line_high); /* CHA_VERTICAL_DISPLAY_SIZE */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_VERT_DISP_SIZE_LOW, dsi2lvds->cha_vert_disp_size_low); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_VERT_DISP_SIZE_HIGH, dsi2lvds->cha_vert_disp_size_high); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_SYNC_DELAY_LOW, dsi2lvds->cha_sync_delay_low); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_SYNC_DELAY_HIGH, dsi2lvds->cha_sync_delay_high); /* CHA_HSYNC_PULSE_WIDTH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_HSYNC_PULSE_WIDTH_LOW, dsi2lvds->h_pulse_width_low); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_HSYNC_PULSE_WIDTH_HIGH, dsi2lvds->h_pulse_width_high); /* CHA_VSYNC_PULSE_WIDTH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_VSYNC_PULSE_WIDTH_LOW, dsi2lvds->v_pulse_width_low); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_VSYNC_PULSE_WIDTH_HIGH, dsi2lvds->v_pulse_width_high); /* CHA_HORIZONTAL_BACK_PORCH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_HORIZONTAL_BACK_PORCH, dsi2lvds->h_back_porch); /* CHA_VERTICAL_BACK_PORCH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_VERTICAL_BACK_PORCH, dsi2lvds->v_back_porch); /* CHA_HORIZONTAL_FRONT_PORCH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_HORIZONTAL_FRONT_PORCH, dsi2lvds->h_front_porch); /* CHA_VERTICAL_FRONT_PORCH */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_CHA_VERTICAL_FRONT_PORCH, dsi2lvds->v_front_porch); /* COLOR BAR for channel A only */ /* sn65dsi85_reg_write(dsi2lvds, SN65DSI85_COLOR_BAR_CFG, 0x10);*/ dsi2lvds->dsi2lvds_enabled = true; mutex_unlock(&dsi2lvds->lock); } /* Excerpt from SN65DSI85 spec, section 9.3: * init seq6: Set the PLL_EN bit * init seq7: Wait >3ms * init seq8: Set the SOFT_RESET bit enable() must be called and DSI video must be enabled before this function is called. */ static void sn65dsi85_dsi2lvds_postpoweron(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi); unsigned val = 0; unsigned retry = 0; if (dsi2lvds == NULL) return; WARN_ON(!dsi2lvds->dsi2lvds_enabled); mutex_lock(&dsi2lvds->lock); /* Enable PLL after all CSR registers are programmed */ do { /* PLL ENABLE */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_PLL_EN, 0x01); usleep_range(10000, 12000); /* PLL_EN_STAT */ sn65dsi85_reg_read(dsi2lvds, SN65DSI85_PLL_REFCLK_CFG, &val); } while (((val & 0x80) == 0) && (retry++ < RETRYLOOP)); if ((val & 0xFF) >> 7 == 0) pr_err("sn65dsi85_dsi2lvds: PLL not locked\n"); usleep_range(10000, 12000); /* Must soft reset after CSR registers are updated */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_SOFT_RESET, 0x01); usleep_range(10000, 12000); mutex_unlock(&dsi2lvds->lock); } static void sn65dsi85_dsi2lvds_disable(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi); mutex_lock(&dsi2lvds->lock); /* disable internal PLL */ sn65dsi85_reg_write(dsi2lvds, SN65DSI85_PLL_EN, 0); sn65dsi85_dsi2lvds_en_gpio(dsi2lvds, false); dsi2lvds->dsi2lvds_enabled = false; mutex_unlock(&dsi2lvds->lock); } #ifdef CONFIG_PM static void sn65dsi85_dsi2lvds_suspend(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi); mutex_lock(&dsi2lvds->lock); sn65dsi85_reg_write(dsi2lvds, SN65DSI85_PLL_EN, 0); mutex_unlock(&dsi2lvds->lock); } static void sn65dsi85_dsi2lvds_resume(struct tegra_dc_dsi_data *dsi) { struct tegra_dc_dsi2lvds_data *dsi2lvds = tegra_dsi_get_outdata(dsi); mutex_lock(&dsi2lvds->lock); sn65dsi85_dsi2lvds_postpoweron(dsi2lvds->dsi); mutex_unlock(&dsi2lvds->lock); } #endif struct tegra_dsi_out_ops tegra_dsi2lvds_ops = { .init = sn65dsi85_dsi2lvds_init, .destroy = sn65dsi85_dsi2lvds_destroy, .enable = sn65dsi85_dsi2lvds_enable, .postpoweron = sn65dsi85_dsi2lvds_postpoweron, .disable = sn65dsi85_dsi2lvds_disable, #ifdef CONFIG_PM .suspend = sn65dsi85_dsi2lvds_suspend, .resume = sn65dsi85_dsi2lvds_resume, #endif }; #ifdef CONFIG_DEBUG_FS static int sn65dsi85_dsi2lvds_regs_show(struct seq_file *s, void *unused) { struct tegra_dc_dsi2lvds_data *dsi2lvds = s->private; unsigned int val; mutex_lock(&dsi2lvds->lock); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_PLL_REFCLK_CFG, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_PLL_REFCLK_CFG", SN65DSI85_PLL_REFCLK_CFG, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_DSI_CFG1, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_DSI_CFG1", SN65DSI85_DSI_CFG1, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_DSI_CHA_CLK_RANGE, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_DSI_CHA_CLK_RANGE", SN65DSI85_DSI_CHA_CLK_RANGE, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_DSI_CHB_CLK_RANGE, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_DSI_CHB_CLK_RANGE", SN65DSI85_DSI_CHB_CLK_RANGE, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_LVDS_FORMAT, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_LVDS_FORMAT", SN65DSI85_LVDS_FORMAT, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_VIDEO_CHA_LINE_LOW, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_VIDEO_CHA_LINE_LOW", SN65DSI85_VIDEO_CHA_LINE_LOW, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_VIDEO_CHA_LINE_HIGH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_VIDEO_CHA_LINE_HIGH", SN65DSI85_VIDEO_CHA_LINE_HIGH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_VIDEO_CHB_LINE_LOW, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_VIDEO_CHB_LINE_LOW", SN65DSI85_VIDEO_CHB_LINE_LOW, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_VIDEO_CHB_LINE_HIGH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_VIDEO_CHB_LINE_HIGH", SN65DSI85_VIDEO_CHB_LINE_HIGH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_VERT_DISP_SIZE_LOW, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_VERT_DISP_SIZE_LOW", SN65DSI85_CHA_VERT_DISP_SIZE_LOW, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_VERT_DISP_SIZE_HIGH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_VERT_DISP_SIZE_HIGH", SN65DSI85_CHA_VERT_DISP_SIZE_HIGH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_HSYNC_PULSE_WIDTH_LOW, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_HSYNC_PULSE_WIDTH_LOW", SN65DSI85_CHA_HSYNC_PULSE_WIDTH_LOW, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_HSYNC_PULSE_WIDTH_HIGH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_HSYNC_PULSE_WIDTH_HIGH", SN65DSI85_CHA_HSYNC_PULSE_WIDTH_HIGH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_VSYNC_PULSE_WIDTH_LOW, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_VSYNC_PULSE_WIDTH_LOW", SN65DSI85_CHA_VSYNC_PULSE_WIDTH_LOW, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_VSYNC_PULSE_WIDTH_HIGH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_VSYNC_PULSE_WIDTH_HIGH", SN65DSI85_CHA_VSYNC_PULSE_WIDTH_HIGH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_HORIZONTAL_BACK_PORCH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_HORIZONTAL_BACK_PORCH", SN65DSI85_CHA_HORIZONTAL_BACK_PORCH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_VERTICAL_BACK_PORCH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_VERTICAL_BACK_PORCH", SN65DSI85_CHA_VERTICAL_BACK_PORCH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_HORIZONTAL_FRONT_PORCH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_HORIZONTAL_FRONT_PORCH", SN65DSI85_CHA_HORIZONTAL_FRONT_PORCH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_VERTICAL_FRONT_PORCH, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_VERTICAL_FRONT_PORCH", SN65DSI85_CHA_VERTICAL_FRONT_PORCH, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_COLOR_BAR_CFG, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_COLOR_BAR_CFG", SN65DSI85_COLOR_BAR_CFG, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHA_IRQ_STATUS, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHA_IRQ_STATUS", SN65DSI85_CHA_IRQ_STATUS, val); sn65dsi85_reg_read(dsi2lvds, SN65DSI85_CHB_IRQ_STATUS, &val); seq_printf(s, "%40s%#6x%#6x\n", "SN65DSI85_CHB_IRQ_STATUS", SN65DSI85_CHB_IRQ_STATUS, val); mutex_unlock(&dsi2lvds->lock); return 0; } static int sn65dsi85_dsi2lvds_regs_open(struct inode *inode, struct file *file) { return single_open(file, sn65dsi85_dsi2lvds_regs_show, inode->i_private); } static const struct file_operations regs_fops = { .open = sn65dsi85_dsi2lvds_regs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static void sn65dsi85_dsi2lvds_panel_remove_debugfs( struct tegra_dc_dsi2lvds_data *dsi2lvds) { /* debugfs_remove_recursive(NULL) is safe */ debugfs_remove_recursive(dsi2lvds->debugdir); dsi2lvds->debugdir = NULL; } static int sn65dsi85_dsi2lvds_panel_create_debugfs( struct tegra_dc_dsi2lvds_data *dsi2lvds) { struct dentry *pEntry; int ret = 0; dsi2lvds->debugdir = debugfs_create_dir("sn65dsi85_dsi2lvds", NULL); pr_info("%s: dsi2lvds->debugdir = %p\n", __func__, dsi2lvds->debugdir); if (!dsi2lvds->debugdir) { ret = ENOTDIR; goto err; } pEntry = debugfs_create_file("regs", 0644, dsi2lvds->debugdir, dsi2lvds, ®s_fops); pr_info("%s: debugfs_create_file returned %p\n", __func__, pEntry); if (pEntry == NULL) { ret = ENOENT; goto err; } return ret; err: sn65dsi85_dsi2lvds_panel_remove_debugfs(dsi2lvds); dev_err(&dsi2lvds->client_i2c->dev, "Failed to create debugfs\n"); return ret; } #else static void sn65dsi85_dsi2lvds_panel_create_debugfs( struct tegra_dc_dsi2lvds_data *dsi2lvds) { } static void sn65dsi85_dsi2lvds_panel_remove_debugfs( struct tegra_dc_dsi2lvds_data *dsi2lvds) { } #endif static int of_dsi2lvds_parse_platform_data(struct i2c_client *client) { struct device_node *np = client->dev.of_node; struct tegra_dc_dsi2lvds_data *dsi2lvds = sn65dsi85_dsi2lvds; enum of_gpio_flags flags; int err = 0; u32 temp; if (!np) { dev_err(&client->dev, "dsi2lvds: device node not defined\n"); err = -EINVAL; goto err; } dsi2lvds->en_gpio = of_get_named_gpio_flags(np, "ti,enable-gpio", 0, &flags); dsi2lvds->en_gpio_flags = flags; if (dsi2lvds->en_gpio < 0) dev_err(&client->dev, "dsi2lvds: gpio number not provided\n"); else if (!gpio_is_valid(dsi2lvds->en_gpio)) dev_err(&client->dev, "dsi2lvds: en gpio is invalid\n"); if (!of_property_read_u32(np, "ti,pll-refclk-cfg", &temp)) dsi2lvds->pll_refclk_cfg = temp; else dsi2lvds->pll_refclk_cfg = 0x0B; /* 137.5 < LVDS CLK < 154 MHz, HS_CLK_SRC=D-PHY */ if (!of_property_read_u32(np, "ti,dsi-clk-divider-multiplier", &temp)) dsi2lvds->dsi_clk_div_mult = temp; else dsi2lvds->dsi_clk_div_mult = 0x10; if (!of_property_read_u32(np, "ti,dsi-cfg1", &temp)) dsi2lvds->dsi_cfg1 = temp; else dsi2lvds->dsi_cfg1 = 0x26; /* Single 4 DSI lanes */ if (!of_property_read_u32(np, "ti,dsi-cha-clk-range", &temp)) dsi2lvds->dsi_cha_clk_range = temp; else dsi2lvds->dsi_cha_clk_range = 0x59; /* 445.5 / 5 = 89.1 */ if (!of_property_read_u32(np, "ti,dsi-chb-clk-range", &temp)) dsi2lvds->dsi_chb_clk_range = temp; else dsi2lvds->dsi_chb_clk_range = 0x59; /* 445.5 / 5 = 89.1, do not use reserved values */ if (!of_property_read_u32(np, "ti,lvds-format", &temp)) dsi2lvds->lvds_format = temp; else dsi2lvds->lvds_format = 0x7F; /*HS and VS neg. pol., only ch. A, enable lane 4 on both ch.*/ /* * These registers are left at their reset values * #define SN65DSI85_LVDS_VOLTAGE 0x19 * #define SN65DSI85_LVDS_TERMINAL 0x1A */ if (!of_property_read_u32(np, "ti,video-cha-line-low", &temp)) dsi2lvds->video_cha_line_low = temp; else dsi2lvds->video_cha_line_low = 0x80; if (!of_property_read_u32(np, "ti,video-cha-line-high", &temp)) dsi2lvds->video_cha_line_high = temp; else dsi2lvds->video_cha_line_high = 0x07; if (!of_property_read_u32(np, "ti,video-chb-line-low", &temp)) dsi2lvds->video_chb_line_low = temp; else dsi2lvds->video_chb_line_low = 0x00; if (!of_property_read_u32(np, "ti,video-chb-line-high", &temp)) dsi2lvds->video_chb_line_high = temp; else dsi2lvds->video_chb_line_high = 0x00; if (!of_property_read_u32(np, "ti,cha-vert-disp-size-low", &temp)) dsi2lvds->cha_vert_disp_size_low = temp; else dsi2lvds->cha_vert_disp_size_low = 0x38; if (!of_property_read_u32(np, "ti,cha-vert-disp-size-high", &temp)) dsi2lvds->cha_vert_disp_size_high = temp; else dsi2lvds->cha_vert_disp_size_high = 0x04; if (!of_property_read_u32(np, "ti,video-cha-sync-delay-low", &temp)) dsi2lvds->cha_sync_delay_low = temp; else dsi2lvds->cha_sync_delay_low = 0x20; if (!of_property_read_u32(np, "ti,video-cha-sync-delay-high", &temp)) dsi2lvds->cha_sync_delay_high = temp; else dsi2lvds->cha_sync_delay_high = 0x0; if (!of_property_read_u32(np, "ti,h-pulse-width-low", &temp)) dsi2lvds->h_pulse_width_low = temp; else dsi2lvds->h_pulse_width_low = 0x10; if (!of_property_read_u32(np, "ti,h-pulse-width-high", &temp)) dsi2lvds->h_pulse_width_high = temp; else dsi2lvds->h_pulse_width_high = 0x00; /* Set bits 3:0 only */ if (!of_property_read_u32(np, "ti,v-pulse-width-low", &temp)) dsi2lvds->v_pulse_width_low = temp; else dsi2lvds->v_pulse_width_low = 0x0e; if (!of_property_read_u32(np, "ti,v-pulse-width-high", &temp)) dsi2lvds->v_pulse_width_high = temp; else dsi2lvds->v_pulse_width_high = 0x00; /* Set bits 3:0 only */ if (!of_property_read_u32(np, "ti,h-back-porch", &temp)) dsi2lvds->h_back_porch = temp; else dsi2lvds->h_back_porch = 0x98; if (!of_property_read_u32(np, "ti,v-back-porch", &temp)) dsi2lvds->v_back_porch = temp; else dsi2lvds->v_back_porch = 0x13; if (!of_property_read_u32(np, "ti,h-front-porch", &temp)) dsi2lvds->h_front_porch = temp; else dsi2lvds->h_front_porch = 0x10; if (!of_property_read_u32(np, "ti,v-front-porch", &temp)) dsi2lvds->v_front_porch = temp; else dsi2lvds->v_front_porch = 0x03; err: return err; } static int sn65dsi85_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { int err = 0; sn65dsi85_i2c_client = client; sn65dsi85_dsi2lvds = devm_kzalloc(&client->dev, sizeof(*sn65dsi85_dsi2lvds), GFP_KERNEL); if (sn65dsi85_dsi2lvds == NULL) return -ENOMEM; memset(sn65dsi85_dsi2lvds, 0, sizeof(struct tegra_dc_dsi2lvds_data)); mutex_init(&sn65dsi85_dsi2lvds->lock); sn65dsi85_dsi2lvds->client_i2c = client; sn65dsi85_dsi2lvds->regmap = devm_regmap_init_i2c(client, &sn65dsi85_regmap_config); if (IS_ERR(sn65dsi85_dsi2lvds->regmap)) { err = PTR_ERR(sn65dsi85_dsi2lvds->regmap); dev_err(&client->dev, "sn65dsi85_dsi2lvds: regmap init failed\n"); return err; } err = of_dsi2lvds_parse_platform_data(client); if (err) return err; sn65dsi85_dsi2lvds_panel_create_debugfs(sn65dsi85_dsi2lvds); pr_info("%s: returning with err=%d\n", __func__, err); return 0; } static int sn65dsi85_i2c_remove(struct i2c_client *client) { sn65dsi85_dsi2lvds_panel_remove_debugfs(sn65dsi85_dsi2lvds); sn65dsi85_i2c_client = NULL; return 0; } static const struct i2c_device_id sn65dsi85_id_table[] = { {"sn65dsi85_dsi2lvds", 0}, {}, }; static const struct of_device_id sn65dsi85_dt_match[] = { { .compatible = "ti,sn65dsi85" }, { } }; static struct i2c_driver sn65dsi85_i2c_drv = { .driver = { .name = "ti,sn65dsi85", .of_match_table = of_match_ptr(sn65dsi85_dt_match), .owner = THIS_MODULE, }, .probe = sn65dsi85_i2c_probe, .remove = sn65dsi85_i2c_remove, .id_table = sn65dsi85_id_table, }; static int __init sn65dsi85_i2c_client_init(void) { int err = 0; err = i2c_add_driver(&sn65dsi85_i2c_drv); if (err) pr_err("sn65dsi85_dsi2lvds: Failed to add i2c client driver\n"); return err; } static void __exit sn65dsi85_i2c_client_exit(void) { i2c_del_driver(&sn65dsi85_i2c_drv); } subsys_initcall(sn65dsi85_i2c_client_init); module_exit(sn65dsi85_i2c_client_exit); MODULE_AUTHOR("Tow Wang "); MODULE_DESCRIPTION(" TI SN65DSI85 dsi bridge to lvds"); MODULE_LICENSE("GPL");