rk_fb: sysfs: add dump_buffer func to fb sysfs
authorMark Yao <mark.yao@rock-chips.com>
Wed, 3 Dec 2014 02:05:26 +0000 (10:05 +0800)
committerMark Yao <mark.yao@rock-chips.com>
Wed, 3 Dec 2014 03:40:55 +0000 (11:40 +0800)
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>
drivers/video/rockchip/Makefile
drivers/video/rockchip/bmp_helper.c [new file with mode: 0755]
drivers/video/rockchip/bmp_helper.h [new file with mode: 0644]
drivers/video/rockchip/rkfb_sysfs.c

index 03f76012b5334cc5e2c276c4607979966f1819d8..f3adbabbaeb80c9eaac94068a9afb65362c61d96 100755 (executable)
@@ -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/
diff --git a/drivers/video/rockchip/bmp_helper.c b/drivers/video/rockchip/bmp_helper.c
new file mode 100755 (executable)
index 0000000..ac2406c
--- /dev/null
@@ -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;
+}
diff --git a/drivers/video/rockchip/bmp_helper.h b/drivers/video/rockchip/bmp_helper.h
new file mode 100644 (file)
index 0000000..0df1938
--- /dev/null
@@ -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_ */
index f9d8efd744b5550b11a25ad28de3e9c3eae098af..ee4f557d746e9d616114f84819dcf5adc282f932 100755 (executable)
 #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),