/* * drivers/video/tegra/dc/lvds.c * * Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include "lvds.h" #include "dc_priv.h" static int tegra_dc_lvds_init(struct tegra_dc *dc) { struct tegra_dc_lvds_data *lvds; int err; lvds = kzalloc(sizeof(*lvds), GFP_KERNEL); if (!lvds) return -ENOMEM; lvds->dc = dc; lvds->sor = tegra_dc_sor_init(dc, NULL); if (IS_ERR_OR_NULL(lvds->sor)) { err = PTR_ERR(lvds->sor); lvds->sor = NULL; goto err_init; } tegra_dc_set_outdata(dc, lvds); return 0; err_init: kfree(lvds); return err; } static void tegra_dc_lvds_destroy(struct tegra_dc *dc) { struct tegra_dc_lvds_data *lvds = tegra_dc_get_outdata(dc); if (lvds->sor) tegra_dc_sor_destroy(lvds->sor); kfree(lvds); } static void tegra_dc_lvds_enable(struct tegra_dc *dc) { struct tegra_dc_lvds_data *lvds = tegra_dc_get_outdata(dc); tegra_dc_io_start(dc); tegra_sor_clk_enable(lvds->sor); /* Power on panel */ tegra_sor_pad_cal_power(lvds->sor, true); tegra_dc_sor_set_internal_panel(lvds->sor, true); tegra_dc_sor_set_power_state(lvds->sor, 1); tegra_dc_sor_enable_lvds(lvds->sor, false, false); tegra_dc_io_end(dc); } static void tegra_dc_lvds_disable(struct tegra_dc *dc) { struct tegra_dc_lvds_data *lvds = tegra_dc_get_outdata(dc); /* Power down SOR */ tegra_dc_sor_disable(lvds->sor, true); } static void tegra_dc_lvds_suspend(struct tegra_dc *dc) { struct tegra_dc_lvds_data *lvds = tegra_dc_get_outdata(dc); tegra_dc_lvds_disable(dc); lvds->suspended = true; } static void tegra_dc_lvds_resume(struct tegra_dc *dc) { struct tegra_dc_lvds_data *lvds = tegra_dc_get_outdata(dc); if (!lvds->suspended) return; tegra_dc_lvds_enable(dc); } static long tegra_dc_lvds_setup_clk(struct tegra_dc *dc, struct clk *clk) { struct tegra_dc_lvds_data *lvds = tegra_dc_get_outdata(dc); struct clk *parent_clk; parent_clk = clk_get_sys(NULL, dc->out->parent_clk ? : "pll_d_out0"); if (clk_get_parent(lvds->sor->sor_clk) != parent_clk) clk_set_parent(lvds->sor->sor_clk, parent_clk); if (clk_get_parent(clk) != parent_clk) clk_set_parent(clk, parent_clk); tegra_sor_setup_clk(lvds->sor, clk, true); return tegra_dc_pclk_round_rate(dc, lvds->sor->dc->mode.pclk); } struct tegra_dc_out_ops tegra_dc_lvds_ops = { .init = tegra_dc_lvds_init, .destroy = tegra_dc_lvds_destroy, .enable = tegra_dc_lvds_enable, .disable = tegra_dc_lvds_disable, .suspend = tegra_dc_lvds_suspend, .resume = tegra_dc_lvds_resume, .setup_clk = tegra_dc_lvds_setup_clk, };