mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
rk_fb: sysfs: add dump_buffer func to fb sysfs
Due to some time we want to know which buffer vop scaning, use "io" cammand to dump buffer is too complex, so we add a sys node to help buffer dump. how to use it: - echo bin > /sys/class/graphics/fb0/disp_info it will create bin file at /data/xxx.bin - or echo bmp > /sys/class/graphics/fb0/disp_info it will create bmp file at /data/xxx.bmp, this file is normal bmp file. Signed-off-by: Mark Yao <mark.yao@rock-chips.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
obj-$(CONFIG_FB_ROCKCHIP) += rk_fb.o rkfb_sysfs.o screen/
|
||||
obj-$(CONFIG_FB_ROCKCHIP) += rk_fb.o rkfb_sysfs.o bmp_helper.o screen/
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) += rk_drm_fb.o screen/
|
||||
obj-$(CONFIG_RK_TRSM) += transmitter/
|
||||
obj-$(CONFIG_DRM_ROCKCHIP) += lcdc/
|
||||
|
||||
171
drivers/video/rockchip/bmp_helper.c
Executable file
171
drivers/video/rockchip/bmp_helper.c
Executable file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* linux/drivers/video/rockchip/bmp_helper.c
|
||||
*
|
||||
* Copyright (C) 2012 Rockchip Corporation
|
||||
* Author: Mark Yao <mark.yao@rock-chips.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rk_fb.h>
|
||||
|
||||
#include "bmp_helper.h"
|
||||
|
||||
static void yuv_to_rgb(int y, int u, int v, int *r, int *g, int *b)
|
||||
{
|
||||
int rdif, invgdif, bdif;
|
||||
|
||||
u -= 128;
|
||||
v -= 128;
|
||||
rdif = v + ((v * 103) >> 8);
|
||||
invgdif = ((u * 88) >> 8) + ((v * 183) >> 8);
|
||||
bdif = u + ((u*198) >> 8);
|
||||
*r = range(y + rdif, 0, 0xff);
|
||||
*g = range(y - invgdif, 0, 0xff);
|
||||
*b = range(y + bdif, 0, 0xff);
|
||||
}
|
||||
|
||||
int datatobmp(void *__iomem *vaddr, int width, int height, u8 data_format,
|
||||
void *data, void (*fn)(void *, void *, int))
|
||||
{
|
||||
uint32_t *d, *d1, *d2;
|
||||
uint8_t *dst, *yrgb, *uv, *y1, *y2;
|
||||
int y, u, v, r, g, b;
|
||||
|
||||
int yu = width * 4 % 4;
|
||||
int byteperline;
|
||||
unsigned int size;
|
||||
BITMAPHEADER header;
|
||||
BITMAPINFOHEADER infoheader;
|
||||
void *buf;
|
||||
int i, j;
|
||||
|
||||
yu = yu != 0 ? 4 - yu : yu;
|
||||
byteperline = width * 4 + yu;
|
||||
size = byteperline * height + 54;
|
||||
memset(&header, 0, sizeof(header));
|
||||
memset(&infoheader, 0, sizeof(infoheader));
|
||||
header.type = 'M'<<8|'B';
|
||||
header.size = size;
|
||||
header.offset = 54;
|
||||
|
||||
infoheader.size = 40;
|
||||
infoheader.width = width;
|
||||
infoheader.height = 0 - height;
|
||||
infoheader.bitcount = 4 * 8;
|
||||
infoheader.compression = 0;
|
||||
infoheader.imagesize = byteperline * height;
|
||||
infoheader.xpelspermeter = 0;
|
||||
infoheader.ypelspermeter = 0;
|
||||
infoheader.colors = 0;
|
||||
infoheader.colorsimportant = 0;
|
||||
fn(data, (void *)&header, sizeof(header));
|
||||
fn(data, (void *)&infoheader, sizeof(infoheader));
|
||||
|
||||
/*
|
||||
* if data_format is ARGB888 or XRGB888, not need convert.
|
||||
*/
|
||||
if (data_format == ARGB888 || data_format == XRGB888) {
|
||||
fn(data, (char *)vaddr, width * height * 4);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* alloc 2 line buffer.
|
||||
*/
|
||||
buf = kmalloc(width * 2 * 4, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
yrgb = (uint8_t *)vaddr;
|
||||
uv = yrgb + width * height;
|
||||
for (j = 0; j < height; j++) {
|
||||
if (j % 2 == 0) {
|
||||
dst = buf;
|
||||
y1 = yrgb + j * width;
|
||||
y2 = y1 + width;
|
||||
d1 = buf;
|
||||
d2 = d1 + width;
|
||||
}
|
||||
|
||||
for (i = 0; i < width; i++) {
|
||||
switch (data_format) {
|
||||
case XBGR888:
|
||||
case ABGR888:
|
||||
dst[0] = yrgb[2];
|
||||
dst[1] = yrgb[1];
|
||||
dst[2] = yrgb[0];
|
||||
dst[3] = yrgb[3];
|
||||
dst += 4;
|
||||
yrgb += 4;
|
||||
break;
|
||||
case RGB888:
|
||||
dst[0] = yrgb[0];
|
||||
dst[1] = yrgb[1];
|
||||
dst[2] = yrgb[2];
|
||||
dst[3] = 0xff;
|
||||
dst += 4;
|
||||
yrgb += 3;
|
||||
break;
|
||||
case RGB565:
|
||||
dst[0] = (yrgb[0] & 0x1f) << 3;
|
||||
dst[1] = (yrgb[0] & 0xe0) >> 3 |
|
||||
(yrgb[1] & 0x7) << 5;
|
||||
dst[2] = yrgb[1] & 0xf8;
|
||||
dst[3] = 0xff;
|
||||
dst += 4;
|
||||
yrgb += 2;
|
||||
break;
|
||||
case YUV420:
|
||||
case YUV422:
|
||||
case YUV444:
|
||||
if (data_format == YUV420) {
|
||||
if (i % 2 == 0) {
|
||||
d = d1++;
|
||||
y = *y1++;
|
||||
} else {
|
||||
d = d2++;
|
||||
y = *y2++;
|
||||
}
|
||||
if (i % 4 == 0) {
|
||||
u = *uv++;
|
||||
v = *uv++;
|
||||
}
|
||||
} else if (data_format == YUV422) {
|
||||
if (i % 2 == 0) {
|
||||
u = *uv++;
|
||||
v = *uv++;
|
||||
}
|
||||
d = d1++;
|
||||
} else {
|
||||
u = *uv++;
|
||||
v = *uv++;
|
||||
d = d1++;
|
||||
}
|
||||
yuv_to_rgb(y, u, v, &r, &g, &b);
|
||||
*d = 0xff<<24 | r << 16 | g << 8 | b;
|
||||
break;
|
||||
case YUV422_A:
|
||||
case YUV444_A:
|
||||
default:
|
||||
pr_err("unsupport now\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (j % 2 == 1)
|
||||
fn(data, (char *)buf, 2 * width * 4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
48
drivers/video/rockchip/bmp_helper.h
Normal file
48
drivers/video/rockchip/bmp_helper.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* drivers/video/rockchip/bmp_helper.h
|
||||
*
|
||||
* Copyright (C) 2012 Rockchip Corporation
|
||||
* Author: Mark Yao <mark.yao@rock-chips.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _BMP_HELPER_H_
|
||||
#define _BMP_HELPER_H_
|
||||
|
||||
typedef struct bmpheader {
|
||||
unsigned short type;
|
||||
unsigned int size;
|
||||
unsigned int reserved;
|
||||
unsigned int offset;
|
||||
}__attribute__((packed)) BITMAPHEADER;
|
||||
|
||||
typedef struct bmpinfoheader {
|
||||
unsigned int size;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
unsigned short planes;
|
||||
unsigned short bitcount;
|
||||
unsigned int compression;
|
||||
unsigned int imagesize;
|
||||
unsigned int xpelspermeter;
|
||||
unsigned int ypelspermeter;
|
||||
unsigned int colors;
|
||||
unsigned int colorsimportant;
|
||||
}__attribute__((packed)) BITMAPINFOHEADER;
|
||||
|
||||
#define range(x, min, max) ((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x))
|
||||
|
||||
int datatobmp(void *__iomem *vaddr,int width, int height, u8 data_format,
|
||||
void *data, void (*fn)(void *, void *, int));
|
||||
#endif /* _BMP_HELPER_H_ */
|
||||
@@ -29,9 +29,46 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/div64.h>
|
||||
#include <linux/rk_screen.h>
|
||||
#include <linux/rk_fb.h>
|
||||
#if defined(CONFIG_ION_ROCKCHIP)
|
||||
#include <linux/rockchip_ion.h>
|
||||
#endif
|
||||
#include "bmp_helper.h"
|
||||
|
||||
static char *get_format_str(enum data_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case ARGB888:
|
||||
return "ARGB888";
|
||||
case RGB888:
|
||||
return "RGB888";
|
||||
case RGB565:
|
||||
return "RGB565";
|
||||
case YUV420:
|
||||
return "YUV420";
|
||||
case YUV422:
|
||||
return "YUV422";
|
||||
case YUV444:
|
||||
return "YUV444";
|
||||
case YUV420_A:
|
||||
return "YUV420_A";
|
||||
case YUV422_A:
|
||||
return "YUV422_A";
|
||||
case YUV444_A:
|
||||
return "YUV444_A";
|
||||
case XRGB888:
|
||||
return "XRGB888";
|
||||
case XBGR888:
|
||||
return "XBGR888";
|
||||
case ABGR888:
|
||||
return "ABGR888";
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
static ssize_t show_screen_info(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@@ -67,6 +104,136 @@ static ssize_t show_disp_info(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fill_buffer(void *handle, void *vaddr, int size)
|
||||
{
|
||||
struct file *filp = handle;
|
||||
|
||||
if (filp)
|
||||
vfs_write(filp, vaddr, size, &filp->f_pos);
|
||||
}
|
||||
|
||||
static int dump_win(struct rk_fb *rk_fb, struct rk_fb_reg_area_data *area_data,
|
||||
u8 data_format, int win_id, int area_id, bool is_bmp)
|
||||
{
|
||||
void __iomem *vaddr;
|
||||
struct file *filp;
|
||||
mm_segment_t old_fs;
|
||||
char name[100];
|
||||
struct ion_handle *ion_handle = area_data->ion_handle;
|
||||
int width = area_data->xvir;
|
||||
int height = area_data->yvir;
|
||||
|
||||
if (ion_handle) {
|
||||
vaddr = ion_map_kernel(rk_fb->ion_client, ion_handle);
|
||||
} else if (area_data->smem_start && area_data->smem_start != -1) {
|
||||
unsigned long start;
|
||||
unsigned int nr_pages;
|
||||
struct page **pages;
|
||||
int i = 0;
|
||||
|
||||
start = area_data->smem_start;
|
||||
nr_pages = width * height * 3 / 2 / PAGE_SIZE;
|
||||
pages = kzalloc(sizeof(struct page) * nr_pages,GFP_KERNEL);
|
||||
while (i < nr_pages) {
|
||||
pages[i] = phys_to_page(start);
|
||||
start += PAGE_SIZE;
|
||||
i++;
|
||||
}
|
||||
vaddr = vmap(pages, nr_pages, VM_MAP, pgprot_writecombine(PAGE_KERNEL));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(name, 100, "/data/win%d_%d_%dx%d_%s.%s", win_id, area_id,
|
||||
width, height, get_format_str(data_format),
|
||||
is_bmp ? "bmp" : "bin");
|
||||
|
||||
pr_info("dump win == > /data/win%d_%d_%dx%d_%s.%s\n", win_id, area_id,
|
||||
width, height, get_format_str(data_format),
|
||||
is_bmp ? "bmp" : "bin");
|
||||
|
||||
filp = filp_open(name, O_RDWR | O_CREAT, 0x664);
|
||||
if (!filp)
|
||||
printk("fail to create %s\n", name);
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
|
||||
if (is_bmp)
|
||||
datatobmp(vaddr, width, height, data_format, filp, fill_buffer);
|
||||
else
|
||||
fill_buffer(filp, vaddr, width * height * 4);
|
||||
|
||||
set_fs(old_fs);
|
||||
|
||||
if (ion_handle) {
|
||||
ion_unmap_kernel(rk_fb->ion_client, ion_handle);
|
||||
|
||||
ion_handle_put(ion_handle);
|
||||
}
|
||||
|
||||
filp_close(filp, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t set_dump_info(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
|
||||
{
|
||||
struct fb_info *fbi = dev_get_drvdata(dev);
|
||||
struct rk_fb_par *fb_par = (struct rk_fb_par *)fbi->par;
|
||||
struct rk_lcdc_driver *dev_drv = fb_par->lcdc_drv;
|
||||
struct rk_fb *rk_fb = dev_get_drvdata(fbi->device);
|
||||
struct rk_fb_reg_data *front_regs;
|
||||
struct rk_fb_reg_win_data *win_data;
|
||||
struct rk_fb_reg_area_data *area_data;
|
||||
bool is_img;
|
||||
int i, j;
|
||||
|
||||
if (!rk_fb->ion_client)
|
||||
return 0;
|
||||
|
||||
front_regs = kmalloc(sizeof(*front_regs), GFP_KERNEL);
|
||||
if (!front_regs)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_lock(&dev_drv->front_lock);
|
||||
|
||||
if (!dev_drv->front_regs) {
|
||||
mutex_unlock(&dev_drv->front_lock);
|
||||
return 0;
|
||||
}
|
||||
memcpy(front_regs, dev_drv->front_regs, sizeof(*front_regs));
|
||||
for (i = 0; i < front_regs->win_num; i++) {
|
||||
for (j = 0; j < RK_WIN_MAX_AREA; j++) {
|
||||
win_data = &front_regs->reg_win_data[i];
|
||||
area_data = &win_data->reg_area_data[j];
|
||||
if (area_data->ion_handle) {
|
||||
ion_handle_get(area_data->ion_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dev_drv->front_lock);
|
||||
|
||||
if (strncmp(buf, "bin", 3))
|
||||
is_img = true;
|
||||
else
|
||||
is_img = false;
|
||||
|
||||
for (i = 0; i < front_regs->win_num; i++) {
|
||||
for (j = 0; j < RK_WIN_MAX_AREA; j++) {
|
||||
win_data = &front_regs->reg_win_data[i];
|
||||
if (dump_win(rk_fb, &win_data->reg_area_data[j],
|
||||
win_data->data_format, i, j, is_img))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
kfree(front_regs);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_phys(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -580,7 +747,7 @@ static ssize_t set_scale(struct device *dev, struct device_attribute *attr,
|
||||
static struct device_attribute rkfb_attrs[] = {
|
||||
__ATTR(phys_addr, S_IRUGO, show_phys, NULL),
|
||||
__ATTR(virt_addr, S_IRUGO, show_virt, NULL),
|
||||
__ATTR(disp_info, S_IRUGO, show_disp_info, NULL),
|
||||
__ATTR(disp_info, S_IRUGO | S_IWUSR, show_disp_info, set_dump_info),
|
||||
__ATTR(screen_info, S_IRUGO, show_screen_info, NULL),
|
||||
__ATTR(dual_mode, S_IRUGO, show_dual_mode, NULL),
|
||||
__ATTR(enable, S_IRUGO | S_IWUSR, show_fb_state, set_fb_state),
|
||||
|
||||
Reference in New Issue
Block a user