/* * drivers/video/tegra/dc/of_dc.c * * Copyright (c) 2013-2014, 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_ARCH_TEGRA_11x_SOC) #include #elif defined(CONFIG_ARCH_TEGRA_12x_SOC) #include #endif #include "dc_reg.h" #include "dc_config.h" #include "dc_priv.h" #include "dev.h" #include "nvsd.h" #include "dsi.h" #ifdef CONFIG_OF /* #define OF_DC_DEBUG 1 */ #undef OF_DC_LOG #ifdef OF_DC_DEBUG #define OF_DC_LOG(fmt, args...) pr_info("OF_DC_LOG: " fmt, ## args) #else #define OF_DC_LOG(fmt, args...) #endif static struct regulator *of_hdmi_vddio; static struct regulator *of_hdmi_reg; static struct regulator *of_hdmi_pll; static struct regulator *of_lvds_dp_reg; static struct regulator *of_lvds_bl_reg; #ifdef CONFIG_TEGRA_DC_CMU static struct tegra_dc_cmu default_cmu = { /* lut1 maps sRGB to linear space. */ { 0, 1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, 40, 42, 45, 48, 50, 53, 56, 59, 62, 66, 69, 72, 76, 79, 83, 87, 91, 95, 99, 103, 107, 112, 116, 121, 126, 131, 136, 141, 146, 151, 156, 162, 168, 173, 179, 185, 191, 197, 204, 210, 216, 223, 230, 237, 244, 251, 258, 265, 273, 280, 288, 296, 304, 312, 320, 329, 337, 346, 354, 363, 372, 381, 390, 400, 409, 419, 428, 438, 448, 458, 469, 479, 490, 500, 511, 522, 533, 544, 555, 567, 578, 590, 602, 614, 626, 639, 651, 664, 676, 689, 702, 715, 728, 742, 755, 769, 783, 797, 811, 825, 840, 854, 869, 884, 899, 914, 929, 945, 960, 976, 992, 1008, 1024, 1041, 1057, 1074, 1091, 1108, 1125, 1142, 1159, 1177, 1195, 1213, 1231, 1249, 1267, 1286, 1304, 1323, 1342, 1361, 1381, 1400, 1420, 1440, 1459, 1480, 1500, 1520, 1541, 1562, 1582, 1603, 1625, 1646, 1668, 1689, 1711, 1733, 1755, 1778, 1800, 1823, 1846, 1869, 1892, 1916, 1939, 1963, 1987, 2011, 2035, 2059, 2084, 2109, 2133, 2159, 2184, 2209, 2235, 2260, 2286, 2312, 2339, 2365, 2392, 2419, 2446, 2473, 2500, 2527, 2555, 2583, 2611, 2639, 2668, 2696, 2725, 2754, 2783, 2812, 2841, 2871, 2901, 2931, 2961, 2991, 3022, 3052, 3083, 3114, 3146, 3177, 3209, 3240, 3272, 3304, 3337, 3369, 3402, 3435, 3468, 3501, 3535, 3568, 3602, 3636, 3670, 3705, 3739, 3774, 3809, 3844, 3879, 3915, 3950, 3986, 4022, 4059, 4095, }, /* csc */ { 0, }, /*lut2*/ { 0, } }; #endif #endif #ifdef CONFIG_OF static int out_type_from_pn(struct device_node *panel_node) { struct device_node *default_out_np = NULL; u32 temp; if (panel_node && of_device_is_available(panel_node)) default_out_np = of_get_child_by_name(panel_node, "disp-default-out"); if (default_out_np && !of_property_read_u32(default_out_np, "nvidia,out-type", &temp)) { return (int)temp; } else return -EINVAL; } static int parse_dc_out_type(struct device_node *np, struct tegra_dc_out *default_out) { int out_type; out_type = out_type_from_pn( of_get_child_by_name(np, "display")); if (out_type >= 0) { default_out->type = out_type; return 0; } pr_err("invalid nvidia,dc-connection or nvidia,out-type\n"); return -EINVAL; } static int parse_tmds(struct device_node *np, u8 *addr) { u32 temp; int i = 0; u32 major = 0; u32 minor = 0; struct property *prop; const __be32 *p; u32 u; struct tmds_config *tmds_cfg_addr; tmds_cfg_addr = (struct tmds_config *)addr; of_property_for_each_u32(np, "version", prop, p, u) i++; if (i == 2) { /* major version, minor version */ i = 0; of_property_for_each_u32(np, "version", prop, p, u) { i++; if (i == 1) major = u; else minor = u; } tmds_cfg_addr->version = MKDEV(major, minor); OF_DC_LOG("tmds version 0x%x\n", tmds_cfg_addr->version); } else if (i == 0) { OF_DC_LOG("there's no tmds conf version.\n"); } else { OF_DC_LOG("need to have major, minor version\n"); goto parse_tmds_fail; } if (!of_property_read_u32(np, "pclk", &temp)) { tmds_cfg_addr->pclk = (int)temp; OF_DC_LOG("tmds pclk %d\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "pll0", &temp)) { tmds_cfg_addr->pll0 = (u32)temp; OF_DC_LOG("tmds pll0 0x%x\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "pll1", &temp)) { tmds_cfg_addr->pll1 = (u32)temp; OF_DC_LOG("tmds pll1 0x%x\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "pe-current", &temp)) { tmds_cfg_addr->pe_current = (u32)temp; OF_DC_LOG("tmds pe-current 0x%x\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "drive-current", &temp)) { tmds_cfg_addr->drive_current = (u32)temp; OF_DC_LOG("tmds drive-current 0x%x\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "peak-current", &temp)) { tmds_cfg_addr->peak_current = (u32)temp; OF_DC_LOG("tmds peak-current 0x%x\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "pad-ctls0-mask", &temp)) { tmds_cfg_addr->pad_ctls0_mask = (u32)temp; OF_DC_LOG("tmds pad_ctls0_mask 0x%x\n", temp); } else { goto parse_tmds_fail; } if (!of_property_read_u32(np, "pad-ctls0-setting", &temp)) { tmds_cfg_addr->pad_ctls0_setting = (u32)temp; OF_DC_LOG("tmds pad_ctls0_setting 0x%x\n", temp); } else { goto parse_tmds_fail; } return 0; parse_tmds_fail: pr_err("parse tmds fail!\n"); return -EINVAL; } static int parse_dt_lt(struct device_node *np, u8 *addr) { u32 temp; struct tegra_dc_dp_lt_settings *dt_lt_cfg_addr; dt_lt_cfg_addr = (struct tegra_dc_dp_lt_settings *)addr; if (!of_property_read_u32_array(np, "drive-current", dt_lt_cfg_addr->drive_current, 4)) { OF_DC_LOG("dp drive-current 0x%x 0x%x 0x%x 0x%x\n", dt_lt_cfg_addr->drive_current[0], dt_lt_cfg_addr->drive_current[1], dt_lt_cfg_addr->drive_current[2], dt_lt_cfg_addr->drive_current[3]); } else { goto parse_dt_lt_fail; } if (!of_property_read_u32_array(np, "lane-preemphasis", dt_lt_cfg_addr->lane_preemphasis, 4)) { OF_DC_LOG("dp lane_preemphasis 0x%x 0x%x 0x%x 0x%x\n", dt_lt_cfg_addr->lane_preemphasis[0], dt_lt_cfg_addr->lane_preemphasis[1], dt_lt_cfg_addr->lane_preemphasis[2], dt_lt_cfg_addr->lane_preemphasis[3]); } else { goto parse_dt_lt_fail; } if (!of_property_read_u32_array(np, "post-cursor", dt_lt_cfg_addr->post_cursor, 4)) { OF_DC_LOG("dp post_cursor 0x%x 0x%x 0x%x 0x%x\n", dt_lt_cfg_addr->post_cursor[0], dt_lt_cfg_addr->post_cursor[1], dt_lt_cfg_addr->post_cursor[2], dt_lt_cfg_addr->post_cursor[3]); } else { goto parse_dt_lt_fail; } if (!of_property_read_u32(np, "tx-pu", &temp)) { dt_lt_cfg_addr->tx_pu = (u32)temp; OF_DC_LOG("dp tx-pu 0x%x\n", temp); } else { goto parse_dt_lt_fail; } if (!of_property_read_u32(np, "load-adj", &temp)) { dt_lt_cfg_addr->load_adj = (u32)temp; OF_DC_LOG("dp load-adj 0x%x\n", temp); } else { goto parse_dt_lt_fail; } return 0; parse_dt_lt_fail: pr_err("parse dp-lt-config fail!\n"); return -EINVAL; } static bool is_dc_default_out_flag(u32 flag) { if ((flag == TEGRA_DC_OUT_HOTPLUG_HIGH) | (flag == TEGRA_DC_OUT_HOTPLUG_LOW) | (flag == TEGRA_DC_OUT_NVHDCP_POLICY_ALWAYS_ON) | (flag == TEGRA_DC_OUT_NVHDCP_POLICY_ON_DEMAND) | (flag == TEGRA_DC_OUT_CONTINUOUS_MODE) | (flag == TEGRA_DC_OUT_ONE_SHOT_MODE) | (flag == TEGRA_DC_OUT_N_SHOT_MODE) | (flag == TEGRA_DC_OUT_ONE_SHOT_LP_MODE) | (flag == TEGRA_DC_OUT_INITIALIZED_MODE) | (flag == TEGRA_DC_OUT_HOTPLUG_WAKE_LP0)) return true; else return false; } static int parse_disp_extra_attributes(struct platform_device *ndev, struct device_node *np, struct tegra_dc_out *default_out) { struct device_node *ddc; struct device_node *np_parent; int hotplug_gpio = 0; enum of_gpio_flags flags; if (default_out->type == TEGRA_DC_OUT_HDMI) { int id; np_parent = of_get_parent(np); if (!np_parent) { pr_err("Error getting parent device-tree node\n"); return -EINVAL; } ddc = of_parse_phandle(np_parent, "nvidia,ddc-i2c-bus", 0); if (!ddc) { pr_err("No ddc device node\n"); of_node_put(np_parent); return -EINVAL; } else id = of_alias_get_id(ddc, "i2c"); if (id >= 0) { default_out->dcc_bus = id; OF_DC_LOG("out_dcc bus %d\n", id); } else { pr_err("Invalid i2c id\n"); of_node_put(np_parent); return -EINVAL; } hotplug_gpio = of_get_named_gpio_flags(np_parent, "nvidia,hpd-gpio", 0, &flags); if (hotplug_gpio != 0) default_out->hotplug_gpio = hotplug_gpio; of_node_put(np_parent); } if (default_out->type == TEGRA_DC_OUT_DP) { np_parent = of_get_parent(np); if (!np_parent) { pr_err("Error getting parent device-tree node\n"); return -EINVAL; } hotplug_gpio = of_get_named_gpio_flags(np_parent, "nvidia,hpd-gpio", 0, &flags); if (hotplug_gpio != 0) default_out->hotplug_gpio = hotplug_gpio; of_node_put(np_parent); } return 0; } static int parse_disp_default_out(struct platform_device *ndev, struct device_node *np, struct tegra_dc_out *default_out, struct tegra_fb_data *fb) { u32 temp; struct property *prop; const __be32 *p; u32 u; const char *temp_str0; /* * construct default_out */ if (!of_property_read_u32(np, "nvidia,out-width", &temp)) { default_out->width = (unsigned) temp; OF_DC_LOG("out_width %d\n", default_out->width); } if (!of_property_read_u32(np, "nvidia,out-height", &temp)) { default_out->height = (unsigned) temp; OF_DC_LOG("out_height %d\n", default_out->height); } if (!of_property_read_u32(np, "nvidia,out-max-pixclk", &temp)) { default_out->max_pixclock = (unsigned)temp; OF_DC_LOG("%u max_pixclock in pico second unit\n", default_out->max_pixclock); } of_property_for_each_u32(np, "nvidia,out-flags", prop, p, u) { if (!is_dc_default_out_flag(u)) { pr_err("invalid out flags\n"); return -EINVAL; } default_out->flags |= (unsigned) u; } OF_DC_LOG("default_out flag %u\n", default_out->flags); if (!of_property_read_u32(np, "nvidia,out-align", &temp)) { if (temp == TEGRA_DC_ALIGN_MSB) OF_DC_LOG("tegra dc align msb\n"); else if (temp == TEGRA_DC_ALIGN_LSB) OF_DC_LOG("tegra dc align lsb\n"); else { pr_err("invalid out align\n"); return -EINVAL; } default_out->align = (unsigned)temp; } if (!of_property_read_u32(np, "nvidia,out-order", &temp)) { if (temp == TEGRA_DC_ORDER_RED_BLUE) OF_DC_LOG("tegra order red to blue\n"); else if (temp == TEGRA_DC_ORDER_BLUE_RED) OF_DC_LOG("tegra order blue to red\n"); else { pr_err("invalid out order\n"); return -EINVAL; } default_out->order = (unsigned)temp; } if (!of_property_read_string(np, "nvidia,out-parent-clk", &temp_str0)) { default_out->parent_clk = temp_str0; OF_DC_LOG("parent clk %s\n", default_out->parent_clk); } else { goto fail_disp_default_out; } if (default_out->type == TEGRA_DC_OUT_HDMI) { default_out->depth = 0; #ifdef CONFIG_FRAMEBUFFER_CONSOLE if (!of_property_read_u32(np, "nvidia,out-depth", &temp)) { default_out->depth = (unsigned) temp; OF_DC_LOG("out-depth for HDMI FB console %d\n", temp); } #endif } else { /* default_out->type == TEGRA_DC_OUT_DSI || TEGRA_DC_OUT_LVDS * || TEGRA_DC_OUT_DP */ if (!of_property_read_u32(np, "nvidia,out-depth", &temp)) { default_out->depth = (unsigned) temp; OF_DC_LOG("out-depth for DSI/LVDS/DP display %d\n", temp); } } if (!of_property_read_u32(np, "nvidia,out-lvds-mode", &temp)) { default_out->lvds_mode = temp; OF_DC_LOG("lvds mode %s\n", default_out->lvds_mode); } else default_out->lvds_mode = TEGRA_DC_LVDS_24_1; /* * construct fb */ fb->win = 0; /* set fb->win to 0 in default */ if (!of_property_read_u32(np, "nvidia,out-xres", &temp)) { fb->xres = (int)temp; OF_DC_LOG("framebuffer xres %d\n", fb->xres); } else { goto fail_disp_default_out; } if (!of_property_read_u32(np, "nvidia,out-yres", &temp)) { fb->yres = (int)temp; OF_DC_LOG("framebuffer yres %d\n", fb->yres); } else { goto fail_disp_default_out; } return 0; fail_disp_default_out: pr_err("%s: a parse error\n", __func__); return -EINVAL; } int parse_tmds_config(struct platform_device *ndev, struct device_node *np, struct tegra_dc_out *default_out) { int err = 0; u8 *addr; struct device_node *tmds_np = NULL; struct device_node *entry = NULL; if (default_out->type == TEGRA_DC_OUT_HDMI) tmds_np = of_get_child_by_name(np, "tmds-config"); if (!tmds_np) { pr_info("%s: No tmds-config node\n", __func__); } else { int tmds_set_count = of_get_child_count(tmds_np); if (!tmds_set_count) { pr_info("tmds node exists but no cfg!\n"); goto success_tmds_config; } default_out->hdmi_out = devm_kzalloc(&ndev->dev, sizeof(struct tegra_hdmi_out), GFP_KERNEL); if (!default_out->hdmi_out) { dev_err(&ndev->dev, "not enough memory\n"); return -ENOMEM; } default_out->hdmi_out->n_tmds_config = tmds_set_count; default_out->hdmi_out->tmds_config = devm_kzalloc(&ndev->dev, tmds_set_count * sizeof(struct tmds_config), GFP_KERNEL); if (!default_out->hdmi_out->tmds_config) { dev_err(&ndev->dev, "not enough memory\n"); return -ENOMEM; } addr = (u8 *)default_out->hdmi_out->tmds_config; for_each_child_of_node(tmds_np, entry) { err = parse_tmds(entry, addr); if (err) goto fail_tmds_config; addr += sizeof(struct tmds_config); } } success_tmds_config: return 0; fail_tmds_config: pr_err("%s: a parse error\n", __func__); return -EINVAL; } int parse_dp_lt_config(struct platform_device *ndev, struct device_node *np, struct tegra_dc_out *default_out) { int err = 0; u8 *addr; struct device_node *dp_lt_np = NULL; struct device_node *entry = NULL; if (default_out->type == TEGRA_DC_OUT_DP) dp_lt_np = of_get_child_by_name(np, "dp-lt-config"); if (!dp_lt_np) { pr_info("%s: No dp-lt-config node\n", __func__); } else { int dp_lt_set_count = of_get_child_count(dp_lt_np); if (!dp_lt_set_count) { pr_info("dp lt node exists but no cfg!\n"); goto success_dp_lt_config; } default_out->dp_out = devm_kzalloc(&ndev->dev, sizeof(struct tegra_dp_out), GFP_KERNEL); if (!default_out->dp_out) { dev_err(&ndev->dev, "not enough memory\n"); return -ENOMEM; } default_out->dp_out->n_lt_settings = dp_lt_set_count; default_out->dp_out->lt_settings = devm_kzalloc(&ndev->dev, dp_lt_set_count * sizeof(struct tegra_dc_dp_lt_settings), GFP_KERNEL); if (!default_out->dp_out->lt_settings) { dev_err(&ndev->dev, "not enough memory\n"); return -ENOMEM; } addr = (u8 *)default_out->dp_out->lt_settings; for_each_child_of_node(dp_lt_np, entry) { err = parse_dt_lt(entry, addr); if (err) goto fail_dp_lt_config; addr += sizeof(struct tegra_dc_dp_lt_settings); } } success_dp_lt_config: return 0; fail_dp_lt_config: pr_err("%s: a parse error\n", __func__); return -EINVAL; } static int parse_sd_settings(struct device_node *np, struct tegra_dc_sd_settings *sd_settings) { struct property *prop; const __be32 *p; u32 u; const char *sd_str1; u8 coeff[3] = {0, }; u8 fc[2] = {0, }; u32 blp[2] = {0, }; int coeff_count = 0; int fc_count = 0; int blp_count = 0; int bltf_count = 0; u8 *addr; int sd_lut[108] = {0, }; int sd_i = 0; int sd_j = 0; int sd_index = 0; u32 temp; if (of_device_is_available(np)) sd_settings->enable = (unsigned) 1; else sd_settings->enable = (unsigned) 0; OF_DC_LOG("nvidia,sd-enable %d\n", sd_settings->enable); if (!of_property_read_u32(np, "nvidia,use-auto-pwm", &temp)) { sd_settings->use_auto_pwm = (bool) temp; OF_DC_LOG("nvidia,use-auto-pwm %d\n", temp); } if (!of_property_read_u32(np, "nvidia,hw-update-delay", &temp)) { sd_settings->hw_update_delay = (u8) temp; OF_DC_LOG("nvidia,hw-update-delay %d\n", temp); } if (!of_property_read_u32(np, "nvidia,bin-width", &temp)) { s32 s32_val; s32_val = (s32)temp; sd_settings->bin_width = (short)s32_val; OF_DC_LOG("nvidia,bin-width %d\n", s32_val); } if (!of_property_read_u32(np, "nvidia,aggressiveness", &temp)) { sd_settings->aggressiveness = (u8) temp; OF_DC_LOG("nvidia,aggressiveness %d\n", temp); } if (!of_property_read_u32(np, "nvidia,use-vid-luma", &temp)) { sd_settings->use_vid_luma = (bool) temp; OF_DC_LOG("nvidia,use-vid-luma %d\n", temp); } if (!of_property_read_u32(np, "nvidia,phase-in-settings", &temp)) { sd_settings->phase_in_settings = (u8) temp; OF_DC_LOG("nvidia,phase-in-settings %d\n", temp); } if (!of_property_read_u32(np, "nvidia,phase-in-adjustments", &temp)) { sd_settings->phase_in_adjustments = (u8) temp; OF_DC_LOG("nvidia,phase-in-adjustments %d\n", temp); } if (!of_property_read_u32(np, "nvidia,k-limit-enable", &temp)) { sd_settings->k_limit_enable = (bool) temp; OF_DC_LOG("nvidia,k-limit-enable %d\n", temp); } if (!of_property_read_u32(np, "nvidia,k-limit", &temp)) { sd_settings->k_limit = (u16) temp; OF_DC_LOG("nvidia,k-limit %d\n", temp); } if (!of_property_read_u32(np, "nvidia,sd-window-enable", &temp)) { sd_settings->sd_window_enable = (bool) temp; OF_DC_LOG("nvidia,sd-window-enable %d\n", temp); } if (!of_property_read_u32(np, "nvidia,soft-clipping-enable", &temp)) { sd_settings->soft_clipping_enable = (bool) temp; OF_DC_LOG("nvidia,soft-clipping-enable %d\n", temp); } if (!of_property_read_u32(np, "nvidia,soft-clipping-threshold", &temp)) { sd_settings->soft_clipping_threshold = (u8) temp; OF_DC_LOG("nvidia,soft-clipping-threshold %d\n", temp); } if (!of_property_read_u32(np, "nvidia,smooth-k-enable", &temp)) { sd_settings->smooth_k_enable = (bool) temp; OF_DC_LOG("nvidia,smooth-k-enable %d\n", temp); } if (!of_property_read_u32(np, "nvidia,smooth-k-incr", &temp)) { sd_settings->smooth_k_incr = (u16) temp; OF_DC_LOG("nvidia,smooth-k-incr %d\n", temp); } sd_settings->sd_brightness = &sd_brightness; if (!of_property_read_u32(np, "nvidia,use-vpulse2", &temp)) { sd_settings->use_vpulse2 = (bool) temp; OF_DC_LOG("nvidia,use-vpulse2 %d\n", temp); } if (!of_property_read_string(np, "nvidia,bl-device-name", &sd_str1)) { sd_settings->bl_device_name = (char *)sd_str1; OF_DC_LOG("nvidia,bl-device-name %s\n", sd_str1); } coeff_count = 0; of_property_for_each_u32(np, "nvidia,coeff", prop, p, u) coeff_count++; if (coeff_count > (sizeof(coeff) / sizeof(coeff[0]))) { pr_err("sd_coeff overflow\n"); return -EINVAL; } else { coeff_count = 0; of_property_for_each_u32(np, "nvidia,coeff", prop, p, u) coeff[coeff_count++] = (u8)u; sd_settings->coeff.r = coeff[0]; sd_settings->coeff.g = coeff[1]; sd_settings->coeff.b = coeff[2]; OF_DC_LOG("nvidia,coeff %d %d %d\n", coeff[0], coeff[1], coeff[2]); } fc_count = 0; of_property_for_each_u32(np, "nvidia,fc", prop, p, u) fc_count++; if (fc_count > sizeof(fc) / sizeof(fc[0])) { pr_err("sd fc overflow\n"); return -EINVAL; } else { fc_count = 0; of_property_for_each_u32(np, "nvidia,fc", prop, p, u) fc[fc_count++] = (u8)u; sd_settings->fc.time_limit = fc[0]; sd_settings->fc.threshold = fc[1]; OF_DC_LOG("nvidia,fc %d %d\n", fc[0], fc[1]); } blp_count = 0; of_property_for_each_u32(np, "nvidia,blp", prop, p, u) blp_count++; if (blp_count > sizeof(blp) / sizeof(blp[0])) { pr_err("sd blp overflow\n"); return -EINVAL; } else { blp_count = 0; of_property_for_each_u32(np, "nvidia,blp", prop, p, u) blp[blp_count++] = (u32)u; sd_settings->blp.time_constant = (u16)blp[0]; sd_settings->blp.step = (u8)blp[1]; OF_DC_LOG("nvidia,blp %d %d\n", blp[0], blp[1]); } bltf_count = 0; of_property_for_each_u32(np, "nvidia,bltf", prop, p, u) bltf_count++; if (bltf_count > (sizeof(sd_settings->bltf) / sizeof(sd_settings->bltf[0][0][0]))) { pr_err("sd bltf overflow of sd_settings\n"); return -EINVAL; } else { addr = &(sd_settings->bltf[0][0][0]); of_property_for_each_u32(np, "nvidia,bltf", prop, p, u) *(addr++) = u; } sd_index = 0; of_property_for_each_u32(np, "nvidia,lut", prop, p, u) sd_index++; if (sd_index > sizeof(sd_lut)/sizeof(sd_lut[0])) { pr_err("sd lut size overflow of sd_settings\n"); return -EINVAL; } else { sd_index = 0; of_property_for_each_u32(np, "nvidia,lut", prop, p, u) sd_lut[sd_index++] = u; sd_index = 0; if (prop) { for (sd_i = 0; sd_i < 4; sd_i++) for (sd_j = 0; sd_j < 9; sd_j++) { sd_settings->lut[sd_i][sd_j].r = sd_lut[sd_index++]; sd_settings->lut[sd_i][sd_j].g = sd_lut[sd_index++]; sd_settings->lut[sd_i][sd_j].b = sd_lut[sd_index++]; } } } return 0; } static int parse_modes(struct device_node *np, struct tegra_dc_mode *modes) { u32 temp; if (!of_property_read_u32(np, "clock-frequency", &temp)) { modes->pclk = temp; OF_DC_LOG("of pclk %d\n", temp); } else { goto parse_modes_fail; } if (!of_property_read_u32(np, "nvidia,h-ref-to-sync", &temp)) { modes->h_ref_to_sync = temp; } else { OF_DC_LOG("of h_ref_to_sync %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "nvidia,v-ref-to-sync", &temp)) { modes->v_ref_to_sync = temp; } else { OF_DC_LOG("of v_ref_to_sync %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "hsync-len", &temp)) { modes->h_sync_width = temp; } else { OF_DC_LOG("of h_sync_width %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "vsync-len", &temp)) { modes->v_sync_width = temp; } else { OF_DC_LOG("of v_sync_width %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "hback-porch", &temp)) { modes->h_back_porch = temp; } else { OF_DC_LOG("of h_back_porch %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "vback-porch", &temp)) { modes->v_back_porch = temp; } else { OF_DC_LOG("of v_back_porch %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "hactive", &temp)) { modes->h_active = temp; } else { OF_DC_LOG("of h_active %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "vactive", &temp)) { modes->v_active = temp; } else { OF_DC_LOG("of v_active %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "hfront-porch", &temp)) { modes->h_front_porch = temp; } else { OF_DC_LOG("of h_front_porch %d\n", temp); goto parse_modes_fail; } if (!of_property_read_u32(np, "vfront-porch", &temp)) { modes->v_front_porch = temp; } else { OF_DC_LOG("of v_front_porch %d\n", temp); goto parse_modes_fail; } return 0; parse_modes_fail: pr_err("a mode parameter parse fail!\n"); return -EINVAL; } #ifdef CONFIG_TEGRA_DC_CMU static int parse_cmu_data(struct device_node *np, struct tegra_dc_cmu *cmu) { u16 *csc_parse; u8 *addr_cmu_lut2; struct property *prop; const __be32 *p; u32 u; int csc_count = 0; int lut2_count = 0; memcpy(cmu, &default_cmu, sizeof(struct tegra_dc_cmu)); csc_parse = &(cmu->csc.krr); addr_cmu_lut2 = &(cmu->lut2[0]); of_property_for_each_u32(np, "nvidia,cmu-csc", prop, p, u) csc_count++; if (csc_count > (sizeof(cmu->csc) / sizeof(cmu->csc.krr))) { pr_err("cmu csc overflow\n"); return -EINVAL; } else { of_property_for_each_u32(np, "nvidia,cmu-csc", prop, p, u) { OF_DC_LOG("cmu csc 0x%x\n", u); *(csc_parse++) = (u16)u; } } of_property_for_each_u32(np, "nvidia,cmu-lut2", prop, p, u) lut2_count++; if (lut2_count > (sizeof(cmu->lut2) / sizeof(cmu->lut2[0]))) { pr_err("cmu lut2 overflow\n"); return -EINVAL; } else { of_property_for_each_u32(np, "nvidia,cmu-lut2", prop, p, u) { /* OF_DC_LOG("cmu lut2 0x%x\n", u); */ *(addr_cmu_lut2++) = (u8)u; } } return 0; } #endif struct tegra_dsi_cmd *tegra_dsi_parse_cmd_dt(struct platform_device *ndev, const struct device_node *node, struct property *prop, u32 n_cmd) { struct tegra_dsi_cmd *dsi_cmd, *temp; u32 *prop_val_ptr; u32 cnt = 0, i = 0; u8 arg1, arg2; if (!n_cmd) return NULL; if (!prop) return NULL; prop_val_ptr = prop->value; dsi_cmd = devm_kzalloc(&ndev->dev, sizeof(*dsi_cmd) * n_cmd, GFP_KERNEL); if (!dsi_cmd) { pr_err("dsi: cmd memory allocation failed\n"); return ERR_PTR(-ENOMEM); } temp = dsi_cmd; for (cnt = 0; cnt < n_cmd; cnt++, temp++) { temp->cmd_type = be32_to_cpu(*prop_val_ptr++); if ((temp->cmd_type == TEGRA_DSI_PACKET_CMD) || (temp->cmd_type == TEGRA_DSI_PACKET_VIDEO_VBLANK_CMD)) { temp->data_id = be32_to_cpu(*prop_val_ptr++); arg1 = be32_to_cpu(*prop_val_ptr++); arg2 = be32_to_cpu(*prop_val_ptr++); prop_val_ptr++; /* skip ecc */ if (temp->data_id == DSI_GENERIC_LONG_WRITE || temp->data_id == DSI_DCS_LONG_WRITE || temp->data_id == DSI_NULL_PKT_NO_DATA || temp->data_id == DSI_BLANKING_PKT_NO_DATA) { /* long pkt */ temp->sp_len_dly.data_len = (arg2 << NUMOF_BIT_PER_BYTE) | arg1; temp->pdata = devm_kzalloc(&ndev->dev, temp->sp_len_dly.data_len, GFP_KERNEL); for (i = 0; i < temp->sp_len_dly.data_len; i++) (temp->pdata)[i] = be32_to_cpu(*prop_val_ptr++); prop_val_ptr += 2; /* skip checksum */ } else { temp->sp_len_dly.sp.data0 = arg1; temp->sp_len_dly.sp.data1 = arg2; } } else if (temp->cmd_type == TEGRA_DSI_DELAY_MS) { temp->sp_len_dly.delay_ms = be32_to_cpu(*prop_val_ptr++); } else if (temp->cmd_type == TEGRA_DSI_SEND_FRAME) { temp->sp_len_dly.frame_cnt = be32_to_cpu(*prop_val_ptr++); } } return dsi_cmd; } static const u32 *tegra_dsi_parse_pkt_seq_dt(struct platform_device *ndev, struct device_node *node, struct property *prop) { u32 *prop_val_ptr; u32 *pkt_seq; int line, i; #define LINE_STOP 0xff if (!prop) return NULL; pkt_seq = devm_kzalloc(&ndev->dev, sizeof(u32) * NUMOF_PKT_SEQ, GFP_KERNEL); if (!pkt_seq) { dev_err(&ndev->dev, "dsi: pkt seq memory allocation failed\n"); return ERR_PTR(-ENOMEM); } prop_val_ptr = prop->value; for (line = 0; line < NUMOF_PKT_SEQ; line += 2) { /* compute line value from dt line */ for (i = 0;; i += 2) { u32 cmd = be32_to_cpu(*prop_val_ptr++); if (cmd == LINE_STOP) break; else if (cmd == PKT_LP) pkt_seq[line] |= PKT_LP; else { u32 len = be32_to_cpu(*prop_val_ptr++); if (i == 0) /* PKT_ID0 */ pkt_seq[line] |= PKT_ID0(cmd) | PKT_LEN0(len); if (i == 2) /* PKT_ID1 */ pkt_seq[line] |= PKT_ID1(cmd) | PKT_LEN1(len); if (i == 4) /* PKT_ID2 */ pkt_seq[line] |= PKT_ID2(cmd) | PKT_LEN2(len); if (i == 6) /* PKT_ID3 */ pkt_seq[line + 1] |= PKT_ID3(cmd) | PKT_LEN3(len); if (i == 8) /* PKT_ID4 */ pkt_seq[line + 1] |= PKT_ID4(cmd) | PKT_LEN4(len); if (i == 10) /* PKT_ID5 */ pkt_seq[line + 1] |= PKT_ID5(cmd) | PKT_LEN5(len); } } } #undef LINE_STOP return pkt_seq; } static int tegra_parse_lvds_pins(struct device_node *np, struct tegra_dc_out_pin *out_pin) { u32 temp; if (!of_property_read_u32(np, "pin-name", &temp)) { out_pin->name = temp; OF_DC_LOG("of name %d\n", temp); } else { goto parse_pins_fail; } if (!of_property_read_u32(np, "pol", &temp)) { out_pin->pol = temp; OF_DC_LOG("of pol %d\n", temp); } else { goto parse_pins_fail; } return 0; parse_pins_fail: pr_err("a out_pins parameter parse fail!\n"); return -EINVAL; } struct device_node *parse_dsi_settings(struct platform_device *ndev, struct device_node *np_dsi, struct tegra_dc_platform_data *pdata) { u32 temp; int dsi_te_gpio = 0; int bl_name_len = 0; struct tegra_dsi_out *dsi = pdata->default_out->dsi; struct device_node *np_dsi_panel = NULL; np_dsi_panel = tegra_panel_get_dt_node(pdata); if (!np_dsi_panel) { pr_err("There is no valid panel node\n"); return NULL; } if (!of_property_read_u32(np_dsi, "nvidia,dsi-controller-vs", &temp)) { dsi->controller_vs = (u8)temp; if (temp == DSI_VS_0) OF_DC_LOG("dsi controller vs DSI_VS_0\n"); else if (temp == DSI_VS_1) OF_DC_LOG("dsi controller vs DSI_VS_1\n"); else { pr_err("invalid dsi controller version\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-n-data-lanes", &temp)) { dsi->n_data_lanes = (u8)temp; OF_DC_LOG("n data lanes %d\n", dsi->n_data_lanes); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-video-burst-mode", &temp)) { dsi->video_burst_mode = (u8)temp; if (temp == TEGRA_DSI_VIDEO_NONE_BURST_MODE) OF_DC_LOG("dsi video NON_BURST_MODE\n"); else if (temp == TEGRA_DSI_VIDEO_NONE_BURST_MODE_WITH_SYNC_END) OF_DC_LOG("dsi video NONE_BURST_MODE_WITH_SYNC_END\n"); else if (temp == TEGRA_DSI_VIDEO_BURST_MODE_LOWEST_SPEED) OF_DC_LOG("dsi video BURST_MODE_LOWEST_SPEED\n"); else if (temp == TEGRA_DSI_VIDEO_BURST_MODE_LOW_SPEED) OF_DC_LOG("dsi video BURST_MODE_LOW_SPEED\n"); else if (temp == TEGRA_DSI_VIDEO_BURST_MODE_MEDIUM_SPEED) OF_DC_LOG("dsi video BURST_MODE_MEDIUM_SPEED\n"); else if (temp == TEGRA_DSI_VIDEO_BURST_MODE_FAST_SPEED) OF_DC_LOG("dsi video BURST_MODE_FAST_SPEED\n"); else if (temp == TEGRA_DSI_VIDEO_BURST_MODE_FASTEST_SPEED) OF_DC_LOG("dsi video BURST_MODE_FASTEST_SPEED\n"); else { pr_err("invalid dsi video burst mode\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-pixel-format", &temp)) { dsi->pixel_format = (u8)temp; if (temp == TEGRA_DSI_PIXEL_FORMAT_16BIT_P) OF_DC_LOG("dsi pixel format 16BIT_P\n"); else if (temp == TEGRA_DSI_PIXEL_FORMAT_18BIT_P) OF_DC_LOG("dsi pixel format 18BIT_P\n"); else if (temp == TEGRA_DSI_PIXEL_FORMAT_18BIT_NP) OF_DC_LOG("dsi pixel format 18BIT_NP\n"); else if (temp == TEGRA_DSI_PIXEL_FORMAT_24BIT_P) OF_DC_LOG("dsi pixel format 24BIT_P\n"); else { pr_err("invalid dsi pixel format\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-refresh-rate", &temp)) { dsi->refresh_rate = (u8)temp; OF_DC_LOG("dsi refresh rate %d\n", dsi->refresh_rate); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-rated-refresh-rate", &temp)) { dsi->rated_refresh_rate = (u8)temp; OF_DC_LOG("dsi rated refresh rate %d\n", dsi->rated_refresh_rate); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-virtual-channel", &temp)) { dsi->virtual_channel = (u8)temp; if (temp == TEGRA_DSI_VIRTUAL_CHANNEL_0) OF_DC_LOG("dsi virtual channel 0\n"); else if (temp == TEGRA_DSI_VIRTUAL_CHANNEL_1) OF_DC_LOG("dsi virtual channel 1\n"); else if (temp == TEGRA_DSI_VIRTUAL_CHANNEL_2) OF_DC_LOG("dsi virtual channel 2\n"); else if (temp == TEGRA_DSI_VIRTUAL_CHANNEL_3) OF_DC_LOG("dsi virtual channel 3\n"); else { pr_err("invalid dsi virtual ch\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-instance", &temp)) { dsi->dsi_instance = (u8)temp; if (temp == DSI_INSTANCE_0) OF_DC_LOG("dsi instance 0\n"); else if (temp == DSI_INSTANCE_1) OF_DC_LOG("dsi instance 1\n"); else { pr_err("invalid dsi instance\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-panel-reset", &temp)) { dsi->panel_reset = (u8)temp; OF_DC_LOG("dsi panel reset %d\n", dsi->panel_reset); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-te-polarity-low", &temp)) { dsi->te_polarity_low = (u8)temp; OF_DC_LOG("dsi panel te polarity low %d\n", dsi->te_polarity_low); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-lp00-pre-panel-wakeup", &temp)) { dsi->lp00_pre_panel_wakeup = (u8)temp; OF_DC_LOG("dsi panel lp00 pre panel wakeup %d\n", dsi->lp00_pre_panel_wakeup); } if (of_find_property(np_dsi_panel, "nvidia,dsi-bl-name", &bl_name_len)) { dsi->bl_name = devm_kzalloc(&ndev->dev, sizeof(u8) * bl_name_len, GFP_KERNEL); if (!of_property_read_string(np_dsi_panel, "nvidia,dsi-bl-name", (const char **)&dsi->bl_name)) OF_DC_LOG("dsi panel bl name %s\n", dsi->bl_name); else { pr_err("dsi error parsing bl name\n"); kfree(dsi->bl_name); } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-ganged-type", &temp)) { dsi->ganged_type = (u8)temp; OF_DC_LOG("dsi ganged_type %d\n", dsi->ganged_type); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-suspend-aggr", &temp)) { dsi->suspend_aggr = (u8)temp; OF_DC_LOG("dsi suspend_aggr %d\n", dsi->suspend_aggr); } dsi_te_gpio = of_get_named_gpio(np_dsi_panel, "nvidia,dsi-te-gpio", 0); if (gpio_is_valid(dsi_te_gpio)) { dsi->te_gpio = dsi_te_gpio; OF_DC_LOG("dsi te_gpio %d\n", dsi_te_gpio); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-power-saving-suspend", &temp)) { dsi->power_saving_suspend = (bool)temp; OF_DC_LOG("dsi power saving suspend %d\n", dsi->power_saving_suspend); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-ulpm-not-support", &temp)) { dsi->ulpm_not_supported = (bool)temp; OF_DC_LOG("dsi ulpm_not_supported %d\n", dsi->ulpm_not_supported); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-video-data-type", &temp)) { dsi->video_data_type = (u8)temp; if (temp == TEGRA_DSI_VIDEO_TYPE_VIDEO_MODE) OF_DC_LOG("dsi video type VIDEO_MODE\n"); else if (temp == TEGRA_DSI_VIDEO_TYPE_COMMAND_MODE) OF_DC_LOG("dsi video type COMMAND_MODE\n"); else { pr_err("invalid dsi video data type\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-video-clock-mode", &temp)) { dsi->video_clock_mode = (u8)temp; if (temp == TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS) OF_DC_LOG("dsi video clock mode CONTINUOUS\n"); else if (temp == TEGRA_DSI_VIDEO_CLOCK_TX_ONLY) OF_DC_LOG("dsi video clock mode TX_ONLY\n"); else { pr_err("invalid dsi video clk mode\n"); return NULL; } } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-n-init-cmd", &temp)) { dsi->n_init_cmd = (u16)temp; OF_DC_LOG("dsi n_init_cmd %d\n", dsi->n_init_cmd); } dsi->dsi_init_cmd = tegra_dsi_parse_cmd_dt(ndev, np_dsi_panel, of_find_property(np_dsi_panel, "nvidia,dsi-init-cmd", NULL), dsi->n_init_cmd); if (dsi->n_init_cmd && IS_ERR_OR_NULL(dsi->dsi_init_cmd)) { dev_err(&ndev->dev, "dsi: copy init cmd from dt failed\n"); return NULL; }; if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-n-suspend-cmd", &temp)) { dsi->n_suspend_cmd = (u16)temp; OF_DC_LOG("dsi n_suspend_cmd %d\n", dsi->n_suspend_cmd); } dsi->dsi_suspend_cmd = tegra_dsi_parse_cmd_dt(ndev, np_dsi_panel, of_find_property(np_dsi_panel, "nvidia,dsi-suspend-cmd", NULL), dsi->n_suspend_cmd); if (dsi->n_suspend_cmd && IS_ERR_OR_NULL(dsi->dsi_suspend_cmd)) { dev_err(&ndev->dev, "dsi: copy suspend cmd from dt failed\n"); return NULL; }; if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-n-early-suspend-cmd", &temp)) { dsi->n_early_suspend_cmd = (u16)temp; OF_DC_LOG("dsi n_early_suspend_cmd %d\n", dsi->n_early_suspend_cmd); } dsi->dsi_early_suspend_cmd = tegra_dsi_parse_cmd_dt(ndev, np_dsi_panel, of_find_property(np_dsi_panel, "nvidia,dsi-early-suspend-cmd", NULL), dsi->n_early_suspend_cmd); if (dsi->n_early_suspend_cmd && IS_ERR_OR_NULL(dsi->dsi_early_suspend_cmd)) { dev_err(&ndev->dev, "dsi: copy early suspend cmd from dt failed\n"); return NULL; }; if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-n-late-resume-cmd", &temp)) { dsi->n_late_resume_cmd = (u16)temp; OF_DC_LOG("dsi n_late_resume_cmd %d\n", dsi->n_late_resume_cmd); } dsi->dsi_late_resume_cmd = tegra_dsi_parse_cmd_dt(ndev, np_dsi_panel, of_find_property(np_dsi_panel, "nvidia,dsi-late-resume-cmd", NULL), dsi->n_late_resume_cmd); if (dsi->n_late_resume_cmd && IS_ERR_OR_NULL(dsi->dsi_late_resume_cmd)) { dev_err(&ndev->dev, "dsi: copy late resume cmd from dt failed\n"); return NULL; }; dsi->pkt_seq = tegra_dsi_parse_pkt_seq_dt(ndev, np_dsi_panel, of_find_property(np_dsi_panel, "nvidia,dsi-pkt-seq", NULL)); if (IS_ERR(dsi->pkt_seq)) { dev_err(&ndev->dev, "dsi pkt seq from dt fail\n"); return NULL; } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-hsdexit", &temp)) { dsi->phy_timing.t_hsdexit_ns = (u16)temp; OF_DC_LOG("phy t_hsdexit_ns %d\n", dsi->phy_timing.t_hsdexit_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-hstrail", &temp)) { dsi->phy_timing.t_hstrail_ns = (u16)temp; OF_DC_LOG("phy t_hstrail_ns %d\n", dsi->phy_timing.t_hstrail_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-datzero", &temp)) { dsi->phy_timing.t_datzero_ns = (u16)temp; OF_DC_LOG("phy t_datzero_ns %d\n", dsi->phy_timing.t_datzero_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-hsprepare", &temp)) { dsi->phy_timing.t_hsprepare_ns = (u16)temp; OF_DC_LOG("phy t_hsprepare_ns %d\n", dsi->phy_timing.t_hsprepare_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-clktrail", &temp)) { dsi->phy_timing.t_clktrail_ns = (u16)temp; OF_DC_LOG("phy t_clktrail_ns %d\n", dsi->phy_timing.t_clktrail_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-clkpost", &temp)) { dsi->phy_timing.t_clkpost_ns = (u16)temp; OF_DC_LOG("phy t_clkpost_ns %d\n", dsi->phy_timing.t_clkpost_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-clkzero", &temp)) { dsi->phy_timing.t_clkzero_ns = (u16)temp; OF_DC_LOG("phy t_clkzero_ns %d\n", dsi->phy_timing.t_clkzero_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-tlpx", &temp)) { dsi->phy_timing.t_tlpx_ns = (u16)temp; OF_DC_LOG("phy t_tlpx_ns %d\n", dsi->phy_timing.t_tlpx_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-clkprepare", &temp)) { dsi->phy_timing.t_clkprepare_ns = (u16)temp; OF_DC_LOG("phy t_clkprepare_ns %d\n", dsi->phy_timing.t_clkprepare_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-clkpre", &temp)) { dsi->phy_timing.t_clkpre_ns = (u16)temp; OF_DC_LOG("phy t_clkpre_ns %d\n", dsi->phy_timing.t_clkpre_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-wakeup", &temp)) { dsi->phy_timing.t_wakeup_ns = (u16)temp; OF_DC_LOG("phy t_wakeup_ns %d\n", dsi->phy_timing.t_wakeup_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-taget", &temp)) { dsi->phy_timing.t_taget_ns = (u16)temp; OF_DC_LOG("phy t_taget_ns %d\n", dsi->phy_timing.t_taget_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-tasure", &temp)) { dsi->phy_timing.t_tasure_ns = (u16)temp; OF_DC_LOG("phy t_tasure_ns %d\n", dsi->phy_timing.t_tasure_ns); } if (!of_property_read_u32(np_dsi_panel, "nvidia,dsi-phy-tago", &temp)) { dsi->phy_timing.t_tago_ns = (u16)temp; OF_DC_LOG("phy t_tago_ns %d\n", dsi->phy_timing.t_tago_ns); } if (!of_find_property(np_dsi_panel, "nvidia,dsi-boardinfo", NULL)) { of_property_read_u32_index(np_dsi_panel, "nvidia,dsi-boardinfo", 0, &dsi->boardinfo.platform_boardid); of_property_read_u32_index(np_dsi_panel, "nvidia,dsi-boardinfo", 1, &dsi->boardinfo.platform_boardversion); of_property_read_u32_index(np_dsi_panel, "nvidia,dsi-boardinfo", 2, &dsi->boardinfo.display_boardid); of_property_read_u32_index(np_dsi_panel, "nvidia,dsi-boardinfo", 3, &dsi->boardinfo.display_boardversion); OF_DC_LOG("boardinfo platform_boardid = %d \ platform_boardversion = %d \ display_boardid = %d \ display_boardversion = %d\n", dsi->boardinfo.platform_boardid, dsi->boardinfo.platform_boardversion, dsi->boardinfo.display_boardid, dsi->boardinfo.display_boardversion); } return np_dsi_panel; } static int dc_hdmi_out_enable(struct device *dev) { int ret; if (!of_hdmi_reg) { of_hdmi_reg = regulator_get(dev, "avdd_hdmi"); if (IS_ERR_OR_NULL(of_hdmi_reg)) { pr_err("hdmi: couldn't get regulator avdd_hdmi\n"); of_hdmi_reg = NULL; return PTR_ERR(of_hdmi_reg); } } ret = regulator_enable(of_hdmi_reg); if (ret < 0) { pr_err("hdmi: couldn't enable regulator avdd_hdmi\n"); return ret; } if (!of_hdmi_pll) { of_hdmi_pll = regulator_get(dev, "avdd_hdmi_pll"); if (IS_ERR_OR_NULL(of_hdmi_pll)) { pr_err("hdmi: couldn't get regulator avdd_hdmi_pll\n"); of_hdmi_pll = NULL; regulator_put(of_hdmi_reg); of_hdmi_reg = NULL; return PTR_ERR(of_hdmi_pll); } } ret = regulator_enable(of_hdmi_pll); if (ret < 0) { pr_err("hdmi: couldn't enable regulator avdd_hdmi_pll\n"); return ret; } return 0; } static int dc_hdmi_out_disable(void) { if (of_hdmi_reg) { regulator_disable(of_hdmi_reg); regulator_put(of_hdmi_reg); of_hdmi_reg = NULL; } if (of_hdmi_pll) { regulator_disable(of_hdmi_pll); regulator_put(of_hdmi_pll); of_hdmi_pll = NULL; } return 0; } static int dc_hdmi_hotplug_init(struct device *dev) { int ret = 0; if (!of_hdmi_vddio) { of_hdmi_vddio = regulator_get(dev, "vdd_hdmi_5v0"); if (IS_ERR_OR_NULL(of_hdmi_vddio)) { ret = PTR_ERR(of_hdmi_vddio); pr_err("hdmi: couldn't get regulator vdd_hdmi_5v0\n"); of_hdmi_vddio = NULL; return ret; } } ret = regulator_enable(of_hdmi_vddio); if (ret < 0) { pr_err("hdmi: couldn't enable regulator vdd_hdmi_5v0\n"); regulator_put(of_hdmi_vddio); of_hdmi_vddio = NULL; return ret; } return ret; } static int dc_hdmi_postsuspend(void) { if (of_hdmi_vddio) { regulator_disable(of_hdmi_vddio); regulator_put(of_hdmi_vddio); of_hdmi_vddio = NULL; } return 0; } #if defined(CONFIG_ARCH_TEGRA_11x_SOC) || \ defined(CONFIG_ARCH_TEGRA_12x_SOC) static void dc_hdmi_hotplug_report(bool state) { if (state) { tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_DDC_SDA, TEGRA_PUPD_PULL_DOWN); tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_DDC_SCL, TEGRA_PUPD_PULL_DOWN); } else { tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_DDC_SDA, TEGRA_PUPD_NORMAL); tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_DDC_SCL, TEGRA_PUPD_NORMAL); } } #endif static int dc_dp_enable(struct device *dev) { int ret; if (!of_lvds_dp_reg) { of_lvds_dp_reg = regulator_get(dev, "avdd_3v3_dp"); if (IS_ERR_OR_NULL(of_lvds_dp_reg)) { pr_err("dp: couldn't get regulator of_lvds_dp_reg\n"); of_lvds_dp_reg = NULL; return PTR_ERR(of_lvds_dp_reg); } } ret = regulator_set_voltage(of_lvds_dp_reg, 3300000, 3300000); if (ret < 0) { pr_err("dp: couldn't set regulator of_lvds_dp_reg voltage\n"); regulator_put(of_lvds_dp_reg); of_lvds_dp_reg = NULL; return ret; } ret = regulator_enable(of_lvds_dp_reg); if (ret < 0) { pr_err("dp: couldn't enable regulator of_lvds_dp_reg\n"); regulator_put(of_lvds_dp_reg); of_lvds_dp_reg = NULL; } return ret; } static int dc_dp_disable(void) { if (of_lvds_dp_reg) { regulator_disable(of_lvds_dp_reg); regulator_put(of_lvds_dp_reg); of_lvds_dp_reg = NULL; } return 0; } static int dc_dp_postsuspend(void) { return 0; } static int dc_lvds_enable(struct device *dev) { int ret; if (!of_lvds_dp_reg) { of_lvds_dp_reg = regulator_get(dev, "avdd_3v3_dp"); if (IS_ERR_OR_NULL(of_lvds_dp_reg)) { pr_err("dp: couldn't get regulator of_lvds_dp_reg\n"); of_lvds_dp_reg = NULL; return PTR_ERR(of_lvds_dp_reg); } } ret = regulator_set_voltage(of_lvds_dp_reg, 1800000, 1800000); if (ret < 0) { pr_err("dp: couldn't set regulator of_lvds_dp_reg voltage\n"); regulator_put(of_lvds_dp_reg); of_lvds_dp_reg = NULL; return ret; } ret = regulator_enable(of_lvds_dp_reg); if (ret < 0) { pr_err("dp: couldn't enable regulator of_lvds_dp_reg\n"); regulator_put(of_lvds_dp_reg); of_lvds_dp_reg = NULL; return ret; } if (!of_lvds_bl_reg) { of_lvds_bl_reg = regulator_get(dev, "vdd_lcd_bl"); if (IS_ERR_OR_NULL(of_lvds_bl_reg)) { pr_err("lvds: couldn't get regulator vdd_lcd_bl\n"); of_lvds_bl_reg = NULL; } } if (of_lvds_bl_reg) { if (regulator_enable(of_lvds_bl_reg) < 0) pr_err("vdd_lcd_bl failed to enable\n"); } return 0; } static int dc_lvds_disable(void) { if (of_lvds_dp_reg) { regulator_disable(of_lvds_dp_reg); regulator_put(of_lvds_dp_reg); of_lvds_dp_reg = NULL; } if (of_lvds_bl_reg) { regulator_disable(of_lvds_bl_reg); regulator_put(of_lvds_bl_reg); of_lvds_bl_reg = NULL; } return 0; } struct tegra_dc_platform_data *of_dc_parse_platform_data(struct platform_device *ndev) { struct tegra_dc_platform_data *pdata; struct device_node *np = ndev->dev.of_node; struct device_node *dc_connection = NULL; struct device_node *timings_np = NULL; struct device_node *pins_np = NULL; struct device_node *np_target_disp = NULL; struct device_node *np_dsi_panel = NULL; struct device_node *sd_np = NULL; struct device_node *default_out_np = NULL; struct device_node *entry = NULL; #ifdef CONFIG_TEGRA_DC_CMU struct device_node *cmu_np = NULL; #endif int err; u32 temp; /* * Memory for pdata, pdata->default_out, pdata->fb * need to be allocated in default * since it is expected data for these needs to be * parsed from DTB. */ pdata = devm_kzalloc(&ndev->dev, sizeof(struct tegra_dc_platform_data), GFP_KERNEL); if (!pdata) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } pdata->default_out = devm_kzalloc(&ndev->dev, sizeof(struct tegra_dc_out), GFP_KERNEL); if (!pdata->default_out) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } pdata->fb = devm_kzalloc(&ndev->dev, sizeof(struct tegra_fb_data), GFP_KERNEL); if (!pdata->fb) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } /* * determine dc out type, * dc node defines nvidia,out-type to indicate * what out type of display is used for * current dc id. */ dc_connection = of_parse_phandle(np, "nvidia,dc-connection", 0); if (dc_connection == NULL) { pr_err("no nvidia,dc-connection\n"); goto fail_parse; } err = parse_dc_out_type(dc_connection, pdata->default_out); if (err) { pr_err("parse_dc_out_type err\n"); goto fail_parse; } if (!of_property_read_u32(np, "nvidia,out-rotation", &temp)) { pdata->default_out->rotation = (unsigned) temp; OF_DC_LOG("out_rotation %d\n", temp); } if (!of_property_read_u32(np, "nvidia,fb-bpp", &temp)) { pdata->fb->bits_per_pixel = (int)temp; OF_DC_LOG("fb bpp %d\n", pdata->fb->bits_per_pixel); } else { goto fail_parse; } if (!of_property_read_u32(np, "nvidia,fb-flags", &temp)) { if (temp == TEGRA_FB_FLIP_ON_PROBE) OF_DC_LOG("fb flip on probe\n"); else if (temp == 0) OF_DC_LOG("do not flip fb on probe time\n"); else { pr_err("invalid fb_flags\n"); goto fail_parse; } pdata->fb->flags = (unsigned long)temp; } if (pdata->default_out->type == TEGRA_DC_OUT_DSI) { if (of_device_is_available(dc_connection)) { pdata->default_out->dsi = devm_kzalloc(&ndev->dev, sizeof(struct tegra_dsi_out), GFP_KERNEL); if (!pdata->default_out->dsi) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } np_dsi_panel = parse_dsi_settings(ndev, dc_connection, pdata); if (!np_dsi_panel) goto fail_parse; else np_target_disp = np_dsi_panel; } } else if (pdata->default_out->type == TEGRA_DC_OUT_HDMI) { bool hotplug_report = false; if (dc_connection && of_device_is_available(dc_connection)) { if (!of_property_read_u32(dc_connection, "nvidia,hotplug-report", &temp)) { hotplug_report = (bool)temp; } } pdata->default_out->enable = dc_hdmi_out_enable; pdata->default_out->disable = dc_hdmi_out_disable; pdata->default_out->hotplug_init = dc_hdmi_hotplug_init; pdata->default_out->postsuspend = dc_hdmi_postsuspend; #if defined(CONFIG_ARCH_TEGRA_11x_SOC) || \ defined(CONFIG_ARCH_TEGRA_12x_SOC) if (hotplug_report) pdata->default_out->hotplug_report = dc_hdmi_hotplug_report; #endif np_target_disp = of_get_child_by_name(dc_connection, "display"); if (!np_target_disp || !of_device_is_available(np_target_disp)) { pr_err("hdmi/display node is NOT valid\n"); goto fail_parse; } err = parse_tmds_config(ndev, np_target_disp, pdata->default_out); if (err) goto fail_parse; } else if (pdata->default_out->type == TEGRA_DC_OUT_LVDS) { np_target_disp = of_get_child_by_name(dc_connection, "display"); if (!np_target_disp || !of_device_is_available(np_target_disp)) { pr_err("lvds/display node is NOT valid\n"); goto fail_parse; } pdata->default_out->enable = dc_lvds_enable; pdata->default_out->disable = dc_lvds_disable; } else if (pdata->default_out->type == TEGRA_DC_OUT_DP) { pdata->default_out->enable = dc_dp_enable; pdata->default_out->disable = dc_dp_disable; pdata->default_out->postsuspend = dc_dp_postsuspend; np_target_disp = of_get_child_by_name(dc_connection, "display"); if (!np_target_disp || !of_device_is_available(np_target_disp)) { pr_err("dp/display node is NOT valid\n"); goto fail_parse; } } default_out_np = of_get_child_by_name(np_target_disp, "disp-default-out"); if (!default_out_np) { pr_err("%s: could not find disp-default-out node\n", __func__); goto fail_parse; } else { err = parse_disp_default_out(ndev, default_out_np, pdata->default_out, pdata->fb); if (err) goto fail_parse; } if (parse_disp_extra_attributes(ndev, np_target_disp, pdata->default_out) != 0) { pr_err("%s: failed parsing extra arguments\n", __func__); goto fail_parse; } timings_np = of_get_child_by_name(np_target_disp, "display-timings"); if (!timings_np) { if (pdata->default_out->type == TEGRA_DC_OUT_DSI || pdata->default_out->type == TEGRA_DC_OUT_LVDS) { pr_err("%s: could not find display-timings node\n", __func__); goto fail_parse; } } else if (pdata->default_out->type == TEGRA_DC_OUT_DSI) { /* pdata->default_out->type == TEGRA_DC_OUT_DSI */ pdata->default_out->n_modes = of_get_child_count(timings_np); if (pdata->default_out->n_modes == 0) { /* * Should never happen ! */ dev_err(&ndev->dev, "no timing given\n"); goto fail_parse; } pdata->default_out->modes = devm_kzalloc(&ndev->dev, pdata->default_out->n_modes * sizeof(struct tegra_dc_mode), GFP_KERNEL); if (!pdata->default_out->modes) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } } else { pdata->default_out->n_modes = 0; #ifdef CONFIG_FRAMEBUFFER_CONSOLE pdata->default_out->n_modes = of_get_child_count(timings_np); if (pdata->default_out->n_modes == 0) { /* * Should never happen ! */ dev_err(&ndev->dev, "no timing given\n"); goto fail_parse; } else { pdata->default_out->modes = devm_kzalloc(&ndev->dev, pdata->default_out->n_modes * sizeof(struct tegra_dc_mode), GFP_KERNEL); if (!pdata->default_out->modes) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } } #endif } pins_np = of_get_child_by_name(np_target_disp, "out-pins"); if (!pins_np) { if (pdata->default_out->type == TEGRA_DC_OUT_LVDS) { pr_err("%s: could not find out-pins node\n", __func__); goto fail_parse; } } else if (pdata->default_out->type == TEGRA_DC_OUT_LVDS) { struct tegra_dc_out_pin *cur_pins; pdata->default_out->n_out_pins = of_get_child_count(pins_np); if (pdata->default_out->n_out_pins == 0) { /* * Should never happen ! */ dev_err(&ndev->dev, "no pins given\n"); goto fail_parse; } pdata->default_out->out_pins = devm_kzalloc(&ndev->dev, pdata->default_out->n_out_pins * sizeof(struct tegra_dc_out_pin), GFP_KERNEL); if (!pdata->default_out->out_pins) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } cur_pins = pdata->default_out->out_pins; for_each_child_of_node(pins_np, entry) { err = tegra_parse_lvds_pins(entry, cur_pins); if (err) goto fail_parse; cur_pins++; } } sd_np = of_get_child_by_name(np_target_disp, "smartdimmer"); if (!sd_np) { pr_info("%s: could not find SD settings node\n", __func__); } else { if (of_device_is_available(sd_np)) { pdata->default_out->sd_settings = devm_kzalloc(&ndev->dev, sizeof(struct tegra_dc_sd_settings), GFP_KERNEL); if (!pdata->default_out->sd_settings) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } } else { dev_err(&ndev->dev, "sd_settings: No data in node\n"); goto fail_parse; } } #ifdef CONFIG_TEGRA_DC_CMU cmu_np = of_get_child_by_name(np_target_disp, "cmu"); if (!cmu_np) { pr_info("%s: could not find cmu node\n", __func__); } else { if (of_device_is_available(cmu_np)) { pdata->cmu = devm_kzalloc(&ndev->dev, sizeof(struct tegra_dc_cmu), GFP_KERNEL); if (!pdata->cmu) { dev_err(&ndev->dev, "not enough memory\n"); goto fail_parse; } } else { dev_err(&ndev->dev, "cmu: No data in node\n"); goto fail_parse; } } #endif /* * parse sd_settings values */ if (pdata->default_out->sd_settings != NULL) { err = parse_sd_settings(sd_np, pdata->default_out->sd_settings); if (err) goto fail_parse; } if (pdata->default_out->modes != NULL) { struct tegra_dc_mode *cur_mode = pdata->default_out->modes; for_each_child_of_node(timings_np, entry) { err = parse_modes(entry, cur_mode); if (err) goto fail_parse; cur_mode++; } } #ifdef CONFIG_TEGRA_DC_CMU if (pdata->cmu != NULL) { err = parse_cmu_data(cmu_np, pdata->cmu); if (err) goto fail_parse; } #endif if (!of_property_read_u32(np, "nvidia,dc-flags", &temp)) { if ((temp != TEGRA_DC_FLAG_ENABLED) && (temp != 0)) { pr_err("%s: invalid dc platform data flag\n", __func__); goto fail_parse; } pdata->flags = (unsigned long)temp; OF_DC_LOG("dc flag %lu\n", pdata->flags); } if (!of_property_read_u32(np, "nvidia,emc-clk-rate", &temp)) { pdata->emc_clk_rate = (unsigned long)temp; OF_DC_LOG("emc clk rate %lu\n", pdata->emc_clk_rate); } #ifdef CONFIG_TEGRA_DC_CMU if (!of_property_read_u32(np, "nvidia,cmu-enable", &temp)) { pdata->cmu_enable = (bool)temp; OF_DC_LOG("cmu enable %d\n", pdata->cmu_enable); } else { pdata->cmu_enable = false; } #endif if (!of_property_read_u32(np, "nvidia,low-v-win", &temp)) { pdata->low_v_win = (unsigned long)temp; OF_DC_LOG("low_v_win %lu\n", pdata->low_v_win); } return pdata; fail_parse: return NULL; } #else struct tegra_dc_platform_data *of_dc_parse_platform_data(struct platform_device *ndev) { return NULL; } #endif