/* * drivers/video/tegra/dc/mipi_cal.c * * Copyright (c) 2012-2015, 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 "dc_priv.h" #include "mipi_cal.h" #include "mipi_cal_regs.h" #include "dsi.h" #include #include "../../../../arch/arm/mach-tegra/iomap.h" #ifdef CONFIG_DEBUG_FS static int dbg_dsi_mipi_show(struct seq_file *s, void *unused) { struct tegra_mipi_cal *mipi_cal = s->private; unsigned long i = 0; u32 col = 0; /* If gated quitely return */ if (!tegra_dc_is_powered(mipi_cal->dc)) return 0; BUG_ON(IS_ERR_OR_NULL(mipi_cal)); mutex_lock(&mipi_cal->lock); tegra_mipi_cal_clk_enable(mipi_cal); /* mem dd dump */ for (col = 0, i = 0; i <= MIPI_VALID_REG_LIMIT ; i += 4) { if (col == 0) seq_printf(s, "%08lX:", TEGRA_MIPI_CAL_BASE + i); seq_printf(s, "%c%08lX", col == 2 ? '-' : ' ', tegra_mipi_cal_read(mipi_cal, i)); if (col == 3) { seq_printf(s, "\n"); col = 0; } else col++; } seq_printf(s, "\n"); tegra_mipi_cal_clk_disable(mipi_cal); mutex_unlock(&mipi_cal->lock); return 0; } static int dbg_dsi_mipi_open(struct inode *inode, struct file *file) { return single_open(file, dbg_dsi_mipi_show, inode->i_private); } static const struct file_operations dbg_fops = { .open = dbg_dsi_mipi_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *mipidir; static void dbg_dsi_mipi_dir_create(struct tegra_mipi_cal *mipi_cal) { struct dentry *retval; mipidir = debugfs_create_dir("tegra_mipi_cal", NULL); if (!mipidir) return; retval = debugfs_create_file("regs", S_IRUGO, mipidir, mipi_cal, &dbg_fops); if (!retval) goto free_out; return; free_out: debugfs_remove_recursive(mipidir); mipidir = NULL; return; } #else static inline void dbg_dsi_mipi_dir_create(struct tegra_mipi_cal *mipi_cal) { } #endif int tegra_mipi_cal_init_hw(struct tegra_mipi_cal *mipi_cal) { BUG_ON(IS_ERR_OR_NULL(mipi_cal)); mutex_lock(&mipi_cal->lock); tegra_mipi_cal_clk_enable(mipi_cal); /* Clear MIPI cal status register */ tegra_mipi_cal_write(mipi_cal, MIPI_AUTO_CAL_DONE_DSID(0x1) | MIPI_AUTO_CAL_DONE_DSIC(0x1) | MIPI_AUTO_CAL_DONE_DSIB(0x1) | MIPI_AUTO_CAL_DONE_DSIA(0x1) | MIPI_AUTO_CAL_DONE_CSIE(0x1) | MIPI_AUTO_CAL_DONE_CSID(0x1) | MIPI_AUTO_CAL_DONE_CSIC(0x1) | MIPI_AUTO_CAL_DONE_CSIB(0x1) | MIPI_AUTO_CAL_DONE_CSIA(0x1) | MIPI_AUTO_CAL_DONE(0x1) | MIPI_CAL_DRIV_DN_ADJ(0x0) | MIPI_CAL_DRIV_UP_ADJ(0x0) | MIPI_CAL_TERMADJ(0x0) | MIPI_CAL_ACTIVE(0x0), MIPI_CAL_CIL_MIPI_CAL_STATUS_0); tegra_mipi_cal_clk_disable(mipi_cal); mutex_unlock(&mipi_cal->lock); return 0; } EXPORT_SYMBOL(tegra_mipi_cal_init_hw); struct tegra_mipi_cal *tegra_mipi_cal_init_sw(struct tegra_dc *dc) { struct tegra_mipi_cal *mipi_cal; struct resource *res; struct resource mipi_res; struct resource *base_res; struct clk *clk; struct clk *fixed_clk; void __iomem *base; int err = 0; #ifdef CONFIG_USE_OF struct device_node *np_mipi_cal = of_find_node_by_path("/mipical"); #else struct device_node *np_mipi_cal = NULL; #endif mipi_cal = devm_kzalloc(&dc->ndev->dev, sizeof(*mipi_cal), GFP_KERNEL); if (!mipi_cal) { dev_err(&dc->ndev->dev, "mipi_cal: memory allocation fail\n"); err = -ENOMEM; goto fail; } if (np_mipi_cal && of_device_is_available(np_mipi_cal)) { of_address_to_resource( np_mipi_cal, 0, &mipi_res); res = &mipi_res; } else { res = platform_get_resource_byname(dc->ndev, IORESOURCE_MEM, "mipi_cal"); } if (!res) { dev_err(&dc->ndev->dev, "mipi_cal: no entry in resource\n"); err = -ENOENT; goto fail_free_mipi_cal; } base_res = devm_request_mem_region(&dc->ndev->dev, res->start, resource_size(res), dc->ndev->name); base = devm_ioremap(&dc->ndev->dev, res->start, resource_size(res)); if (!base) { dev_err(&dc->ndev->dev, "mipi_cal: bus to virtual mapping failed\n"); err = -EBUSY; goto fail_free_res; } clk = clk_get_sys("mipi-cal", NULL); if (IS_ERR_OR_NULL(clk)) { dev_err(&dc->ndev->dev, "mipi_cal: clk get failed\n"); err = PTR_ERR(clk); goto fail_free_map; } fixed_clk = clk_get_sys("mipi-cal-fixed", NULL); if (IS_ERR_OR_NULL(fixed_clk)) { dev_err(&dc->ndev->dev, "mipi_cal: fixed clk get failed\n"); err = PTR_ERR(fixed_clk); goto fail_free_map; } mutex_init(&mipi_cal->lock); mipi_cal->dc = dc; mipi_cal->res = res; mipi_cal->base = base; mipi_cal->base_res = base_res; mipi_cal->clk = clk; mipi_cal->fixed_clk = fixed_clk; dbg_dsi_mipi_dir_create(mipi_cal); return mipi_cal; fail_free_map: devm_iounmap(&dc->ndev->dev, base); devm_release_mem_region(&dc->ndev->dev, res->start, resource_size(res)); fail_free_res: if (!np_mipi_cal || !of_device_is_available(np_mipi_cal)) release_resource(res); fail_free_mipi_cal: devm_kfree(&dc->ndev->dev, mipi_cal); fail: return ERR_PTR(err); } EXPORT_SYMBOL(tegra_mipi_cal_init_sw); void tegra_mipi_cal_destroy(struct tegra_dc *dc) { #ifdef CONFIG_USE_OF struct device_node *np_mipi_cal = of_find_node_by_path("/mipical"); #else struct device_node *np_mipi_cal = NULL; #endif struct tegra_mipi_cal *mipi_cal = ((struct tegra_dc_dsi_data *) (tegra_dc_get_outdata(dc)))->mipi_cal; BUG_ON(IS_ERR_OR_NULL(mipi_cal)); mutex_lock(&mipi_cal->lock); clk_put(mipi_cal->clk); devm_iounmap(&dc->ndev->dev, mipi_cal->base); devm_release_mem_region(&dc->ndev->dev, mipi_cal->res->start, resource_size(mipi_cal->res)); if (!np_mipi_cal || !of_device_is_available(np_mipi_cal)) release_resource(mipi_cal->res); mutex_unlock(&mipi_cal->lock); mutex_destroy(&mipi_cal->lock); devm_kfree(&dc->ndev->dev, mipi_cal); #ifdef CONFIG_DEBUG_FS debugfs_remove_recursive(mipidir); #endif } EXPORT_SYMBOL(tegra_mipi_cal_destroy);