blob: 9e3288be4cce13c3c1377254c4718d184681de74 [file] [log] [blame]
/*
* drivers/video/tegra/dc/dsi_debug.c
*
* Copyright (c) 2013-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 <linux/kernel.h>
#include <linux/clk.h>
#include <linux/moduleparam.h>
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include "dc_reg.h"
#include "dc_priv.h"
#include "dev.h"
#include "dsi_regs.h"
#include "dsi.h"
/* HACK! This needs to come from DT */
#include "../../../../arch/arm/mach-tegra/iomap.h"
#ifdef CONFIG_DEBUG_FS
static int dbg_dsi_show(struct seq_file *s, void *unused)
{
struct tegra_dc_dsi_data *dsi = s->private;
unsigned long i = 0, j = 0;
u32 col = 0;
u32 base[MAX_DSI_INSTANCE] = {TEGRA_DSI_BASE, TEGRA_DSIB_BASE};
if (!dsi->enabled) {
seq_puts(s, "DSI controller suspended\n");
return 0;
}
tegra_dc_io_start(dsi->dc);
tegra_dsi_clk_enable(dsi);
/* mem dd dump */
for (i = 0; i < dsi->max_instances; i++) {
for (col = 0, j = 0; j < 0x64; j++) {
if (col == 0)
seq_printf(s, "%08lX:", base[i] + 4*j);
seq_printf(s, "%c%08lX", col == 2 ? '-' : ' ',
tegra_dsi_controller_readl(dsi, j, i));
if (col == 3) {
seq_puts(s, "\n");
col = 0;
} else
col++;
}
seq_puts(s, "\n");
}
#define DUMP_REG(a) seq_printf(s, "%-45s | %#05x | %#010lx |\n", \
#a, a, tegra_dsi_readl(dsi, a));
DUMP_REG(DSI_INCR_SYNCPT_CNTRL);
DUMP_REG(DSI_INCR_SYNCPT_ERROR);
DUMP_REG(DSI_CTXSW);
DUMP_REG(DSI_POWER_CONTROL);
DUMP_REG(DSI_INT_ENABLE);
DUMP_REG(DSI_HOST_DSI_CONTROL);
DUMP_REG(DSI_CONTROL);
DUMP_REG(DSI_SOL_DELAY);
DUMP_REG(DSI_MAX_THRESHOLD);
DUMP_REG(DSI_TRIGGER);
DUMP_REG(DSI_TX_CRC);
DUMP_REG(DSI_STATUS);
DUMP_REG(DSI_INIT_SEQ_CONTROL);
DUMP_REG(DSI_INIT_SEQ_DATA_0);
DUMP_REG(DSI_INIT_SEQ_DATA_1);
DUMP_REG(DSI_INIT_SEQ_DATA_2);
DUMP_REG(DSI_INIT_SEQ_DATA_3);
DUMP_REG(DSI_INIT_SEQ_DATA_4);
DUMP_REG(DSI_INIT_SEQ_DATA_5);
DUMP_REG(DSI_INIT_SEQ_DATA_6);
DUMP_REG(DSI_INIT_SEQ_DATA_7);
DUMP_REG(DSI_PKT_SEQ_0_LO);
DUMP_REG(DSI_PKT_SEQ_0_HI);
DUMP_REG(DSI_PKT_SEQ_1_LO);
DUMP_REG(DSI_PKT_SEQ_1_HI);
DUMP_REG(DSI_PKT_SEQ_2_LO);
DUMP_REG(DSI_PKT_SEQ_2_HI);
DUMP_REG(DSI_PKT_SEQ_3_LO);
DUMP_REG(DSI_PKT_SEQ_3_HI);
DUMP_REG(DSI_PKT_SEQ_4_LO);
DUMP_REG(DSI_PKT_SEQ_4_HI);
DUMP_REG(DSI_PKT_SEQ_5_LO);
DUMP_REG(DSI_PKT_SEQ_5_HI);
DUMP_REG(DSI_DCS_CMDS);
DUMP_REG(DSI_PKT_LEN_0_1);
DUMP_REG(DSI_PKT_LEN_2_3);
DUMP_REG(DSI_PKT_LEN_4_5);
DUMP_REG(DSI_PKT_LEN_6_7);
DUMP_REG(DSI_PHY_TIMING_0);
DUMP_REG(DSI_PHY_TIMING_1);
DUMP_REG(DSI_PHY_TIMING_2);
DUMP_REG(DSI_BTA_TIMING);
DUMP_REG(DSI_TIMEOUT_0);
DUMP_REG(DSI_TIMEOUT_1);
DUMP_REG(DSI_TO_TALLY);
DUMP_REG(DSI_PAD_CONTROL);
DUMP_REG(DSI_PAD_CONTROL_CD);
DUMP_REG(DSI_PAD_CD_STATUS);
DUMP_REG(DSI_VID_MODE_CONTROL);
DUMP_REG(DSI_PAD_CONTROL_0_VS1);
DUMP_REG(DSI_PAD_CONTROL_CD_VS1);
DUMP_REG(DSI_PAD_CD_STATUS_VS1);
DUMP_REG(DSI_PAD_CONTROL_1_VS1);
DUMP_REG(DSI_PAD_CONTROL_2_VS1);
DUMP_REG(DSI_PAD_CONTROL_3_VS1);
DUMP_REG(DSI_PAD_CONTROL_4_VS1);
DUMP_REG(DSI_GANGED_MODE_CONTROL);
DUMP_REG(DSI_GANGED_MODE_START);
DUMP_REG(DSI_GANGED_MODE_SIZE);
#undef DUMP_REG
tegra_dsi_clk_disable(dsi);
tegra_dc_io_end(dsi->dc);
return 0;
}
static int dbg_dsi_open(struct inode *inode, struct file *file)
{
return single_open(file, dbg_dsi_show, inode->i_private);
}
static const struct file_operations dbg_fops = {
.open = dbg_dsi_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static u32 max_ret_payload_size;
static u32 panel_reg_addr;
static int read_panel_get(struct seq_file *s, void *unused)
{
struct tegra_dc_dsi_data *dsi = s->private;
struct tegra_dc *dc = dsi->dc;
int err = 0;
u8 buf[300] = {0};
int j = 0 , b = 0 , k;
u32 payload_size = 0;
if (!dsi->enabled) {
dev_info(&dc->ndev->dev, " controller suspended\n");
return -EINVAL;
}
seq_printf(s, "max ret payload size:0x%x\npanel reg addr:0x%x\n",
max_ret_payload_size, panel_reg_addr);
if (max_ret_payload_size == 0) {
seq_puts(s, "echo was not successful\n");
return err;
}
err = tegra_dsi_read_data(dsi->dc, dsi,
max_ret_payload_size,
panel_reg_addr, buf);
seq_printf(s, " Read data[%d] ", b);
for (b = 1; b < (max_ret_payload_size+1); b++) {
j = (b*4)-1;
for (k = j; k > (j-4); k--)
if ((k%4) == 0 && b != max_ret_payload_size) {
seq_printf(s, " %x ", buf[k]);
seq_printf(s, "\n Read data[%d] ", b);
}
else
seq_printf(s, " %x ", buf[k]);
}
seq_puts(s, "\n");
switch (buf[0]) {
case DSI_ESCAPE_CMD:
seq_printf(s, "escape cmd[0x%x]\n", buf[0]);
break;
case DSI_ACK_NO_ERR:
seq_printf(s,
"Panel ack, no err[0x%x]\n", buf[0]);
goto fail;
break;
default:
seq_puts(s, "Invalid read response\n");
break;
}
switch (buf[4] & 0xff) {
case GEN_LONG_RD_RES:
/* Fall through */
case DCS_LONG_RD_RES:
payload_size = (buf[5] |
(buf[6] << 8)) & 0xFFFF;
seq_printf(s, "Long read response Packet\n"
"payload_size[0x%x]\n", payload_size);
break;
case GEN_1_BYTE_SHORT_RD_RES:
/* Fall through */
case DCS_1_BYTE_SHORT_RD_RES:
payload_size = 1;
seq_printf(s, "Short read response Packet\n"
"payload_size[0x%x]\n", payload_size);
break;
case GEN_2_BYTE_SHORT_RD_RES:
/* Fall through */
case DCS_2_BYTE_SHORT_RD_RES:
payload_size = 2;
seq_printf(s, "Short read response Packet\n"
"payload_size[0x%x]\n", payload_size);
break;
case ACK_ERR_RES:
payload_size = 2;
seq_printf(s, "Acknowledge error report response\n"
"Packet payload_size[0x%x]\n", payload_size);
break;
default:
seq_puts(s, "Invalid response packet\n");
break;
}
fail:
return err;
}
static ssize_t read_panel_set(struct file *file, const char *buf,
size_t count, loff_t *off)
{
struct seq_file *s = file->private_data;
struct tegra_dc_dsi_data *dsi = s->private;
struct tegra_dc *dc = dsi->dc;
if (sscanf(buf, "%x %x", &max_ret_payload_size, &panel_reg_addr) != 2)
return -EINVAL;
dev_info(&dc->ndev->dev, "max ret payload size:0x%x\npanel reg addr:0x%x\n",
max_ret_payload_size, panel_reg_addr);
return count;
}
static int read_panel_open(struct inode *inode, struct file *file)
{
return single_open(file, read_panel_get, inode->i_private);
}
static const struct file_operations read_panel_fops = {
.open = read_panel_open,
.read = seq_read,
.write = read_panel_set,
.llseek = seq_lseek,
.release = single_release,
};
static int panel_sanity_check(struct seq_file *s, void *unused)
{
struct tegra_dc_dsi_data *dsi = s->private;
struct tegra_dc *dc = dsi->dc;
struct sanity_status *san = NULL;
int err = 0;
san = devm_kzalloc(&dc->ndev->dev, sizeof(*san), GFP_KERNEL);
if (!san) {
dev_info(&dc->ndev->dev, "No memory available\n");
return err;
}
tegra_dsi_enable_read_debug(dsi);
err = tegra_dsi_panel_sanity_check(dc, dsi, san);
tegra_dsi_disable_read_debug(dsi);
if (err < 0)
seq_puts(s, "Sanity check failed\n");
else
seq_puts(s, "Sanity check successful\n");
return err;
}
static int sanity_panel_open(struct inode *inode, struct file *file)
{
return single_open(file, panel_sanity_check, inode->i_private);
}
static const struct file_operations sanity_panel_fops = {
.open = sanity_panel_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static u32 command_value;
static u32 data_id;
static u32 command_value1;
static int send_host_cmd_v_blank_dcs(struct seq_file *s, void *unused)
{
struct tegra_dc_dsi_data *dsi = s->private;
int err;
struct tegra_dsi_cmd user_command[] = {
DSI_CMD_SHORT(data_id, command_value, command_value1),
DSI_DLY_MS(20),
};
if (!dsi->enabled) {
seq_puts(s, "DSI controller suspended\n");
return 0;
}
seq_printf(s, "data_id taken :0x%x\n", data_id);
seq_printf(s, "command value taken :0x%x\n", command_value);
seq_printf(s, "second command value taken :0x%x\n", command_value1);
err = tegra_dsi_start_host_cmd_v_blank_dcs(dsi, user_command);
return err;
}
static ssize_t host_cmd_v_blank_dcs_get_cmd(struct file *file,
const char *buf, size_t count, loff_t *off)
{
struct seq_file *s = file->private_data;
struct tegra_dc_dsi_data *dsi = s->private;
struct tegra_dc *dc = dsi->dc;
if (!dsi->enabled) {
dev_info(&dc->ndev->dev, "DSI controller suspended\n");
return count;
}
if (sscanf(buf, "%x %x %x", &data_id, &command_value, &command_value1)
!= 3)
return -EINVAL;
dev_info(&dc->ndev->dev, "data id taken :0x%x\n", data_id);
dev_info(&dc->ndev->dev, "command value taken :0x%x\n", command_value);
dev_info(&dc->ndev->dev, "second command value taken :0x%x\n",
command_value1);
return count;
}
static int host_cmd_v_blank_dcs_open(struct inode *inode, struct file *file)
{
return single_open(file, send_host_cmd_v_blank_dcs, inode->i_private);
}
static const struct file_operations host_cmd_v_blank_dcs_fops = {
.open = host_cmd_v_blank_dcs_open,
.read = seq_read,
.write = host_cmd_v_blank_dcs_get_cmd,
.llseek = seq_lseek,
.release = single_release,
};
static int remove_host_cmd_dcs(struct seq_file *s, void *unused)
{
struct tegra_dc_dsi_data *dsi = s->private;
tegra_dsi_stop_host_cmd_v_blank_dcs(dsi);
seq_puts(s, "host_cmd_v_blank_dcs stopped\n");
return 0;
}
static int rm_host_cmd_dcs_open(struct inode *inode, struct file *file)
{
return single_open(file, remove_host_cmd_dcs, inode->i_private);
}
static const struct file_operations remove_host_cmd_dcs_fops = {
.open = rm_host_cmd_dcs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int send_write_data_cmd(struct seq_file *s, void *unused)
{
struct tegra_dc_dsi_data *dsi = s->private;
struct tegra_dc *dc = dsi->dc;
int err;
u8 del = 100;
struct tegra_dsi_cmd user_command[] = {
DSI_CMD_SHORT(data_id, command_value, command_value1),
DSI_DLY_MS(20),
};
if (!dsi->enabled) {
seq_puts(s, "DSI controller suspended\n");
return 0;
}
seq_printf(s, "data_id taken :0x%x\n", data_id);
seq_printf(s, "command value taken :0x%x\n", command_value);
seq_printf(s, "second command value taken :0x%x\n", command_value1);
err = tegra_dsi_write_data(dc, dsi, user_command, del);
return err;
}
static ssize_t write_data_get_cmd(struct file *file,
const char *buf, size_t count, loff_t *off)
{
struct seq_file *s = file->private_data;
struct tegra_dc_dsi_data *dsi = s->private;
struct tegra_dc *dc = dsi->dc;
if (sscanf(buf, "%x %x %x", &data_id,
&command_value, &command_value1) != 3)
return -EINVAL;
dev_info(&dc->ndev->dev, "data_id taken :0x%x\n", data_id);
dev_info(&dc->ndev->dev, "command value taken :0x%x\n", command_value);
dev_info(&dc->ndev->dev, "second command value taken :0x%x\n",
command_value1);
return count;
}
static int write_data_open(struct inode *inode, struct file *file)
{
return single_open(file, send_write_data_cmd, inode->i_private);
}
static const struct file_operations write_data_fops = {
.open = write_data_open,
.read = seq_read,
.write = write_data_get_cmd,
.llseek = seq_lseek,
.release = single_release,
};
static struct dentry *dsidir;
void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi)
{
struct dentry *retval;
dsidir = debugfs_create_dir("tegra_dsi", NULL);
if (!dsidir)
return;
retval = debugfs_create_file("regs", S_IRUGO, dsidir, dsi,
&dbg_fops);
if (!retval)
goto free_out;
retval = debugfs_create_file("read_panel", S_IRUGO|S_IWUSR, dsidir,
dsi, &read_panel_fops);
if (!retval)
goto free_out;
retval = debugfs_create_file("panel_sanity", S_IRUGO, dsidir,
dsi, &sanity_panel_fops);
if (!retval)
goto free_out;
retval = debugfs_create_file("host_cmd_v_blank_dcs", S_IRUGO|S_IWUSR,
dsidir, dsi, &host_cmd_v_blank_dcs_fops);
if (!retval)
goto free_out;
retval = debugfs_create_file("remove_host_cmd_dcs", S_IRUGO|S_IWUSR,
dsidir, dsi, &remove_host_cmd_dcs_fops);
if (!retval)
goto free_out;
retval = debugfs_create_file("write_data", S_IRUGO|S_IWUSR,
dsidir, dsi, &write_data_fops);
if (!retval)
goto free_out;
return;
free_out:
debugfs_remove_recursive(dsidir);
dsidir = NULL;
return;
}
EXPORT_SYMBOL(tegra_dc_dsi_debug_create);
#else
void tegra_dc_dsi_debug_create(struct tegra_dc_dsi_data *dsi)
{}
EXPORT_SYMBOL(tegra_dc_dsi_debug_create);
#endif