* [PATCHv4 2/9] video: mmp fb support
From: Zhou Zhu @ 2012-10-24 6:56 UTC (permalink / raw)
To: linux-fbdev
Added fb support for Marvell mmp display subsystem.
This driver is configured using "buffer driver mach info".
With configured name of path, this driver get path using
using exported interface of mmp display driver.
Then this driver get ovly using configured id and operates
on this ovly to show buffers on display devices.
Signed-off-by: Zhou Zhu <zzhu3@marvell.com>
Signed-off-by: Lisa Du <cldu@marvell.com>
---
drivers/video/mmp/Kconfig | 4 +
drivers/video/mmp/Makefile | 2 +-
drivers/video/mmp/fb/Kconfig | 13 +
drivers/video/mmp/fb/Makefile | 1 +
drivers/video/mmp/fb/mmpfb.c | 710 +++++++++++++++++++++++++++++++++++++++++
drivers/video/mmp/fb/mmpfb.h | 54 +++
6 files changed, 783 insertions(+), 1 deletions(-)
create mode 100644 drivers/video/mmp/fb/Kconfig
create mode 100644 drivers/video/mmp/fb/Makefile
create mode 100644 drivers/video/mmp/fb/mmpfb.c
create mode 100644 drivers/video/mmp/fb/mmpfb.h
diff --git a/drivers/video/mmp/Kconfig b/drivers/video/mmp/Kconfig
index 0554336..6a0b056 100644
--- a/drivers/video/mmp/Kconfig
+++ b/drivers/video/mmp/Kconfig
@@ -3,3 +3,7 @@ menuconfig MMP_DISP
depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
help
Marvell Display Subsystem support.
+
+if MMP_DISP
+source "drivers/video/mmp/fb/Kconfig"
+endif
diff --git a/drivers/video/mmp/Makefile b/drivers/video/mmp/Makefile
index 820eb10..fdcd833 100644
--- a/drivers/video/mmp/Makefile
+++ b/drivers/video/mmp/Makefile
@@ -1 +1 @@
-obj-y += core.o
+obj-y += core.o fb/
diff --git a/drivers/video/mmp/fb/Kconfig b/drivers/video/mmp/fb/Kconfig
new file mode 100644
index 0000000..9b0141f
--- /dev/null
+++ b/drivers/video/mmp/fb/Kconfig
@@ -0,0 +1,13 @@
+if MMP_DISP
+
+config MMP_FB
+ bool "fb driver for Marvell MMP Display Subsystem"
+ depends on FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ default y
+ help
+ fb driver for Marvell MMP Display Subsystem
+
+endif
diff --git a/drivers/video/mmp/fb/Makefile b/drivers/video/mmp/fb/Makefile
new file mode 100644
index 0000000..709fd1f
--- /dev/null
+++ b/drivers/video/mmp/fb/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MMP_FB) += mmpfb.o
diff --git a/drivers/video/mmp/fb/mmpfb.c b/drivers/video/mmp/fb/mmpfb.c
new file mode 100644
index 0000000..9363231
--- /dev/null
+++ b/drivers/video/mmp/fb/mmpfb.c
@@ -0,0 +1,710 @@
+/*
+ * linux/drivers/video/mmp/fb/mmpfb.c
+ * Framebuffer driver for Marvell Display controller.
+ *
+ * Copyright (C) 2012 Marvell Technology Group Ltd.
+ * Authors: Zhou Zhu <zzhu3@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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/module.h>
+#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
+#include "mmpfb.h"
+
+static int var_to_pixfmt(struct fb_var_screeninfo *var)
+{
+ /*
+ * Pseudocolor mode?
+ */
+ if (var->bits_per_pixel = 8)
+ return PIXFMT_PSEUDOCOLOR;
+
+ /*
+ * Check for YUV422PLANAR.
+ */
+ if (var->bits_per_pixel = 16 && var->red.length = 8 &&
+ var->green.length = 4 && var->blue.length = 4) {
+ if (var->green.offset >= var->blue.offset)
+ return PIXFMT_YUV422P;
+ else
+ return PIXFMT_YVU422P;
+ }
+
+ /*
+ * Check for YUV420PLANAR.
+ */
+ if (var->bits_per_pixel = 12 && var->red.length = 8 &&
+ var->green.length = 2 && var->blue.length = 2) {
+ if (var->green.offset >= var->blue.offset)
+ return PIXFMT_YUV420P;
+ else
+ return PIXFMT_YVU420P;
+ }
+
+ /*
+ * Check for YUV422PACK.
+ */
+ if (var->bits_per_pixel = 16 && var->red.length = 16 &&
+ var->green.length = 16 && var->blue.length = 16) {
+ if (var->red.offset = 0)
+ return PIXFMT_YUYV;
+ else if (var->green.offset >= var->blue.offset)
+ return PIXFMT_UYVY;
+ else
+ return PIXFMT_VYUY;
+ }
+
+ /*
+ * Check for 565/1555.
+ */
+ if (var->bits_per_pixel = 16 && var->red.length <= 5 &&
+ var->green.length <= 6 && var->blue.length <= 5) {
+ if (var->transp.length = 0) {
+ if (var->red.offset >= var->blue.offset)
+ return PIXFMT_RGB565;
+ else
+ return PIXFMT_BGR565;
+ }
+ }
+
+ /*
+ * Check for 888/A888.
+ */
+ if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
+ var->green.length <= 8 && var->blue.length <= 8) {
+ if (var->bits_per_pixel = 24 && var->transp.length = 0) {
+ if (var->red.offset >= var->blue.offset)
+ return PIXFMT_RGB888PACK;
+ else
+ return PIXFMT_BGR888PACK;
+ }
+
+ if (var->bits_per_pixel = 32 && var->transp.offset = 24) {
+ if (var->red.offset >= var->blue.offset)
+ return PIXFMT_RGBA888;
+ else
+ return PIXFMT_BGRA888;
+ } else {
+ if (var->red.offset >= var->blue.offset)
+ return PIXFMT_RGB888UNPACK;
+ else
+ return PIXFMT_BGR888UNPACK;
+ }
+
+ /* fall through */
+ }
+
+ return -EINVAL;
+}
+
+static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
+{
+ switch (pix_fmt) {
+ case PIXFMT_RGB565:
+ var->bits_per_pixel = 16;
+ var->red.offset = 11; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 6;
+ var->blue.offset = 0; var->blue.length = 5;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_BGR565:
+ var->bits_per_pixel = 16;
+ var->red.offset = 0; var->red.length = 5;
+ var->green.offset = 5; var->green.length = 6;
+ var->blue.offset = 11; var->blue.length = 5;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_RGB888UNPACK:
+ var->bits_per_pixel = 32;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_BGR888UNPACK:
+ var->bits_per_pixel = 32;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_RGBA888:
+ var->bits_per_pixel = 32;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 24; var->transp.length = 8;
+ break;
+ case PIXFMT_BGRA888:
+ var->bits_per_pixel = 32;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 24; var->transp.length = 8;
+ break;
+ case PIXFMT_RGB888PACK:
+ var->bits_per_pixel = 24;
+ var->red.offset = 16; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_BGR888PACK:
+ var->bits_per_pixel = 24;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 8; var->green.length = 8;
+ var->blue.offset = 16; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_YUV420P:
+ var->bits_per_pixel = 12;
+ var->red.offset = 4; var->red.length = 8;
+ var->green.offset = 2; var->green.length = 2;
+ var->blue.offset = 0; var->blue.length = 2;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_YVU420P:
+ var->bits_per_pixel = 12;
+ var->red.offset = 4; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 2;
+ var->blue.offset = 2; var->blue.length = 2;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_YUV422P:
+ var->bits_per_pixel = 16;
+ var->red.offset = 8; var->red.length = 8;
+ var->green.offset = 4; var->green.length = 4;
+ var->blue.offset = 0; var->blue.length = 4;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_YVU422P:
+ var->bits_per_pixel = 16;
+ var->red.offset = 8; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 4;
+ var->blue.offset = 4; var->blue.length = 4;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_UYVY:
+ var->bits_per_pixel = 16;
+ var->red.offset = 8; var->red.length = 16;
+ var->green.offset = 4; var->green.length = 16;
+ var->blue.offset = 0; var->blue.length = 16;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_VYUY:
+ var->bits_per_pixel = 16;
+ var->red.offset = 8; var->red.length = 16;
+ var->green.offset = 0; var->green.length = 16;
+ var->blue.offset = 4; var->blue.length = 16;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_YUYV:
+ var->bits_per_pixel = 16;
+ var->red.offset = 0; var->red.length = 16;
+ var->green.offset = 4; var->green.length = 16;
+ var->blue.offset = 8; var->blue.length = 16;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ case PIXFMT_PSEUDOCOLOR:
+ var->bits_per_pixel = 8;
+ var->red.offset = 0; var->red.length = 8;
+ var->green.offset = 0; var->green.length = 8;
+ var->blue.offset = 0; var->blue.length = 8;
+ var->transp.offset = 0; var->transp.length = 0;
+ break;
+ }
+}
+
+/*
+ * fb framework has its limitation:
+ * 1. input color/output color is not seprated
+ * 2. fb_videomode not include output color
+ * so for fb usage, we keep a output format which is not changed
+ * then it's added for mmpmode
+ */
+static void fbmode_to_mmpmode(struct mmp_mode *mode,
+ struct fb_videomode *videomode, int output_fmt)
+{
+ u64 div_result = 1000000000000ll;
+ mode->name = videomode->name;
+ mode->refresh = videomode->refresh;
+ mode->xres = videomode->xres;
+ mode->yres = videomode->yres;
+
+ do_div(div_result, videomode->pixclock);
+ mode->pixclock_freq = (u32)div_result;
+
+ mode->left_margin = videomode->left_margin;
+ mode->right_margin = videomode->right_margin;
+ mode->upper_margin = videomode->upper_margin;
+ mode->lower_margin = videomode->lower_margin;
+ mode->hsync_len = videomode->hsync_len;
+ mode->vsync_len = videomode->vsync_len;
+ mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
+ mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
+ /* no defined flag in fb, use vmode>>3*/
+ mode->invert_pixclock = !!(videomode->vmode & 8);
+ mode->pix_fmt_out = output_fmt;
+}
+
+static void mmpmode_to_fbmode(struct fb_videomode *videomode,
+ struct mmp_mode *mode)
+{
+ u64 div_result = 1000000000000ll;
+
+ videomode->name = mode->name;
+ videomode->refresh = mode->refresh;
+ videomode->xres = mode->xres;
+ videomode->yres = mode->yres;
+
+ do_div(div_result, mode->pixclock_freq);
+ videomode->pixclock = (u32)div_result;
+
+ videomode->left_margin = mode->left_margin;
+ videomode->right_margin = mode->right_margin;
+ videomode->upper_margin = mode->upper_margin;
+ videomode->lower_margin = mode->lower_margin;
+ videomode->hsync_len = mode->hsync_len;
+ videomode->vsync_len = mode->vsync_len;
+ videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
+ | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
+ videomode->vmode = mode->invert_pixclock ? 8 : 0;
+}
+
+
+static void *alloc_framebuffer(size_t size, dma_addr_t *dma)
+{
+ int nr, i = 0;
+ struct page **pages;
+ void *start;
+
+ nr = size >> PAGE_SHIFT;
+ start = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (start = NULL)
+ return NULL;
+
+ *dma = virt_to_phys(start);
+ pages = vmalloc(sizeof(struct page *) * nr);
+ if (pages = NULL)
+ return NULL;
+
+ while (i < nr) {
+ pages[i] = phys_to_page(*dma + (i << PAGE_SHIFT));
+ i++;
+ }
+ start = vmap(pages, nr, 0, pgprot_writecombine(pgprot_kernel));
+
+ vfree(pages);
+ return start;
+}
+
+static int mmpfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mmpfb_info *fbi = info->par;
+
+ if (var->bits_per_pixel = 8)
+ return -EINVAL;
+ /*
+ * Basic geometry sanity checks.
+ */
+ if (var->xoffset + var->xres > var->xres_virtual)
+ return -EINVAL;
+ if (var->yoffset + var->yres > var->yres_virtual)
+ return -EINVAL;
+
+ /*
+ * Check size of framebuffer.
+ */
+ if (var->xres_virtual * var->yres_virtual *
+ (var->bits_per_pixel >> 3) > fbi->fb_size)
+ return -EINVAL;
+
+ return 0;
+}
+
+static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
+{
+ return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
+}
+
+static u32 to_rgb(u16 red, u16 green, u16 blue)
+{
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ return (red << 16) | (green << 8) | blue;
+}
+
+static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
+ unsigned int green, unsigned int blue,
+ unsigned int trans, struct fb_info *info)
+{
+ struct mmpfb_info *fbi = info->par;
+ u32 val;
+
+ if (info->fix.visual = FB_VISUAL_TRUECOLOR && regno < 16) {
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue , &info->var.blue);
+ fbi->pseudo_palette[regno] = val;
+ }
+
+ if (info->fix.visual = FB_VISUAL_PSEUDOCOLOR && regno < 256) {
+ val = to_rgb(red, green, blue);
+ /* TODO */
+ }
+
+ return 0;
+}
+
+static int mmpfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct mmpfb_info *fbi = info->par;
+ struct mmp_addr addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
+ * var->bits_per_pixel / 8 + fbi->fb_start_dma;
+ mmp_ovly_set_addr(fbi->ovly, &addr);
+
+ return 0;
+}
+
+static int var_update(struct fb_info *info)
+{
+ struct mmpfb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct fb_videomode *m;
+ int pix_fmt;
+
+ /* set pix_fmt */
+ pix_fmt = var_to_pixfmt(var);
+ if (pix_fmt < 0)
+ return -EINVAL;
+ pixfmt_to_var(var, pix_fmt);
+ fbi->pix_fmt = pix_fmt;
+
+ /* set var according to best video mode*/
+ m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
+ if (!m) {
+ dev_err(fbi->dev, "set par: no match mode, use best mode\n");
+ m = (struct fb_videomode *)fb_find_best_mode(var,
+ &info->modelist);
+ fb_videomode_to_var(var, m);
+ }
+ memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
+
+ /* fix to 2* yres */
+ var->yres_virtual = var->yres * 2;
+ info->fix.visual = (pix_fmt = PIXFMT_PSEUDOCOLOR) ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+ info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+ info->fix.ypanstep = var->yres;
+ return 0;
+}
+
+static int mmpfb_set_par(struct fb_info *info)
+{
+ struct mmpfb_info *fbi = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct mmp_addr addr;
+ struct mmp_win win;
+ struct mmp_mode mode;
+
+ int ret = var_update(info);
+ if (ret != 0)
+ return ret;
+
+ /* set window/path according to new videomode */
+ fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
+ mmp_path_set_mode(fbi->path, &mode);
+
+ memset(&win, 0, sizeof(win));
+ win.xsrc = win.xdst = fbi->mode.xres;
+ win.ysrc = win.ydst = fbi->mode.yres;
+ win.pix_fmt = fbi->pix_fmt;
+ mmp_ovly_set_win(fbi->ovly, &win);
+
+ /* set address always */
+ memset(&addr, 0, sizeof(addr));
+ addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
+ * var->bits_per_pixel / 8 + fbi->fb_start_dma;
+ mmp_ovly_set_addr(fbi->ovly, &addr);
+
+ return 0;
+}
+
+static void mmpfb_power(struct mmpfb_info *fbi, int power)
+{
+ struct mmp_addr addr;
+ struct mmp_win win;
+ struct fb_var_screeninfo *var = &fbi->fb_info->var;
+
+ /* for power on, always set address/window again */
+ if (power) {
+ memset(&win, 0, sizeof(win));
+ win.xsrc = win.xdst = fbi->mode.xres;
+ win.ysrc = win.ydst = fbi->mode.yres;
+ win.pix_fmt = fbi->pix_fmt;
+ mmp_ovly_set_win(fbi->ovly, &win);
+
+ /* set address always */
+ memset(&addr, 0, sizeof(addr));
+ addr.phys[0] = fbi->fb_start_dma +
+ (var->yoffset * var->xres_virtual + var->xoffset)
+ * var->bits_per_pixel / 8;
+ mmp_ovly_set_addr(fbi->ovly, &addr);
+ }
+ mmp_ovly_set_onoff(fbi->ovly, power);
+}
+
+static int mmpfb_blank(int blank, struct fb_info *info)
+{
+ struct mmpfb_info *fbi = info->par;
+
+ mmpfb_power(fbi, (blank = FB_BLANK_UNBLANK));
+
+ return 0;
+}
+
+static struct fb_ops mmpfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_blank = mmpfb_blank,
+ .fb_check_var = mmpfb_check_var,
+ .fb_set_par = mmpfb_set_par,
+ .fb_setcolreg = mmpfb_setcolreg,
+ .fb_pan_display = mmpfb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int __devinit modes_setup(struct mmpfb_info *fbi)
+{
+ struct fb_videomode *videomodes;
+ struct mmp_mode *mmp_modes;
+ struct fb_info *info = fbi->fb_info;
+ int videomode_num, i;
+
+ /* get videomodes from path */
+ videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
+ if (!videomode_num) {
+ dev_warn(fbi->dev, "can't get videomode num\n");
+ return 0;
+ }
+ /* put videomode list to info structure */
+ videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num,
+ GFP_KERNEL);
+ if (!videomodes) {
+ dev_err(fbi->dev, "can't malloc video modes\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < videomode_num; i++)
+ mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
+ fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
+
+ /* set videomode[0] as default mode */
+ memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
+ fbi->output_fmt = mmp_modes[0].pix_fmt_out;
+ fb_videomode_to_var(&info->var, &fbi->mode);
+ mmp_path_set_mode(fbi->path, &mmp_modes[0]);
+
+ kfree(videomodes);
+ return videomode_num;
+}
+
+static int __devinit fb_info_setup(struct fb_info *info,
+ struct mmpfb_info *fbi)
+{
+ int ret = 0;
+ /* Initialise static fb parameters.*/
+ info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
+ FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
+ info->node = -1;
+ strcpy(info->fix.id, fbi->name);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type_aux = 0;
+ info->fix.xpanstep = 0;
+ info->fix.ypanstep = info->var.yres;
+ info->fix.ywrapstep = 0;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_start = fbi->fb_start_dma;
+ info->fix.smem_len = fbi->fb_size;
+ info->fix.visual = (fbi->pix_fmt = PIXFMT_PSEUDOCOLOR) ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
+ info->fix.line_length = info->var.xres_virtual *
+ info->var.bits_per_pixel / 8;
+ info->fbops = &mmpfb_ops;
+ info->pseudo_palette = fbi->pseudo_palette;
+ info->screen_base = fbi->fb_start;
+ info->screen_size = fbi->fb_size;
+
+ /* For FB framework: Allocate color map and Register framebuffer*/
+ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
+ ret = -ENOMEM;
+
+ return ret;
+}
+
+static void __devinit fb_info_clear(struct fb_info *info)
+{
+ fb_dealloc_cmap(&info->cmap);
+}
+
+static int __devinit mmpfb_probe(struct platform_device *pdev)
+{
+ struct mmp_buffer_driver_mach_info *mi;
+ struct fb_info *info = 0;
+ struct mmpfb_info *fbi = 0;
+ int ret, modes_num;
+
+ mi = pdev->dev.platform_data;
+ if (mi = NULL) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ /* initialize fb */
+ info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
+ if (info = NULL)
+ return -ENOMEM;
+ fbi = info->par;
+ if (!fbi) {
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ /* init fb */
+ fbi->fb_info = info;
+ platform_set_drvdata(pdev, fbi);
+ fbi->dev = &pdev->dev;
+ fbi->name = mi->name;
+ fbi->pix_fmt = mi->default_pixfmt;
+ pixfmt_to_var(&info->var, fbi->pix_fmt);
+ mutex_init(&fbi->access_ok);
+
+ /* get display path by name */
+ fbi->path = mmp_get_path(mi->path_name);
+ if (!fbi->path) {
+ dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
+ ret = -EINVAL;
+ goto failed_destroy_mutex;
+ }
+
+ dev_info(fbi->dev, "path %s get\n", fbi->path->name);
+
+ /* get ovly */
+ fbi->ovly = mmp_path_get_ovly(fbi->path, mi->ovly_id);
+ if (!fbi->ovly) {
+ ret = -EINVAL;
+ goto failed_destroy_mutex;
+ }
+ /* set fetch used */
+ mmp_ovly_set_fetch(fbi->ovly, mi->dmafetch_id);
+
+ modes_num = modes_setup(fbi);
+ if (modes_num < 0) {
+ ret = modes_num;
+ goto failed_destroy_mutex;
+ }
+
+ /*
+ * if get modes success, means not hotplug panels, use caculated buffer
+ * or use default size
+ */
+ if (modes_num > 0) {
+ /* fix to 2* yres */
+ info->var.yres_virtual = info->var.yres * 2;
+
+ /* Allocate framebuffer memory: size = modes xy *4 */
+ fbi->fb_size = PAGE_ALIGN(info->var.xres_virtual *
+ info->var.yres_virtual * info->var.bits_per_pixel / 8);
+ } else {
+ fbi->fb_size = MMPFB_DEFAULT_SIZE;
+ }
+
+ fbi->fb_start = alloc_framebuffer(fbi->fb_size + PAGE_SIZE,
+ &fbi->fb_start_dma);
+ if (fbi->fb_start = NULL) {
+ dev_err(&pdev->dev, "can't alloc framebuffer\n");
+ ret = -ENOMEM;
+ goto failed_destroy_mutex;
+ }
+ memset(fbi->fb_start, 0, fbi->fb_size);
+ dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
+
+ /* fb power on */
+ if (modes_num > 0)
+ mmpfb_power(fbi, 1);
+
+ ret = fb_info_setup(info, fbi);
+ if (ret < 0)
+ goto failed_free_buff;
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
+ ret = -ENXIO;
+ goto failed_clear_info;
+ }
+
+ dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
+ info->node, info->fix.id);
+
+#ifdef CONFIG_LOGO
+ if (fbi->fb_start) {
+ fb_prepare_logo(info, 0);
+ fb_show_logo(info, 0);
+ }
+#endif
+
+ return 0;
+
+failed_clear_info:
+ fb_info_clear(info);
+failed_free_buff:
+ vfree(fbi->fb_start);
+failed_destroy_mutex:
+ mutex_destroy(&fbi->access_ok);
+failed:
+ dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
+ platform_set_drvdata(pdev, NULL);
+
+ framebuffer_release(info);
+
+ return ret;
+}
+
+static struct platform_driver mmpfb_driver = {
+ .driver = {
+ .name = "mmp-fb",
+ .owner = THIS_MODULE,
+ },
+ .probe = mmpfb_probe,
+};
+
+static int __devinit mmpfb_init(void)
+{
+ return platform_driver_register(&mmpfb_driver);
+}
+module_init(mmpfb_init);
+
+MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
+MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mmp/fb/mmpfb.h b/drivers/video/mmp/fb/mmpfb.h
new file mode 100644
index 0000000..4b0d2db
--- /dev/null
+++ b/drivers/video/mmp/fb/mmpfb.h
@@ -0,0 +1,54 @@
+/*
+ * linux/drivers/video/mmp/fb/mmpfb.h
+ * Framebuffer driver for Marvell Display controller.
+ *
+ * Copyright (C) 2012 Marvell Technology Group Ltd.
+ * Authors: Zhou Zhu <zzhu3@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 _MMP_FB_H_
+#define _MMP_FB_H_
+
+#include <video/mmp_disp.h>
+#include <linux/fb.h>
+
+/* LCD controller private state. */
+struct mmpfb_info {
+ struct device *dev;
+ int id;
+ const char *name;
+
+ struct fb_info *fb_info;
+ /* basicaly videomode is for output */
+ struct fb_videomode mode;
+ int pix_fmt;
+
+ void *fb_start;
+ int fb_size;
+ dma_addr_t fb_start_dma;
+
+ struct mmp_ovly *ovly;
+ struct mmp_path *path;
+
+ struct mutex access_ok;
+
+ unsigned int pseudo_palette[16];
+ int output_fmt;
+};
+
+#define MMPFB_DEFAULT_SIZE (PAGE_ALIGN(1920 * 1080 * 4 * 2))
+#endif /* _MMP_FB_H_ */
--
1.7.0.4
^ permalink raw reply related
* [PATCHv4 1/9] video: mmp display subsystem
From: Zhou Zhu @ 2012-10-24 6:56 UTC (permalink / raw)
To: linux-fbdev
Added mmp display subsystem to support Marvell MMP display controllers.
This subsystem contains 4 parts:
--fb folder
--core.c
--hw folder
--panel folder
1. fb folder contains implementation of fb.
fb get path and ovly from common interface and operates on these structures.
2. core.c provides common interface for a hardware abstraction.
Major parts of this interface are:
a) Path: path is a output device connected to a panel or HDMI TV.
Main operations of the path is set/get timing/output color.
fb operates output device through path structure.
b) Ovly: Ovly is a buffer shown on the path.
Ovly describes frame buffer and its source/destination size, offset, input
color, buffer address, z-order, and so on.
Each fb device maps to one ovly.
3. hw folder contains implementation of hardware operations defined by core.c.
It registers paths for fb use.
4. panel folder contains implementation of panels.
It's connected to path. Panel drivers would also regiester panels and linked
to path when probe.
Signed-off-by: Zhou Zhu <zzhu3@marvell.com>
Signed-off-by: Lisa Du <cldu@marvell.com>
---
drivers/video/Kconfig | 1 +
drivers/video/Makefile | 1 +
drivers/video/mmp/Kconfig | 5 +
drivers/video/mmp/Makefile | 1 +
drivers/video/mmp/core.c | 217 +++++++++++++++++++++++++++
include/video/mmp_disp.h | 351 ++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 576 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/mmp/Kconfig
create mode 100644 drivers/video/mmp/Makefile
create mode 100644 drivers/video/mmp/core.c
create mode 100644 include/video/mmp_disp.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f74..b71a5c9 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2447,6 +2447,7 @@ config FB_PUV3_UNIGFX
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
source "drivers/video/exynos/Kconfig"
+source "drivers/video/mmp/Kconfig"
source "drivers/video/backlight/Kconfig"
if VT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ee8dafb..6b0ae31 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -106,6 +106,7 @@ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
obj-$(CONFIG_FB_PXA) += pxafb.o
obj-$(CONFIG_FB_PXA168) += pxa168fb.o
obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o
+obj-$(CONFIG_MMP_DISP) += mmp/
obj-$(CONFIG_FB_W100) += w100fb.o
obj-$(CONFIG_FB_TMIO) += tmiofb.o
obj-$(CONFIG_FB_AU1100) += au1100fb.o
diff --git a/drivers/video/mmp/Kconfig b/drivers/video/mmp/Kconfig
new file mode 100644
index 0000000..0554336
--- /dev/null
+++ b/drivers/video/mmp/Kconfig
@@ -0,0 +1,5 @@
+menuconfig MMP_DISP
+ tristate "Marvell MMP Display Subsystem support"
+ depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
+ help
+ Marvell Display Subsystem support.
diff --git a/drivers/video/mmp/Makefile b/drivers/video/mmp/Makefile
new file mode 100644
index 0000000..820eb10
--- /dev/null
+++ b/drivers/video/mmp/Makefile
@@ -0,0 +1 @@
+obj-y += core.o
diff --git a/drivers/video/mmp/core.c b/drivers/video/mmp/core.c
new file mode 100644
index 0000000..51e2e62
--- /dev/null
+++ b/drivers/video/mmp/core.c
@@ -0,0 +1,217 @@
+/*
+ * linux/drivers/video/mmp/common.c
+ * This driver is a common framework for Marvell Display Controller
+ *
+ * Copyright (C) 2012 Marvell Technology Group Ltd.
+ * Authors: Zhou Zhu <zzhu3@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/export.h>
+#include <video/mmp_disp.h>
+
+static struct mmp_ovly *path_get_ovly(struct mmp_path *path,
+ int ovly_id)
+{
+ if (path && ovly_id < path->ovly_num)
+ return &path->ovlys[ovly_id];
+ return 0;
+}
+
+static int path_check_status(struct mmp_path *path)
+{
+ int i;
+ for (i = 0; i < path->ovly_num; i++)
+ if (path->ovlys[i].status)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Get modelist write pointer of modelist.
+ * It also returns modelist number
+ * this function fetches modelist from phy/panel:
+ * for HDMI/parallel or dsi to hdmi cases, get from phy
+ * or get from panel
+ */
+static int path_get_modelist(struct mmp_path *path,
+ struct mmp_mode **modelist)
+{
+ BUG_ON(!path || !modelist);
+
+ if (path->panel && path->panel->get_modelist)
+ return path->panel->get_modelist(path->panel, modelist);
+
+ return 0;
+}
+
+#define list_find(_item, _list, _field, _name)\
+ do {\
+ int found = 0;\
+ list_for_each_entry(_item, &_list, node) {\
+ dev_dbg(_item->dev, "checking %s, target %s",\
+ _item->_field, _name);\
+ if (strcmp(_name, _item->_field) = 0) {\
+ found = 1;\
+ break;\
+ } \
+ } \
+ if (!found)\
+ _item = NULL;\
+ } while (0)
+
+/*
+ * panel list is used to pair panel/path when path/panel registered
+ * path list is used for both buffer driver and platdriver
+ * plat driver do path register/unregister
+ * panel driver do panel register/unregister
+ * buffer driver get registered path
+ */
+static LIST_HEAD(panel_list);
+static LIST_HEAD(path_list);
+static DEFINE_MUTEX(disp_lock);
+
+int mmp_register_panel(struct mmp_panel *panel)
+{
+ struct mmp_path *path;
+
+ mutex_lock(&disp_lock);
+
+ /* add */
+ list_add_tail(&panel->node, &panel_list);
+
+ /* try to register to path */
+ list_find(path, path_list, name, panel->plat_path_name);
+ if (path) {
+ dev_info(panel->dev, "register to path %s\n",
+ panel->plat_path_name);
+ path->panel = panel;
+ }
+
+ mutex_unlock(&disp_lock);
+ return 1;
+}
+EXPORT_SYMBOL_GPL(mmp_register_panel);
+
+void mmp_unregister_panel(struct mmp_panel *panel)
+{
+ mutex_lock(&disp_lock);
+ list_del(&panel->node);
+ mutex_unlock(&disp_lock);
+}
+EXPORT_SYMBOL_GPL(mmp_unregister_panel);
+
+struct mmp_path *mmp_get_path(const char *name)
+{
+ struct mmp_path *path;
+
+ mutex_lock(&disp_lock);
+ list_find(path, path_list, name, name);
+ mutex_unlock(&disp_lock);
+
+ return path;
+}
+EXPORT_SYMBOL_GPL(mmp_get_path);
+
+struct mmp_path *mmp_register_path(struct mmp_path_info *info)
+{
+ int i, size;
+ struct mmp_path *path = NULL;
+ struct mmp_panel *panel;
+
+ size = sizeof(struct mmp_path)
+ + sizeof(struct mmp_ovly) * info->ovly_num;
+ path = kzalloc(size, GFP_KERNEL);
+ if (!path)
+ goto failed;
+
+ /* path set */
+ path->ovlys = (void *)path + sizeof(struct mmp_path);
+ mutex_init(&path->access_ok);
+ path->dev = info->dev;
+ path->id = info->id;
+ path->name = info->name;
+ path->output_type = info->output_type;
+ path->ovly_num = info->ovly_num;
+ path->plat_data = info->plat_data;
+ path->ops.set_mode = info->set_mode;
+
+ mutex_lock(&disp_lock);
+ /* get panel */
+ list_find(panel, panel_list, plat_path_name, info->name);
+ if (panel) {
+ dev_info(path->dev, "get panel %s\n", panel->name);
+ path->panel = panel;
+ }
+
+ dev_info(path->dev, "register %s, ovly_num %d\n",
+ path->name, path->ovly_num);
+
+ /* default op set: if already set by driver, never cover it */
+ if (!path->ops.check_status)
+ path->ops.check_status = path_check_status;
+ if (!path->ops.get_ovly)
+ path->ops.get_ovly = path_get_ovly;
+ if (!path->ops.get_modelist)
+ path->ops.get_modelist = path_get_modelist;
+
+ /* step3: init ovlys */
+ for (i = 0; i < path->ovly_num; i++) {
+ path->ovlys[i].path = path;
+ path->ovlys[i].id = i;
+ mutex_init(&path->ovlys[i].access_ok);
+ path->ovlys[i].ops = info->ovly_ops;
+ }
+
+ /* add to pathlist */
+ list_add_tail(&path->node, &path_list);
+
+ mutex_unlock(&disp_lock);
+ return path;
+
+failed:
+ kfree(path);
+ mutex_unlock(&disp_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mmp_register_path);
+
+void mmp_unregister_path(struct mmp_path *path)
+{
+ int i;
+
+ if (!path)
+ return;
+
+ mutex_lock(&disp_lock);
+ /* del from pathlist */
+ list_del(&path->node);
+
+ /* deinit ovlys */
+ for (i = 0; i < path->ovly_num; i++)
+ mutex_destroy(&path->ovlys[i].access_ok);
+
+ mutex_destroy(&path->access_ok);
+
+ kfree(path);
+ mutex_unlock(&disp_lock);
+
+ dev_info(path->dev, "de-register %s\n", path->name);
+}
+EXPORT_SYMBOL_GPL(mmp_unregister_path);
diff --git a/include/video/mmp_disp.h b/include/video/mmp_disp.h
new file mode 100644
index 0000000..34fbd7c
--- /dev/null
+++ b/include/video/mmp_disp.h
@@ -0,0 +1,351 @@
+/*
+ * linux/include/video/mmp_disp.h
+ * Header file for Marvell MMP Display Controller
+ *
+ * Copyright (C) 2012 Marvell Technology Group Ltd.
+ * Authors: Zhou Zhu <zzhu3@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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 _MMP_DISP_H_
+#define _MMP_DISP_H_
+#include <linux/kthread.h>
+
+enum {
+ PIXFMT_UYVY = 0,
+ PIXFMT_VYUY,
+ PIXFMT_YUYV,
+ PIXFMT_YUV422P,
+ PIXFMT_YVU422P,
+ PIXFMT_YUV420P,
+ PIXFMT_YVU420P,
+ PIXFMT_RGB565 = 0x100,
+ PIXFMT_BGR565,
+ PIXFMT_RGB1555,
+ PIXFMT_BGR1555,
+ PIXFMT_RGB888PACK,
+ PIXFMT_BGR888PACK,
+ PIXFMT_RGB888UNPACK,
+ PIXFMT_BGR888UNPACK,
+ PIXFMT_RGBA888,
+ PIXFMT_BGRA888,
+ PIXFMT_RGB666, /* for output usage */
+ PIXFMT_PSEUDOCOLOR = 0x200,
+};
+
+static inline int pixfmt_to_stride(int pix_fmt)
+{
+ switch (pix_fmt) {
+ case PIXFMT_RGB565:
+ case PIXFMT_BGR565:
+ case PIXFMT_RGB1555:
+ case PIXFMT_BGR1555:
+ case PIXFMT_UYVY:
+ case PIXFMT_VYUY:
+ case PIXFMT_YUYV:
+ return 2;
+ case PIXFMT_RGB888UNPACK:
+ case PIXFMT_BGR888UNPACK:
+ case PIXFMT_RGBA888:
+ case PIXFMT_BGRA888:
+ return 4;
+ case PIXFMT_RGB888PACK:
+ case PIXFMT_BGR888PACK:
+ return 3;
+ case PIXFMT_YUV422P:
+ case PIXFMT_YVU422P:
+ case PIXFMT_YUV420P:
+ case PIXFMT_YVU420P:
+ case PIXFMT_PSEUDOCOLOR:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* parameters used by path/ovly */
+/* ovly related para: win/addr */
+struct mmp_win {
+ /* position/size of window */
+ u16 xsrc;
+ u16 ysrc;
+ u16 xdst;
+ u16 ydst;
+ u16 xpos;
+ u16 ypos;
+ u16 left_crop;
+ u16 right_crop;
+ u16 up_crop;
+ u16 bottom_crop;
+ int pix_fmt;
+};
+
+struct mmp_addr {
+ /* phys address */
+ u32 phys[6];
+};
+
+/* path related para: mode */
+struct mmp_mode {
+ const char *name;
+ u32 refresh;
+ u32 xres;
+ u32 yres;
+ u32 left_margin;
+ u32 right_margin;
+ u32 upper_margin;
+ u32 lower_margin;
+ u32 hsync_len;
+ u32 vsync_len;
+ u32 hsync_invert;
+ u32 vsync_invert;
+ u32 invert_pixclock;
+ u32 pixclock_freq;
+ int pix_fmt_out;
+};
+
+/* main structures */
+struct mmp_path;
+struct mmp_ovly;
+struct mmp_panel;
+
+/* status types */
+enum {
+ mmp_OFF = 0,
+ mmp_ON,
+};
+
+static inline const char *stat_name(int stat)
+{
+ switch (stat) {
+ case mmp_OFF:
+ return "OFF";
+ case mmp_ON:
+ return "ON";
+ default:
+ return "UNKNOWNSTAT";
+ }
+}
+
+struct mmp_ovly_ops {
+ /* should be provided by driver */
+ void (*set_fetch)(struct mmp_ovly *ovly, int fetch_id);
+ void (*set_onoff)(struct mmp_ovly *ovly, int status);
+ void (*set_win)(struct mmp_ovly *ovly, struct mmp_win *win);
+ int (*set_addr)(struct mmp_ovly *ovly, struct mmp_addr *addr);
+};
+
+/* ovly describes a z-order indexed slot in each path. */
+struct mmp_ovly {
+ int id;
+ const char *name;
+ struct mmp_path *path;
+
+ /* ovly info: private data */
+ int dmafetch_id;
+ struct mmp_addr addr;
+ struct mmp_win win;
+
+ /* state */
+ int open_count;
+ int status;
+ struct mutex access_ok;
+
+ struct mmp_ovly_ops *ops;
+};
+
+/* panel type */
+enum {
+ PANELTYPE_Active = 0,
+ PANELTYPE_Smart,
+ PANELTYPE_TV,
+ PANELTYPE_DSI_CMD,
+ PANELTYPE_DSI_VIDEO,
+};
+
+struct mmp_panel {
+ /* use node to register to list */
+ struct list_head node;
+ const char *name;
+ /* path name used to connect to proper path configed */
+ const char *plat_path_name;
+ struct device *dev;
+ int panel_type;
+ void *plat_data;
+ int (*get_modelist)(struct mmp_panel *panel,
+ struct mmp_mode **modelist);
+ void (*set_mode)(struct mmp_panel *panel,
+ struct mmp_mode *mode);
+ void (*set_onoff)(struct mmp_panel *panel,
+ int status);
+};
+
+struct mmp_path_ops {
+ int (*check_status)(struct mmp_path *path);
+ struct mmp_ovly *(*get_ovly)(struct mmp_path *path,
+ int ovly_id);
+ int (*get_modelist)(struct mmp_path *path,
+ struct mmp_mode **modelist);
+
+ /* follow ops should be provided by driver */
+ void (*set_mode)(struct mmp_path *path, struct mmp_mode *mode);
+ void (*set_onoff)(struct mmp_path *path, int status);
+ /* todo: add query */
+};
+
+/* path output types */
+enum {
+ PATH_OUT_PARALLEL,
+ PATH_OUT_DSI,
+ PATH_OUT_HDMI,
+};
+
+/* path is main part of mmp-disp */
+struct mmp_path {
+ /* use node to register to list */
+ struct list_head node;
+
+ /* init data */
+ struct device *dev;
+
+ int id;
+ const char *name;
+ int output_type;
+ struct mmp_panel *panel;
+ void *plat_data;
+
+ /* dynamic use */
+ struct mmp_mode mode;
+
+ /* state */
+ int open_count;
+ int status;
+ struct mutex access_ok;
+
+ struct mmp_path_ops ops;
+
+ /* layers */
+ int ovly_num;
+ struct mmp_ovly *ovlys;
+};
+
+extern struct mmp_path *mmp_get_path(const char *name);
+static inline void mmp_path_set_mode(struct mmp_path *path,
+ struct mmp_mode *mode)
+{
+ if (path)
+ path->ops.set_mode(path, mode);
+}
+static inline void mmp_path_set_onoff(struct mmp_path *path, int status)
+{
+ if (path)
+ path->ops.set_onoff(path, status);
+}
+static inline int mmp_path_get_modelist(struct mmp_path *path,
+ struct mmp_mode **modelist)
+{
+ if (path)
+ return path->ops.get_modelist(path, modelist);
+ return 0;
+}
+static inline struct mmp_ovly *mmp_path_get_ovly(
+ struct mmp_path *path, int ovly_id)
+{
+ if (path)
+ return path->ops.get_ovly(path, ovly_id);
+ return NULL;
+}
+static inline void mmp_ovly_set_fetch(struct mmp_ovly *ovly,
+ int fetch_id)
+{
+ if (ovly)
+ ovly->ops->set_fetch(ovly, fetch_id);
+}
+static inline void mmp_ovly_set_onoff(struct mmp_ovly *ovly, int status)
+{
+ if (ovly)
+ ovly->ops->set_onoff(ovly, status);
+}
+static inline void mmp_ovly_set_win(struct mmp_ovly *ovly,
+ struct mmp_win *win)
+{
+ if (ovly)
+ ovly->ops->set_win(ovly, win);
+}
+static inline int mmp_ovly_set_addr(struct mmp_ovly *ovly,
+ struct mmp_addr *addr)
+{
+ if (ovly)
+ return ovly->ops->set_addr(ovly, addr);
+ return 0;
+}
+
+/*
+ * driver data is set from each detailed ctrl driver for path usage
+ * it defined a common interface that plat driver need to implement
+ */
+struct mmp_path_info {
+ /* driver data, set when registed*/
+ const char *name;
+ struct device *dev;
+ int id;
+ int output_type;
+ int ovly_num;
+ void (*set_mode)(struct mmp_path *path, struct mmp_mode *mode);
+ void (*set_onoff)(struct mmp_path *path, int status);
+ struct mmp_ovly_ops *ovly_ops;
+ void *plat_data;
+};
+
+extern struct mmp_path *mmp_register_path(
+ struct mmp_path_info *info);
+extern void mmp_unregister_path(struct mmp_path *path);
+extern int mmp_register_panel(struct mmp_panel *panel);
+extern void mmp_unregister_panel(struct mmp_panel *panel);
+
+/* defintions for platform data */
+/* interface for buffer driver */
+struct mmp_buffer_driver_mach_info {
+ const char *name;
+ const char *path_name;
+ int ovly_id;
+ int dmafetch_id;
+ int default_pixfmt;
+};
+
+/* interface for controllers driver */
+struct mmp_mach_path_config {
+ const char *name;
+ int ovly_num;
+ int output_type;
+ u32 path_config;
+ u32 link_config;
+};
+
+struct mmp_mach_plat_info {
+ const char *name;
+ const char *clk_name;
+ int path_num;
+ struct mmp_mach_path_config *paths;
+};
+
+/* interface for panel drivers */
+struct mmp_mach_panel_info {
+ const char *name;
+ void (*plat_set_onoff)(int status);
+ const char *plat_path_name;
+};
+#endif /* _MMP_DISP_H_ */
--
1.7.0.4
^ permalink raw reply related
* Re: [PATCH 4/5] xen-fbfront: handle backend CLOSED without CLOSING
From: Florian Tobias Schandinat @ 2012-10-23 4:50 UTC (permalink / raw)
To: Konrad Rzeszutek Wilk; +Cc: David Vrabel, xen-devel, linux-kernel, linux-fbdev
In-Reply-To: <20121019130011.GD26830@phenom.dumpdata.com>
Hi Konrad,
On 10/19/2012 01:00 PM, Konrad Rzeszutek Wilk wrote:
> On Thu, Oct 18, 2012 at 11:03:37AM +0100, David Vrabel wrote:
>> From: David Vrabel <david.vrabel@citrix.com>
>>
>> Backend drivers shouldn't transistion to CLOSED unless the frontend is
>> CLOSED. If a backend does transition to CLOSED too soon then the
>> frontend may not see the CLOSING state and will not properly shutdown.
>>
>> So, treat an unexpected backend CLOSED state the same as CLOSING.
>>
>> Signed-off-by: David Vrabel <david.vrabel@citrix.com>
>> Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
>> ---
>> Cc: linux-fbdev@vger.kernel.org
>> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
>
> Hey Florian,
>
> Should I prep a git pull for you with this or would it be OK
> if I just have your Ack to put this in my git pull for Linus?
Feel free to take it and add
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Best regards,
Florian Tobias Schandinat
>
> Thanks!
>> ---
>> drivers/video/xen-fbfront.c | 5 ++++-
>> 1 files changed, 4 insertions(+), 1 deletions(-)
>>
>> diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
>> index b7f5173..917bb56 100644
>> --- a/drivers/video/xen-fbfront.c
>> +++ b/drivers/video/xen-fbfront.c
>> @@ -641,7 +641,6 @@ static void xenfb_backend_changed(struct xenbus_device *dev,
>> case XenbusStateReconfiguring:
>> case XenbusStateReconfigured:
>> case XenbusStateUnknown:
>> - case XenbusStateClosed:
>> break;
>>
>> case XenbusStateInitWait:
>> @@ -670,6 +669,10 @@ InitWait:
>> info->feature_resize = val;
>> break;
>>
>> + case XenbusStateClosed:
>> + if (dev->state = XenbusStateClosed)
>> + break;
>> + /* Missed the backend's CLOSING state -- fallthrough */
>> case XenbusStateClosing:
>> xenbus_frontend_closed(dev);
>> break;
>> --
>> 1.7.2.5
>
^ permalink raw reply
* Re: [PATCH 1/2 v6] of: add helper to parse display timings
From: Steffen Trumtrar @ 2012-10-22 7:40 UTC (permalink / raw)
To: Thierry Reding
Cc: devicetree-discuss, linux-fbdev, dri-devel, Rob Herring,
Laurent Pinchart, linux-media
In-Reply-To: <20121020195950.GA13902@avionic-0098.mockup.avionic-design.de>
On Sat, Oct 20, 2012 at 09:59:51PM +0200, Thierry Reding wrote:
> On Thu, Oct 04, 2012 at 07:59:19PM +0200, Steffen Trumtrar wrote:
> [...]
> > diff --git a/include/linux/of_display_timings.h b/include/linux/of_display_timings.h
> [...]
> > +struct display_timings {
> > + unsigned int num_timings;
> > + unsigned int default_timing;
> > +
> > + struct signal_timing **timings;
> > +};
> > +
> > +struct timing_entry {
> > + u32 min;
> > + u32 typ;
> > + u32 max;
> > +};
> > +
> > +struct signal_timing {
>
> I'm slightly confused by the naming here. signal_timing seems overly
> generic in this context. Is there any reason why this isn't called
> display_timing or even display_mode?
>
You are right. I actually already changed that, for the same reasons.
It will be called display_timing in the next version, as I think that's what it really
is.
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* Re: [PATCH 2/2 v6] of: add generic videomode description
From: Steffen Trumtrar @ 2012-10-22 7:35 UTC (permalink / raw)
To: Thierry Reding
Cc: devicetree-discuss, linux-fbdev, dri-devel, Rob Herring,
Laurent Pinchart, linux-media
In-Reply-To: <20121020110412.GD12545@avionic-0098.mockup.avionic-design.de>
On Sat, Oct 20, 2012 at 01:04:12PM +0200, Thierry Reding wrote:
> On Thu, Oct 04, 2012 at 07:59:20PM +0200, Steffen Trumtrar wrote:
> [...]
> > diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
> [...]
> > +#if defined(CONFIG_DRM)
>
> This should be:
>
> #if IS_ENABLED(CONFIG_DRM)
>
> or the code below won't be included if DRM is built as a module. But see
> my other replies as to how we can probably handle this better by moving
> this into the DRM subsystem.
>
I already started with moving...now I only need some time to finish with it.
> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> > +{
> > + memset(dmode, 0, sizeof(*dmode));
>
> It appears the usual method to obtain a drm_display_mode to allocate it
> using drm_mode_create(), which will allocate it and associate it with
> the struct drm_device.
>
> Now, if you do a memset() on the structure you'll overwrite a number of
> fields that have previously been initialized and are actually required
> to get everything cleaned up properly later on.
>
> So I think we should remove the call to memset().
>
I was not aware of that. The memset has to go than, of course.
> > +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
> > + int index)
> > +{
> [...]
> > +}
> > +EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
>
> This should be:
>
> EXPORT_SYMBOL_GPL(of_get_fb_videomode);
Oops.
Regrads,
Steffen
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* Re: [PATCH 0/9] OMAPDSS: minor fixes & cleanups
From: Tomi Valkeinen @ 2012-10-22 6:54 UTC (permalink / raw)
To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <5084DE3D.1050502@ti.com>
[-- Attachment #1: Type: text/plain, Size: 1443 bytes --]
On 2012-10-22 08:48, Archit Taneja wrote:
> On Wednesday 17 October 2012 04:50 PM, Tomi Valkeinen wrote:
>> Hi,
>>
>> This series contains minor cleanups and fixes for omapdss.
>
> This series looks fine to me. I have a related cleanup patch which you
> may consider adding to the series.
>
> Thanks,
> Archit
>
> From: Archit Taneja <archit@ti.com>
> Date: Thu, 18 Oct 2012 16:51:57 +0530
> Subject: [PATCH] OMAPDSS: Remove acb and acbi fields from omap_dss_device
>
> acbi and acb fields were meant for passive matrix panels which omapdss
> doesn't
> support any longer. Remove these fields from omap_dss_device struct.
>
> Signed-off-by: Archit Taneja <archit@ti.com>
> ---
> include/video/omapdss.h | 4 ----
> 1 file changed, 4 deletions(-)
>
> diff --git a/include/video/omapdss.h b/include/video/omapdss.h
> index 88c8294..f39e6aa 100644
> --- a/include/video/omapdss.h
> +++ b/include/video/omapdss.h
> @@ -621,10 +621,6 @@ struct omap_dss_device {
> struct {
> struct omap_video_timings timings;
>
> - int acbi; /* ac-bias pin transitions per interrupt */
> - /* Unit: line clocks */
> - int acb; /* ac-bias pin frequency */
> -
> enum omap_dss_dsi_pixel_format dsi_pix_fmt;
> enum omap_dss_dsi_mode dsi_mode;
> struct omap_dss_dsi_videomode_timings dsi_vm_timings;
Thanks, applied to omapdss tree.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply
* Re: [PATCH] OMAPDSS: HDMI: fix missing unlock on error in hdmi_dump_regs()
From: Tomi Valkeinen @ 2012-10-22 6:53 UTC (permalink / raw)
To: Wei Yongjun; +Cc: FlorianSchandinat, yongjun_wei, linux-omap, linux-fbdev
In-Reply-To: <CAPgLHd--5Y4cq3ieoRSueg-i0boqRa4JqRPL7+cJ0UfesunjoQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 957 bytes --]
On 2012-10-21 15:54, Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
>
> Add the missing unlock on the error handling path in function
> hdmi_dump_regs().
>
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> ---
> no test
> ---
> drivers/video/omap2/dss/hdmi.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
> index a48a7dd..8c9b8b3 100644
> --- a/drivers/video/omap2/dss/hdmi.c
> +++ b/drivers/video/omap2/dss/hdmi.c
> @@ -644,8 +644,10 @@ static void hdmi_dump_regs(struct seq_file *s)
> {
> mutex_lock(&hdmi.lock);
>
> - if (hdmi_runtime_get())
> + if (hdmi_runtime_get()) {
> + mutex_unlock(&hdmi.lock);
> return;
> + }
>
> hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s);
> hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
>
Thanks, applied to omapdss tree.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply
* Re: [PATCH] OMAPDSS: HDMI: fix missing unlock on error in hdmi_dump_regs()
From: Sumit Semwal @ 2012-10-22 6:14 UTC (permalink / raw)
To: Wei Yongjun
Cc: tomi.valkeinen, FlorianSchandinat, yongjun_wei, linux-omap,
linux-fbdev
In-Reply-To: <CAPgLHd--5Y4cq3ieoRSueg-i0boqRa4JqRPL7+cJ0UfesunjoQ@mail.gmail.com>
On Sunday 21 October 2012 06:24 PM, Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
>
> Add the missing unlock on the error handling path in function
> hdmi_dump_regs().
>
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
Looks good to me; feel free to add:
Reviewed-by: Sumit Semwal <sumit.semwal@ti.com>
> ---
> no test
> ---
> drivers/video/omap2/dss/hdmi.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
> index a48a7dd..8c9b8b3 100644
> --- a/drivers/video/omap2/dss/hdmi.c
> +++ b/drivers/video/omap2/dss/hdmi.c
> @@ -644,8 +644,10 @@ static void hdmi_dump_regs(struct seq_file *s)
> {
> mutex_lock(&hdmi.lock);
>
> - if (hdmi_runtime_get())
> + if (hdmi_runtime_get()) {
> + mutex_unlock(&hdmi.lock);
> return;
> + }
>
> hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s);
> hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH 0/9] OMAPDSS: minor fixes & cleanups
From: Archit Taneja @ 2012-10-22 5:48 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>
On Wednesday 17 October 2012 04:50 PM, Tomi Valkeinen wrote:
> Hi,
>
> This series contains minor cleanups and fixes for omapdss.
This series looks fine to me. I have a related cleanup patch which you
may consider adding to the series.
Thanks,
Archit
From: Archit Taneja <archit@ti.com>
Date: Thu, 18 Oct 2012 16:51:57 +0530
Subject: [PATCH] OMAPDSS: Remove acb and acbi fields from omap_dss_device
acbi and acb fields were meant for passive matrix panels which omapdss
doesn't
support any longer. Remove these fields from omap_dss_device struct.
Signed-off-by: Archit Taneja <archit@ti.com>
---
include/video/omapdss.h | 4 ----
1 file changed, 4 deletions(-)
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 88c8294..f39e6aa 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -621,10 +621,6 @@ struct omap_dss_device {
struct {
struct omap_video_timings timings;
- int acbi; /* ac-bias pin transitions per interrupt */
- /* Unit: line clocks */
- int acb; /* ac-bias pin frequency */
-
enum omap_dss_dsi_pixel_format dsi_pix_fmt;
enum omap_dss_dsi_mode dsi_mode;
struct omap_dss_dsi_videomode_timings dsi_vm_timings;
--
1.7.9.5
^ permalink raw reply related
* [PATCH] OMAPDSS: HDMI: fix missing unlock on error in hdmi_dump_regs()
From: Wei Yongjun @ 2012-10-21 12:54 UTC (permalink / raw)
To: tomi.valkeinen, FlorianSchandinat; +Cc: yongjun_wei, linux-omap, linux-fbdev
From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
Add the missing unlock on the error handling path in function
hdmi_dump_regs().
Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
no test
---
drivers/video/omap2/dss/hdmi.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index a48a7dd..8c9b8b3 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -644,8 +644,10 @@ static void hdmi_dump_regs(struct seq_file *s)
{
mutex_lock(&hdmi.lock);
- if (hdmi_runtime_get())
+ if (hdmi_runtime_get()) {
+ mutex_unlock(&hdmi.lock);
return;
+ }
hdmi.ip_data.ops->dump_wrapper(&hdmi.ip_data, s);
hdmi.ip_data.ops->dump_pll(&hdmi.ip_data, s);
^ permalink raw reply related
* Re: [PATCH 1/2 v6] of: add helper to parse display timings
From: Thierry Reding @ 2012-10-20 19:59 UTC (permalink / raw)
To: Steffen Trumtrar
Cc: devicetree-discuss, linux-fbdev, dri-devel, Rob Herring,
Laurent Pinchart, linux-media
In-Reply-To: <1349373560-11128-2-git-send-email-s.trumtrar@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 581 bytes --]
On Thu, Oct 04, 2012 at 07:59:19PM +0200, Steffen Trumtrar wrote:
[...]
> diff --git a/include/linux/of_display_timings.h b/include/linux/of_display_timings.h
[...]
> +struct display_timings {
> + unsigned int num_timings;
> + unsigned int default_timing;
> +
> + struct signal_timing **timings;
> +};
> +
> +struct timing_entry {
> + u32 min;
> + u32 typ;
> + u32 max;
> +};
> +
> +struct signal_timing {
I'm slightly confused by the naming here. signal_timing seems overly
generic in this context. Is there any reason why this isn't called
display_timing or even display_mode?
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [RFC 0/5] Generic panel framework
From: Inki Dae @ 2012-10-20 14:22 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-fbdev, dri-devel, linux-leds, linux-media, Bryan Wu,
Richard Purdie, Tomi Valkeinen, Marcus Lorentzon, Sumit Semwal,
Archit Taneja, Sebastien Guiriec, Kyungmin Park
In-Reply-To: <CAAQKjZOZ9+NSQbNkG3qyWh+oLAE1e44DQ_bQCEr4Wvg2WLiGtA@mail.gmail.com>
correct some typo. Sorry for this.
2012/10/20 Inki Dae <inki.dae@samsung.com>:
> Hi Laurent. sorry for being late.
>
> 2012/8/17 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
>> Hi everybody,
>>
>> While working on DT bindings for the Renesas Mobile SoC display controller
>> (a.k.a. LCDC) I quickly realized that display panel implementation based on
>> board code callbacks would need to be replaced by a driver-based panel
>> framework.
>>
>> Several driver-based panel support solution already exist in the kernel.
>>
>> - The LCD device class is implemented in drivers/video/backlight/lcd.c and
>> exposes a kernel API in include/linux/lcd.h. That API is tied to the FBDEV
>> API for historical reason, uses board code callback for reset and power
>> management, and doesn't include support for standard features available in
>> today's "smart panels".
>>
>> - OMAP2+ based systems use custom panel drivers available in
>> drivers/video/omap2/displays. Those drivers are based on OMAP DSS (display
>> controller) specific APIs.
>>
>> - Similarly, Exynos based systems use custom panel drivers available in
>> drivers/video/exynos. Only a single driver (s6e8ax0) is currently available.
>> That driver is based on Exynos display controller specific APIs and on the
>> LCD device class API.
>>
>> I've brought up the issue with Tomi Valkeinen (OMAP DSS maintainer) and Marcus
>> Lorentzon (working on panel support for ST/Linaro), and we agreed that a
>> generic panel framework for display devices is needed. These patches implement
>> a first proof of concept.
>>
>> One of the main reasons for creating a new panel framework instead of adding
>> missing features to the LCD framework is to avoid being tied to the FBDEV
>> framework. Panels will be used by DRM drivers as well, and their API should
>> thus be subsystem-agnostic. Note that the panel framework used the
>> fb_videomode structure in its API, this will be replaced by a common video
>> mode structure shared across subsystems (there's only so many hours per day).
>>
>> Panels, as used in these patches, are defined as physical devices combining a
>> matrix of pixels and a controller capable of driving that matrix.
>>
>> Panel physical devices are registered as children of the control bus the panel
>> controller is connected to (depending on the panel type, we can find platform
>> devices for dummy panels with no control bus, or I2C, SPI, DBI, DSI, ...
>> devices). The generic panel framework matches registered panel devices with
>> panel drivers and call the panel drivers probe method, as done by other device
>> classes in the kernel. The driver probe() method is responsible for
>> instantiating a struct panel instance and registering it with the generic
>> panel framework.
>>
>> Display drivers are panel consumers. They register a panel notifier with the
>> framework, which then calls the notifier when a matching panel is registered.
>> The reason for this asynchronous mode of operation, compared to how drivers
>> acquire regulator or clock resources, is that the panel can use resources
>> provided by the display driver. For instance a panel can be a child of the DBI
>> or DSI bus controlled by the display device, or use a clock provided by that
>> device. We can't defer the display device probe until the panel is registered
>> and also defer the panel device probe until the display is registered. As
>> most display drivers need to handle output devices hotplug (HDMI monitors for
>> instance), handling panel through a notification system seemed to be the
>> easiest solution.
>>
>> Note that this brings a different issue after registration, as display and
>> panel drivers would take a reference to each other. Those circular references
>> would make driver unloading impossible. I haven't found a good solution for
>> that problem yet (hence the RFC state of those patches), and I would
>> appreciate your input here. This might also be a hint that the framework
>> design is wrong to start with. I guess I can't get everything right on the
>> first try ;-)
>>
>> Getting hold of the panel is the most complex part. Once done, display drivers
>> can call abstract operations provided by panel drivers to control the panel
>> operation. These patches implement three of those operations (enable, start
>> transfer and get modes). More operations will be needed, and those three
>> operations will likely get modified during review. Most of the panels on
>> devices I own are dumb panels with no control bus, and are thus not the best
>> candidates to design a framework that needs to take complex panels' needs into
>> account.
>>
>> In addition to the generic panel core, I've implemented MIPI DBI (Display Bus
>> Interface, a parallel bus for panels that supports read/write transfers of
>> commands and data) bus support, as well as three panel drivers (dummy panels
>> with no control bus, and Renesas R61505- and R61517-based panels, both using
>> DBI as their control bus). Only the dummy panel driver has been tested as I
>> lack hardware for the two other drivers.
>>
>> I will appreciate all reviews, comments, criticisms, ideas, remarks, ... If
>> you can find a clever way to solve the cyclic references issue described above
>> I'll buy you a beer at the next conference we will both attend. If you think
>> the proposed solution is too complex, or too simple, I'm all ears. I
>> personally already feel that we might need something even more generic to
>> support other kinds of external devices connected to display controllers, such
>> as external DSI to HDMI converters for instance. Some kind of video entity
>> exposing abstract operations like the panels do would make sense, in which
>> case panels would "inherit" from that video entity.
>>
>> Speaking of conferences, I will attend the KS/LPC in San Diego in a bit more
>> than a week, and would be happy to discuss this topic face to face there.
>>
>> Laurent Pinchart (5):
>> video: Add generic display panel core
>> video: panel: Add dummy panel support
>> video: panel: Add MIPI DBI bus support
>> video: panel: Add R61505 panel support
>> video: panel: Add R61517 panel support
>
> how about using 'buses' directory instead of 'panel' and adding
s/buses/busses
> 'panel' under that like below?
> video/buess: display bus frameworks such as MIPI-DBI/DSI and eDP are placed.
> video/buess/panel: panel drivers based on display bus-based drivers are placed.
video/busses: display bus frameworks such as MIPI-DBI/DSI and eDP are placed.
video/busses/panel: panel drivers based on display bus-based drivers are placed.
>
> I think MIPI-DBI(Display Bus Interface)/DSI(Display Serial Interface)
> and eDP are the bus interfaces for display controllers such as
> DISC(OMAP SoC) and FIMC(Exynos SoC).
>
> Thanks,
> Inki Dae
>
>>
>> drivers/video/Kconfig | 1 +
>> drivers/video/Makefile | 1 +
>> drivers/video/panel/Kconfig | 37 +++
>> drivers/video/panel/Makefile | 5 +
>> drivers/video/panel/panel-dbi.c | 217 +++++++++++++++
>> drivers/video/panel/panel-dummy.c | 103 +++++++
>> drivers/video/panel/panel-r61505.c | 520 ++++++++++++++++++++++++++++++++++++
>> drivers/video/panel/panel-r61517.c | 408 ++++++++++++++++++++++++++++
>> drivers/video/panel/panel.c | 269 +++++++++++++++++++
>> include/video/panel-dbi.h | 92 +++++++
>> include/video/panel-dummy.h | 25 ++
>> include/video/panel-r61505.h | 27 ++
>> include/video/panel-r61517.h | 28 ++
>> include/video/panel.h | 111 ++++++++
>> 14 files changed, 1844 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/video/panel/Kconfig
>> create mode 100644 drivers/video/panel/Makefile
>> create mode 100644 drivers/video/panel/panel-dbi.c
>> create mode 100644 drivers/video/panel/panel-dummy.c
>> create mode 100644 drivers/video/panel/panel-r61505.c
>> create mode 100644 drivers/video/panel/panel-r61517.c
>> create mode 100644 drivers/video/panel/panel.c
>> create mode 100644 include/video/panel-dbi.h
>> create mode 100644 include/video/panel-dummy.h
>> create mode 100644 include/video/panel-r61505.h
>> create mode 100644 include/video/panel-r61517.h
>> create mode 100644 include/video/panel.h
>>
>> --
>> Regards,
>>
>> Laurent Pinchart
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC 0/5] Generic panel framework
From: Inki Dae @ 2012-10-20 14:18 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: Laurent Pinchart, linux-fbdev, Marcus Lorentzon, dri-devel,
Kyungmin Park, Richard Purdie, Sebastien Guiriec, Bryan Wu,
linux-leds, linux-media
In-Reply-To: <1345192694.3158.49.camel@deskari>
Hi Tomi,
2012/8/17 Tomi Valkeinen <tomi.valkeinen@ti.com>:
> Hi,
>
> On Fri, 2012-08-17 at 02:49 +0200, Laurent Pinchart wrote:
>
>> I will appreciate all reviews, comments, criticisms, ideas, remarks, ... If
>
> Oookay, where to start... ;)
>
> A few cosmetic/general comments first.
>
> I find the file naming a bit strange. You have panel.c, which is the
> core framework, panel-dbi.c, which is the DBI bus, panel-r61517.c, which
> is driver for r61517 panel...
>
> Perhaps something in this direction (in order): panel-core.c,
> mipi-dbi-bus.c, panel-r61517.c? And we probably end up with quite a lot
> of panel drivers, perhaps we should already divide these into separate
> directories, and then we wouldn't need to prefix each panel with
> "panel-" at all.
>
> ---
>
> Should we aim for DT only solution from the start? DT is the direction
> we are going, and I feel the older platform data stuff would be
> deprecated soon.
>
> ---
>
> Something missing from the intro is how this whole thing should be used.
> It doesn't help if we know how to turn on the panel, we also need to
> display something on it =). So I think some kind of diagram/example of
> how, say, drm would use this thing, and also how the SoC specific DBI
> bus driver would be done, would clarify things.
>
> ---
>
> We have discussed face to face about the different hardware setups and
> scenarios that we should support, but I'll list some of them here for
> others:
>
> 1) We need to support chains of external display chips and panels. A
> simple example is a chip that takes DSI in, and outputs DPI. In that
> case we'd have a chain of SoC -> DSI2DPI -> DPI panel.
>
> In final products I think two external devices is the maximum (at least
> I've never seen three devices in a row), but in theory and in
> development environments the chain can be arbitrarily long. Also the
> connections are not necessarily 1-to-1, but a device can take one input
> while it has two outputs, or a device can take two inputs.
>
> Now, I think two external devices is a must requirement. I'm not sure if
> supporting more is an important requirement. However, if we support two
> devices, it could be that it's trivial to change the framework to
> support n devices.
>
> 2) Panels and display chips are all but standard. They very often have
> their own sequences how to do things, have bugs, or implement some
> feature in slightly different way than some other panel. This is why the
> panel driver should be able to control or define the way things happen.
>
> As an example, Sharp LQ043T1DG01 panel
> (www.sharpsme.com/download/LQ043T1DG01-SP-072106pdf). It is enabled with
> the following sequence:
>
> - Enable VCC and AVDD regulators
> - Wait min 50ms
> - Enable full video stream (pck, syncs, pixels) from SoC
> - Wait min 0.5ms
> - Set DISP GPIO, which turns on the display panel
>
> Here we could split the enabling of panel to two parts, prepare (in this
> case starts regulators and waits 50ms) and finish (wait 0.5ms and set
> DISP GPIO), and the upper layer would start the video stream in between.
>
> I realize this could be done with the PANEL_ENABLE_* levels in your RFC,
> but I don't think the concepts quite match:
>
> - PANEL_ENABLE_BLANK level is needed for "smart panels", as we need to
> configure them and send the initial frame at that operating level. With
The smart panel means command mode way(same as cpu mode)? This panel
includes framebuffer internally and needs triggering from Display
controller to update a new frame on that internal framebuffer. I think
we also need this trigger interface.
Thanks,
Inki Dae
> dummy panels there's really no such level, there's just one enable
> sequence that is always done right away.
>
> - I find waiting at the beginning of a function very ugly (what are we
> waiting for?) and we'd need that when changing the panel to
> PANEL_ENABLE_ON level.
>
> - It's still limited if the panel is a stranger one (see following
> example).
>
> Consider the following theoretical panel enable example, taken to absurd
> level just to show the general problem:
>
> - Enable regulators
> - Enable video stream
> - Wait 50ms
> - Disable video stream
> - Set enable GPIO
> - Enable video stream
>
> This one would be rather impossible with the upper layer handling the
> enabling of the video stream. Thus I see that the panel driver needs to
> control the sequences, and the Sharp panel driver's enable would look
> something like:
>
> regulator_enable(...);
> sleep();
> dpi_enable_video();
> sleep();
> gpip_set(..);
>
> Note that even with this model we still need the PANEL_ENABLE levels you
> have.
>
> ---
>
> I'm not sure I understand the panel unload problem you mentioned. Nobody
> should have direct references to the panel functions, so there shouldn't
> be any automatic references that would prevent module unloading. So when
> the user does rmmod panel-mypanel, the panel driver's remove will be
> called. It'll unregister itself from the panel framework, which causes
> notifications and the display driver will stop using the panel. After
> that nobody has pointers to the panel, and it can safely be unloaded.
>
> It could cause some locking issues, though. First the panel's remove
> could take a lock, but the remove sequence would cause the display
> driver to call disable on the panel, which could again try to take the
> same lock...
>
> Tomi
>
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
^ permalink raw reply
* Re: [RFC 0/5] Generic panel framework
From: Inki Dae @ 2012-10-20 13:10 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-fbdev, dri-devel, linux-leds, linux-media, Bryan Wu,
Richard Purdie, Tomi Valkeinen, Marcus Lorentzon, Sumit Semwal,
Archit Taneja, Sebastien Guiriec, Kyungmin Park
In-Reply-To: <1345164583-18924-1-git-send-email-laurent.pinchart@ideasonboard.com>
Hi Laurent. sorry for being late.
2012/8/17 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
> Hi everybody,
>
> While working on DT bindings for the Renesas Mobile SoC display controller
> (a.k.a. LCDC) I quickly realized that display panel implementation based on
> board code callbacks would need to be replaced by a driver-based panel
> framework.
>
> Several driver-based panel support solution already exist in the kernel.
>
> - The LCD device class is implemented in drivers/video/backlight/lcd.c and
> exposes a kernel API in include/linux/lcd.h. That API is tied to the FBDEV
> API for historical reason, uses board code callback for reset and power
> management, and doesn't include support for standard features available in
> today's "smart panels".
>
> - OMAP2+ based systems use custom panel drivers available in
> drivers/video/omap2/displays. Those drivers are based on OMAP DSS (display
> controller) specific APIs.
>
> - Similarly, Exynos based systems use custom panel drivers available in
> drivers/video/exynos. Only a single driver (s6e8ax0) is currently available.
> That driver is based on Exynos display controller specific APIs and on the
> LCD device class API.
>
> I've brought up the issue with Tomi Valkeinen (OMAP DSS maintainer) and Marcus
> Lorentzon (working on panel support for ST/Linaro), and we agreed that a
> generic panel framework for display devices is needed. These patches implement
> a first proof of concept.
>
> One of the main reasons for creating a new panel framework instead of adding
> missing features to the LCD framework is to avoid being tied to the FBDEV
> framework. Panels will be used by DRM drivers as well, and their API should
> thus be subsystem-agnostic. Note that the panel framework used the
> fb_videomode structure in its API, this will be replaced by a common video
> mode structure shared across subsystems (there's only so many hours per day).
>
> Panels, as used in these patches, are defined as physical devices combining a
> matrix of pixels and a controller capable of driving that matrix.
>
> Panel physical devices are registered as children of the control bus the panel
> controller is connected to (depending on the panel type, we can find platform
> devices for dummy panels with no control bus, or I2C, SPI, DBI, DSI, ...
> devices). The generic panel framework matches registered panel devices with
> panel drivers and call the panel drivers probe method, as done by other device
> classes in the kernel. The driver probe() method is responsible for
> instantiating a struct panel instance and registering it with the generic
> panel framework.
>
> Display drivers are panel consumers. They register a panel notifier with the
> framework, which then calls the notifier when a matching panel is registered.
> The reason for this asynchronous mode of operation, compared to how drivers
> acquire regulator or clock resources, is that the panel can use resources
> provided by the display driver. For instance a panel can be a child of the DBI
> or DSI bus controlled by the display device, or use a clock provided by that
> device. We can't defer the display device probe until the panel is registered
> and also defer the panel device probe until the display is registered. As
> most display drivers need to handle output devices hotplug (HDMI monitors for
> instance), handling panel through a notification system seemed to be the
> easiest solution.
>
> Note that this brings a different issue after registration, as display and
> panel drivers would take a reference to each other. Those circular references
> would make driver unloading impossible. I haven't found a good solution for
> that problem yet (hence the RFC state of those patches), and I would
> appreciate your input here. This might also be a hint that the framework
> design is wrong to start with. I guess I can't get everything right on the
> first try ;-)
>
> Getting hold of the panel is the most complex part. Once done, display drivers
> can call abstract operations provided by panel drivers to control the panel
> operation. These patches implement three of those operations (enable, start
> transfer and get modes). More operations will be needed, and those three
> operations will likely get modified during review. Most of the panels on
> devices I own are dumb panels with no control bus, and are thus not the best
> candidates to design a framework that needs to take complex panels' needs into
> account.
>
> In addition to the generic panel core, I've implemented MIPI DBI (Display Bus
> Interface, a parallel bus for panels that supports read/write transfers of
> commands and data) bus support, as well as three panel drivers (dummy panels
> with no control bus, and Renesas R61505- and R61517-based panels, both using
> DBI as their control bus). Only the dummy panel driver has been tested as I
> lack hardware for the two other drivers.
>
> I will appreciate all reviews, comments, criticisms, ideas, remarks, ... If
> you can find a clever way to solve the cyclic references issue described above
> I'll buy you a beer at the next conference we will both attend. If you think
> the proposed solution is too complex, or too simple, I'm all ears. I
> personally already feel that we might need something even more generic to
> support other kinds of external devices connected to display controllers, such
> as external DSI to HDMI converters for instance. Some kind of video entity
> exposing abstract operations like the panels do would make sense, in which
> case panels would "inherit" from that video entity.
>
> Speaking of conferences, I will attend the KS/LPC in San Diego in a bit more
> than a week, and would be happy to discuss this topic face to face there.
>
> Laurent Pinchart (5):
> video: Add generic display panel core
> video: panel: Add dummy panel support
> video: panel: Add MIPI DBI bus support
> video: panel: Add R61505 panel support
> video: panel: Add R61517 panel support
how about using 'buses' directory instead of 'panel' and adding
'panel' under that like below?
video/buess: display bus frameworks such as MIPI-DBI/DSI and eDP are placed.
video/buess/panel: panel drivers based on display bus-based drivers are placed.
I think MIPI-DBI(Display Bus Interface)/DSI(Display Serial Interface)
and eDP are the bus interfaces for display controllers such as
DISC(OMAP SoC) and FIMC(Exynos SoC).
Thanks,
Inki Dae
>
> drivers/video/Kconfig | 1 +
> drivers/video/Makefile | 1 +
> drivers/video/panel/Kconfig | 37 +++
> drivers/video/panel/Makefile | 5 +
> drivers/video/panel/panel-dbi.c | 217 +++++++++++++++
> drivers/video/panel/panel-dummy.c | 103 +++++++
> drivers/video/panel/panel-r61505.c | 520 ++++++++++++++++++++++++++++++++++++
> drivers/video/panel/panel-r61517.c | 408 ++++++++++++++++++++++++++++
> drivers/video/panel/panel.c | 269 +++++++++++++++++++
> include/video/panel-dbi.h | 92 +++++++
> include/video/panel-dummy.h | 25 ++
> include/video/panel-r61505.h | 27 ++
> include/video/panel-r61517.h | 28 ++
> include/video/panel.h | 111 ++++++++
> 14 files changed, 1844 insertions(+), 0 deletions(-)
> create mode 100644 drivers/video/panel/Kconfig
> create mode 100644 drivers/video/panel/Makefile
> create mode 100644 drivers/video/panel/panel-dbi.c
> create mode 100644 drivers/video/panel/panel-dummy.c
> create mode 100644 drivers/video/panel/panel-r61505.c
> create mode 100644 drivers/video/panel/panel-r61517.c
> create mode 100644 drivers/video/panel/panel.c
> create mode 100644 include/video/panel-dbi.h
> create mode 100644 include/video/panel-dummy.h
> create mode 100644 include/video/panel-r61505.h
> create mode 100644 include/video/panel-r61517.h
> create mode 100644 include/video/panel.h
>
> --
> Regards,
>
> Laurent Pinchart
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 0/2 v6] of: add display helper
From: Thierry Reding @ 2012-10-20 11:35 UTC (permalink / raw)
To: Leela Krishna Amudala, linux-fbdev, devicetree-discuss, dri-devel,
Tomi Valkeinen, Laurent Pinchart, linux-media
In-Reply-To: <20121015141751.GA11396@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 705 bytes --]
On Mon, Oct 15, 2012 at 04:17:51PM +0200, Steffen Trumtrar wrote:
> Hi Leela,
>
> On Mon, Oct 15, 2012 at 04:24:43PM +0530, Leela Krishna Amudala wrote:
> > Hello Steffen,
> >
> > To which version of the kernel we can expect this patch set to be merged into?
> > Because I'm waiting for this from long time to add DT support for my
> > display controller :)
> >
>
> I have no idea, sorry. It seems like we have almost settled with the binding
> (clock-name needs to be changed), but I'm not responsible for any merging/inclusions
> in the kernel.
I want to use this in the Tegra DRM driver which I hope to get into 3.8.
If you need any help with this, please let me know.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2 v6] of: add generic videomode description
From: Thierry Reding @ 2012-10-20 11:04 UTC (permalink / raw)
To: Steffen Trumtrar
Cc: devicetree-discuss, linux-fbdev, dri-devel, Rob Herring,
Laurent Pinchart, linux-media
In-Reply-To: <1349373560-11128-3-git-send-email-s.trumtrar@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 1160 bytes --]
On Thu, Oct 04, 2012 at 07:59:20PM +0200, Steffen Trumtrar wrote:
[...]
> diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
[...]
> +#if defined(CONFIG_DRM)
This should be:
#if IS_ENABLED(CONFIG_DRM)
or the code below won't be included if DRM is built as a module. But see
my other replies as to how we can probably handle this better by moving
this into the DRM subsystem.
> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> +{
> + memset(dmode, 0, sizeof(*dmode));
It appears the usual method to obtain a drm_display_mode to allocate it
using drm_mode_create(), which will allocate it and associate it with
the struct drm_device.
Now, if you do a memset() on the structure you'll overwrite a number of
fields that have previously been initialized and are actually required
to get everything cleaned up properly later on.
So I think we should remove the call to memset().
> +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
> + int index)
> +{
[...]
> +}
> +EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
This should be:
EXPORT_SYMBOL_GPL(of_get_fb_videomode);
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2 v6] of: add helper to parse display timings
From: Thierry Reding @ 2012-10-20 10:58 UTC (permalink / raw)
To: Steffen Trumtrar
Cc: devicetree-discuss, linux-fbdev, dri-devel, Rob Herring,
Laurent Pinchart, linux-media
In-Reply-To: <1349373560-11128-2-git-send-email-s.trumtrar@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 905 bytes --]
On Thu, Oct 04, 2012 at 07:59:19PM +0200, Steffen Trumtrar wrote:
[...]
> diff --git a/include/linux/of_display_timings.h b/include/linux/of_display_timings.h
> new file mode 100644
> index 0000000..1ad719e
> --- /dev/null
> +++ b/include/linux/of_display_timings.h
> @@ -0,0 +1,85 @@
> +/*
> + * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
> + *
> + * description of display timings
> + *
> + * This file is released under the GPLv2
> + */
> +
> +#ifndef __LINUX_OF_DISPLAY_TIMINGS_H
> +#define __LINUX_OF_DISPLAY_TIMINGS_H
This file needs to include linux/slab.h because it uses kfree() in the
inline functions. Alternatively I think I'd rather see the inline
functions moved out of the header, with the exception of the
signal_timing_get_value() function perhaps.
Moreover there should be a forward declaration of struct display_node
to avoid the need to include linux/of.h.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2 v6] of: add generic videomode description
From: Thierry Reding @ 2012-10-20 10:54 UTC (permalink / raw)
To: Laurent Pinchart, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
Rob Herring, linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
linux-media-u79uwXL29TY76Z2rM5mHXA, Tomi Valkeinen
In-Reply-To: <20121009072608.GA2519-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 1676 bytes --]
On Tue, Oct 09, 2012 at 09:26:08AM +0200, Steffen Trumtrar wrote:
> Hi Laurent,
>
> On Mon, Oct 08, 2012 at 10:52:04PM +0200, Laurent Pinchart wrote:
> > Hi Steffen,
> >
> > On Monday 08 October 2012 14:48:01 Steffen Trumtrar wrote:
> > > On Mon, Oct 08, 2012 at 02:13:50PM +0200, Laurent Pinchart wrote:
> > > > On Thursday 04 October 2012 19:59:20 Steffen Trumtrar wrote:
[...]
> > > > > +int of_get_videomode(struct device_node *np, struct videomode *vm, int
> > > > > index)
> > > >
> > > > I wonder how to avoid abuse of this functions. It's a useful helper for
> > > > drivers that need to get a video mode once only, but would result in lower
> > > > performances if a driver calls it for every mode. Drivers must call
> > > > of_get_display_timing_list instead in that case and case the display
> > > > timings. I'm wondering whether we should really expose of_get_videomode.
> > >
> > > The intent was to let the driver decide. That way all the other overhead may
> > > be skipped.
> >
> > My point is that driver writers might just call of_get_videomode() in a loop,
> > not knowing that it's expensive. I want to avoid that. We need to at least add
> > kerneldoc to the function stating that this shouldn't be done.
> >
>
> You're right. That should be made clear in the code.
In that case we should export videomode_from_timing(). For Tegra DRM I
wrote a small utility function that takes a struct display_timings and
converts every timing to a struct videomode which is then converted to
a struct drm_display_mode and added to the DRM connector. The code is
really generic and could be part of the DRM core.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH 2/2 v6] of: add generic videomode description
From: Thierry Reding @ 2012-10-20 10:45 UTC (permalink / raw)
To: Laurent Pinchart
Cc: Steffen Trumtrar, linux-fbdev, Stephen Warren, devicetree-discuss,
dri-devel, linux-media
In-Reply-To: <1865954.iaNNHHSnLW@avalon>
[-- Attachment #1: Type: text/plain, Size: 1921 bytes --]
On Sun, Oct 07, 2012 at 03:38:33PM +0200, Laurent Pinchart wrote:
> Hi Steffen,
>
> On Friday 05 October 2012 17:51:21 Steffen Trumtrar wrote:
> > On Thu, Oct 04, 2012 at 12:51:00PM -0600, Stephen Warren wrote:
> > > On 10/04/2012 11:59 AM, Steffen Trumtrar wrote:
> > > > Get videomode from devicetree in a format appropriate for the
> > > > backend. drm_display_mode and fb_videomode are supported atm.
> > > > Uses the display signal timings from of_display_timings
> > > >
> > > > +++ b/drivers/of/of_videomode.c
> > > >
> > > > +int videomode_from_timing(struct display_timings *disp, struct
> > > > videomode *vm,
> > > >
> > > > + st = display_timings_get(disp, index);
> > > > +
> > > > + if (!st) {
> > >
> > > It's a little odd to leave a blank line between those two lines.
> >
> > Hm, well okay. That can be remedied
> >
> > > Only half of the code in this file seems OF-related; the routines to
> > > convert a timing to a videomode or drm display mode seem like they'd be
> > > useful outside device tree, so I wonder if putting them into
> > > of_videomode.c is the correct thing to do. Still, it's probably not a
> > > big deal.
> >
> > I am not sure, what the appropriate way to do this is. I can split it up
> > (again).
>
> I think it would make sense to move them to their respective subsystems.
I agree. While looking at integrating this for Tegra DRM, I came across
the issue that if I build DRM as a module, linking with this code will
fail. The reason for that was that it was that the code, itself builtin,
uses drm_mode_set_name(), which would be exported by the drm module. So
I had to modifiy the Kconfig entries to be "def_tristate DRM". That
obviously isn't very nice since the code can also be used without DRM.
Moving the subsystem specific conversion routines to the respective
subsystems should solve any of these issues.
Thierry
[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v7 2/3] pwm_backlight: use power sequences
From: Stephen Warren @ 2012-10-19 16:00 UTC (permalink / raw)
To: Tony Prisk
Cc: Alexandre Courbot, linux-fbdev, Mark Brown, Stephen Warren,
linux-pm, Leela Krishna Amudala, linux-doc, linux-kernel,
Rob Herring, Anton Vorontsov, linux-tegra, David Woodhouse,
devicetree-discuss
In-Reply-To: <1350638436.3339.2.camel@gitbox>
On 10/19/2012 03:20 AM, Tony Prisk wrote:
> On Fri, 2012-10-19 at 18:06 +0900, Alexandre Courbot wrote:
>> Make use of the power sequences specified in the device tree or platform
>> data to control how the backlight is powered on and off.
Tony, please do cut down the amount of the patch that you quote. If you
don't, it's impossible to find your comments. Thanks.
^ permalink raw reply
* Re: [PATCH 4/5] xen-fbfront: handle backend CLOSED without CLOSING
From: Konrad Rzeszutek Wilk @ 2012-10-19 13:00 UTC (permalink / raw)
To: David Vrabel, FlorianSchandinat; +Cc: xen-devel, linux-kernel, linux-fbdev
In-Reply-To: <1350554618-14582-4-git-send-email-david.vrabel@citrix.com>
On Thu, Oct 18, 2012 at 11:03:37AM +0100, David Vrabel wrote:
> From: David Vrabel <david.vrabel@citrix.com>
>
> Backend drivers shouldn't transistion to CLOSED unless the frontend is
> CLOSED. If a backend does transition to CLOSED too soon then the
> frontend may not see the CLOSING state and will not properly shutdown.
>
> So, treat an unexpected backend CLOSED state the same as CLOSING.
>
> Signed-off-by: David Vrabel <david.vrabel@citrix.com>
> Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> ---
> Cc: linux-fbdev@vger.kernel.org
> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Hey Florian,
Should I prep a git pull for you with this or would it be OK
if I just have your Ack to put this in my git pull for Linus?
Thanks!
> ---
> drivers/video/xen-fbfront.c | 5 ++++-
> 1 files changed, 4 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
> index b7f5173..917bb56 100644
> --- a/drivers/video/xen-fbfront.c
> +++ b/drivers/video/xen-fbfront.c
> @@ -641,7 +641,6 @@ static void xenfb_backend_changed(struct xenbus_device *dev,
> case XenbusStateReconfiguring:
> case XenbusStateReconfigured:
> case XenbusStateUnknown:
> - case XenbusStateClosed:
> break;
>
> case XenbusStateInitWait:
> @@ -670,6 +669,10 @@ InitWait:
> info->feature_resize = val;
> break;
>
> + case XenbusStateClosed:
> + if (dev->state = XenbusStateClosed)
> + break;
> + /* Missed the backend's CLOSING state -- fallthrough */
> case XenbusStateClosing:
> xenbus_frontend_closed(dev);
> break;
> --
> 1.7.2.5
^ permalink raw reply
* Re: [Qemu-devel] [PATCH] add bochs dispi interface framebuffer driver
From: Vasilis Liaskovitis @ 2012-10-19 10:35 UTC (permalink / raw)
To: Gerd Hoffmann; +Cc: linux-fbdev, qemu-devel
In-Reply-To: <1331201626-23402-1-git-send-email-kraxel@redhat.com>
Hi,
On Thu, Mar 08, 2012 at 11:13:46AM +0100, Gerd Hoffmann wrote:
> This patchs adds a frame buffer driver for (virtual/emulated) vga cards
> implementing the bochs dispi interface. Supported hardware are the
> bochs vga card with vbe extension and the qemu standard vga.
>
> The driver uses a fixed depth of 32bpp. Otherwise it supports the full
> (but small) feature set of the bochs dispi interface: Resolution
> switching and display panning. It is tweaked to maximize fbcon speed,
> so you'll get the comfort of the framebuffer console in kvm guests
> without performance penalty.
I am testing this driver with qemu-kvm-1.2 or qemu-kvm master (commit)
and "-std vga". The driver works fine in general.
When I test a guest that runs X (ubuntu-12.04 desktop amd64), sometimes parts of
the screen and keyboard input is mixed between the X terminal and fbconsole
terminals. This happens only on the initial X11 login (right after boot or
reboot) and only sometimes.
During this time, there is a second keyboard cursor at top of the screen on the
X11 login. When switching to an fbconsole (ctrl+alt+f1), screen output of the X11
login screen gets mixed with fbconsole screen. And vice-versa when I go back to the
X11 terminal(I can send you 2 screendumps if needed, I haven't attached them here
due to size)
If I try to login (pressing enter), the X11-login is redrawn and from then on vt
switching works with no problems (I have to retype login, I am not sure where the
original keyboard input goes to)
Xorg driver used is fbdev (i can send xorg log), not sure if another driver
should be used/implemented for the bochsfb.
According to "xrandr -q" same resolution as bochsfb is used:
Screen 0: minimum 1024 x 768, current 1024 x 768, maximum 1024 x 768
1024x768 116.0*
"fbset -i" output is as expected:
mode "1024x768-116"
# D: 100.000 MHz, H: 93.985 kHz, V: 116.318 Hz
geometry 1024 768 1024 4096 32
timings 10000 16 16 16 16 8 8
rgba 8/16,8/8,8/0,8/24
endmode
Frame buffer device information:
Name : bochsfb
Address : 0xfd000000
Size : 16777216
Type : PACKED PIXELS
Visual : TRUECOLOR
XPanStep : 1
YPanStep : 1
YWrapStep : 0
LineLength : 4096
Accelerator : No
Some framebuffer-relevant guest kernel options used:
CONFIG_FB_BOOT_VESA_SUPPORT=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
CONFIG_FB_DEFERRED_IO=y
#
# Frame buffer hardware drivers
#
CONFIG_FB_BOCHS=m
CONFIG_FB_VESA=y
# CONFIG_FB_EFI is not set
Should FB_VESA be turned to "not set" for this test? (it's not tristate in Kconfig)
Btw (slightly off-topic) are other framebuffer drivers suitable for the
standard qemu vga-pci device? Would vesafb or uvesafb work?
I haven't been able to load uvesafb in a guest, because the userspace helper
program v86d segfaults (maybe it tries to access vga ioports that are not
implemented in qemu?)
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
> drivers/video/Kconfig | 18 +++
> drivers/video/Makefile | 1 +
> drivers/video/bochsfb.c | 385 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 404 insertions(+), 0 deletions(-)
> create mode 100644 drivers/video/bochsfb.c
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 6ca0c40..4d21f90 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -286,6 +286,24 @@ config FB_CIRRUS
> Say N unless you have such a graphics board or plan to get one
> before you next recompile the kernel.
>
> +config FB_BOCHS
> + tristate "Bochs dispi interface support"
> + depends on FB && PCI
> + select FB_CFB_FILLRECT
> + select FB_CFB_COPYAREA
> + select FB_CFB_IMAGEBLIT
> + ---help---
> + This is the frame buffer driver for (virtual/emulated) vga
> + cards implementing the bochs dispi interface. Supported
> + hardware are the bochs vga card with vbe extension and the
> + qemu standard vga.
> +
> + The driver handles the PCI variants only. It uses a fixed
> + depth of 32bpp, anything else doesn't make sense these days.
> +
> + Say Y here if you plan to run the kernel in a virtual machine
> + emulated by bochs or qemu.
> +
> config FB_PM2
> tristate "Permedia2 support"
> depends on FB && ((AMIGA && BROKEN) || PCI)
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 1426068..a065ad3 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -99,6 +99,7 @@ obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
> obj-$(CONFIG_FB_68328) += 68328fb.o
> obj-$(CONFIG_FB_GBE) += gbefb.o
> obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
> +obj-$(CONFIG_FB_BOCHS) += bochsfb.o
> obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
> obj-$(CONFIG_FB_PXA) += pxafb.o
> obj-$(CONFIG_FB_PXA168) += pxa168fb.o
> diff --git a/drivers/video/bochsfb.c b/drivers/video/bochsfb.c
> new file mode 100644
> index 0000000..18a94dc
> --- /dev/null
> +++ b/drivers/video/bochsfb.c
> @@ -0,0 +1,385 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file COPYING in the main directory of this archive for
> + * more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/mm.h>
> +#include <linux/vmalloc.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/fb.h>
> +#include <linux/pm.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/console.h>
> +#include <asm/io.h>
> +
> +#define VBE_DISPI_IOPORT_INDEX 0x01CE
> +#define VBE_DISPI_IOPORT_DATA 0x01CF
> +
> +#define VBE_DISPI_INDEX_ID 0x0
> +#define VBE_DISPI_INDEX_XRES 0x1
> +#define VBE_DISPI_INDEX_YRES 0x2
> +#define VBE_DISPI_INDEX_BPP 0x3
> +#define VBE_DISPI_INDEX_ENABLE 0x4
> +#define VBE_DISPI_INDEX_BANK 0x5
> +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
> +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
> +#define VBE_DISPI_INDEX_X_OFFSET 0x8
> +#define VBE_DISPI_INDEX_Y_OFFSET 0x9
> +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
> +
> +#define VBE_DISPI_ID0 0xB0C0
> +#define VBE_DISPI_ID1 0xB0C1
> +#define VBE_DISPI_ID2 0xB0C2
> +#define VBE_DISPI_ID3 0xB0C3
> +#define VBE_DISPI_ID4 0xB0C4
> +#define VBE_DISPI_ID5 0xB0C5
> +
> +#define VBE_DISPI_DISABLED 0x00
> +#define VBE_DISPI_ENABLED 0x01
> +#define VBE_DISPI_GETCAPS 0x02
> +#define VBE_DISPI_8BIT_DAC 0x20
> +#define VBE_DISPI_LFB_ENABLED 0x40
> +#define VBE_DISPI_NOCLEARMEM 0x80
> +
> +enum bochs_types {
> + BOCHS_QEMU_STDVGA,
> + BOCHS_UNKNOWN,
> +};
> +
> +static const char *bochs_names[] = {
> + [ BOCHS_QEMU_STDVGA ] = "QEMU standard vga",
> + [ BOCHS_UNKNOWN ] = "unknown",
> +};
> +
> +static struct fb_fix_screeninfo bochsfb_fix __devinitdata = {
> + .id = "bochsfb",
> + .type = FB_TYPE_PACKED_PIXELS,
> + .visual = FB_VISUAL_TRUECOLOR,
> + .accel = FB_ACCEL_NONE,
> + .xpanstep = 1,
> + .ypanstep = 1,
> +};
> +
> +static struct fb_var_screeninfo bochsfb_var __devinitdata = {
> + .xres = 1024,
> + .yres = 768,
> + .bits_per_pixel = 32,
> +#ifdef __BIG_ENDIAN
> + .transp = { .length = 8, .offset = 0 },
> + .red = { .length = 8, .offset = 8 },
> + .green = { .length = 8, .offset = 16 },
> + .blue = { .length = 8, .offset = 24 },
> +#else
> + .transp = { .length = 8, .offset = 24 },
> + .red = { .length = 8, .offset = 16 },
> + .green = { .length = 8, .offset = 8 },
> + .blue = { .length = 8, .offset = 0 },
> +#endif
> + .height = -1,
> + .width = -1,
> + .vmode = FB_VMODE_NONINTERLACED,
> + .pixclock = 10000,
> + .left_margin = 16,
> + .right_margin = 16,
> + .upper_margin = 16,
> + .lower_margin = 16,
> + .hsync_len = 8,
> + .vsync_len = 8,
> +};
> +
> +static char *mode __devinitdata;
> +module_param(mode, charp, 0);
> +MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480'");
> +
> +static u16 bochs_read(u16 reg)
> +{
> + outw(reg, VBE_DISPI_IOPORT_INDEX);
> + return inw(VBE_DISPI_IOPORT_DATA);
> +}
> +
> +static void bochs_write(u16 reg, u16 val)
> +{
> + outw(reg, VBE_DISPI_IOPORT_INDEX);
> + outw(val, VBE_DISPI_IOPORT_DATA);
> +}
> +
> +static int bochsfb_check_var(struct fb_var_screeninfo *var,
> + struct fb_info *info)
> +{
> + uint32_t x,y, xv,yv, pixels;
> +
> + if (var->bits_per_pixel != 32 ||
> + var->xres > 65535 ||
> + var->xres_virtual > 65535 ||
> + (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
> + return -EINVAL;
> +
> + x = var->xres & ~0x0f;
> + y = var->yres & ~0x03;
> + xv = var->xres_virtual & ~0x0f;
> + yv = var->yres_virtual & ~0x03;
> + if (xv < x)
> + xv = x;
> + pixels = info->fix.smem_len * 8 / info->var.bits_per_pixel;
> + yv = pixels / xv;
> + if (y > yv)
> + return -EINVAL;
> +
> + var->xres = x;
> + var->yres = y;
> + var->xres_virtual = xv;
> + var->yres_virtual = yv;
> + var->xoffset = 0;
> + var->yoffset = 0;
> +
> + return 0;
> +}
> +
> +static int bochsfb_set_par(struct fb_info *info)
> +{
> + dev_dbg(info->dev, "set mode: real: %dx%d, virtual: %dx%d\n",
> + info->var.xres, info->var.yres,
> + info->var.xres_virtual, info->var.yres_virtual);
> +
> + info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;
> +
> + bochs_write(VBE_DISPI_INDEX_BPP, info->var.bits_per_pixel);
> + bochs_write(VBE_DISPI_INDEX_XRES, info->var.xres);
> + bochs_write(VBE_DISPI_INDEX_YRES, info->var.yres);
> + bochs_write(VBE_DISPI_INDEX_BANK, 0);
> + bochs_write(VBE_DISPI_INDEX_VIRT_WIDTH, info->var.xres_virtual);
> + bochs_write(VBE_DISPI_INDEX_VIRT_HEIGHT, info->var.yres_virtual);
> + bochs_write(VBE_DISPI_INDEX_X_OFFSET, info->var.xoffset);
> + bochs_write(VBE_DISPI_INDEX_Y_OFFSET, info->var.yoffset);
> +
> + bochs_write(VBE_DISPI_INDEX_ENABLE,
> + VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
> + return 0;
> +}
> +
> +static int bochsfb_setcolreg(unsigned regno, unsigned red, unsigned green,
> + unsigned blue, unsigned transp,
> + struct fb_info *info)
> +{
> + if (regno < 16 && info->var.bits_per_pixel = 32) {
> + red >>= 8;
> + green >>= 8;
> + blue >>= 8;
> + ((u32 *)(info->pseudo_palette))[regno] > + (red << info->var.red.offset) |
> + (green << info->var.green.offset) |
> + (blue << info->var.blue.offset);
> + }
> + return 0;
> +}
> +
> +static int bochsfb_pan_display(struct fb_var_screeninfo *var,
> + struct fb_info *info)
> +{
> + bochs_write(VBE_DISPI_INDEX_X_OFFSET, var->xoffset);
> + bochs_write(VBE_DISPI_INDEX_Y_OFFSET, var->yoffset);
> + return 0;
> +}
> +
> +static struct fb_ops bochsfb_ops = {
> + .owner = THIS_MODULE,
> + .fb_check_var = bochsfb_check_var,
> + .fb_set_par = bochsfb_set_par,
> + .fb_setcolreg = bochsfb_setcolreg,
> + .fb_pan_display = bochsfb_pan_display,
> + .fb_fillrect = cfb_fillrect,
> + .fb_copyarea = cfb_copyarea,
> + .fb_imageblit = cfb_imageblit,
> +};
> +
> +static int __devinit
> +bochsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
> +{
> + struct fb_info *p;
> + unsigned long addr, size, mem;
> + u16 id;
> + int rc = -ENODEV;
> +
> + id = bochs_read(VBE_DISPI_INDEX_ID);;
> + mem = bochs_read(VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024;
> + dev_info(&dp->dev,"Found bochs VGA, ID 0x%x, mem %ldk, type \"%s\".\n",
> + id, mem / 1024, bochs_names[ent->driver_data]);
> + if ((id & 0xfff0) != VBE_DISPI_ID0) {
> + dev_err(&dp->dev, "ID mismatch\n");
> + goto err_out;
> + }
> +
> + if (pci_enable_device(dp) < 0) {
> + dev_err(&dp->dev, "Cannot enable PCI device\n");
> + goto err_out;
> + }
> +
> + if ((dp->resource[0].flags & IORESOURCE_MEM) = 0)
> + goto err_disable;
> + addr = pci_resource_start(dp, 0);
> + size = pci_resource_len(dp, 0);
> + if (addr = 0)
> + goto err_disable;
> + if (size != mem) {
> + dev_err(&dp->dev, "Size mismatch: pci=%ld, bochs=%ld\n", size, mem);
> + size = min(size, mem);
> + }
> +
> + p = framebuffer_alloc(0, &dp->dev);
> + if (p = NULL) {
> + dev_err(&dp->dev, "Cannot allocate framebuffer structure\n");
> + rc = -ENOMEM;
> + goto err_disable;
> + }
> +
> + if (pci_request_region(dp, 0, "bochsfb") != 0) {
> + dev_err(&dp->dev, "Cannot request framebuffer\n");
> + rc = -EBUSY;
> + goto err_release_fb;
> + }
> +
> + if (!request_region(VBE_DISPI_IOPORT_INDEX, 2, "bochsfb")) {
> + dev_err(&dp->dev, "Cannot request ioports\n");
> + rc = -EBUSY;
> + goto err_release_pci;
> + }
> +
> + p->screen_base = ioremap(addr, size);
> + if (p->screen_base = NULL) {
> + dev_err(&dp->dev, "Cannot map framebuffer\n");
> + rc = -ENOMEM;
> + goto err_release_ports;
> + }
> + memset(p->screen_base, 0, size);
> +
> + pci_set_drvdata(dp, p);
> + p->fbops = &bochsfb_ops;
> + p->flags = FBINFO_FLAG_DEFAULT
> + | FBINFO_READS_FAST
> + | FBINFO_HWACCEL_XPAN
> + | FBINFO_HWACCEL_YPAN;
> + p->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
> + p->fix = bochsfb_fix;
> + p->fix.smem_start = addr;
> + p->fix.smem_len = size;
> +
> + p->var = bochsfb_var;
> + bochsfb_check_var(&p->var, p);
> + if (mode) {
> + fb_find_mode(&p->var, p, mode, NULL, 0, NULL, 32);
> + }
> +
> + if (register_framebuffer(p) < 0) {
> + dev_err(&dp->dev,"Framebuffer failed to register\n");
> + goto err_unmap;
> + }
> +
> + dev_info(&dp->dev,"fb%d: bochs VGA frame buffer initialized.\n",
> + p->node);
> +
> + return 0;
> +
> + err_unmap:
> + iounmap(p->screen_base);
> + err_release_ports:
> + release_region(VBE_DISPI_IOPORT_INDEX, 2);
> + err_release_pci:
> + pci_release_region(dp, 0);
> + err_release_fb:
> + framebuffer_release(p);
> + err_disable:
> + err_out:
> + return rc;
> +}
> +
> +static void __devexit bochsfb_remove(struct pci_dev *dp)
> +{
> + struct fb_info *p = pci_get_drvdata(dp);
> +
> + if (p->screen_base = NULL)
> + return;
> + unregister_framebuffer(p);
> + iounmap(p->screen_base);
> + p->screen_base = NULL;
> + release_region(VBE_DISPI_IOPORT_INDEX, 2);
> + pci_release_region(dp, 0);
> + kfree(p->pseudo_palette);
> + framebuffer_release(p);
> +}
> +
> +static struct pci_device_id bochsfb_pci_tbl[] = {
> + {
> + .vendor = 0x1234,
> + .device = 0x1111,
> + .subvendor = 0x1af4,
> + .subdevice = 0x1100,
> + .driver_data = BOCHS_QEMU_STDVGA,
> + },
> + {
> + .vendor = 0x1234,
> + .device = 0x1111,
> + .subvendor = PCI_ANY_ID,
> + .subdevice = PCI_ANY_ID,
> + .driver_data = BOCHS_UNKNOWN,
> + },
> + { /* end of list */ }
> +};
> +
> +MODULE_DEVICE_TABLE(pci, bochsfb_pci_tbl);
> +
> +static struct pci_driver bochsfb_driver = {
> + .name = "bochsfb",
> + .id_table = bochsfb_pci_tbl,
> + .probe = bochsfb_pci_init,
> + .remove = __devexit_p(bochsfb_remove),
> +};
> +
> +#ifndef MODULE
> +static int __init bochsfb_setup(char *options)
> +{
> + char *this_opt;
> +
> + if (!options || !*options)
> + return 0;
> +
> + while ((this_opt = strsep(&options, ",")) != NULL) {
> + if (!*this_opt)
> + continue;
> + if (!strncmp(this_opt, "mode:", 5))
> + mode = this_opt + 5;
> + else
> + mode = this_opt;
> + }
> + return 0;
> +}
> +#endif
> +
> +int __init bochs_init(void)
> +{
> +#ifndef MODULE
> + char *option = NULL;
> +
> + if (fb_get_options("bochsfb", &option))
> + return -ENODEV;
> + bochsfb_setup(option);
> +#endif
> + return pci_register_driver(&bochsfb_driver);
> +}
> +
> +module_init(bochs_init);
> +
> +static void __exit bochsfb_exit(void)
> +{
> + pci_unregister_driver(&bochsfb_driver);
> +}
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
> +MODULE_DESCRIPTION("bochs dispi interface framebuffer driver");
> --
> 1.7.1
>
>
^ permalink raw reply
* Re: [PATCH v7 2/3] pwm_backlight: use power sequences
From: Alex Courbot @ 2012-10-19 9:31 UTC (permalink / raw)
To: Tony Prisk
Cc: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
Arnd Bergmann, linux-fbdev@vger.kernel.org,
linux-pm@vger.kernel.org, Leela Krishna Amudala,
devicetree-discuss@lists.ozlabs.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org
In-Reply-To: <1350638436.3339.2.camel@gitbox>
On Friday 19 October 2012 17:20:36 Tony Prisk wrote:
> On Fri, 2012-10-19 at 18:06 +0900, Alexandre Courbot wrote:
> > +static void pwm_backlight_on(struct backlight_device *bl)
> > +{
> > + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> > + int ret;
> > +
> > + if (pb->enabled)
> > + return;
> > +
> > + if (pb->power_on_seq) {
> > + ret = power_seq_run(pb->power_on_seq);
> > + if (ret < 0) {
> > + dev_err(&bl->dev, "cannot run power on
> > sequence\n");
> > + return;
> > + }
> > + } else {
> > + /* legacy framework */
> > + pwm_config(pb->pwm, 0, pb->period);
> > + pwm_disable(pb->pwm);
>
> Is this right? pwm_disable() in the backlight_on function?
Now everybody will notice that I never really tested the legacy interface. >_<
Thanks, this was totally wrong indeed.
Alex.
^ permalink raw reply
* Re: [PATCH v7 2/3] pwm_backlight: use power sequences
From: Tony Prisk @ 2012-10-19 9:20 UTC (permalink / raw)
To: Alexandre Courbot
Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Mark Brown, Stephen Warren,
linux-pm-u79uwXL29TY76Z2rM5mHXA, Leela Krishna Amudala,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Anton Vorontsov,
linux-tegra-u79uwXL29TY76Z2rM5mHXA, David Woodhouse,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <1350637589-7405-3-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
On Fri, 2012-10-19 at 18:06 +0900, Alexandre Courbot wrote:
> Make use of the power sequences specified in the device tree or platform
> data to control how the backlight is powered on and off.
>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
> .../bindings/video/backlight/pwm-backlight.txt | 72 ++++++++-
> drivers/video/backlight/Kconfig | 1 +
> drivers/video/backlight/pwm_bl.c | 161 ++++++++++++++++-----
> include/linux/pwm_backlight.h | 18 ++-
> 4 files changed, 213 insertions(+), 39 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
> index 1e4fc72..3ba25ea 100644
> --- a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
> +++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
> @@ -14,15 +14,83 @@ Required properties:
> Optional properties:
> - pwm-names: a list of names for the PWM devices specified in the
> "pwms" property (see PWM binding[0])
> + - low-threshold-brightness: brightness threshold low level. Sets the lowest
> + brightness value.
> + On some panels the backlight misbehaves if the duty cycle percentage of the
> + PWM wave is less than a certain level (say 20%). In this example the user
> + can set low-threshold-brightness to a value above 50 (ie, 20% of 255), thus
> + preventing the PWM duty cycle from going too low.
> + On setting low-threshold-brightness the range of brightness levels is
> + calculated in the range low-threshold-brightness to the maximum value in
> + brightness-levels, described above.
> + - pwm-names: name for the PWM device specified in the "pwms" property (see PWM
> + binding[0]). Necessary if power sequences are used
> + - power-sequences: Power sequences (see Power sequences[1]) used to bring the
> + backlight on and off. If this property is present, then two power
> + sequences named "power-on" and "power-off" must be defined to control how
> + the backlight is to be powered on and off. These sequences must reference
> + the PWM specified in the pwms property by its name, and can also reference
> + other resources supported by the power sequences mechanism
>
> [0]: Documentation/devicetree/bindings/pwm/pwm.txt
> +[1]: Documentation/devicetree/bindings/power/power_seq.txt
>
> Example:
>
> backlight {
> compatible = "pwm-backlight";
> - pwms = <&pwm 0 5000000>;
> -
> brightness-levels = <0 4 8 16 32 64 128 255>;
> default-brightness-level = <6>;
> + low-threshold-brightness = <50>;
> +
> + /* resources used by the power sequences */
> + pwms = <&pwm 0 5000000>;
> + pwm-names = "backlight";
> + power-supply = <&backlight_reg>;
> +
> + power-sequences {
> + power-on {
> + step0 {
> + type = "regulator";
> + id = "power";
> + enable;
> + };
> + step1 {
> + type = "delay";
> + delay = <10000>;
> + };
> + step2 {
> + type = "pwm";
> + id = "backlight";
> + enable;
> + };
> + step3 {
> + type = "gpio";
> + gpio = <&gpio 28 0>;
> + value = <1>;
> + };
> + };
> +
> + power-off {
> + step0 {
> + type = "gpio";
> + gpio = <&gpio 28 0>;
> + value = <0>;
> + };
> + step1 {
> + type = "pwm";
> + id = "backlight";
> + disable;
> + };
> + step2 {
> + type = "delay";
> + delay = <10000>;
> + };
> + step3 {
> + type = "regulator";
> + id = "power";
> + disable;
> + };
> + };
> + };
> };
> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> index c101697..c2e5f79 100644
> --- a/drivers/video/backlight/Kconfig
> +++ b/drivers/video/backlight/Kconfig
> @@ -239,6 +239,7 @@ config BACKLIGHT_CARILLO_RANCH
> config BACKLIGHT_PWM
> tristate "Generic PWM based Backlight Driver"
> depends on PWM
> + select POWER_SEQ
> help
> If you have a LCD backlight adjustable by PWM, say Y to enable
> this driver.
> diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
> index 069983c..e607c6e 100644
> --- a/drivers/video/backlight/pwm_bl.c
> +++ b/drivers/video/backlight/pwm_bl.c
> @@ -27,6 +27,12 @@ struct pwm_bl_data {
> unsigned int period;
> unsigned int lth_brightness;
> unsigned int *levels;
> + bool enabled;
> + struct power_seq_set power_seqs;
> + struct power_seq *power_on_seq;
> + struct power_seq *power_off_seq;
> +
> + /* Legacy callbacks */
> int (*notify)(struct device *,
> int brightness);
> void (*notify_after)(struct device *,
> @@ -35,6 +41,52 @@ struct pwm_bl_data {
> void (*exit)(struct device *);
> };
>
> +static void pwm_backlight_on(struct backlight_device *bl)
> +{
> + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> + int ret;
> +
> + if (pb->enabled)
> + return;
> +
> + if (pb->power_on_seq) {
> + ret = power_seq_run(pb->power_on_seq);
> + if (ret < 0) {
> + dev_err(&bl->dev, "cannot run power on sequence\n");
> + return;
> + }
> + } else {
> + /* legacy framework */
> + pwm_config(pb->pwm, 0, pb->period);
> + pwm_disable(pb->pwm);
Is this right? pwm_disable() in the backlight_on function?
> + }
> +
> + pb->enabled = true;
> +}
> +
> +static void pwm_backlight_off(struct backlight_device *bl)
> +{
> + struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> + int ret;
> +
> + if (!pb->enabled)
> + return;
> +
> + if (pb->power_off_seq) {
> + ret = power_seq_run(pb->power_off_seq);
> + if (ret < 0) {
> + dev_err(&bl->dev, "cannot run power off sequence\n");
> + return;
> + }
> + } else {
> + /* legacy framework */
> + pwm_enable(pb->pwm);
Same here.. owm_enable() in backlight_off()
> + return;
> + }
> +
> + pb->enabled = false;
> +}
> +
> static int pwm_backlight_update_status(struct backlight_device *bl)
> {
> struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> @@ -51,8 +103,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
> brightness = pb->notify(pb->dev, brightness);
>
> if (brightness = 0) {
> - pwm_config(pb->pwm, 0, pb->period);
> - pwm_disable(pb->pwm);
> + pwm_backlight_off(bl);
> } else {
> int duty_cycle;
>
> @@ -66,7 +117,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
> duty_cycle = pb->lth_brightness +
> (duty_cycle * (pb->period - pb->lth_brightness) / max);
> pwm_config(pb->pwm, duty_cycle, pb->period);
> - pwm_enable(pb->pwm);
> + pwm_backlight_on(bl);
> }
>
> if (pb->notify_after)
> @@ -145,11 +196,10 @@ static int pwm_backlight_parse_dt(struct device *dev,
> data->max_brightness--;
> }
>
> - /*
> - * TODO: Most users of this driver use a number of GPIOs to control
> - * backlight power. Support for specifying these needs to be
> - * added.
> - */
> + /* read power sequences */
> + data->power_seqs = devm_of_parse_power_seq_set(dev);
> + if (IS_ERR(data->power_seqs))
> + return PTR_ERR(data->power_seqs);
>
> return 0;
> }
> @@ -172,6 +222,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
> {
> struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
> struct platform_pwm_backlight_data defdata;
> + struct power_seq_resource *res;
> struct backlight_properties props;
> struct backlight_device *bl;
> struct pwm_bl_data *pb;
> @@ -180,7 +231,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
>
> if (!data) {
> ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
> - if (ret < 0) {
> + if (ret = -EPROBE_DEFER) {
> + return ret;
> + } else if (ret < 0) {
> dev_err(&pdev->dev, "failed to find platform data\n");
> return ret;
> }
> @@ -201,6 +254,68 @@ static int pwm_backlight_probe(struct platform_device *pdev)
> goto err_alloc;
> }
>
> + if (data->power_seqs) {
> + /* use power sequences */
> + struct power_seq_set *seqs = &pb->power_seqs;
> +
> + power_seq_set_init(seqs, &pdev->dev);
> + power_seq_set_add_sequences(seqs, data->power_seqs);
> +
> + /* Check that the required sequences are here */
> + pb->power_on_seq = power_seq_lookup(seqs, "power-on");
> + if (!pb->power_on_seq) {
> + dev_err(&pdev->dev, "missing power-on sequence\n");
> + return -EINVAL;
> + }
> + pb->power_off_seq = power_seq_lookup(seqs, "power-off");
> + if (!pb->power_off_seq) {
> + dev_err(&pdev->dev, "missing power-off sequence\n");
> + return -EINVAL;
> + }
> +
> + /* we must have exactly one PWM resource for this driver */
> + power_seq_for_each_resource(res, seqs) {
> + if (res->type != POWER_SEQ_PWM)
> + continue;
> + if (pb->pwm) {
> + dev_err(&pdev->dev, "more than one PWM used\n");
> + return -EINVAL;
> + }
> + /* keep the pwm at hand */
> + pb->pwm = res->pwm.pwm;
> + }
> + /* from here we should have a PWM */
> + if (!pb->pwm) {
> + dev_err(&pdev->dev, "no PWM defined!\n");
> + return -EINVAL;
> + }
> + } else {
> + /* use legacy interface */
> + pb->pwm = devm_pwm_get(&pdev->dev, NULL);
> + if (IS_ERR(pb->pwm)) {
> + dev_err(&pdev->dev,
> + "unable to request PWM, trying legacy API\n");
> +
> + pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
> + if (IS_ERR(pb->pwm)) {
> + dev_err(&pdev->dev,
> + "unable to request legacy PWM\n");
> + ret = PTR_ERR(pb->pwm);
> + goto err_alloc;
> + }
> + }
> +
> + dev_dbg(&pdev->dev, "got pwm for backlight\n");
> +
> + /*
> + * The DT case will set the pwm_period_ns field to 0 and store
> + * the period, parsed from the DT, in the PWM device. For the
> + * non-DT case, set the period from platform data.
> + */
> + if (data->pwm_period_ns > 0)
> + pwm_set_period(pb->pwm, data->pwm_period_ns);
> + }
> +
> if (data->levels) {
> max = data->levels[data->max_brightness];
> pb->levels = data->levels;
> @@ -213,28 +328,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
> pb->exit = data->exit;
> pb->dev = &pdev->dev;
>
> - pb->pwm = devm_pwm_get(&pdev->dev, NULL);
> - if (IS_ERR(pb->pwm)) {
> - dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
> -
> - pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
> - if (IS_ERR(pb->pwm)) {
> - dev_err(&pdev->dev, "unable to request legacy PWM\n");
> - ret = PTR_ERR(pb->pwm);
> - goto err_alloc;
> - }
> - }
> -
> - dev_dbg(&pdev->dev, "got pwm for backlight\n");
> -
> - /*
> - * The DT case will set the pwm_period_ns field to 0 and store the
> - * period, parsed from the DT, in the PWM device. For the non-DT case,
> - * set the period from platform data.
> - */
> - if (data->pwm_period_ns > 0)
> - pwm_set_period(pb->pwm, data->pwm_period_ns);
> -
> pb->period = pwm_get_period(pb->pwm);
> pb->lth_brightness = data->lth_brightness * (pb->period / max);
>
> @@ -267,8 +360,7 @@ static int pwm_backlight_remove(struct platform_device *pdev)
> struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
>
> backlight_device_unregister(bl);
> - pwm_config(pb->pwm, 0, pb->period);
> - pwm_disable(pb->pwm);
> + pwm_backlight_off(bl);
> if (pb->exit)
> pb->exit(&pdev->dev);
> return 0;
> @@ -282,8 +374,7 @@ static int pwm_backlight_suspend(struct device *dev)
>
> if (pb->notify)
> pb->notify(pb->dev, 0);
> - pwm_config(pb->pwm, 0, pb->period);
> - pwm_disable(pb->pwm);
> + pwm_backlight_off(bl);
> if (pb->notify_after)
> pb->notify_after(pb->dev, 0);
> return 0;
> diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
> index 56f4a86..0dcec1d 100644
> --- a/include/linux/pwm_backlight.h
> +++ b/include/linux/pwm_backlight.h
> @@ -5,14 +5,28 @@
> #define __LINUX_PWM_BACKLIGHT_H
>
> #include <linux/backlight.h>
> +#include <linux/power_seq.h>
>
> struct platform_pwm_backlight_data {
> - int pwm_id;
> unsigned int max_brightness;
> unsigned int dft_brightness;
> unsigned int lth_brightness;
> - unsigned int pwm_period_ns;
> unsigned int *levels;
> + /*
> + * New interface using power sequences. Must include exactly
> + * two power sequences named 'power-on' and 'power-off'. If NULL,
> + * the legacy interface is used.
> + */
> + struct platform_power_seq_set *power_seqs;
> +
> + /*
> + * Legacy interface - use power sequences instead!
> + *
> + * pwm_id and pwm_period_ns need only be specified
> + * if get_pwm(dev, NULL) would return NULL.
> + */
> + int pwm_id;
> + unsigned int pwm_period_ns;
> int (*init)(struct device *dev);
> int (*notify)(struct device *dev, int brightness);
> void (*notify_after)(struct device *dev, int brightness);
Regards
Tony P
^ permalink raw reply
* [PATCH v7 3/3] tegra: ventana: add PWM backlight to device tree
From: Alexandre Courbot @ 2012-10-19 9:06 UTC (permalink / raw)
To: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
Arnd Bergmann
Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
linux-pm-u79uwXL29TY76Z2rM5mHXA, Leela Krishna Amudala,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-tegra-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1350637589-7405-1-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
arch/arm/boot/dts/tegra20-ventana.dts | 59 ++++++++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index bec8bb2..877a1bb 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -454,6 +454,63 @@
bus-width = <8>;
};
+ backlight {
+ compatible = "pwm-backlight";
+ brightness-levels = <0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 255>;
+ default-brightness-level = <12>;
+
+ /* resources used by the power sequences */
+ pwms = <&pwm 2 5000000>;
+ pwm-names = "backlight";
+ power-supply = <&vdd_bl_reg>;
+
+ power-sequences {
+ power-on {
+ step0 {
+ type = "regulator";
+ id = "power";
+ enable;
+ };
+ step1 {
+ type = "delay";
+ delay = <10000>;
+ };
+ step2 {
+ type = "pwm";
+ id = "backlight";
+ enable;
+ };
+ step3 {
+ type = "gpio";
+ gpio = <&gpio 28 0>;
+ value = <1>;
+ };
+ };
+
+ power-off {
+ step0 {
+ type = "gpio";
+ gpio = <&gpio 28 0>;
+ value = <0>;
+ };
+ step1 {
+ type = "pwm";
+ id = "backlight";
+ disable;
+ };
+ step2 {
+ type = "delay";
+ delay = <10000>;
+ };
+ step3 {
+ type = "regulator";
+ id = "power";
+ disable;
+ };
+ };
+ };
+ };
+
regulators {
compatible = "simple-bus";
#address-cells = <1>;
@@ -497,7 +554,7 @@
enable-active-high;
};
- regulator@4 {
+ vdd_bl_reg: regulator@4 {
compatible = "regulator-fixed";
reg = <4>;
regulator-name = "vdd_bl";
--
1.7.12.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox