822 lines
19 KiB
C

/*
* arch/arm/mach-tegra/board-venice-panel.c
*
* Copyright (c) 2011-2013, NVIDIA Corporation.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/ioport.h>
#include <linux/fb.h>
#include <linux/nvhost.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/ktime.h>
#include <linux/regulator/consumer.h>
#include <linux/pwm_backlight.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#ifdef CONFIG_TEGRA_DC
#include <linux/platform_data/tegra_dc.h>
#endif
#include <dt-bindings/gpio/tegra-gpio.h>
#include "irq.h"
#include "iomap.h"
#include "board.h"
#include "board-panel.h"
#include "common.h"
#ifdef CONFIG_TEGRA_DC
atomic_t sd_brightness = ATOMIC_INIT(255);
EXPORT_SYMBOL(sd_brightness);
static struct regulator *avdd_lcd_3v3;
static int power_off_time;
static ktime_t last_power_off;
struct platform_device * __init venice_host1x_init(void)
{
struct device_node *node = NULL;
struct platform_device *pdev = NULL;
node = of_find_compatible_node(NULL, NULL, "nvidia,tegra124-host1x");
if (!node)
return NULL;
pdev = of_find_device_by_node(node);
of_node_put(node);
return pdev;
}
#endif
static struct regulator *vdd_lcd_bl;
static struct regulator *lcd_bl_en;
static int pwm_to_bl_on;
static int bl_off_to_pwm;
static bool bl_enabled;
#ifdef CONFIG_TEGRA_DC
static bool edp_probed, edp_enabled;
static int venice_edp_regulator_probe(struct device *dev)
{
int ret;
avdd_lcd_3v3 = devm_regulator_get_optional(dev, "avdd-lcd");
if (IS_ERR(avdd_lcd_3v3)) {
dev_err(dev, "edp: avdd_lcd regulator get failed: %ld\n",
PTR_ERR(avdd_lcd_3v3));
return PTR_ERR(avdd_lcd_3v3);
}
vdd_lcd_bl = devm_regulator_get_optional(dev, "vdd-lcd-bl");
if (IS_ERR(vdd_lcd_bl)) {
dev_err(dev, "edp: vdd_bl regulator get failed: %ld\n",
PTR_ERR(vdd_lcd_bl));
return PTR_ERR(vdd_lcd_bl);
}
lcd_bl_en = devm_regulator_get_optional(dev, "lcd-bl-en");
if (IS_ERR(lcd_bl_en)) {
dev_err(dev, "edp: bl_en regulator get failed: %ld\n",
PTR_ERR(lcd_bl_en));
ret = PTR_ERR(lcd_bl_en);
lcd_bl_en = NULL;
return ret;
}
edp_probed = true;
return 0;
}
static int venice_edp_enable(struct device *dev)
{
int err = 0;
int diff = ktime_to_ms(ktime_sub(ktime_get_real(), last_power_off));
if (edp_enabled)
return 0;
if (ktime_to_ms(last_power_off) > 0 && diff < power_off_time)
msleep(power_off_time - diff);
err = regulator_enable(avdd_lcd_3v3);
if (err) {
dev_err(dev, "Enable regulator avdd_lcd failed: %d\n", err);
goto fail;
}
err = regulator_enable(vdd_lcd_bl);
if (err) {
dev_err(dev, "Enable regulator vdd_lcd_bl failed: %d\n", err);
regulator_disable(avdd_lcd_3v3);
goto fail;
}
edp_enabled = 1;
return 0;
fail:
return err;
}
static int venice_edp_disable(void)
{
if (!edp_enabled)
return 1;
if (vdd_lcd_bl)
regulator_disable(vdd_lcd_bl);
if (avdd_lcd_3v3)
regulator_disable(avdd_lcd_3v3);
last_power_off = ktime_get_real();
edp_enabled = 0;
return 0;
}
static int venice_edp_prepoweroff(void)
{
int ret;
if (lcd_bl_en && bl_enabled) {
ret = regulator_disable(lcd_bl_en);
if (ret)
pr_err("Disable lcd_bl_en failed: %d\n", ret);
else
bl_enabled = false;
msleep(bl_off_to_pwm);
}
return ret;
}
static struct tegra_dc_sd_settings venice_sd_settings = {
.enable = 0, /* disabled by default. */
.use_auto_pwm = false,
.hw_update_delay = 0,
.bin_width = -1,
.aggressiveness = 5,
.use_vid_luma = false,
.phase_in_adjustments = 0,
.k_limit_enable = true,
.k_limit = 200,
.sd_window_enable = false,
.soft_clipping_enable = true,
/* Low soft clipping threshold to compensate for aggressive k_limit */
.soft_clipping_threshold = 128,
.smooth_k_enable = false,
.smooth_k_incr = 64,
/* Default video coefficients */
.coeff = {5, 9, 2},
.fc = {0, 0},
/* Immediate backlight changes */
.blp = {1024, 255},
/* Gammas: R: 2.2 G: 2.2 B: 2.2 */
/* Default BL TF */
.bltf = {
{
{57, 65, 73, 82},
{92, 103, 114, 125},
{138, 150, 164, 178},
{193, 208, 224, 241},
},
},
/* Default LUT */
.lut = {
{
{255, 255, 255},
{199, 199, 199},
{153, 153, 153},
{116, 116, 116},
{85, 85, 85},
{59, 59, 59},
{36, 36, 36},
{17, 17, 17},
{0, 0, 0},
},
},
.sd_brightness = &sd_brightness,
.use_vpulse2 = true,
.bl_device_name = "backlight",
};
static struct tegra_dc_out_pin venice_edp_out_pins[] = {
{
.name = TEGRA_DC_OUT_PIN_H_SYNC,
.pol = TEGRA_DC_OUT_PIN_POL_LOW,
},
{
.name = TEGRA_DC_OUT_PIN_V_SYNC,
.pol = TEGRA_DC_OUT_PIN_POL_LOW,
},
{
.name = TEGRA_DC_OUT_PIN_PIXEL_CLOCK,
.pol = TEGRA_DC_OUT_PIN_POL_LOW,
},
{
.name = TEGRA_DC_OUT_PIN_DATA_ENABLE,
.pol = TEGRA_DC_OUT_PIN_POL_HIGH,
},
};
static struct tegra_dc_mode venice_mode;
static struct tegra_dp_out venice_dp;
static struct tegra_dc_out venice_disp1_out = {
.type = TEGRA_DC_OUT_DP,
.flags = TEGRA_DC_OUT_CONTINUOUS_MODE,
.parent_clk = "pll_d_out0",
.align = TEGRA_DC_ALIGN_MSB,
.order = TEGRA_DC_ORDER_RED_BLUE,
.dither = TEGRA_DC_TEMPORAL_DITHER,
.sd_settings = &venice_sd_settings,
.out_pins = venice_edp_out_pins,
.n_out_pins = ARRAY_SIZE(venice_edp_out_pins),
.enable = venice_edp_enable,
.disable = venice_edp_disable,
.prepoweroff = venice_edp_prepoweroff,
.regulator_probe = venice_edp_regulator_probe,
.dp = &venice_dp,
};
static struct tegra_fb_data venice_disp1_fb_data = {
.win = 0,
.bits_per_pixel = 32,
.flags = TEGRA_FB_FLIP_ON_PROBE,
};
struct tegra_dc_platform_data venice_disp1_pdata = {
.flags = TEGRA_DC_FLAG_ENABLED,
.default_out = &venice_disp1_out,
.fb = &venice_disp1_fb_data,
.emc_clk_rate = 204000000,
#ifdef CONFIG_TEGRA_DC_CMU
.cmu_enable = 1,
#endif
};
EXPORT_SYMBOL(venice_disp1_pdata);
static struct regulator *avdd_hdmi_pex;
static struct regulator *avdd_hdmi_pll;
static struct regulator *vdd_hdmi_5v0;
static int venice_hdmi_regulator_probe(struct device *dev)
{
struct tegra_dc_platform_data *pdata = dev_get_platdata(dev);
int gpio_hdmi_hpd;
if (!edp_probed)
return -EPROBE_DEFER;
avdd_hdmi_pex = devm_regulator_get_optional(dev, "avdd-hdmi-pex");
if (IS_ERR(avdd_hdmi_pex)) {
dev_err(dev, "HDMI: avdd-hdmi-pex regulator get failed: %ld\n",
PTR_ERR(avdd_hdmi_pex));
return PTR_ERR(avdd_hdmi_pex);
}
avdd_hdmi_pll = devm_regulator_get_optional(dev, "avdd-hdmi-pll");
if (IS_ERR(avdd_hdmi_pll)) {
dev_err(dev, "HDMI: avdd-hdmi-pll regulator get failed: %ld\n",
PTR_ERR(avdd_hdmi_pll));
return PTR_ERR(avdd_hdmi_pll);
}
vdd_hdmi_5v0 = devm_regulator_get_optional(dev, "vdd-hdmi-5v0");
if (IS_ERR(vdd_hdmi_5v0)) {
dev_err(dev, "HDMI: vdd-hdmi-5v0 regulator get failed: %ld\n",
PTR_ERR(vdd_hdmi_5v0));
return PTR_ERR(vdd_hdmi_5v0);
}
gpio_hdmi_hpd = of_get_named_gpio(dev->of_node, "hdmi-hpd-gpios", 0);
if (!gpio_is_valid(gpio_hdmi_hpd)) {
dev_err(dev, "HDMI: hdmi-hpd gpio get failed: %d\n",
gpio_hdmi_hpd);
return gpio_hdmi_hpd;
}
pdata->default_out->hotplug_gpio = gpio_hdmi_hpd;
return 0;
}
static int venice_hdmi_enable(struct device *dev)
{
int ret;
ret = regulator_enable(avdd_hdmi_pex);
if (ret < 0) {
pr_err("hdmi: couldn't enable regulator pex\n");
return ret;
}
ret = regulator_enable(avdd_hdmi_pll);
if (ret < 0) {
pr_err("hdmi: couldn't enable regulator pll\n");
regulator_disable(avdd_hdmi_pex);
return ret;
}
return 0;
}
static int venice_hdmi_disable(void)
{
if (avdd_hdmi_pex)
regulator_disable(avdd_hdmi_pex);
if (avdd_hdmi_pll)
regulator_disable(avdd_hdmi_pll);
return 0;
}
static int venice_hdmi_hotplug_init(struct device *dev)
{
int ret;
ret = regulator_enable(vdd_hdmi_5v0);
if (ret < 0)
pr_err("hdmi: couldn't enable regulator vdd\n");
return ret;
}
static int venice_hdmi_postsuspend(void)
{
if (vdd_hdmi_5v0)
regulator_disable(vdd_hdmi_5v0);
return 0;
}
struct tmds_config nyan_tmds_config[] = {
{ /* 480p/576p / 25.2MHz/27MHz modes */
.pclk = 27000000,
.pll0 = 0x01003010,
.pll1 = 0x00301B00,
.pe_current = 0x00000000,
.drive_current = 0x1F1F1F1F,
.peak_current = 0x03030303,
.bg_vref_level = 4,
},
{ /* 720p / 74.25MHz modes */
.pclk = 74250000,
.pll0 = 0x01003110,
.pll1 = 0x00301500,
.pe_current = 0x00000000,
.drive_current = 0x2C2C2C2C,
.peak_current = 0x07070707,
.bg_vref_level = 4,
},
{ /* 1080p / 148.5MHz modes */
.pclk = 148500000,
.pll0 = 0x01003310,
.pll1 = 0x00301500,
.pe_current = 0x00000000,
.drive_current = 0x33333333,
.peak_current = 0x0C0C0C0C,
.bg_vref_level = 4,
},
{
.pclk = INT_MAX,
.pll0 = 0x01003F10,
.pll1 = 0x00300F00,
.pe_current = 0x00000000,
.drive_current = 0x37373737, /* lane3 needs a slightly lower current */
.peak_current = 0x17171717,
.bg_vref_level = 6,
},
};
struct tegra_hdmi_out nyan_hdmi_out = {
.tmds_config = nyan_tmds_config,
.n_tmds_config = ARRAY_SIZE(nyan_tmds_config),
};
static struct tegra_dc_out venice_disp2_out = {
.type = TEGRA_DC_OUT_HDMI,
.flags = TEGRA_DC_OUT_HOTPLUG_HIGH |
TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND,
.parent_clk = "pll_d2_out0",
.dcc_bus = 3,
.max_pixclock = KHZ2PICOS(297000),
.align = TEGRA_DC_ALIGN_MSB,
.order = TEGRA_DC_ORDER_RED_BLUE,
.enable = venice_hdmi_enable,
.disable = venice_hdmi_disable,
.hotplug_init = venice_hdmi_hotplug_init,
.regulator_probe = venice_hdmi_regulator_probe,
.postsuspend = venice_hdmi_postsuspend,
.hdmi_out = &nyan_hdmi_out,
};
static struct tegra_fb_data venice_disp2_fb_data = {
.win = 0,
.xres = 1280,
.yres = 720,
.bits_per_pixel = 32,
.flags = TEGRA_FB_FLIP_ON_PROBE,
};
struct tegra_dc_platform_data venice_disp2_pdata = {
.default_out = &venice_disp2_out,
.fb = &venice_disp2_fb_data,
.emc_clk_rate = 300000000,
};
EXPORT_SYMBOL(venice_disp2_pdata);
static struct platform_device *disp1_device;
static int venice_bl_init(struct device *dev)
{
struct platform_pwm_backlight_data *data = dev_get_platdata(dev);
struct platform_pwm_backlight_data defdata;
int ret;
ret = pwm_backlight_parse_dt(dev, &defdata);
if (ret < 0) {
dev_err(dev, "failed to find platform data\n");
return ret;
}
data->max_brightness = defdata.max_brightness;
data->dft_brightness = defdata.dft_brightness;
data->levels = defdata.levels;
return 0;
}
static int venice_bl_notify(struct device *unused, int brightness)
{
int cur_sd_brightness = atomic_read(&sd_brightness);
int ret;
ret = (brightness * cur_sd_brightness) / 255;
if (lcd_bl_en) {
if (!brightness && bl_enabled) {
ret = regulator_disable(lcd_bl_en);
if (ret)
pr_err("Disable lcd_bl_en failed: %d\n", ret);
else
bl_enabled = false;
msleep(bl_off_to_pwm);
}
}
/* SD brightness is a percentage, max SD brightness is 255. */
return ret;
}
static void venice_bl_notify_after(struct device *unused, int brightness)
{
int ret;
/* Make sure that tegra dc 0 enable is completed */
if (tegra_dc_is_enabled(disp1_device) != 1)
return;
if (lcd_bl_en) {
if (brightness && !bl_enabled) {
msleep(pwm_to_bl_on);
ret = regulator_enable(lcd_bl_en);
if (ret)
pr_err("Enable lcd_bl_en failed: %d\n", ret);
else
bl_enabled = true;
}
}
}
static int venice_check_fb(struct device *dev, struct fb_info *info)
{
return info->device == &disp1_device->dev;
}
#else
static int venice_bl_init(struct device *dev)
{
struct platform_pwm_backlight_data *data = dev_get_platdata(dev);
struct platform_pwm_backlight_data defdata;
int ret;
u32 val;
ret = pwm_backlight_parse_dt(dev, &defdata);
if (ret < 0) {
dev_err(dev, "failed to find platform data\n");
return ret;
}
data->max_brightness = defdata.max_brightness;
data->dft_brightness = defdata.dft_brightness;
data->levels = defdata.levels;
/* avdd_lcd_3v3 is controlled by panel driver now */
vdd_lcd_bl = devm_regulator_get_optional(dev, "vdd-lcd-bl");
if (IS_ERR(vdd_lcd_bl)) {
dev_err(dev, "edp: vdd_bl regulator get failed: %ld\n",
PTR_ERR(vdd_lcd_bl));
return PTR_ERR(vdd_lcd_bl);
}
lcd_bl_en = devm_regulator_get_optional(dev, "lcd-bl-en");
if (IS_ERR(lcd_bl_en)) {
dev_err(dev, "edp: bl_en regulator get failed: %ld\n",
PTR_ERR(lcd_bl_en));
ret = PTR_ERR(lcd_bl_en);
lcd_bl_en = NULL;
return ret;
}
ret = of_property_read_u32(dev->of_node, "pwm-to-bl-on", &val);
if (ret < 0)
pwm_to_bl_on = 0;
else
pwm_to_bl_on = val;
ret = of_property_read_u32(dev->of_node, "bl-off-to-pwm", &val);
if (ret < 0)
bl_off_to_pwm = 0;
else
bl_off_to_pwm = val;
return 0;
}
static int venice_bl_notify(struct device *dev, int brightness)
{
int ret;
if (!lcd_bl_en)
return 0;
if (!brightness && bl_enabled) {
ret = regulator_disable(lcd_bl_en);
if (ret) {
dev_err(dev, "Disable lcd_bl_en failed: %d\n", ret);
return brightness;
}
msleep(bl_off_to_pwm); /* bl-off-to-pwm */
ret = regulator_disable(vdd_lcd_bl);
if (ret) {
dev_err(dev, "Disable lcd_bl_en failed: %d\n", ret);
ret = regulator_enable(lcd_bl_en);
return brightness;
}
bl_enabled = false;
}
dev_dbg(dev, "notify brightness=%d, bl_enabled=%d\n",
brightness, bl_enabled);
return brightness;
}
static void venice_bl_notify_after(struct device *dev, int brightness)
{
int ret;
if (!lcd_bl_en)
return;
if (brightness && !bl_enabled) {
ret = regulator_enable(vdd_lcd_bl);
if (ret) {
dev_err(dev, "Enable vdd_lcd_bl failed: %d\n", ret);
return;
}
msleep(pwm_to_bl_on);
ret = regulator_enable(lcd_bl_en);
if (ret) {
dev_err(dev, "Enable lcd_bl_en failed: %d\n", ret);
regulator_disable(vdd_lcd_bl);
return;
}
bl_enabled = true;
}
dev_dbg(dev, "notify_after brightness=%d, bl_enabled=%d\n",
brightness, bl_enabled);
}
#endif
struct platform_pwm_backlight_data venice_bl_data = {
.init = venice_bl_init,
.notify = venice_bl_notify,
.notify_after = venice_bl_notify_after,
#ifdef CONFIG_TEGRA_DC
/* Only toggle backlight on fb blank notifications for disp1 */
.check_fb = venice_check_fb,
#endif
.enable_gpio = -1,
};
EXPORT_SYMBOL(venice_bl_data);
#ifdef CONFIG_TEGRA_DC
static int venice_panel_mode_init(struct platform_device *dcs)
{
struct device *dev = &dcs->dev;
u32 val;
int ret;
/* Get mode information from dc0 */
ret = of_property_read_u32(dev->of_node, "depth", &val);
if (ret < 0) {
dev_err(dev, "Could not find depth property\n");
return ret;
}
venice_disp1_out.depth = val;
ret = of_property_read_u32(dev->of_node, "width", &val);
if (ret < 0) {
dev_err(dev, "Could not find width property\n");
return ret;
}
venice_disp1_out.width = val;
ret = of_property_read_u32(dev->of_node, "height", &val);
if (ret < 0) {
dev_err(dev, "Could not find height property\n");
return ret;
}
venice_disp1_out.height = val;
ret = of_property_read_u32(dev->of_node, "power-off-time", &val);
if (ret < 0)
power_off_time = 0;
else
power_off_time = val;
ret = of_property_read_u32(dev->of_node, "pwm-to-bl-on", &val);
if (ret < 0)
pwm_to_bl_on = 0;
else
pwm_to_bl_on = val;
ret = of_property_read_u32(dev->of_node, "bl-off-to-pwm", &val);
if (ret < 0)
bl_off_to_pwm = 0;
else
bl_off_to_pwm = val;
ret = of_property_read_u32(dev->of_node, "drive-current",
&venice_dp.drive_current);
if (ret < 0)
venice_dp.drive_current = 0;
ret = of_property_read_u32(dev->of_node, "preemphasis",
&venice_dp.preemphasis);
if (ret < 0)
venice_dp.preemphasis = 0;
/* Optional mode definitions follows */
ret = of_property_read_u32(dev->of_node, "pclk", &val);
if (ret == 0) {
venice_mode.pclk = val;
ret = of_property_read_u32(dev->of_node, "h-ref-to-sync",
&val);
if (ret < 0) {
dev_err(dev, "Could not find h-ref-to-sync property\n");
return ret;
}
venice_mode.h_ref_to_sync = val;
ret = of_property_read_u32(dev->of_node, "v-ref-to-sync",
&val);
if (ret < 0) {
dev_err(dev, "Could not find v-ref-to-sync property\n");
return ret;
}
venice_mode.v_ref_to_sync = val;
ret = of_property_read_u32(dev->of_node, "h-sync-width", &val);
if (ret < 0) {
dev_err(dev, "Could not find h-sync-width property\n");
return ret;
}
venice_mode.h_sync_width = val;
ret = of_property_read_u32(dev->of_node, "v-sync-width", &val);
if (ret < 0) {
dev_err(dev, "Could not find v-sync-width property\n");
return ret;
}
venice_mode.v_sync_width = val;
ret = of_property_read_u32(dev->of_node, "h-back-porch", &val);
if (ret < 0) {
dev_err(dev, "Could not find h-back-porch property\n");
return ret;
}
venice_mode.h_back_porch = val;
ret = of_property_read_u32(dev->of_node, "v-back-porch", &val);
if (ret < 0) {
dev_err(dev, "Could not find v-back-porch property\n");
return ret;
}
venice_mode.v_back_porch = val;
ret = of_property_read_u32(dev->of_node, "h-active", &val);
if (ret < 0) {
dev_err(dev, "Could not find h-active property\n");
return ret;
}
venice_mode.h_active = val;
ret = of_property_read_u32(dev->of_node, "v-active", &val);
if (ret < 0) {
dev_err(dev, "Could not find v-active property\n");
return ret;
}
venice_mode.v_active = val;
ret = of_property_read_u32(dev->of_node, "h-front-porch", &val);
if (ret < 0) {
dev_err(dev, "Could not find h-front-porch property\n");
return ret;
}
venice_mode.h_front_porch = val;
ret = of_property_read_u32(dev->of_node, "v-front-porch", &val);
if (ret < 0) {
dev_err(dev, "Could not find v-front-porch property\n");
return ret;
}
venice_mode.v_front_porch = val;
venice_disp1_out.modes = &venice_mode;
venice_disp1_out.n_modes = 1;
venice_disp1_fb_data.xres = venice_mode.h_active;
venice_disp1_fb_data.yres = venice_mode.v_active;
}
return 0;
}
void find_dc_dev(struct platform_device **dcs)
{
struct platform_device *pdev = NULL;
pdev = to_platform_device(bus_find_device_by_name(
&platform_bus_type, NULL, "tegradc.0"));
dcs[0] = pdev;
pdev = to_platform_device(bus_find_device_by_name(
&platform_bus_type, NULL, "tegradc.1"));
dcs[1] = pdev;
}
EXPORT_SYMBOL(find_dc_dev);
int __init venice_panel_init(void)
{
struct platform_device *phost1x = NULL;
struct platform_device *dc_devs[2];
int ret;
phost1x = venice_host1x_init();
if (!phost1x) {
pr_err("Could not find host1x device.\n");
return -EINVAL;
}
phost1x->dev.parent = NULL;
find_dc_dev(dc_devs);
if (!dc_devs[0] || !dc_devs[1]) {
pr_err("Could not find dc devices.\n");
return -EINVAL;
}
disp1_device = dc_devs[0];
ret = venice_panel_mode_init(disp1_device);
if (ret < 0)
return ret;
return 0;
}
#endif