blob: e3989acdcc9fb9ad61f2dde59a87c46966ecbc3a [file] [log] [blame]
/*
* drivers/video/tegra/dc/mipi_cal.c
*
* Copyright (c) 2012-2013, 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 <linux/debugfs.h>
#include <linux/ioport.h>
#include <linux/gfp.h>
#include <linux/export.h>
#include "dc_priv.h"
#include "mipi_cal.h"
#include "mipi_cal_regs.h"
#include "dsi.h"
#include <linux/of_address.h>
#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)
{
unsigned cnt = MIPI_CAL_MIPI_CAL_CTRL_0;
BUG_ON(IS_ERR_OR_NULL(mipi_cal));
mutex_lock(&mipi_cal->lock);
tegra_mipi_cal_clk_enable(mipi_cal);
for (; cnt <= MIPI_VALID_REG_LIMIT; cnt += 4)
tegra_mipi_cal_write(mipi_cal, 0, cnt);
/* 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);