blob: cf9631bebe9bfc1b892bbcf56ebb67a63bdf4d72 [file] [log] [blame]
/*
* linux/drivers/video/mvdisp_debug.c
*/
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include "pxa95xfb.h"
struct bmp_info_header {
unsigned int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} __attribute((packed));
struct bmp_file_header {
short bfType;
unsigned int bfSize;
short bfReserved1;
short bfReserved2;
unsigned int bfOffBits;
} __attribute((packed));
static void dump_bmp(char *name, unsigned char *src, int fmt, int w, int h)
{
struct bmp_file_header file_header;
struct bmp_info_header info_header;
int ret, i, j, size = w * h * 3;
unsigned char *src_line, *dst_line, *dst;
struct file *f;
mm_segment_t old_fs;
memset(&file_header, 0, sizeof(struct bmp_file_header));
file_header.bfType = 0x4D42;
file_header.bfSize = sizeof(struct bmp_file_header);
file_header.bfReserved1 = 0;
file_header.bfReserved2 = 0;
file_header.bfOffBits = sizeof(struct bmp_file_header)
+ sizeof(struct bmp_info_header);
memset(&info_header, 0, sizeof(struct bmp_info_header));
info_header.biSize = sizeof(struct bmp_info_header);
info_header.biWidth = w;
info_header.biHeight = h;
info_header.biPlanes = 1;
info_header.biBitCount = 24;
info_header.biCompression = 0;/*BI_RGB;*/
dst = vmalloc(size);
if (!dst) {
vfree(dst);
return ;
}
dst_line = dst + (h-1) * w * 3;
src_line = src;
switch (fmt)
{
case PIX_FMTIN_RGB_16:
for(i = 0; i < h; i++) {
unsigned char *s = src_line;
unsigned char *d = dst_line;
for(j = 0; j < w; j++) {
unsigned short rgb = *(unsigned short*)s;
d[0] = ((rgb & 0x001f) << 3);
d[1] = ((rgb & 0x07e0) >> 3);
d[2] = ((rgb & 0xf800) >> 8);
s += 2;
d += 3;
}
src_line += 2*w;
dst_line -= 3*w;
}
break;
case PIX_FMTIN_RGB_24:
case PIX_FMTIN_RGB_32:
for(i = 0; i < h; i++) {
unsigned char *s = src_line;
unsigned char *d = dst_line;
for(j = 0; j < w; j++) {
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
s += 4;
d += 3;
}
src_line += 4*w;
dst_line -= 3*w;
}
break;
case PIX_FMTIN_RGB_24_PACK:
for(i = 0; i < h; i++) {
memcpy(dst_line, src_line, 3*w);
src_line += 3*w;
dst_line -= 3*w;
}
break;
default:
pr_err("format not supported\n");
break;
}
printk("creat %s\n", name);
f = filp_open(name, O_RDWR | O_CREAT | O_LARGEFILE, 0);
if (IS_ERR(f)) {
pr_err("dump fail: fail to open\n");
vfree(dst);
return;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = f->f_op->write(f, (const char *)&file_header, sizeof(struct bmp_file_header), &f->f_pos);
ret = f->f_op->write(f, (const char *)&info_header, sizeof(struct bmp_info_header), &f->f_pos);
ret = f->f_op->write(f, dst, size, &f->f_pos);
set_fs(old_fs);
if (ret < 0)
pr_err("dump fail: fail to write\n");
filp_close(f, NULL);
vfree(dst);
}
static void dump_yuv(char *name, unsigned char *src, int fmt, int w, int h)
{
struct file *f;
int ret, size;
mm_segment_t old_fs;
switch (fmt)
{
case PIX_FMTIN_YUV420:
case PIX_FMTIN_YVU420:
case PIX_FMTIN_YUV422:
case PIX_FMTIN_YUV422IL:
size = w*h*2;
break;
case PIX_FMTIN_YUV444:
size = w*h*3;
break;
default:
pr_err("format not supported\n");
break;
}
printk("creat %s\n", name);
f = filp_open(name, O_RDWR | O_CREAT | O_LARGEFILE, 0);
if (IS_ERR(f)) {
pr_err("dump fail: fail to open\n");
return;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = f->f_op->write(f, src, size, &f->f_pos);
set_fs(old_fs);
if (ret < 0)
pr_err("dump fail: fail to write\n");
filp_close(f, NULL);
}
void dump_buffer(struct pxa95xfb_info *fbi, int yoffset)
{
static int c;
char name[128];
unsigned char *src;
int w = fbi->mode.xres, h = fbi->mode.yres;
if (fbi->user_addr) {
printk("buffer dump... using user pointer\n");
w = fbi->surface.viewPortInfo.srcWidth;
h = fbi->surface.viewPortInfo.srcHeight;
src = (unsigned char *)phys_to_virt(fbi->user_addr);
} else {
printk("buffer dump... using fb buffer\n");
w = fbi->mode.xres;
h = fbi->mode.yres;
src = (unsigned char *)(fbi->fb_start + yoffset * pix_fmt_to_bpp(fbi->pix_fmt) * w);
}
if (format_is_yuv(fbi->pix_fmt)) {
sprintf(name, "/data/fb%d.%04d.yuv", fbi->id, c++);
dump_yuv(name, src, fbi->pix_fmt, w, h);
} else {
sprintf(name, "/data/fb%d.%04d.bmp", fbi->id, c++);
dump_bmp(name, src, fbi->pix_fmt, w, h);
}
}
static void dump_regs_controller(struct pxa95xfb_info *fbi)
{
/* LCD CTL related */
printk("LCD_CTL = %08x\n", readl(fbi->reg_base + LCD_CTL));
printk("LCD_CTL_INT_STS = %08x\n", readl(fbi->reg_base + LCD_CTL_INT_STS));
/* LCD fetch related */
printk("LCD_FETCH_CTL%d = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_FETCH_CTL0 + 0x40 * fbi->window));
printk("LCD_NXT_DESC_ADDR%d = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_NXT_DESC_ADDR0 + 0x40 * fbi->window));
printk("LCD_FETCH_STS%d = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_FETCH_STS0 + 0x40 * fbi->window));
printk("LCD_FR_ADDR%d = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_FR_ADDR0 + 0x40 * fbi->window));
printk("LCD_FETCH_INT_STS0 = %08x\n", readl(fbi->reg_base + LCD_FETCH_INT_STS0));
printk("LCD_FETCH_INT_STS1 = %08x\n", readl(fbi->reg_base + LCD_FETCH_INT_STS1));
/* LCD alpha related */
printk("LCD_CH%d_ALPHA = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_CH0_ALPHA + 0x4 * fbi->window));
printk("LCD_CH%d_CLR_MATCH = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_CH0_CLR_MATCH + 0x4 * fbi->window));
/* LCD window related */
if (fbi->window == 4) {
printk("LCD_WIN4_CTL = %08x\n", readl(fbi->reg_base + LCD_WIN4_CTL));
printk("LCD_WIN4_CFG = %08x\n", readl(fbi->reg_base + LCD_WIN4_CFG));
printk("LCD_WIN4_CROP0 = %08x\n", readl(fbi->reg_base + LCD_WIN4_CROP0));
printk("LCD_WIN4_CROP1 = %08x\n", readl(fbi->reg_base + LCD_WIN4_CROP1));
printk("LCD_GWIN_INT_STS = %08x\n", readl(fbi->reg_base + LCD_GWIN_INT_STS));
printk("LCD_WIN4_INT_STS = %08x\n", readl(fbi->reg_base + LCD_WIN4_INT_STS));
} else {
printk("LCD_WIN%d_CTL = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_WIN0_CTL + 0x40 * fbi->window));
printk("LCD_WIN%d_CFG = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_WIN0_CFG + 0x40 * fbi->window));
printk("LCD_WIN%d_CROP0 = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_WIN0_CROP0 + 0x40 * fbi->window));
printk("LCD_WIN%d_CROP1 = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_WIN0_CROP1 + 0x40 * fbi->window));
printk("LCD_GWIN_INT_STS = %08x\n", readl(fbi->reg_base + LCD_GWIN_INT_STS));
printk("LCD_WIN%d_INT_STS = %08x\n", fbi->window,
readl(fbi->reg_base + LCD_WIN0_INT_STS + 0x40 * fbi->window));
}
/* LCD mixer related */
printk("LCD_MIXER%d_CTL0 = %08x\n", fbi->mixer_id,
readl(fbi->reg_base + LCD_MIXER0_CTL0 + 0x100 * fbi->mixer_id));
printk("LCD_MIXER%d_CTL1 = %08x\n", fbi->mixer_id,
readl(fbi->reg_base + LCD_MIXER0_CTL1 + 0x100 * fbi->mixer_id));
printk("LCD_MIXER%d_CTL2 = %08x\n", fbi->mixer_id,
readl(fbi->reg_base + LCD_MIXER0_CTL2 + 0x100 * fbi->mixer_id));
printk("LCD_MIXER%d_STS = %08x\n", fbi->mixer_id,
readl(fbi->reg_base + LCD_MIXER0_STS + 0x100 * fbi->mixer_id));
printk("LCD_MIXER%d_STS1 = %08x\n", fbi->mixer_id,
readl(fbi->reg_base + LCD_MIXER0_STS1 + 0x100 * fbi->mixer_id));
printk("LCD_MIXER%d_OL%d_CFG0 = %08x\n", fbi->mixer_id, fbi->zorder,
readl(fbi->reg_base + LCD_MIXER0_BP_CFG0 + 0x100 * fbi->mixer_id + 0x8 * fbi->zorder));
printk("LCD_MIXER%d_OL%d_CFG1 = %08x\n", fbi->mixer_id, fbi->zorder,
readl(fbi->reg_base + LCD_MIXER0_BP_CFG1 + 0x100 * fbi->mixer_id + 0x8 * fbi->zorder));
printk("LCD_GMIX_INT_STS = %08x\n", readl(fbi->reg_base + LCD_GMIX_INT_STS));
printk("LCD_MIXER%d_INT_STS = %08x\n", fbi->mixer_id,
readl(fbi->reg_base + LCD_MIXER0_INT_STS + 0x100 * fbi->mixer_id));
}
static void dump_regs_parallel(struct pxa95xfb_info *fbi)
{
printk("LCD_CONV0_CTL = %08x\n", readl(fbi->reg_base + LCD_CONV0_CTL));
printk("LCD_MIXER0_TIM0 = %08x\n", readl(fbi->reg_base + LCD_MIXER0_TIM0));
printk("LCD_MIXER0_TIM1 = %08x\n", readl(fbi->reg_base + LCD_MIXER0_TIM1));
printk("LCD_MIXER0_INT_STS = %08x\n", readl(fbi->reg_base + LCD_MIXER0_INT_STS));
printk("LCD_DITHER_CTL = %08x\n", readl(fbi->reg_base + LCD_DITHER_CTL));
}
static void dump_regs_dsi(struct pxa95xfb_info *fbi)
{
void *conv_base =
CONVERTER_BASE_ADDRESS(fbi->reg_base, fbi->converter);
printk("LCD_DSI_D%dSCR0 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxSCR0_OFFSET));
printk("LCD_DSI_D%dCONV_GEN_NULL_BLANK = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxCONV_GEN_NULL_BLANK_OFFSET));
printk("LCD_DSI_D%dCONV_RD0 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxCONV_DSI_RD0_OFFSET));
printk("LCD_DSI_D%dCONV_RD1 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxCONV_DSI_RD1_OFFSET));
printk("LCD_DSI_D%dCONV_FIFO_THRESH = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxCONV_FIFO_THRESH_OFFSET));
printk("LCD_DSI_D%dCONV_FIFO_THRESH_INT = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxCONV_FIFO_THRESH_INT_OFFSET));
printk("LCD_DSI_D%dSCR1 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxSCR1_OFFSET));
printk("LCD_DSI_D%dSSR = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxSSR_OFFSET));
printk("LCD_DSI_D%d_G_CTL = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_Dx_G_CTL_OFFSET));
printk("LCD_DSI_D%d_G_DAT_RED = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_Dx_G_DAT_RED_OFFSET));
printk("LCD_DSI_D%d_G_DAT_GREEN = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_Dx_G_DAT_GREEN_OFFSET));
printk("LCD_DSI_D%d_G_DAT_BLUE = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_Dx_G_DAT_BLUE_OFFSET));
printk("LCD_DSI_D%dCFIF = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxCFIF_OFFSET));
printk("LCD_DSI_D%dTRIG = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxTRIG_OFFSET));
printk("LCD_DSI_D%dPRSR = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxPRSR_OFFSET));
printk("LCD_DSI_D%dTEINTCNT = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxTEINTCNT_OFFSET));
printk("LCD_DSI_D%dINEN = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxINEN_OFFSET));
printk("LCD_DSI_D%dINST0 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxINST0_OFFSET));
printk("LCD_DSI_D%dINST1 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxINST1_OFFSET));
printk("LCD_DSI_D%dADAT = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxADAT_OFFSET));
printk("LCD_DSI_D%dTIM0 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxTIM0_OFFSET));
printk("LCD_DSI_D%dTIM1 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxTIM1_OFFSET));
printk("LCD_DSI_D%dPHY_TIM0 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxPHY_TIM0_OFFSET));
printk("LCD_DSI_D%dPHY_TIM1 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxPHY_TIM1_OFFSET));
printk("LCD_DSI_D%dVSYNC_JITT_TIM0 = %08x\n", fbi->converter - 1,
readl(conv_base + LCD_DSI_DxVSYNC_JITT_TIM0));
if (fbi->converter == LCD_M2DSI1)
printk("DSI_DITHER_CTL = %08x\n", readl(conv_base + LCD_DSI_DITHER_CTL));
}
static void dump_regs_ihdmi(struct pxa95xfb_info *fbi)
{
void *conv_base =
CONVERTER_BASE_ADDRESS(fbi->reg_base, fbi->converter);
printk("HDMI_CONV_CTL = %08x\n", readl(conv_base + HDMI_CONV_CTL));
printk("HDMI_MIXER_TIM0 = %08x\n", readl(conv_base + HDMI_MIXER_TIM0));
printk("HDMI_MIXER_TIM1 = %08x\n", readl(conv_base + HDMI_MIXER_TIM1));
printk("HDMI_MIXER_TIM2 = %08x\n", readl(conv_base + HDMI_MIXER_TIM2));
printk("HDMI_MIXER_TIM3 = %08x\n", readl(conv_base + HDMI_MIXER_TIM3));
printk("HDMI_MIXER_TIM4 = %08x\n", readl(conv_base + HDMI_MIXER_TIM4));
printk("HDMI_CONV_INT_STS = %08x\n", readl(conv_base + HDMI_CONV_INT_STS));
printk("HDMI_CONV_CTL = %08x\n", readl(conv_base + HDMI_CONV_CTL));
printk("HDMI_CLK_DIV = %08x\n", readl(conv_base + HDMI_CLK_DIV));
printk("HDMI_PCLK_DIV = %08x\n", readl(conv_base + HDMI_PCLK_DIV));
printk("HDMI_TCLK_DIV = %08x\n", readl(conv_base + HDMI_TCLK_DIV));
printk("HDMI_PRCLK_DIV = %08x\n", readl(conv_base + HDMI_PRCLK_DIV));
printk("HDMI_MIXER_TIM4 = %08x\n", readl(conv_base + HDMI_MIXER_TIM4));
}
static void dump_regs(struct pxa95xfb_info *fbi)
{
switch (fbi->converter)
{
case LCD_M2PARALELL_CONVERTER:
printk("dump regs...using parallel interface\n");
dump_regs_controller(fbi);
dump_regs_parallel(fbi);
break;
case LCD_M2DSI0:
case LCD_M2DSI1:
printk("dump regs...using parallel interface\n");
dump_regs_controller(fbi);
dump_regs_dsi(fbi);
break;
case LCD_M2HDMI:
printk("dump regs...using internal HDMI interface\n");
dump_regs_controller(fbi);
dump_regs_ihdmi(fbi);
default:
printk("dump regs...converter disabled stop dump\n");
break;
}
}
static void dump_fb_info(struct pxa95xfb_info *fbi)
{
printk("fb%d info:\n", fbi->id);
printk("\tfetch%d, ol%d on mixer%d\n", fbi->window, fbi->zorder, fbi->mixer_id);
switch (fbi->converter)
{
case LCD_M2PARALELL_CONVERTER:
printk("\tParallel interface\n");
break;
case LCD_M2DSI0:
printk("\tDSI0 interface\n");
break;
case LCD_M2DSI1:
printk("\tDSI1 interface\n");
break;
case LCD_M2HDMI:
printk("\tInternal HDMI interface\n");
break;
default:
printk("\tconverter disabled\n");
break;
}
printk("\tOutput resolution %d X %d\n", fbi->mode.xres, fbi->mode.yres);
printk("buffer status:\n");
if (fbi->user_addr) {
printk("\tbuffer user pointer phys addr %08x\n", (int)fbi->user_addr);
printk("\tbuffer size %d X %d\n",
fbi->surface.viewPortInfo.srcWidth, fbi->surface.viewPortInfo.srcHeight);
printk("\tbuffer zoom dst %d X %d\n",
fbi->surface.viewPortInfo.zoomXSize, fbi->surface.viewPortInfo.zoomYSize);
printk("\tbuffer position %d X %d\n",
fbi->surface.viewPortOffset.xOffset, fbi->surface.viewPortOffset.yOffset);
} else {
printk("\tbuffer fb buffer phys addr %08x\n", fbi->fb_start_dma);
printk("\tbuffer size %d X %d\n",
fbi->mode.xres, fbi->mode.yres);
}
switch (fbi->pix_fmt)
{
case PIX_FMTIN_RGB_16:
printk("\tbuffer format RGB565\n");
break;
case PIX_FMTIN_RGB_24:
printk("\tbuffer format XRGB888\n");
break;
case PIX_FMTIN_RGB_32:
printk("\tbuffer format ARGB888\n");
break;
case PIX_FMTIN_RGB_24_PACK:
printk("\tbuffer format RGB888\n");
break;
case PIX_FMTIN_YUV420:
printk("\tbuffer format YUV420p\n");
break;
case PIX_FMTIN_YVU420:
printk("\tbuffer format YVU420\n");
break;
case PIX_FMTIN_YUV422:
printk("\tbuffer format YUV422p\n");
break;
case PIX_FMTIN_YUV444:
printk("\tbuffer format YUV444p\n");
break;
case PIX_FMTIN_YUV422IL:
printk("\tbuffer format YUV422il\n");
break;
default:
printk("\tbuffer format not supported\n");
break;
}
}
static void dump_helper(void)
{
printk("***how to dump****:\n");
printk("\tdump = 0 would dump infos\n");
printk("\tdump = 1 would dump registers\n");
printk("\tdump = 2 would dump buffer instantly\n");
printk("\tdump = 3 would start dump when each frame passed down\n");
printk("\tdump = 4 would stop continous dump\n");
}
/* dump support:
* echo <value> > /sys/class/graphics/fb0/device/dump
* 0. dump fb info:
* 1. reg dump
* 2. dump buffer instantly, support both yuv/rgb and user pointer/fb buffer
* 3 .start dump when each frame passed down
* 4. stop continous dump
*/
static ssize_t dump_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
dump_helper();
return 0;
}
static ssize_t dump_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct pxa95xfb_info *fbi = dev_get_drvdata(dev);
int dump;
sscanf(buf, "%d", &dump);
switch (dump)
{
case 0:
dump_fb_info(fbi);
break;
case 1:
dump_regs(fbi);
break;
case 2:
dump_buffer(fbi, 0);
break;
case 3:
fbi->dump = 1;
break;
case 4:
fbi->dump = 0;
break;
default:
printk("dump command %d is not recoginzed\n", dump);
dump_helper();
break;
}
return size;
}
static DEVICE_ATTR(dump, S_IRUGO | S_IWUSR, dump_show, dump_store);
int mvdisp_debug_init(struct device *dev)
{
return device_create_file(dev, &dev_attr_dump);
}