Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH 1/3] Move FIMD register headers to include/video/
From: Tomasz Figa @ 2012-07-30 14:32 UTC (permalink / raw)
  To: Leela Krishna Amudala
  Cc: Jingoo Han, linux-arm-kernel, linux-samsung-soc, dri-devel,
	linux-fbdev, ben-linux, inki.dae, kgene.kim, joshi,
	Marek Szyprowski
In-Reply-To: <CAL1wa8e7Joghf9S=SgbQLJFWcYOrP5uK6qYXsV_N6rX1MVEALg@mail.gmail.com>

Hi,

On Monday 30 of July 2012 at 16:46:03, Leela Krishna Amudala wrote:
> Hello Jingoo Han,
> 
> On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> >> to include/video/samsung_fimd.h
> >> 
> >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> >> ---
> >> 
> >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403 -----------------
> >>  include/video/samsung_fimd.h                    |  533
> >>  +++++++++++++++++++++++ 3 files changed, 533 insertions(+), 562
> >>  deletions(-)
> >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> >>  create mode 100644 include/video/samsung_fimd.h
> >> 
> >> diff --git a/arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> >> b/arch/arm/plat-samsung/include/plat/regs- fb-v4.h
> >> deleted file mode 100644
> >> index 4c3647f..0000000
> >> --- a/arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> >> +++ /dev/null
> >> @@ -1,159 +0,0 @@
> >> -/* arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> >> - *
> >> - * Copyright 2008 Openmoko, Inc.
> >> - * Copyright 2008 Simtec Electronics
> >> - *      http://armlinux.simtec.co.uk/
> >> - *      Ben Dooks <ben@simtec.co.uk>
> >> - *
> >> - * S3C64XX - new-style framebuffer register definitions
> >> - *
> >> - * This is the register set for the new style framebuffer interface
> >> - * found from the S3C2443 onwards and specifically the S3C64XX series
> >> - * S3C6400 and S3C6410.
> >> - *
> >> - * The file contains the cpu specific items which change between
> >> whichever
> >> - * architecture is selected. See <plat/regs-fb.h> for the core
> >> definitions
> >> - * that are the same.
> >> - *
> >> - * This program is free software; you can redistribute it and/or modify
> >> - * it under the terms of the GNU General Public License version 2 as
> >> - * published by the Free Software Foundation.
> >> -*/
> >> -
> >> -/* include the core definitions here, in case we really do need to
> >> - * override them at a later date.
> >> -*/
> >> -
> >> -#include <plat/regs-fb.h>
> >> -
> >> -#define S3C_FB_MAX_WIN (5)  /* number of hardware windows available. */
> >> -#define VIDCON1_FSTATUS_EVEN (1 << 15)
> >> -
> >> -/* Video timing controls */
> >> -#define VIDTCON0                             (0x10)
> >> -#define VIDTCON1                             (0x14)
> >> -#define VIDTCON2                             (0x18)
> >> -
> >> -/* Window position controls */
> >> -
> >> -#define WINCON(_win)                         (0x20 + ((_win) * 4))
> >> -
> >> -/* OSD1 and OSD4 do not have register D */
> >> -
> >> -#define VIDOSD_BASE                          (0x40)
> >> -
> >> -#define VIDINTCON0                           (0x130)
> >> -
> >> -/* WINCONx */
> >> -
> >> -#define WINCONx_CSCWIDTH_MASK                        (0x3 << 26)
> >> -#define WINCONx_CSCWIDTH_SHIFT                       (26)
> >> -#define WINCONx_CSCWIDTH_WIDE                        (0x0 << 26)
> >> -#define WINCONx_CSCWIDTH_NARROW                      (0x3 << 26)
> >> -
> >> -#define WINCONx_ENLOCAL                              (1 << 22)
> >> -#define WINCONx_BUFSTATUS                    (1 << 21)
> >> -#define WINCONx_BUFSEL                               (1 << 20)
> >> -#define WINCONx_BUFAUTOEN                    (1 << 19)
> >> -#define WINCONx_YCbCr                                (1 << 13)
> >> -
> >> -#define WINCON1_LOCALSEL_CAMIF                       (1 << 23)
> >> -
> >> -#define WINCON2_LOCALSEL_CAMIF                       (1 << 23)
> >> -#define WINCON2_BLD_PIX                              (1 << 6)
> >> -
> >> -#define WINCON2_ALPHA_SEL                    (1 << 1)
> >> -#define WINCON2_BPPMODE_MASK                 (0xf << 2)
> >> -#define WINCON2_BPPMODE_SHIFT                        (2)
> >> -#define WINCON2_BPPMODE_1BPP                 (0x0 << 2)
> >> -#define WINCON2_BPPMODE_2BPP                 (0x1 << 2)
> >> -#define WINCON2_BPPMODE_4BPP                 (0x2 << 2)
> >> -#define WINCON2_BPPMODE_8BPP_1232            (0x4 << 2)
> >> -#define WINCON2_BPPMODE_16BPP_565            (0x5 << 2)
> >> -#define WINCON2_BPPMODE_16BPP_A1555          (0x6 << 2)
> >> -#define WINCON2_BPPMODE_16BPP_I1555          (0x7 << 2)
> >> -#define WINCON2_BPPMODE_18BPP_666            (0x8 << 2)
> >> -#define WINCON2_BPPMODE_18BPP_A1665          (0x9 << 2)
> >> -#define WINCON2_BPPMODE_19BPP_A1666          (0xa << 2)
> >> -#define WINCON2_BPPMODE_24BPP_888            (0xb << 2)
> >> -#define WINCON2_BPPMODE_24BPP_A1887          (0xc << 2)
> >> -#define WINCON2_BPPMODE_25BPP_A1888          (0xd << 2)
> >> -#define WINCON2_BPPMODE_28BPP_A4888          (0xd << 2)
> >> -
> >> -#define WINCON3_BLD_PIX                              (1 << 6)
> >> -
> >> -#define WINCON3_ALPHA_SEL                    (1 << 1)
> >> -#define WINCON3_BPPMODE_MASK                 (0xf << 2)
> >> -#define WINCON3_BPPMODE_SHIFT                        (2)
> >> -#define WINCON3_BPPMODE_1BPP                 (0x0 << 2)
> >> -#define WINCON3_BPPMODE_2BPP                 (0x1 << 2)
> >> -#define WINCON3_BPPMODE_4BPP                 (0x2 << 2)
> >> -#define WINCON3_BPPMODE_16BPP_565            (0x5 << 2)
> >> -#define WINCON3_BPPMODE_16BPP_A1555          (0x6 << 2)
> >> -#define WINCON3_BPPMODE_16BPP_I1555          (0x7 << 2)
> >> -#define WINCON3_BPPMODE_18BPP_666            (0x8 << 2)
> >> -#define WINCON3_BPPMODE_18BPP_A1665          (0x9 << 2)
> >> -#define WINCON3_BPPMODE_19BPP_A1666          (0xa << 2)
> >> -#define WINCON3_BPPMODE_24BPP_888            (0xb << 2)
> >> -#define WINCON3_BPPMODE_24BPP_A1887          (0xc << 2)
> >> -#define WINCON3_BPPMODE_25BPP_A1888          (0xd << 2)
> >> -#define WINCON3_BPPMODE_28BPP_A4888          (0xd << 2)
> >> -
> >> -#define VIDINTCON0_FIFIOSEL_WINDOW2          (0x10 << 5)
> >> -#define VIDINTCON0_FIFIOSEL_WINDOW3          (0x20 << 5)
> >> -#define VIDINTCON0_FIFIOSEL_WINDOW4          (0x40 << 5)
> >> -
> >> -#define DITHMODE                             (0x170)
> >> -#define WINxMAP(_win)                                (0x180 + ((_win) *
> >> 4)) -
> >> -
> >> -#define DITHMODE_R_POS_MASK                  (0x3 << 5)
> >> -#define DITHMODE_R_POS_SHIFT                 (5)
> >> -#define DITHMODE_R_POS_8BIT                  (0x0 << 5)
> >> -#define DITHMODE_R_POS_6BIT                  (0x1 << 5)
> >> -#define DITHMODE_R_POS_5BIT                  (0x2 << 5)
> >> -
> >> -#define DITHMODE_G_POS_MASK                  (0x3 << 3)
> >> -#define DITHMODE_G_POS_SHIFT                 (3)
> >> -#define DITHMODE_G_POS_8BIT                  (0x0 << 3)
> >> -#define DITHMODE_G_POS_6BIT                  (0x1 << 3)
> >> -#define DITHMODE_G_POS_5BIT                  (0x2 << 3)
> >> -
> >> -#define DITHMODE_B_POS_MASK                  (0x3 << 1)
> >> -#define DITHMODE_B_POS_SHIFT                 (1)
> >> -#define DITHMODE_B_POS_8BIT                  (0x0 << 1)
> >> -#define DITHMODE_B_POS_6BIT                  (0x1 << 1)
> >> -#define DITHMODE_B_POS_5BIT                  (0x2 << 1)
> >> -
> >> -#define DITHMODE_DITH_EN                     (1 << 0)
> >> -
> >> -#define WPALCON                                      (0x1A0)
> >> -
> >> -/* Palette control */
> >> -/* Note for S5PC100: you can still use those macros on WPALCON (aka
> >> WPALCON_L), - * but make sure that WPALCON_H W2PAL-W4PAL entries are
> >> zeroed out */ -#define WPALCON_W4PAL_16BPP_A555             (1 << 8)
> >> -#define WPALCON_W3PAL_16BPP_A555             (1 << 7)
> >> -#define WPALCON_W2PAL_16BPP_A555             (1 << 6)
> >> -
> >> -
> >> -/* Notes on per-window bpp settings
> >> - *
> >> - * Value     Win0     Win1     Win2     Win3     Win 4
> >> - * 0000              1(P)     1(P)     1(P)     1(P)     1(P)
> >> - * 0001              2(P)     2(P)     2(P)     2(P)     2(P)
> >> - * 0010              4(P)     4(P)     4(P)     4(P)     -none-
> >> - * 0011              8(P)     8(P)     -none-   -none-   -none-
> >> - * 0100              -none-   8(A232)  8(A232)  -none-   -none-
> >> - * 0101              16(565)  16(565)  16(565)  16(565)   16(565)
> >> - * 0110              -none-   16(A555) 16(A555) 16(A555)  16(A555)
> >> - * 0111              16(I555) 16(I565) 16(I555) 16(I555)  16(I555)
> >> - * 1000              18(666)  18(666)  18(666)  18(666)   18(666)
> >> - * 1001              -none-   18(A665) 18(A665) 18(A665)  16(A665)
> >> - * 1010              -none-   19(A666) 19(A666) 19(A666)  19(A666)
> >> - * 1011              24(888)  24(888)  24(888)  24(888)   24(888)
> >> - * 1100              -none-   24(A887) 24(A887) 24(A887)  24(A887)
> >> - * 1101              -none-   25(A888) 25(A888) 25(A888)  25(A888)
> >> - * 1110              -none-   -none-   -none-   -none-    -none-
> >> - * 1111              -none-   -none-   -none-   -none-    -none-
> >> -*/
> >> diff --git a/arch/arm/plat-samsung/include/plat/regs-fb.h
> >> b/arch/arm/plat-samsung/include/plat/regs-fb.h deleted file mode 100644
> >> index 9a78012..0000000
> >> --- a/arch/arm/plat-samsung/include/plat/regs-fb.h
> >> +++ /dev/null
> >> @@ -1,403 +0,0 @@
> >> -/* arch/arm/plat-samsung/include/plat/regs-fb.h
> >> - *
> >> - * Copyright 2008 Openmoko, Inc.
> >> - * Copyright 2008 Simtec Electronics
> >> - *      http://armlinux.simtec.co.uk/
> >> - *      Ben Dooks <ben@simtec.co.uk>
> >> - *
> >> - * S3C Platform - new-style framebuffer register definitions
> >> - *
> >> - * This is the register set for the new style framebuffer interface
> >> - * found from the S3C2443 onwards into the S3C2416, S3C2450 and the
> >> - * S3C64XX series such as the S3C6400 and S3C6410.
> >> - *
> >> - * The file does not contain the cpu specific items which are based on
> >> - * whichever architecture is selected, it only contains the core of the
> >> - * register set. See <mach/regs-fb.h> to get the specifics.
> >> - *
> >> - * Note, we changed to using regs-fb.h as it avoids any clashes with
> >> - * the original regs-lcd.h so out of the way of regs-lcd.h as well as
> >> - * indicating the newer block is much more than just an LCD interface.
> >> - *
> >> - * This program is free software; you can redistribute it and/or modify
> >> - * it under the terms of the GNU General Public License version 2 as
> >> - * published by the Free Software Foundation.
> >> -*/
> >> -
> >> -/* Please do not include this file directly, use <mach/regs-fb.h> to
> >> - * ensure all the localised SoC support is included as necessary.
> >> -*/
> >> -
> >> -/* VIDCON0 */
> >> -
> >> -#define VIDCON0                                      (0x00)
> >> -#define VIDCON0_INTERLACE                    (1 << 29)
> >> -#define VIDCON0_VIDOUT_MASK                  (0x3 << 26)
> >> -#define VIDCON0_VIDOUT_SHIFT                 (26)
> >> -#define VIDCON0_VIDOUT_RGB                   (0x0 << 26)
> >> -#define VIDCON0_VIDOUT_TV                    (0x1 << 26)
> >> -#define VIDCON0_VIDOUT_I80_LDI0                      (0x2 << 26)
> >> -#define VIDCON0_VIDOUT_I80_LDI1                      (0x3 << 26)
> >> -
> >> -#define VIDCON0_L1_DATA_MASK                 (0x7 << 23)
> >> -#define VIDCON0_L1_DATA_SHIFT                        (23)
> >> -#define VIDCON0_L1_DATA_16BPP                        (0x0 << 23)
> >> -#define VIDCON0_L1_DATA_18BPP16                      (0x1 << 23)
> >> -#define VIDCON0_L1_DATA_18BPP9                       (0x2 << 23)
> >> -#define VIDCON0_L1_DATA_24BPP                        (0x3 << 23)
> >> -#define VIDCON0_L1_DATA_18BPP                        (0x4 << 23)
> >> -#define VIDCON0_L1_DATA_16BPP8                       (0x5 << 23)
> >> -
> >> -#define VIDCON0_L0_DATA_MASK                 (0x7 << 20)
> >> -#define VIDCON0_L0_DATA_SHIFT                        (20)
> >> -#define VIDCON0_L0_DATA_16BPP                        (0x0 << 20)
> >> -#define VIDCON0_L0_DATA_18BPP16                      (0x1 << 20)
> >> -#define VIDCON0_L0_DATA_18BPP9                       (0x2 << 20)
> >> -#define VIDCON0_L0_DATA_24BPP                        (0x3 << 20)
> >> -#define VIDCON0_L0_DATA_18BPP                        (0x4 << 20)
> >> -#define VIDCON0_L0_DATA_16BPP8                       (0x5 << 20)
> >> -
> >> -#define VIDCON0_PNRMODE_MASK                 (0x3 << 17)
> >> -#define VIDCON0_PNRMODE_SHIFT                        (17)
> >> -#define VIDCON0_PNRMODE_RGB                  (0x0 << 17)
> >> -#define VIDCON0_PNRMODE_BGR                  (0x1 << 17)
> >> -#define VIDCON0_PNRMODE_SERIAL_RGB           (0x2 << 17)
> >> -#define VIDCON0_PNRMODE_SERIAL_BGR           (0x3 << 17)
> >> -
> >> -#define VIDCON0_CLKVALUP                     (1 << 16)
> >> -#define VIDCON0_CLKVAL_F_MASK                        (0xff << 6)
> >> -#define VIDCON0_CLKVAL_F_SHIFT                       (6)
> >> -#define VIDCON0_CLKVAL_F_LIMIT                       (0xff)
> >> -#define VIDCON0_CLKVAL_F(_x)                 ((_x) << 6)
> >> -#define VIDCON0_VLCKFREE                     (1 << 5)
> >> -#define VIDCON0_CLKDIR                               (1 << 4)
> >> -
> >> -#define VIDCON0_CLKSEL_MASK                  (0x3 << 2)
> >> -#define VIDCON0_CLKSEL_SHIFT                 (2)
> >> -#define VIDCON0_CLKSEL_HCLK                  (0x0 << 2)
> >> -#define VIDCON0_CLKSEL_LCD                   (0x1 << 2)
> >> -#define VIDCON0_CLKSEL_27M                   (0x3 << 2)
> >> -
> >> -#define VIDCON0_ENVID                                (1 << 1)
> >> -#define VIDCON0_ENVID_F                              (1 << 0)
> >> -
> >> -#define VIDCON1                                      (0x04)
> >> -#define VIDCON1_LINECNT_MASK                 (0x7ff << 16)
> >> -#define VIDCON1_LINECNT_SHIFT                        (16)
> >> -#define VIDCON1_LINECNT_GET(_v)                      (((_v) >> 16) &
> >> 0x7ff) -#define VIDCON1_VSTATUS_MASK                 (0x3 << 13)
> >> -#define VIDCON1_VSTATUS_SHIFT                        (13)
> >> -#define VIDCON1_VSTATUS_VSYNC                        (0x0 << 13)
> >> -#define VIDCON1_VSTATUS_BACKPORCH            (0x1 << 13)
> >> -#define VIDCON1_VSTATUS_ACTIVE                       (0x2 << 13)
> >> -#define VIDCON1_VSTATUS_FRONTPORCH           (0x0 << 13)
> >> -#define VIDCON1_VCLK_MASK                    (0x3 << 9)
> >> -#define VIDCON1_VCLK_HOLD                    (0x0 << 9)
> >> -#define VIDCON1_VCLK_RUN                     (0x1 << 9)
> >> -
> >> -#define VIDCON1_INV_VCLK                     (1 << 7)
> >> -#define VIDCON1_INV_HSYNC                    (1 << 6)
> >> -#define VIDCON1_INV_VSYNC                    (1 << 5)
> >> -#define VIDCON1_INV_VDEN                     (1 << 4)
> >> -
> >> -/* VIDCON2 */
> >> -
> >> -#define VIDCON2                                      (0x08)
> >> -#define VIDCON2_EN601                                (1 << 23)
> >> -#define VIDCON2_TVFMTSEL_SW                  (1 << 14)
> >> -
> >> -#define VIDCON2_TVFMTSEL1_MASK                       (0x3 << 12)
> >> -#define VIDCON2_TVFMTSEL1_SHIFT                      (12)
> >> -#define VIDCON2_TVFMTSEL1_RGB                        (0x0 << 12)
> >> -#define VIDCON2_TVFMTSEL1_YUV422             (0x1 << 12)
> >> -#define VIDCON2_TVFMTSEL1_YUV444             (0x2 << 12)
> >> -
> >> -#define VIDCON2_ORGYCbCr                     (1 << 8)
> >> -#define VIDCON2_YUVORDCrCb                   (1 << 7)
> >> -
> >> -/* PRTCON (S3C6410, S5PC100)
> >> - * Might not be present in the S3C6410 documentation,
> >> - * but tests prove it's there almost for sure; shouldn't hurt in any
> >> case.
> >> - */
> >> -#define PRTCON                                       (0x0c)
> >> -#define PRTCON_PROTECT                               (1 << 11)
> >> -
> >> -/* VIDTCON0 */
> >> -
> >> -#define VIDTCON0_VBPDE_MASK                  (0xff << 24)
> >> -#define VIDTCON0_VBPDE_SHIFT                 (24)
> >> -#define VIDTCON0_VBPDE_LIMIT                 (0xff)
> >> -#define VIDTCON0_VBPDE(_x)                   ((_x) << 24)
> >> -
> >> -#define VIDTCON0_VBPD_MASK                   (0xff << 16)
> >> -#define VIDTCON0_VBPD_SHIFT                  (16)
> >> -#define VIDTCON0_VBPD_LIMIT                  (0xff)
> >> -#define VIDTCON0_VBPD(_x)                    ((_x) << 16)
> >> -
> >> -#define VIDTCON0_VFPD_MASK                   (0xff << 8)
> >> -#define VIDTCON0_VFPD_SHIFT                  (8)
> >> -#define VIDTCON0_VFPD_LIMIT                  (0xff)
> >> -#define VIDTCON0_VFPD(_x)                    ((_x) << 8)
> >> -
> >> -#define VIDTCON0_VSPW_MASK                   (0xff << 0)
> >> -#define VIDTCON0_VSPW_SHIFT                  (0)
> >> -#define VIDTCON0_VSPW_LIMIT                  (0xff)
> >> -#define VIDTCON0_VSPW(_x)                    ((_x) << 0)
> >> -
> >> -/* VIDTCON1 */
> >> -
> >> -#define VIDTCON1_VFPDE_MASK                  (0xff << 24)
> >> -#define VIDTCON1_VFPDE_SHIFT                 (24)
> >> -#define VIDTCON1_VFPDE_LIMIT                 (0xff)
> >> -#define VIDTCON1_VFPDE(_x)                   ((_x) << 24)
> >> -
> >> -#define VIDTCON1_HBPD_MASK                   (0xff << 16)
> >> -#define VIDTCON1_HBPD_SHIFT                  (16)
> >> -#define VIDTCON1_HBPD_LIMIT                  (0xff)
> >> -#define VIDTCON1_HBPD(_x)                    ((_x) << 16)
> >> -
> >> -#define VIDTCON1_HFPD_MASK                   (0xff << 8)
> >> -#define VIDTCON1_HFPD_SHIFT                  (8)
> >> -#define VIDTCON1_HFPD_LIMIT                  (0xff)
> >> -#define VIDTCON1_HFPD(_x)                    ((_x) << 8)
> >> -
> >> -#define VIDTCON1_HSPW_MASK                   (0xff << 0)
> >> -#define VIDTCON1_HSPW_SHIFT                  (0)
> >> -#define VIDTCON1_HSPW_LIMIT                  (0xff)
> >> -#define VIDTCON1_HSPW(_x)                    ((_x) << 0)
> >> -
> >> -#define VIDTCON2                             (0x18)
> >> -#define VIDTCON2_LINEVAL_E(_x)                       ((((_x) & 0x800) >>
> >> 11) << 23) -#define VIDTCON2_LINEVAL_MASK                        (0x7ff
> >> << 11) -#define VIDTCON2_LINEVAL_SHIFT                       (11)
> >> -#define VIDTCON2_LINEVAL_LIMIT                       (0x7ff)
> >> -#define VIDTCON2_LINEVAL(_x)                 (((_x) & 0x7ff) << 11)
> >> -
> >> -#define VIDTCON2_HOZVAL_E(_x)                        ((((_x) & 0x800) >>
> >> 11) << 22) -#define VIDTCON2_HOZVAL_MASK                 (0x7ff << 0)
> >> -#define VIDTCON2_HOZVAL_SHIFT                        (0)
> >> -#define VIDTCON2_HOZVAL_LIMIT                        (0x7ff)
> >> -#define VIDTCON2_HOZVAL(_x)                  (((_x) & 0x7ff) << 0)
> >> -
> >> -/* WINCONx */
> >> -
> >> -
> >> -#define WINCONx_BITSWP                               (1 << 18)
> >> -#define WINCONx_BYTSWP                               (1 << 17)
> >> -#define WINCONx_HAWSWP                               (1 << 16)
> >> -#define WINCONx_WSWP                         (1 << 15)
> >> -#define WINCONx_BURSTLEN_MASK                        (0x3 << 9)
> >> -#define WINCONx_BURSTLEN_SHIFT                       (9)
> >> -#define WINCONx_BURSTLEN_16WORD                      (0x0 << 9)
> >> -#define WINCONx_BURSTLEN_8WORD                       (0x1 << 9)
> >> -#define WINCONx_BURSTLEN_4WORD                       (0x2 << 9)
> >> -
> >> -#define WINCONx_ENWIN                                (1 << 0)
> >> -#define WINCON0_BPPMODE_MASK                 (0xf << 2)
> >> -#define WINCON0_BPPMODE_SHIFT                        (2)
> >> -#define WINCON0_BPPMODE_1BPP                 (0x0 << 2)
> >> -#define WINCON0_BPPMODE_2BPP                 (0x1 << 2)
> >> -#define WINCON0_BPPMODE_4BPP                 (0x2 << 2)
> >> -#define WINCON0_BPPMODE_8BPP_PALETTE         (0x3 << 2)
> >> -#define WINCON0_BPPMODE_16BPP_565            (0x5 << 2)
> >> -#define WINCON0_BPPMODE_16BPP_1555           (0x7 << 2)
> >> -#define WINCON0_BPPMODE_18BPP_666            (0x8 << 2)
> >> -#define WINCON0_BPPMODE_24BPP_888            (0xb << 2)
> >> -
> >> -#define WINCON1_BLD_PIX                              (1 << 6)
> >> -
> >> -#define WINCON1_ALPHA_SEL                    (1 << 1)
> >> -#define WINCON1_BPPMODE_MASK                 (0xf << 2)
> >> -#define WINCON1_BPPMODE_SHIFT                        (2)
> >> -#define WINCON1_BPPMODE_1BPP                 (0x0 << 2)
> >> -#define WINCON1_BPPMODE_2BPP                 (0x1 << 2)
> >> -#define WINCON1_BPPMODE_4BPP                 (0x2 << 2)
> >> -#define WINCON1_BPPMODE_8BPP_PALETTE         (0x3 << 2)
> >> -#define WINCON1_BPPMODE_8BPP_1232            (0x4 << 2)
> >> -#define WINCON1_BPPMODE_16BPP_565            (0x5 << 2)
> >> -#define WINCON1_BPPMODE_16BPP_A1555          (0x6 << 2)
> >> -#define WINCON1_BPPMODE_16BPP_I1555          (0x7 << 2)
> >> -#define WINCON1_BPPMODE_18BPP_666            (0x8 << 2)
> >> -#define WINCON1_BPPMODE_18BPP_A1665          (0x9 << 2)
> >> -#define WINCON1_BPPMODE_19BPP_A1666          (0xa << 2)
> >> -#define WINCON1_BPPMODE_24BPP_888            (0xb << 2)
> >> -#define WINCON1_BPPMODE_24BPP_A1887          (0xc << 2)
> >> -#define WINCON1_BPPMODE_25BPP_A1888          (0xd << 2)
> >> -#define WINCON1_BPPMODE_28BPP_A4888          (0xd << 2)
> >> -
> >> -/* S5PV210 */
> >> -#define SHADOWCON                            (0x34)
> >> -#define SHADOWCON_WINx_PROTECT(_win)         (1 << (10 + (_win)))
> >> -/* DMA channels (all windows) */
> >> -#define SHADOWCON_CHx_ENABLE(_win)           (1 << (_win))
> >> -/* Local input channels (windows 0-2) */
> >> -#define SHADOWCON_CHx_LOCAL_ENABLE(_win)     (1 << (5 + (_win)))
> >> -
> >> -#define VIDOSDxA_TOPLEFT_X_E(_x)             ((((_x) & 0x800) >> 11) <<
> >> 23) -#define VIDOSDxA_TOPLEFT_X_MASK                      (0x7ff << 11)
> >> -#define VIDOSDxA_TOPLEFT_X_SHIFT             (11)
> >> -#define VIDOSDxA_TOPLEFT_X_LIMIT             (0x7ff)
> >> -#define VIDOSDxA_TOPLEFT_X(_x)                       (((_x) & 0x7ff) <<
> >> 11) -
> >> -#define VIDOSDxA_TOPLEFT_Y_E(_x)             ((((_x) & 0x800) >> 11) <<
> >> 22) -#define VIDOSDxA_TOPLEFT_Y_MASK                      (0x7ff << 0)
> >> -#define VIDOSDxA_TOPLEFT_Y_SHIFT             (0)
> >> -#define VIDOSDxA_TOPLEFT_Y_LIMIT             (0x7ff)
> >> -#define VIDOSDxA_TOPLEFT_Y(_x)                       (((_x) & 0x7ff) <<
> >> 0)
> >> -
> >> -#define VIDOSDxB_BOTRIGHT_X_E(_x)            ((((_x) & 0x800) >> 11) <<
> >> 23) -#define VIDOSDxB_BOTRIGHT_X_MASK             (0x7ff << 11)
> >> -#define VIDOSDxB_BOTRIGHT_X_SHIFT            (11)
> >> -#define VIDOSDxB_BOTRIGHT_X_LIMIT            (0x7ff)
> >> -#define VIDOSDxB_BOTRIGHT_X(_x)                      (((_x) & 0x7ff) <<
> >> 11) -
> >> -#define VIDOSDxB_BOTRIGHT_Y_E(_x)            ((((_x) & 0x800) >> 11) <<
> >> 22) -#define VIDOSDxB_BOTRIGHT_Y_MASK             (0x7ff << 0)
> >> -#define VIDOSDxB_BOTRIGHT_Y_SHIFT            (0)
> >> -#define VIDOSDxB_BOTRIGHT_Y_LIMIT            (0x7ff)
> >> -#define VIDOSDxB_BOTRIGHT_Y(_x)                      (((_x) & 0x7ff) <<
> >> 0)
> >> -
> >> -/* For VIDOSD[1..4]C */
> >> -#define VIDISD14C_ALPHA0_R(_x)                       ((_x) << 20)
> >> -#define VIDISD14C_ALPHA0_G_MASK                      (0xf << 16)
> >> -#define VIDISD14C_ALPHA0_G_SHIFT             (16)
> >> -#define VIDISD14C_ALPHA0_G_LIMIT             (0xf)
> >> -#define VIDISD14C_ALPHA0_G(_x)                       ((_x) << 16)
> >> -#define VIDISD14C_ALPHA0_B_MASK                      (0xf << 12)
> >> -#define VIDISD14C_ALPHA0_B_SHIFT             (12)
> >> -#define VIDISD14C_ALPHA0_B_LIMIT             (0xf)
> >> -#define VIDISD14C_ALPHA0_B(_x)                       ((_x) << 12)
> >> -#define VIDISD14C_ALPHA1_R_MASK                      (0xf << 8)
> >> -#define VIDISD14C_ALPHA1_R_SHIFT             (8)
> >> -#define VIDISD14C_ALPHA1_R_LIMIT             (0xf)
> >> -#define VIDISD14C_ALPHA1_R(_x)                       ((_x) << 8)
> >> -#define VIDISD14C_ALPHA1_G_MASK                      (0xf << 4)
> >> -#define VIDISD14C_ALPHA1_G_SHIFT             (4)
> >> -#define VIDISD14C_ALPHA1_G_LIMIT             (0xf)
> >> -#define VIDISD14C_ALPHA1_G(_x)                       ((_x) << 4)
> >> -#define VIDISD14C_ALPHA1_B_MASK                      (0xf << 0)
> >> -#define VIDISD14C_ALPHA1_B_SHIFT             (0)
> >> -#define VIDISD14C_ALPHA1_B_LIMIT             (0xf)
> >> -#define VIDISD14C_ALPHA1_B(_x)                       ((_x) << 0)
> >> -
> >> -/* Video buffer addresses */
> >> -#define VIDW_BUF_START(_buff)                        (0xA0 + ((_buff) *
> >> 8)) -#define VIDW_BUF_START1(_buff)                       (0xA4 +
> >> ((_buff) * 8)) -#define VIDW_BUF_END(_buff)                  (0xD0 +
> >> ((_buff) * 8)) -#define VIDW_BUF_END1(_buff)                 (0xD4 +
> >> ((_buff) * 8)) -#define VIDW_BUF_SIZE(_buff)                 (0x100 +
> >> ((_buff) * 4)) -
> >> -#define VIDW_BUF_SIZE_OFFSET_E(_x)           ((((_x) & 0x2000) >> 13) <<
> >> 27) -#define VIDW_BUF_SIZE_OFFSET_MASK            (0x1fff << 13)
> >> -#define VIDW_BUF_SIZE_OFFSET_SHIFT           (13)
> >> -#define VIDW_BUF_SIZE_OFFSET_LIMIT           (0x1fff)
> >> -#define VIDW_BUF_SIZE_OFFSET(_x)             (((_x) & 0x1fff) << 13)
> >> -
> >> -#define VIDW_BUF_SIZE_PAGEWIDTH_E(_x)                ((((_x) & 0x2000)
> >> >> 13) << 26) -#define VIDW_BUF_SIZE_PAGEWIDTH_MASK         (0x1fff <<
> >> 0)
> >> -#define VIDW_BUF_SIZE_PAGEWIDTH_SHIFT                (0)
> >> -#define VIDW_BUF_SIZE_PAGEWIDTH_LIMIT                (0x1fff)
> >> -#define VIDW_BUF_SIZE_PAGEWIDTH(_x)          (((_x) & 0x1fff) << 0)
> >> -
> >> -/* Interrupt controls and status */
> >> -
> >> -#define VIDINTCON0_FIFOINTERVAL_MASK         (0x3f << 20)
> >> -#define VIDINTCON0_FIFOINTERVAL_SHIFT                (20)
> >> -#define VIDINTCON0_FIFOINTERVAL_LIMIT                (0x3f)
> >> -#define VIDINTCON0_FIFOINTERVAL(_x)          ((_x) << 20)
> >> -
> >> -#define VIDINTCON0_INT_SYSMAINCON            (1 << 19)
> >> -#define VIDINTCON0_INT_SYSSUBCON             (1 << 18)
> >> -#define VIDINTCON0_INT_I80IFDONE             (1 << 17)
> >> -
> >> -#define VIDINTCON0_FRAMESEL0_MASK            (0x3 << 15)
> >> -#define VIDINTCON0_FRAMESEL0_SHIFT           (15)
> >> -#define VIDINTCON0_FRAMESEL0_BACKPORCH               (0x0 << 15)
> >> -#define VIDINTCON0_FRAMESEL0_VSYNC           (0x1 << 15)
> >> -#define VIDINTCON0_FRAMESEL0_ACTIVE          (0x2 << 15)
> >> -#define VIDINTCON0_FRAMESEL0_FRONTPORCH              (0x3 << 15)
> >> -
> >> -#define VIDINTCON0_FRAMESEL1                 (1 << 13)
> >> -#define VIDINTCON0_FRAMESEL1_MASK            (0x3 << 13)
> >> -#define VIDINTCON0_FRAMESEL1_NONE            (0x0 << 13)
> >> -#define VIDINTCON0_FRAMESEL1_BACKPORCH               (0x1 << 13)
> >> -#define VIDINTCON0_FRAMESEL1_VSYNC           (0x2 << 13)
> >> -#define VIDINTCON0_FRAMESEL1_FRONTPORCH              (0x3 << 13)
> >> -
> >> -#define VIDINTCON0_INT_FRAME                 (1 << 12)
> >> -#define VIDINTCON0_FIFIOSEL_MASK             (0x7f << 5)
> >> -#define VIDINTCON0_FIFIOSEL_SHIFT            (5)
> >> -#define VIDINTCON0_FIFIOSEL_WINDOW0          (0x1 << 5)
> >> -#define VIDINTCON0_FIFIOSEL_WINDOW1          (0x2 << 5)
> >> -
> >> -#define VIDINTCON0_FIFOLEVEL_MASK            (0x7 << 2)
> >> -#define VIDINTCON0_FIFOLEVEL_SHIFT           (2)
> >> -#define VIDINTCON0_FIFOLEVEL_TO25PC          (0x0 << 2)
> >> -#define VIDINTCON0_FIFOLEVEL_TO50PC          (0x1 << 2)
> >> -#define VIDINTCON0_FIFOLEVEL_TO75PC          (0x2 << 2)
> >> -#define VIDINTCON0_FIFOLEVEL_EMPTY           (0x3 << 2)
> >> -#define VIDINTCON0_FIFOLEVEL_FULL            (0x4 << 2)
> >> -
> >> -#define VIDINTCON0_INT_FIFO_MASK             (0x3 << 0)
> >> -#define VIDINTCON0_INT_FIFO_SHIFT            (0)
> >> -#define VIDINTCON0_INT_ENABLE                        (1 << 0)
> >> -
> >> -#define VIDINTCON1                           (0x134)
> >> -#define VIDINTCON1_INT_I180                  (1 << 2)
> >> -#define VIDINTCON1_INT_FRAME                 (1 << 1)
> >> -#define VIDINTCON1_INT_FIFO                  (1 << 0)
> >> -
> >> -/* Window colour-key control registers */
> >> -#define WKEYCON                                      (0x140) /*
> >> 6410,V210 */ -
> >> -#define WKEYCON0                             (0x00)
> >> -#define WKEYCON1                             (0x04)
> >> -
> >> -#define WxKEYCON0_KEYBL_EN                   (1 << 26)
> >> -#define WxKEYCON0_KEYEN_F                    (1 << 25)
> >> -#define WxKEYCON0_DIRCON                     (1 << 24)
> >> -#define WxKEYCON0_COMPKEY_MASK                       (0xffffff << 0)
> >> -#define WxKEYCON0_COMPKEY_SHIFT                      (0)
> >> -#define WxKEYCON0_COMPKEY_LIMIT                      (0xffffff)
> >> -#define WxKEYCON0_COMPKEY(_x)                        ((_x) << 0)
> >> -#define WxKEYCON1_COLVAL_MASK                        (0xffffff << 0)
> >> -#define WxKEYCON1_COLVAL_SHIFT                       (0)
> >> -#define WxKEYCON1_COLVAL_LIMIT                       (0xffffff)
> >> -#define WxKEYCON1_COLVAL(_x)                 ((_x) << 0)
> >> -
> >> -
> >> -/* Window blanking (MAP) */
> >> -
> >> -#define WINxMAP_MAP                          (1 << 24)
> >> -#define WINxMAP_MAP_COLOUR_MASK                      (0xffffff << 0)
> >> -#define WINxMAP_MAP_COLOUR_SHIFT             (0)
> >> -#define WINxMAP_MAP_COLOUR_LIMIT             (0xffffff)
> >> -#define WINxMAP_MAP_COLOUR(_x)                       ((_x) << 0)
> >> -
> >> -#define WPALCON_PAL_UPDATE                   (1 << 9)
> >> -#define WPALCON_W1PAL_MASK                   (0x7 << 3)
> >> -#define WPALCON_W1PAL_SHIFT                  (3)
> >> -#define WPALCON_W1PAL_25BPP_A888             (0x0 << 3)
> >> -#define WPALCON_W1PAL_24BPP                  (0x1 << 3)
> >> -#define WPALCON_W1PAL_19BPP_A666             (0x2 << 3)
> >> -#define WPALCON_W1PAL_18BPP_A665             (0x3 << 3)
> >> -#define WPALCON_W1PAL_18BPP                  (0x4 << 3)
> >> -#define WPALCON_W1PAL_16BPP_A555             (0x5 << 3)
> >> -#define WPALCON_W1PAL_16BPP_565                      (0x6 << 3)
> >> -
> >> -#define WPALCON_W0PAL_MASK                   (0x7 << 0)
> >> -#define WPALCON_W0PAL_SHIFT                  (0)
> >> -#define WPALCON_W0PAL_25BPP_A888             (0x0 << 0)
> >> -#define WPALCON_W0PAL_24BPP                  (0x1 << 0)
> >> -#define WPALCON_W0PAL_19BPP_A666             (0x2 << 0)
> >> -#define WPALCON_W0PAL_18BPP_A665             (0x3 << 0)
> >> -#define WPALCON_W0PAL_18BPP                  (0x4 << 0)
> >> -#define WPALCON_W0PAL_16BPP_A555             (0x5 << 0)
> >> -#define WPALCON_W0PAL_16BPP_565                      (0x6 << 0)
> >> -
> >> -/* Blending equation control */
> >> -#define BLENDCON                             (0x260)
> >> -#define BLENDCON_NEW_MASK                    (1 << 0)
> >> -#define BLENDCON_NEW_8BIT_ALPHA_VALUE                (1 << 0)
> >> -#define BLENDCON_NEW_4BIT_ALPHA_VALUE                (0 << 0)
> >> -
> >> diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h
> >> new file mode 100644
> >> index 0000000..1b5ff4c
> >> --- /dev/null
> >> +++ b/include/video/samsung_fimd.h
> >> @@ -0,0 +1,533 @@
> >> +/* include/video/samsung_fimd.h
> >> + *
> >> + * Copyright 2008 Openmoko, Inc.
> >> + * Copyright 2008 Simtec Electronics
> >> + *      http://armlinux.simtec.co.uk/
> >> + *      Ben Dooks <ben@simtec.co.uk>
> >> + *
> >> + * S3C Platform - new-style fimd and framebuffer register definitions
> >> + *
> >> + * This is the register set for the fimd and new style framebuffer
> >> interface + * found from the S3C2443 onwards into the S3C2416, S3C2450
> >> and the + * S3C64XX series such as the S3C6400 and S3C6410.
> >> + *
> >> + * The file does not contain the cpu specific items which are based on
> >> + * whichever architecture is selected, it only contains the core of the
> >> + * register set. See <mach/regs-fb.h> to get the specifics.
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> +*/
> >> +
> >> +/* VIDCON0 */
> >> +
> >> +#define VIDCON0                                      (0x00)
> >> +#define VIDCON0_INTERLACE                    (1 << 29)
> >> +#define VIDCON0_VIDOUT_MASK                  (0x3 << 26)
> >> +#define VIDCON0_VIDOUT_SHIFT                 (26)
> >> +#define VIDCON0_VIDOUT_RGB                   (0x0 << 26)
> >> +#define VIDCON0_VIDOUT_TV                    (0x1 << 26)
> >> +#define VIDCON0_VIDOUT_I80_LDI0                      (0x2 << 26)
> >> +#define VIDCON0_VIDOUT_I80_LDI1                      (0x3 << 26)
> >> +
> >> +#define VIDCON0_L1_DATA_MASK                 (0x7 << 23)
> >> +#define VIDCON0_L1_DATA_SHIFT                        (23)
> >> +#define VIDCON0_L1_DATA_16BPP                        (0x0 << 23)
> >> +#define VIDCON0_L1_DATA_18BPP16                      (0x1 << 23)
> >> +#define VIDCON0_L1_DATA_18BPP9                       (0x2 << 23)
> >> +#define VIDCON0_L1_DATA_24BPP                        (0x3 << 23)
> >> +#define VIDCON0_L1_DATA_18BPP                        (0x4 << 23)
> >> +#define VIDCON0_L1_DATA_16BPP8                       (0x5 << 23)
> >> +
> >> +#define VIDCON0_L0_DATA_MASK                 (0x7 << 20)
> >> +#define VIDCON0_L0_DATA_SHIFT                        (20)
> >> +#define VIDCON0_L0_DATA_16BPP                        (0x0 << 20)
> >> +#define VIDCON0_L0_DATA_18BPP16                      (0x1 << 20)
> >> +#define VIDCON0_L0_DATA_18BPP9                       (0x2 << 20)
> >> +#define VIDCON0_L0_DATA_24BPP                        (0x3 << 20)
> >> +#define VIDCON0_L0_DATA_18BPP                        (0x4 << 20)
> >> +#define VIDCON0_L0_DATA_16BPP8                       (0x5 << 20)
> >> +
> >> +#define VIDCON0_PNRMODE_MASK                 (0x3 << 17)
> >> +#define VIDCON0_PNRMODE_SHIFT                        (17)
> >> +#define VIDCON0_PNRMODE_RGB                  (0x0 << 17)
> >> +#define VIDCON0_PNRMODE_BGR                  (0x1 << 17)
> >> +#define VIDCON0_PNRMODE_SERIAL_RGB           (0x2 << 17)
> >> +#define VIDCON0_PNRMODE_SERIAL_BGR           (0x3 << 17)
> >> +
> >> +#define VIDCON0_CLKVALUP                     (1 << 16)
> >> +#define VIDCON0_CLKVAL_F_MASK                        (0xff << 6)
> >> +#define VIDCON0_CLKVAL_F_SHIFT                       (6)
> >> +#define VIDCON0_CLKVAL_F_LIMIT                       (0xff)
> >> +#define VIDCON0_CLKVAL_F(_x)                 ((_x) << 6)
> >> +#define VIDCON0_VLCKFREE                     (1 << 5)
> >> +#define VIDCON0_CLKDIR                               (1 << 4)
> >> +
> >> +#define VIDCON0_CLKSEL_MASK                  (0x3 << 2)
> >> +#define VIDCON0_CLKSEL_SHIFT                 (2)
> >> +#define VIDCON0_CLKSEL_HCLK                  (0x0 << 2)
> >> +#define VIDCON0_CLKSEL_LCD                   (0x1 << 2)
> >> +#define VIDCON0_CLKSEL_27M                   (0x3 << 2)
> >> +
> >> +#define VIDCON0_ENVID                                (1 << 1)
> >> +#define VIDCON0_ENVID_F                              (1 << 0)
> >> +
> >> +#define VIDCON1                                      (0x04)
> >> +#define VIDCON1_LINECNT_MASK                 (0x7ff << 16)
> >> +#define VIDCON1_LINECNT_SHIFT                        (16)
> >> +#define VIDCON1_LINECNT_GET(_v)                      (((_v) >> 16) &
> >> 0x7ff) +#define VIDCON1_VSTATUS_MASK                 (0x3 << 13)
> >> +#define VIDCON1_VSTATUS_SHIFT                        (13)
> >> +#define VIDCON1_VSTATUS_VSYNC                        (0x0 << 13)
> >> +#define VIDCON1_VSTATUS_BACKPORCH            (0x1 << 13)
> >> +#define VIDCON1_VSTATUS_ACTIVE                       (0x2 << 13)
> >> +#define VIDCON1_VSTATUS_FRONTPORCH           (0x0 << 13)
> >> +#define VIDCON1_VCLK_MASK                    (0x3 << 9)
> >> +#define VIDCON1_VCLK_HOLD                    (0x0 << 9)
> >> +#define VIDCON1_VCLK_RUN                     (0x1 << 9)
> >> +
> >> +#define VIDCON1_INV_VCLK                     (1 << 7)
> >> +#define VIDCON1_INV_HSYNC                    (1 << 6)
> >> +#define VIDCON1_INV_VSYNC                    (1 << 5)
> >> +#define VIDCON1_INV_VDEN                     (1 << 4)
> >> +
> >> +/* VIDCON2 */
> >> +
> >> +#define VIDCON2                                      (0x08)
> >> +#define VIDCON2_EN601                                (1 << 23)
> >> +#define VIDCON2_TVFMTSEL_SW                  (1 << 14)
> >> +
> >> +#define VIDCON2_TVFMTSEL1_MASK                       (0x3 << 12)
> >> +#define VIDCON2_TVFMTSEL1_SHIFT                      (12)
> >> +#define VIDCON2_TVFMTSEL1_RGB                        (0x0 << 12)
> >> +#define VIDCON2_TVFMTSEL1_YUV422             (0x1 << 12)
> >> +#define VIDCON2_TVFMTSEL1_YUV444             (0x2 << 12)
> >> +
> >> +#define VIDCON2_ORGYCbCr                     (1 << 8)
> >> +#define VIDCON2_YUVORDCrCb                   (1 << 7)
> >> +
> >> +/* PRTCON (S3C6410, S5PC100)
> >> + * Might not be present in the S3C6410 documentation,
> >> + * but tests prove it's there almost for sure; shouldn't hurt in any
> >> case.
> >> + */
> >> +#define PRTCON                                       (0x0c)
> >> +#define PRTCON_PROTECT                               (1 << 11)
> >> +
> >> +/* VIDTCON0 */
> >> +
> >> +#define VIDTCON0_VBPDE_MASK                  (0xff << 24)
> >> +#define VIDTCON0_VBPDE_SHIFT                 (24)
> >> +#define VIDTCON0_VBPDE_LIMIT                 (0xff)
> >> +#define VIDTCON0_VBPDE(_x)                   ((_x) << 24)
> >> +
> >> +#define VIDTCON0_VBPD_MASK                   (0xff << 16)
> >> +#define VIDTCON0_VBPD_SHIFT                  (16)
> >> +#define VIDTCON0_VBPD_LIMIT                  (0xff)
> >> +#define VIDTCON0_VBPD(_x)                    ((_x) << 16)
> >> +
> >> +#define VIDTCON0_VFPD_MASK                   (0xff << 8)
> >> +#define VIDTCON0_VFPD_SHIFT                  (8)
> >> +#define VIDTCON0_VFPD_LIMIT                  (0xff)
> >> +#define VIDTCON0_VFPD(_x)                    ((_x) << 8)
> >> +
> >> +#define VIDTCON0_VSPW_MASK                   (0xff << 0)
> >> +#define VIDTCON0_VSPW_SHIFT                  (0)
> >> +#define VIDTCON0_VSPW_LIMIT                  (0xff)
> >> +#define VIDTCON0_VSPW(_x)                    ((_x) << 0)
> >> +
> >> +/* VIDTCON1 */
> >> +
> >> +#define VIDTCON1_VFPDE_MASK                  (0xff << 24)
> >> +#define VIDTCON1_VFPDE_SHIFT                 (24)
> >> +#define VIDTCON1_VFPDE_LIMIT                 (0xff)
> >> +#define VIDTCON1_VFPDE(_x)                   ((_x) << 24)
> >> +
> >> +#define VIDTCON1_HBPD_MASK                   (0xff << 16)
> >> +#define VIDTCON1_HBPD_SHIFT                  (16)
> >> +#define VIDTCON1_HBPD_LIMIT                  (0xff)
> >> +#define VIDTCON1_HBPD(_x)                    ((_x) << 16)
> >> +
> >> +#define VIDTCON1_HFPD_MASK                   (0xff << 8)
> >> +#define VIDTCON1_HFPD_SHIFT                  (8)
> >> +#define VIDTCON1_HFPD_LIMIT                  (0xff)
> >> +#define VIDTCON1_HFPD(_x)                    ((_x) << 8)
> >> +
> >> +#define VIDTCON1_HSPW_MASK                   (0xff << 0)
> >> +#define VIDTCON1_HSPW_SHIFT                  (0)
> >> +#define VIDTCON1_HSPW_LIMIT                  (0xff)
> >> +#define VIDTCON1_HSPW(_x)                    ((_x) << 0)
> >> +
> >> +#define VIDTCON2                             (0x18)
> >> +#define VIDTCON2_LINEVAL_E(_x)                       ((((_x) & 0x800) >>
> >> 11) << 23) +#define VIDTCON2_LINEVAL_MASK                        (0x7ff
> >> << 11) +#define VIDTCON2_LINEVAL_SHIFT                       (11)
> >> +#define VIDTCON2_LINEVAL_LIMIT                       (0x7ff)
> >> +#define VIDTCON2_LINEVAL(_x)                 (((_x) & 0x7ff) << 11)
> >> +
> >> +#define VIDTCON2_HOZVAL_E(_x)                        ((((_x) & 0x800) >>
> >> 11) << 22) +#define VIDTCON2_HOZVAL_MASK                 (0x7ff << 0)
> >> +#define VIDTCON2_HOZVAL_SHIFT                        (0)
> >> +#define VIDTCON2_HOZVAL_LIMIT                        (0x7ff)
> >> +#define VIDTCON2_HOZVAL(_x)                  (((_x) & 0x7ff) << 0)
> >> +
> >> +/* WINCONx */
> >> +
> >> +
> >> +#define WINCONx_BITSWP                               (1 << 18)
> >> +#define WINCONx_BYTSWP                               (1 << 17)
> >> +#define WINCONx_HAWSWP                               (1 << 16)
> >> +#define WINCONx_WSWP                         (1 << 15)
> >> +#define WINCONx_BURSTLEN_MASK                        (0x3 << 9)
> >> +#define WINCONx_BURSTLEN_SHIFT                       (9)
> >> +#define WINCONx_BURSTLEN_16WORD                      (0x0 << 9)
> >> +#define WINCONx_BURSTLEN_8WORD                       (0x1 << 9)
> >> +#define WINCONx_BURSTLEN_4WORD                       (0x2 << 9)
> >> +
> >> +#define WINCONx_ENWIN                                (1 << 0)
> >> +#define WINCON0_BPPMODE_MASK                 (0xf << 2)
> >> +#define WINCON0_BPPMODE_SHIFT                        (2)
> >> +#define WINCON0_BPPMODE_1BPP                 (0x0 << 2)
> >> +#define WINCON0_BPPMODE_2BPP                 (0x1 << 2)
> >> +#define WINCON0_BPPMODE_4BPP                 (0x2 << 2)
> >> +#define WINCON0_BPPMODE_8BPP_PALETTE         (0x3 << 2)
> >> +#define WINCON0_BPPMODE_16BPP_565            (0x5 << 2)
> >> +#define WINCON0_BPPMODE_16BPP_1555           (0x7 << 2)
> >> +#define WINCON0_BPPMODE_18BPP_666            (0x8 << 2)
> >> +#define WINCON0_BPPMODE_24BPP_888            (0xb << 2)
> >> +
> >> +#define WINCON1_BLD_PIX                              (1 << 6)
> >> +
> >> +#define WINCON1_ALPHA_SEL                    (1 << 1)
> >> +#define WINCON1_BPPMODE_MASK                 (0xf << 2)
> >> +#define WINCON1_BPPMODE_SHIFT                        (2)
> >> +#define WINCON1_BPPMODE_1BPP                 (0x0 << 2)
> >> +#define WINCON1_BPPMODE_2BPP                 (0x1 << 2)
> >> +#define WINCON1_BPPMODE_4BPP                 (0x2 << 2)
> >> +#define WINCON1_BPPMODE_8BPP_PALETTE         (0x3 << 2)
> >> +#define WINCON1_BPPMODE_8BPP_1232            (0x4 << 2)
> >> +#define WINCON1_BPPMODE_16BPP_565            (0x5 << 2)
> >> +#define WINCON1_BPPMODE_16BPP_A1555          (0x6 << 2)
> >> +#define WINCON1_BPPMODE_16BPP_I1555          (0x7 << 2)
> >> +#define WINCON1_BPPMODE_18BPP_666            (0x8 << 2)
> >> +#define WINCON1_BPPMODE_18BPP_A1665          (0x9 << 2)
> >> +#define WINCON1_BPPMODE_19BPP_A1666          (0xa << 2)
> >> +#define WINCON1_BPPMODE_24BPP_888            (0xb << 2)
> >> +#define WINCON1_BPPMODE_24BPP_A1887          (0xc << 2)
> >> +#define WINCON1_BPPMODE_25BPP_A1888          (0xd << 2)
> >> +#define WINCON1_BPPMODE_28BPP_A4888          (0xd << 2)
> >> +
> >> +/* S5PV210 */
> >> +#define SHADOWCON                            (0x34)
> >> +#define SHADOWCON_WINx_PROTECT(_win)         (1 << (10 + (_win)))
> >> +/* DMA channels (all windows) */
> >> +#define SHADOWCON_CHx_ENABLE(_win)           (1 << (_win))
> >> +/* Local input channels (windows 0-2) */
> >> +#define SHADOWCON_CHx_LOCAL_ENABLE(_win)     (1 << (5 + (_win)))
> >> +
> >> +#define VIDOSDxA_TOPLEFT_X_E(_x)             ((((_x) & 0x800) >> 11) <<
> >> 23) +#define VIDOSDxA_TOPLEFT_X_MASK                      (0x7ff << 11)
> >> +#define VIDOSDxA_TOPLEFT_X_SHIFT             (11)
> >> +#define VIDOSDxA_TOPLEFT_X_LIMIT             (0x7ff)
> >> +#define VIDOSDxA_TOPLEFT_X(_x)                       (((_x) & 0x7ff) <<
> >> 11) +
> >> +#define VIDOSDxA_TOPLEFT_Y_E(_x)             ((((_x) & 0x800) >> 11) <<
> >> 22) +#define VIDOSDxA_TOPLEFT_Y_MASK                      (0x7ff << 0)
> >> +#define VIDOSDxA_TOPLEFT_Y_SHIFT             (0)
> >> +#define VIDOSDxA_TOPLEFT_Y_LIMIT             (0x7ff)
> >> +#define VIDOSDxA_TOPLEFT_Y(_x)                       (((_x) & 0x7ff) <<
> >> 0)
> >> +
> >> +#define VIDOSDxB_BOTRIGHT_X_E(_x)            ((((_x) & 0x800) >> 11) <<
> >> 23) +#define VIDOSDxB_BOTRIGHT_X_MASK             (0x7ff << 11)
> >> +#define VIDOSDxB_BOTRIGHT_X_SHIFT            (11)
> >> +#define VIDOSDxB_BOTRIGHT_X_LIMIT            (0x7ff)
> >> +#define VIDOSDxB_BOTRIGHT_X(_x)                      (((_x) & 0x7ff) <<
> >> 11) +
> >> +#define VIDOSDxB_BOTRIGHT_Y_E(_x)            ((((_x) & 0x800) >> 11) <<
> >> 22) +#define VIDOSDxB_BOTRIGHT_Y_MASK             (0x7ff << 0)
> >> +#define VIDOSDxB_BOTRIGHT_Y_SHIFT            (0)
> >> +#define VIDOSDxB_BOTRIGHT_Y_LIMIT            (0x7ff)
> >> +#define VIDOSDxB_BOTRIGHT_Y(_x)                      (((_x) & 0x7ff) <<
> >> 0)
> >> +
> >> +/* For VIDOSD[1..4]C */
> >> +#define VIDISD14C_ALPHA0_R(_x)                       ((_x) << 20)
> >> +#define VIDISD14C_ALPHA0_G_MASK                      (0xf << 16)
> >> +#define VIDISD14C_ALPHA0_G_SHIFT             (16)
> >> +#define VIDISD14C_ALPHA0_G_LIMIT             (0xf)
> >> +#define VIDISD14C_ALPHA0_G(_x)                       ((_x) << 16)
> >> +#define VIDISD14C_ALPHA0_B_MASK                      (0xf << 12)
> >> +#define VIDISD14C_ALPHA0_B_SHIFT             (12)
> >> +#define VIDISD14C_ALPHA0_B_LIMIT             (0xf)
> >> +#define VIDISD14C_ALPHA0_B(_x)                       ((_x) << 12)
> >> +#define VIDISD14C_ALPHA1_R_MASK                      (0xf << 8)
> >> +#define VIDISD14C_ALPHA1_R_SHIFT             (8)
> >> +#define VIDISD14C_ALPHA1_R_LIMIT             (0xf)
> >> +#define VIDISD14C_ALPHA1_R(_x)                       ((_x) << 8)
> >> +#define VIDISD14C_ALPHA1_G_MASK                      (0xf << 4)
> >> +#define VIDISD14C_ALPHA1_G_SHIFT             (4)
> >> +#define VIDISD14C_ALPHA1_G_LIMIT             (0xf)
> >> +#define VIDISD14C_ALPHA1_G(_x)                       ((_x) << 4)
> >> +#define VIDISD14C_ALPHA1_B_MASK                      (0xf << 0)
> >> +#define VIDISD14C_ALPHA1_B_SHIFT             (0)
> >> +#define VIDISD14C_ALPHA1_B_LIMIT             (0xf)
> >> +#define VIDISD14C_ALPHA1_B(_x)                       ((_x) << 0)
> >> +
> >> +/* Video buffer addresses */
> >> +#define VIDW_BUF_START(_buff)                        (0xA0 + ((_buff) *
> >> 8)) +#define VIDW_BUF_START1(_buff)                       (0xA4 +
> >> ((_buff) * 8)) +#define VIDW_BUF_END(_buff)                  (0xD0 +
> >> ((_buff) * 8)) +#define VIDW_BUF_END1(_buff)                 (0xD4 +
> >> ((_buff) * 8)) +#define VIDW_BUF_SIZE(_buff)                 (0x100 +
> >> ((_buff) * 4)) +
> >> +#define VIDW_BUF_SIZE_OFFSET_E(_x)           ((((_x) & 0x2000) >> 13) <<
> >> 27) +#define VIDW_BUF_SIZE_OFFSET_MASK            (0x1fff << 13)
> >> +#define VIDW_BUF_SIZE_OFFSET_SHIFT           (13)
> >> +#define VIDW_BUF_SIZE_OFFSET_LIMIT           (0x1fff)
> >> +#define VIDW_BUF_SIZE_OFFSET(_x)             (((_x) & 0x1fff) << 13)
> >> +
> >> +#define VIDW_BUF_SIZE_PAGEWIDTH_E(_x)                ((((_x) & 0x2000)
> >> >> 13) << 26) +#define VIDW_BUF_SIZE_PAGEWIDTH_MASK         (0x1fff <<
> >> 0)
> >> +#define VIDW_BUF_SIZE_PAGEWIDTH_SHIFT                (0)
> >> +#define VIDW_BUF_SIZE_PAGEWIDTH_LIMIT                (0x1fff)
> >> +#define VIDW_BUF_SIZE_PAGEWIDTH(_x)          (((_x) & 0x1fff) << 0)
> >> +
> >> +/* Interrupt controls and status */
> >> +
> >> +#define VIDINTCON0_FIFOINTERVAL_MASK         (0x3f << 20)
> >> +#define VIDINTCON0_FIFOINTERVAL_SHIFT                (20)
> >> +#define VIDINTCON0_FIFOINTERVAL_LIMIT                (0x3f)
> >> +#define VIDINTCON0_FIFOINTERVAL(_x)          ((_x) << 20)
> >> +
> >> +#define VIDINTCON0_INT_SYSMAINCON            (1 << 19)
> >> +#define VIDINTCON0_INT_SYSSUBCON             (1 << 18)
> >> +#define VIDINTCON0_INT_I80IFDONE             (1 << 17)
> >> +
> >> +#define VIDINTCON0_FRAMESEL0_MASK            (0x3 << 15)
> >> +#define VIDINTCON0_FRAMESEL0_SHIFT           (15)
> >> +#define VIDINTCON0_FRAMESEL0_BACKPORCH               (0x0 << 15)
> >> +#define VIDINTCON0_FRAMESEL0_VSYNC           (0x1 << 15)
> >> +#define VIDINTCON0_FRAMESEL0_ACTIVE          (0x2 << 15)
> >> +#define VIDINTCON0_FRAMESEL0_FRONTPORCH              (0x3 << 15)
> >> +
> >> +#define VIDINTCON0_FRAMESEL1                 (1 << 13)
> >> +#define VIDINTCON0_FRAMESEL1_MASK            (0x3 << 13)
> >> +#define VIDINTCON0_FRAMESEL1_NONE            (0x0 << 13)
> >> +#define VIDINTCON0_FRAMESEL1_BACKPORCH               (0x1 << 13)
> >> +#define VIDINTCON0_FRAMESEL1_VSYNC           (0x2 << 13)
> >> +#define VIDINTCON0_FRAMESEL1_FRONTPORCH              (0x3 << 13)
> >> +
> >> +#define VIDINTCON0_INT_FRAME                 (1 << 12)
> >> +#define VIDINTCON0_FIFIOSEL_MASK             (0x7f << 5)
> >> +#define VIDINTCON0_FIFIOSEL_SHIFT            (5)
> >> +#define VIDINTCON0_FIFIOSEL_WINDOW0          (0x1 << 5)
> >> +#define VIDINTCON0_FIFIOSEL_WINDOW1          (0x2 << 5)
> >> +
> >> +#define VIDINTCON0_FIFOLEVEL_MASK            (0x7 << 2)
> >> +#define VIDINTCON0_FIFOLEVEL_SHIFT           (2)
> >> +#define VIDINTCON0_FIFOLEVEL_TO25PC          (0x0 << 2)
> >> +#define VIDINTCON0_FIFOLEVEL_TO50PC          (0x1 << 2)
> >> +#define VIDINTCON0_FIFOLEVEL_TO75PC          (0x2 << 2)
> >> +#define VIDINTCON0_FIFOLEVEL_EMPTY           (0x3 << 2)
> >> +#define VIDINTCON0_FIFOLEVEL_FULL            (0x4 << 2)
> >> +
> >> +#define VIDINTCON0_INT_FIFO_MASK             (0x3 << 0)
> >> +#define VIDINTCON0_INT_FIFO_SHIFT            (0)
> >> +#define VIDINTCON0_INT_ENABLE                        (1 << 0)
> >> +
> >> +#define VIDINTCON1                           (0x134)
> >> +#define VIDINTCON1_INT_I180                  (1 << 2)
> >> +#define VIDINTCON1_INT_FRAME                 (1 << 1)
> >> +#define VIDINTCON1_INT_FIFO                  (1 << 0)
> >> +
> >> +/* Window colour-key control registers */
> >> +#define WKEYCON                                      (0x140) /*
> >> 6410,V210 */ +
> >> +#define WKEYCON0                             (0x00)
> >> +#define WKEYCON1                             (0x04)
> >> +
> >> +#define WxKEYCON0_KEYBL_EN                   (1 << 26)
> >> +#define WxKEYCON0_KEYEN_F                    (1 << 25)
> >> +#define WxKEYCON0_DIRCON                     (1 << 24)
> >> +#define WxKEYCON0_COMPKEY_MASK                       (0xffffff << 0)
> >> +#define WxKEYCON0_COMPKEY_SHIFT                      (0)
> >> +#define WxKEYCON0_COMPKEY_LIMIT                      (0xffffff)
> >> +#define WxKEYCON0_COMPKEY(_x)                        ((_x) << 0)
> >> +#define WxKEYCON1_COLVAL_MASK                        (0xffffff << 0)
> >> +#define WxKEYCON1_COLVAL_SHIFT                       (0)
> >> +#define WxKEYCON1_COLVAL_LIMIT                       (0xffffff)
> >> +#define WxKEYCON1_COLVAL(_x)                 ((_x) << 0)
> >> +
> >> +
> >> +/* Window blanking (MAP) */
> >> +
> >> +#define WINxMAP_MAP                          (1 << 24)
> >> +#define WINxMAP_MAP_COLOUR_MASK                      (0xffffff << 0)
> >> +#define WINxMAP_MAP_COLOUR_SHIFT             (0)
> >> +#define WINxMAP_MAP_COLOUR_LIMIT             (0xffffff)
> >> +#define WINxMAP_MAP_COLOUR(_x)                       ((_x) << 0)
> >> +
> >> +#define WPALCON_PAL_UPDATE                   (1 << 9)
> >> +#define WPALCON_W1PAL_MASK                   (0x7 << 3)
> >> +#define WPALCON_W1PAL_SHIFT                  (3)
> >> +#define WPALCON_W1PAL_25BPP_A888             (0x0 << 3)
> >> +#define WPALCON_W1PAL_24BPP                  (0x1 << 3)
> >> +#define WPALCON_W1PAL_19BPP_A666             (0x2 << 3)
> >> +#define WPALCON_W1PAL_18BPP_A665             (0x3 << 3)
> >> +#define WPALCON_W1PAL_18BPP                  (0x4 << 3)
> >> +#define WPALCON_W1PAL_16BPP_A555             (0x5 << 3)
> >> +#define WPALCON_W1PAL_16BPP_565                      (0x6 << 3)
> >> +
> >> +#define WPALCON_W0PAL_MASK                   (0x7 << 0)
> >> +#define WPALCON_W0PAL_SHIFT                  (0)
> >> +#define WPALCON_W0PAL_25BPP_A888             (0x0 << 0)
> >> +#define WPALCON_W0PAL_24BPP                  (0x1 << 0)
> >> +#define WPALCON_W0PAL_19BPP_A666             (0x2 << 0)
> >> +#define WPALCON_W0PAL_18BPP_A665             (0x3 << 0)
> >> +#define WPALCON_W0PAL_18BPP                  (0x4 << 0)
> >> +#define WPALCON_W0PAL_16BPP_A555             (0x5 << 0)
> >> +#define WPALCON_W0PAL_16BPP_565                      (0x6 << 0)
> >> +
> >> +/* Blending equation control */
> >> +#define BLENDCON                             (0x260)
> >> +#define BLENDCON_NEW_MASK                    (1 << 0)
> >> +#define BLENDCON_NEW_8BIT_ALPHA_VALUE                (1 << 0)
> >> +#define BLENDCON_NEW_4BIT_ALPHA_VALUE                (0 << 0)
> >> +
> >> +#define S3C_FB_MAX_WIN (5)  /* number of hardware windows available. */
> >> +#define VIDCON1_FSTATUS_EVEN (1 << 15)
> >> +
> >> +/* Video timing controls */
> >> +#define VIDTCON0                             (0x10)
> >> +#define VIDTCON1                             (0x14)
> >> +#define VIDTCON2                             (0x18)
> >> +
> >> +/* Window position controls */
> >> +
> >> +#define WINCON(_win)                         (0x20 + ((_win) * 4))
> >> +
> >> +/* OSD1 and OSD4 do not have register D */
> >> +
> >> +#define VIDOSD_BASE                          (0x40)
> >> +
> >> +#define VIDINTCON0                           (0x130)
> >> +
> >> +/* WINCONx */
> >> +
> >> +#define WINCONx_CSCWIDTH_MASK                        (0x3 << 26)
> >> +#define WINCONx_CSCWIDTH_SHIFT                       (26)
> >> +#define WINCONx_CSCWIDTH_WIDE                        (0x0 << 26)
> >> +#define WINCONx_CSCWIDTH_NARROW                      (0x3 << 26)
> >> +
> >> +#define WINCONx_ENLOCAL                              (1 << 22)
> >> +#define WINCONx_BUFSTATUS                    (1 << 21)
> >> +#define WINCONx_BUFSEL                               (1 << 20)
> >> +#define WINCONx_BUFAUTOEN                    (1 << 19)
> >> +#define WINCONx_YCbCr                                (1 << 13)
> >> +
> >> +#define WINCON1_LOCALSEL_CAMIF                       (1 << 23)
> >> +
> >> +#define WINCON2_LOCALSEL_CAMIF                       (1 << 23)
> >> +#define WINCON2_BLD_PIX                              (1 << 6)
> >> +
> >> +#define WINCON2_ALPHA_SEL                    (1 << 1)
> >> +#define WINCON2_BPPMODE_MASK                 (0xf << 2)
> >> +#define WINCON2_BPPMODE_SHIFT                        (2)
> >> +#define WINCON2_BPPMODE_1BPP                 (0x0 << 2)
> >> +#define WINCON2_BPPMODE_2BPP                 (0x1 << 2)
> >> +#define WINCON2_BPPMODE_4BPP                 (0x2 << 2)
> >> +#define WINCON2_BPPMODE_8BPP_1232            (0x4 << 2)
> >> +#define WINCON2_BPPMODE_16BPP_565            (0x5 << 2)
> >> +#define WINCON2_BPPMODE_16BPP_A1555          (0x6 << 2)
> >> +#define WINCON2_BPPMODE_16BPP_I1555          (0x7 << 2)
> >> +#define WINCON2_BPPMODE_18BPP_666            (0x8 << 2)
> >> +#define WINCON2_BPPMODE_18BPP_A1665          (0x9 << 2)
> >> +#define WINCON2_BPPMODE_19BPP_A1666          (0xa << 2)
> >> +#define WINCON2_BPPMODE_24BPP_888            (0xb << 2)
> >> +#define WINCON2_BPPMODE_24BPP_A1887          (0xc << 2)
> >> +#define WINCON2_BPPMODE_25BPP_A1888          (0xd << 2)
> >> +#define WINCON2_BPPMODE_28BPP_A4888          (0xd << 2)
> >> +
> >> +#define WINCON3_BLD_PIX                              (1 << 6)
> >> +
> >> +#define WINCON3_ALPHA_SEL                    (1 << 1)
> >> +#define WINCON3_BPPMODE_MASK                 (0xf << 2)
> >> +#define WINCON3_BPPMODE_SHIFT                        (2)
> >> +#define WINCON3_BPPMODE_1BPP                 (0x0 << 2)
> >> +#define WINCON3_BPPMODE_2BPP                 (0x1 << 2)
> >> +#define WINCON3_BPPMODE_4BPP                 (0x2 << 2)
> >> +#define WINCON3_BPPMODE_16BPP_565            (0x5 << 2)
> >> +#define WINCON3_BPPMODE_16BPP_A1555          (0x6 << 2)
> >> +#define WINCON3_BPPMODE_16BPP_I1555          (0x7 << 2)
> >> +#define WINCON3_BPPMODE_18BPP_666            (0x8 << 2)
> >> +#define WINCON3_BPPMODE_18BPP_A1665          (0x9 << 2)
> >> +#define WINCON3_BPPMODE_19BPP_A1666          (0xa << 2)
> >> +#define WINCON3_BPPMODE_24BPP_888            (0xb << 2)
> >> +#define WINCON3_BPPMODE_24BPP_A1887          (0xc << 2)
> >> +#define WINCON3_BPPMODE_25BPP_A1888          (0xd << 2)
> >> +#define WINCON3_BPPMODE_28BPP_A4888          (0xd << 2)
> >> +
> >> +#define VIDINTCON0_FIFIOSEL_WINDOW2          (0x10 << 5)
> >> +#define VIDINTCON0_FIFIOSEL_WINDOW3          (0x20 << 5)
> >> +#define VIDINTCON0_FIFIOSEL_WINDOW4          (0x40 << 5)
> >> +
> >> +#define DITHMODE                             (0x170)
> >> +#define WINxMAP(_win)                                (0x180 + ((_win) *
> >> 4)) +
> >> +
> >> +#define DITHMODE_R_POS_MASK                  (0x3 << 5)
> >> +#define DITHMODE_R_POS_SHIFT                 (5)
> >> +#define DITHMODE_R_POS_8BIT                  (0x0 << 5)
> >> +#define DITHMODE_R_POS_6BIT                  (0x1 << 5)
> >> +#define DITHMODE_R_POS_5BIT                  (0x2 << 5)
> >> +
> >> +#define DITHMODE_G_POS_MASK                  (0x3 << 3)
> >> +#define DITHMODE_G_POS_SHIFT                 (3)
> >> +#define DITHMODE_G_POS_8BIT                  (0x0 << 3)
> >> +#define DITHMODE_G_POS_6BIT                  (0x1 << 3)
> >> +#define DITHMODE_G_POS_5BIT                  (0x2 << 3)
> >> +
> >> +#define DITHMODE_B_POS_MASK                  (0x3 << 1)
> >> +#define DITHMODE_B_POS_SHIFT                 (1)
> >> +#define DITHMODE_B_POS_8BIT                  (0x0 << 1)
> >> +#define DITHMODE_B_POS_6BIT                  (0x1 << 1)
> >> +#define DITHMODE_B_POS_5BIT                  (0x2 << 1)
> >> +
> >> +#define DITHMODE_DITH_EN                     (1 << 0)
> >> +
> >> +#define WPALCON                                      (0x1A0)
> >> +
> >> +/* Palette control */
> >> +/* Note for S5PC100: you can still use those macros on WPALCON (aka
> >> WPALCON_L), + * but make sure that WPALCON_H W2PAL-W4PAL entries are
> >> zeroed out */ +#define WPALCON_W4PAL_16BPP_A555             (1 << 8)
> >> +#define WPALCON_W3PAL_16BPP_A555             (1 << 7)
> >> +#define WPALCON_W2PAL_16BPP_A555             (1 << 6)
> >> +
> >> +
> >> +/* Notes on per-window bpp settings
> >> + *
> >> + * Value     Win0     Win1     Win2     Win3     Win 4
> >> + * 0000              1(P)     1(P)     1(P)     1(P)     1(P)
> >> + * 0001              2(P)     2(P)     2(P)     2(P)     2(P)
> >> + * 0010              4(P)     4(P)     4(P)     4(P)     -none-
> >> + * 0011              8(P)     8(P)     -none-   -none-   -none-
> >> + * 0100              -none-   8(A232)  8(A232)  -none-   -none-
> >> + * 0101              16(565)  16(565)  16(565)  16(565)   16(565)
> >> + * 0110              -none-   16(A555) 16(A555) 16(A555)  16(A555)
> >> + * 0111              16(I555) 16(I565) 16(I555) 16(I555)  16(I555)
> >> + * 1000              18(666)  18(666)  18(666)  18(666)   18(666)
> >> + * 1001              -none-   18(A665) 18(A665) 18(A665)  16(A665)
> >> + * 1010              -none-   19(A666) 19(A666) 19(A666)  19(A666)
> >> + * 1011              24(888)  24(888)  24(888)  24(888)   24(888)
> >> + * 1100              -none-   24(A887) 24(A887) 24(A887)  24(A887)
> >> + * 1101              -none-   25(A888) 25(A888) 25(A888)  25(A888)
> >> + * 1110              -none-   -none-   -none-   -none-    -none-
> >> + * 1111              -none-   -none-   -none-   -none-    -none-
> >> +*/
> >> +
> >> +/*FIMD V8 REG OFFSET */
> >> +#define FIMD_V8_VIDTCON0     (0x20010)
> >> +#define FIMD_V8_VIDTCON1     (0x20014)
> >> +#define FIMD_V8_VIDTCON2     (0x20018)
> >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> >> +#define FIMD_V8_VIDCON1              (0x20004)
> > 
> > CC'ed Marek.
> > 
> > To Leela Krishna Amudala,
> > 
> > Don't add these definitions for FIMD_V8_xxx registers, which are not
> > related to current "regs-fb-v4.h and regs-fb.h". Just "move" and "merge"
> > regs-fb-v4.h and regs-fb.h to one header file, not "add" new definitions.
> > If you want to add these definitions, please make new patch for this.
> Will do it in the suggested way,
> 
> > Also, "#define FIMD_V8_xxx" is ugly.
> > I think that there is better way.
> > Please, find other way.
> 
> I used FIMD_V8_xxx instead of  EXYNOS5_FIMD_*, because in future,
> there is a possibility that version 8 FIMD can be used in other
> application processors also.
> Thanks for reviewing the patch.

Possibly a simple "#define REGNAME_V8 ..." would be better?

Best regards,
Tomasz Figa

> 
> Best Wishes,
> Leela Krishna.
> 
> >> --
> >> 1.7.0.4
> > 
> > --
> > 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
> 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][PATCH v3 1/3] runtime interpreted power sequences
From: Rob Herring @ 2012-07-30 15:44 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Greg Kroah-Hartman, Mark Brown, Arnd Bergmann,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <1343390750-3642-2-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

On 07/27/2012 07:05 AM, Alexandre Courbot wrote:
> Some device drivers (panel backlights especially) need to follow precise
> sequences for powering on and off, involving gpios, regulators, PWMs
> with a precise powering order and delays to respect between each steps.
> These sequences are board-specific, and do not belong to a particular
> driver - therefore they have been performed by board-specific hook
> functions to far.
> 
> With the advent of the device tree and of ARM kernels that are not
> board-tied, we cannot rely on these board-specific hooks anymore but
> need a way to implement these sequences in a portable manner. This patch
> introduces a simple interpreter that can execute such power sequences
> encoded either as platform data or within the device tree.
> 

Why not? We'll always have some amount of board code. The key is to
limit parts that are just data. I'm not sure this is something that
should be in devicetree.

Perhaps what is needed is a better way to hook into the driver like
notifiers?

> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
>  Documentation/power/power_seq.txt | 120 +++++++++++++++
>  drivers/base/Kconfig              |   4 +
>  drivers/base/Makefile             |   1 +
>  drivers/base/power_seq.c          | 300 ++++++++++++++++++++++++++++++++++++++
>  include/linux/power_seq.h         | 139 ++++++++++++++++++
>  5 files changed, 564 insertions(+)
>  create mode 100644 Documentation/power/power_seq.txt
>  create mode 100644 drivers/base/power_seq.c
>  create mode 100644 include/linux/power_seq.h
> 
> diff --git a/Documentation/power/power_seq.txt b/Documentation/power/power_seq.txt
> new file mode 100644
> index 0000000..aa2ceb5
> --- /dev/null
> +++ b/Documentation/power/power_seq.txt
> @@ -0,0 +1,120 @@
> +Runtime Interpreted Power Sequences
> +-----------------------------------
> +
> +Problem
> +-------
> +One very common board-dependent code is the out-of-driver code that is used to
> +turn a device on or off. For instance, SoC boards very commonly use a GPIO
> +(abstracted to a regulator or not) to control the power supply of a backlight,
> +disabling it when the backlight is not used in order to save power. The GPIO
> +that should be used, however, as well as the exact power sequence that may
> +involve different resources, is board-dependent and thus unknown of the driver.
> +
> +This has been addressed so far by using hooks in the device's platform data that
> +are called whenever the state of the device might reflect a power change. This
> +approach, however, introduces board-dependant code into the kernel and is not
> +compatible with the device tree.
> +
> +The Runtime Interpreted Power Sequences (or power sequences for short) aim at
> +turning this code into platform data or device tree nodes. Power sequences are
> +described using a simple format and run by a simple interpreter whenever needed.
> +This allows to remove the callback mechanism and makes the kernel less
> +board-dependant.
> +
> +Sequences Format
> +----------------
> +Power sequences are a series of sequential steps during which an action is
> +performed on a resource. The supported resources so far are:
> +- GPIOs
> +- Regulators
> +- PWMs
> +
> +Each step designates a resource and the following parameters:
> +- Whether the step should enable or disable the resource,
> +- Delay to wait before performing the action,
> +- Delay to wait after performing the action.
> +
> +Both new resources and parameters can be introduced, but the goal is of course
> +to keep things as simple and compact as possible.
> +
> +The platform data is a simple array of platform_power_seq_step instances, each
> +instance describing a step. The type as well as one of id or gpio members
> +(depending on the type) must be specified. The last step must be of type
> +POWER_SEQ_STOP. Regulator and PWM resources are identified by name. GPIO are
> +identified by number. For example, the following sequence will turn on the
> +"power" regulator of the device, wait 10ms, and set GPIO number 110 to 1:
> +
> +struct platform_power_seq_step power_on_seq[] = {
> +	{
> +		.type = POWER_SEQ_REGULATOR,
> +		.id = "power",
> +		.params = {
> +			.enable = 1,
> +			.post_delay = 10,
> +		},
> +	},
> +	{
> +		.type = POWER_SEQ_GPIO,
> +		.gpio = 110,
> +		.params = {
> +			.enable = 1,
> +		},
> +	},
> +	{
> +		.type = POWER_SEQ_STOP,
> +	},
> +};
> +
> +Usage by Drivers and Resources Management
> +-----------------------------------------
> +Power sequences make use of resources that must be properly allocated and
> +managed. The power_seq_build() function takes care of resolving the resources as
> +they are met in the sequence and to allocate them if needed:
> +
> +power_seq *power_seq_build(struct device *dev, power_seq_resources *ress,
> +			   platform_power_seq *pseq);
> +
> +You will need an instance of power_seq_resources to keep track of the resources
> +that are already allocated. On success, the function returns a devm allocated
> +resolved sequence that is ready to be passed to power_seq_run(). In case of
> +failure, and error code is returned.
> +
> +A resolved power sequence returned by power_seq_build can be run by
> +power_run_run():
> +
> +int power_seq_run(struct device *dev, power_seq *seq);
> +
> +It returns 0 if the sequence has successfully been run, or an error code if a
> +problem occured.
> +
> +Finally, some resources that cannot be allocated through devm need to be freed
> +manually. Therefore, be sure to call power_seq_free_resources() in your device
> +remove function:
> +
> +void power_seq_free_resources(power_seq_resources *ress);
> +
> +Device tree

Bindings need to documented in Documentation/devicetree

> +-----------
> +All the same, power sequences can be encoded as device tree nodes. The following
> +properties and nodes are equivalent to the platform data defined previously:
> +
> +		power-supply = <&mydevice_reg>;
> +		enable-gpio = <&gpio 6 0>;
> +
> +		power-on-sequence {
> +			regulator@0 {
> +				id = "power";
> +				enable;

What do this mean? Isn't this implied for a regulator?

> +				post-delay = <10>;
> +			};
> +			gpio@1 {
> +				id = "enable-gpio";
> +				enable;
> +			};
> +		};

This looks like you designed the platform_data structs first and then
came up with device nodes to mirror the struct.

Rob

^ permalink raw reply

* Re: [RFC][PATCH v3 1/3] runtime interpreted power sequences
From: Mark Brown @ 2012-07-30 15:47 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Stephen Warren,
	Greg Kroah-Hartman, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	Alexandre Courbot, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <5016ABDD.5010809-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On Mon, Jul 30, 2012 at 10:44:29AM -0500, Rob Herring wrote:
> On 07/27/2012 07:05 AM, Alexandre Courbot wrote:

> > +		power-on-sequence {
> > +			regulator@0 {
> > +				id = "power";
> > +				enable;

> What do this mean? Isn't this implied for a regulator?

I assume you might have some sequences which need some things to be
turned off for some reason; it at least seems to be something you'd want
to design for.

> This looks like you designed the platform_data structs first and then
> came up with device nodes to mirror the struct.

Judging by most of the DT I review this seems idiomatic :)

^ permalink raw reply

* [PATCH] fbcon: Fix bit_putcs() call to kmalloc(s, GFP_KERNEL)
From: Bruno Prémont @ 2012-07-30 19:09 UTC (permalink / raw)
  To: linux-fbdev, FlorianSchandinat; +Cc: linux-kernel

Switch to kmalloc(,GFP_ATOMIC) in bit_putcs to fix below trace:

[    9.771812] BUG: sleeping function called from invalid context at /usr/src/linux-git/mm/slub.c:943
[    9.771814] in_atomic(): 1, irqs_disabled(): 1, pid: 1063, name: mount
[    9.771818] Pid: 1063, comm: mount Not tainted 3.5.0-jupiter-00003-g8d858b1-dirty #2
[    9.771819] Call Trace:
[    9.771838]  [<c104f79b>] __might_sleep+0xcb/0xe0
[    9.771844]  [<c10c00d4>] __kmalloc+0xb4/0x1c0
[    9.771851]  [<c1041d4a>] ? queue_work+0x1a/0x30
[    9.771854]  [<c1041dcf>] ? queue_delayed_work+0xf/0x30
[    9.771862]  [<c1205832>] ? bit_putcs+0xf2/0x3e0
[    9.771865]  [<c1041e01>] ? schedule_delayed_work+0x11/0x20
[    9.771868]  [<c1205832>] bit_putcs+0xf2/0x3e0
[    9.771875]  [<c12002b8>] ? get_color.clone.14+0x28/0x100
[    9.771878]  [<c1200d2f>] fbcon_putcs+0x11f/0x130
[    9.771882]  [<c1205740>] ? bit_clear+0xe0/0xe0
[    9.771885]  [<c1200f6d>] fbcon_redraw.clone.21+0x11d/0x160
[    9.771889]  [<c120383d>] fbcon_scroll+0x79d/0xe10
[    9.771892]  [<c12002b8>] ? get_color.clone.14+0x28/0x100
[    9.771897]  [<c124c0b4>] scrup+0x64/0xd0
[    9.771900]  [<c124c22b>] lf+0x2b/0x60
[    9.771903]  [<c124cc95>] vt_console_print+0x1d5/0x2f0
[    9.771907]  [<c124cac0>] ? register_vt_notifier+0x20/0x20
[    9.771913]  [<c102b335>] call_console_drivers.clone.5+0xa5/0xc0
[    9.771916]  [<c102c58e>] console_unlock+0x2fe/0x3c0
[    9.771920]  [<c102ca16>] vprintk_emit+0x2e6/0x300
[    9.771924]  [<c13f01ae>] printk+0x38/0x3a
[    9.771931]  [<c112e8fe>] reiserfs_remount+0x2ae/0x3e0
[    9.771934]  [<c112e650>] ? reiserfs_fill_super+0xb00/0xb00
[    9.771939]  [<c10ca0ab>] do_remount_sb+0xab/0x150
[    9.771943]  [<c1034476>] ? ns_capable+0x46/0x70
[    9.771948]  [<c10e059c>] do_mount+0x20c/0x6b0
[    9.771955]  [<c10a7044>] ? strndup_user+0x34/0x50
[    9.771958]  [<c10e0acc>] sys_mount+0x6c/0xa0
[    9.771964]  [<c13f2557>] sysenter_do_call+0x12/0x26

According to comment in bit_putcs() that kammloc() call only happens
when fbcon is drawing to a monochrome framebuffer (which is my case with
hid-picolcd).

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/video/console/bitblit.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index 28b1a83..61b182b 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -162,7 +162,7 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info,
 	image.depth = 1;
 
 	if (attribute) {
-		buf = kmalloc(cellsize, GFP_KERNEL);
+		buf = kmalloc(cellsize, GFP_ATOMIC);
 		if (!buf)
 			return;
 	}
-- 
1.7.8.6


^ permalink raw reply related

* Re: [PATCH] video/smscufx: fix line counting in fb_write
From: Alexander Holler @ 2012-07-30 19:28 UTC (permalink / raw)
  To: Florian Tobias Schandinat; +Cc: linux-kernel, linux-fbdev
In-Reply-To: <50117DD9.6070200@gmx.de>

Hello,

Am 26.07.2012 19:26, schrieb Florian Tobias Schandinat:

> Well, as this patch fixes a bug I applied it as is.

Thanks a lot, actually I was more interested to get that one-line-patch 
for udlfb, because I've discovered that bug using one of those Mimo-LCDs. ;)

Are you responsible for that too?

Regards,

Alexander

^ permalink raw reply

* [PATCH 0/7] HID: picoLCD updates
From: Bruno Prémont @ 2012-07-30 19:36 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev

Hi,

This series updates picoLCD driver:
- split the driver functions into separate files which get included
  depending on Kconfig selection
  (implementation for CIR using RC_CORE will follow later)
- drop private framebuffer refcounting in favor of refcounting added
  to fb_info some time ago
- fix various bugs issues
- disabled firmware version checking in probe() as it does not work
  anymore since commit 4ea5454203d991ec85264f64f89ca8855fce69b0
  [HID: Fix race condition between driver core and ll-driver]

Note: I still get weird behavior on quick unbind/bind sequences
issued via sysfs (CONFIG_SMP=n system) that are triggered by framebuffer
support and apparently more specifically fb_defio part of it.

Unfortunately I'm out of ideas as to how to track down the problem which
shows either as SLAB corruption (detected with SLUB debugging, e.g.

[ 6383.521833] ======================================[ 6383.530020] BUG kmalloc-64 (Not tainted): Object already free
[ 6383.530020] -----------------------------------------------------------------------------
[ 6383.530020] 
[ 6383.530020] INFO: Slab 0xdde0ea20 objectsQ used@ fp=0xcef516e0 flags=0x40000080
[ 6383.530020] INFO: Object 0xcef51190 @offset@0 fp=0xcef51f50
[ 6383.530020] 
[ 6383.530020] Bytes b4 cef51180: cc cc cc cc d0 12 f5 ce 5a 5a 5a 5a 5a 5a 5a 5a  ........ZZZZZZZZ
[ 6383.530020] Object cef51190: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
[ 6383.530020] Object cef511a0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
[ 6383.530020] Object cef511b0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
[ 6383.530020] Object cef511c0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5  kkkkkkkkkkkkkkk.
[ 6383.530020] Redzone cef511d0: bb bb bb bb                                      ....
[ 6383.530020] Padding cef511d8: 5a 5a 5a 5a 5a 5a 5a 5a                          ZZZZZZZZ
[ 6383.530020] Pid: 1922, comm: bash Not tainted 3.5.0-jupiter-00003-g8d858b1-dirty #2
[ 6383.530020] Call Trace:
[ 6383.530020]  [<c10bd3cc>] print_trailer+0x11c/0x130
[ 6383.530020]  [<c10bd415>] object_err+0x35/0x40
[ 6383.530020]  [<c10be809>] free_debug_processing+0x99/0x200
[ 6383.530020]  [<c10bf77e>] __slab_free+0x2e/0x280
[ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
[ 6383.530020]  [<c1322870>] ? __usbhid_submit_report+0xc0/0x3c0
[ 6383.530020]  [<c10bfbda>] ? kfree+0xfa/0x110
[ 6383.530020]  [<de932aa4>] ? picolcd_debug_out_report+0x8c4/0x8e0 [hid_picolcd]
[ 6383.530020]  [<c10bfbda>] kfree+0xfa/0x110
[ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
[ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
[ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
[ 6383.530020]  [<c1322284>] hid_submit_out+0xa4/0x120
[ 6383.530020]  [<c1322908>] __usbhid_submit_report+0x158/0x3c0
[ 6383.530020]  [<c1322c2b>] usbhid_submit_report+0x1b/0x30
[ 6383.530020]  [<de930789>] picolcd_fb_reset+0xb9/0x180 [hid_picolcd]
[ 6383.530020]  [<de930f1d>] picolcd_init_framebuffer+0x20d/0x2e0 [hid_picolcd]
[ 6383.530020]  [<de92fb9c>] picolcd_probe+0x3cc/0x580 [hid_picolcd]
[ 6383.530020]  [<c1319147>] hid_device_probe+0x67/0xf0
[ 6383.530020]  [<c1282f97>] ? driver_sysfs_add+0x57/0x80
[ 6383.530020]  [<c128329d>] driver_probe_device+0xbd/0x1c0
[ 6383.530020]  [<c1318a1b>] ? hid_match_device+0x7b/0x90
[ 6383.530020]  [<c12821e5>] driver_bind+0x75/0xd0
[ 6383.530020]  [<c1282170>] ? driver_unbind+0x90/0x90
[ 6383.530020]  [<c12818b7>] drv_attr_store+0x27/0x30
[ 6383.530020]  [<c1114aec>] sysfs_write_file+0xac/0xf0
[ 6383.530020]  [<c10c794c>] vfs_write+0x9c/0x130
[ 6383.530020]  [<c10d4a1f>] ? sys_dup3+0x11f/0x160
[ 6383.530020]  [<c1114a40>] ? sysfs_poll+0x90/0x90
[ 6383.530020]  [<c10c7bbd>] sys_write+0x3d/0x70
[ 6383.530020]  [<c13f2557>] sysenter_do_call+0x12/0x26
[ 6383.530020] FIX kmalloc-64: Object at 0xcef51190 not freed

or worse spontaneous reboot of the system without any trace on netconsole or
serial console.

echo $devid > bind; echo $devid > unbind
or
echo $devid > bind; echo $devid > unbind; sleep 0.2; echo $devid > bind; echo $devid > unbind

is sufficient to trigger the above issue while waiting a few seconds between bind and unbind
shows no sign of trouble.

Suggestions as to how to debug this and fix it are welcome!

Thanks,
Bruno

^ permalink raw reply

* [PATCH 2/7] HID: picoLCD: Replace own refcounting with fbdev's
From: Bruno Prémont @ 2012-07-30 19:38 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>


Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/hid-picolcd.h      |    6 --
 drivers/hid/hid-picolcd_core.c |    1 -
 drivers/hid/hid-picolcd_fb.c   |  114 ++++++---------------------------------
 3 files changed, 18 insertions(+), 103 deletions(-)

diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h
index 4ddb318..9200be1 100644
--- a/drivers/hid/hid-picolcd.h
+++ b/drivers/hid/hid-picolcd.h
@@ -96,7 +96,6 @@ struct picolcd_data {
 	u8 *fb_vbitmap;		/* local copy of what was sent to PicoLCD */
 	u8 *fb_bitmap;		/* framebuffer */
 	struct fb_info *fb_info;
-	struct fb_deferred_io fb_defio;
 #endif /* CONFIG_HID_PICOLCD_FB */
 #ifdef CONFIG_HID_PICOLCD_LCD
 	struct lcd_device *lcd;
@@ -179,8 +178,6 @@ int picolcd_init_framebuffer(struct picolcd_data *data);
 
 void picolcd_exit_framebuffer(struct picolcd_data *data);
 
-void picolcd_fb_unload(void);
-
 void picolcd_fb_refresh(struct picolcd_data *data);
 #define picolcd_fbinfo(d) ((d)->fb_info)
 #else
@@ -195,9 +192,6 @@ static inline int picolcd_init_framebuffer(struct picolcd_data *data)
 static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
 {
 }
-static inline void picolcd_fb_unload(void)
-{
-}
 static inline void picolcd_fb_refresh(struct picolcd_data *data)
 {
 }
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index 36e7548..8d5d341 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -695,7 +695,6 @@ static int __init picolcd_init(void)
 static void __exit picolcd_exit(void)
 {
 	hid_unregister_driver(&picolcd_driver);
-	picolcd_fb_unload();
 }
 
 module_init(picolcd_init);
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
index b2124f5..602786c 100644
--- a/drivers/hid/hid-picolcd_fb.c
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -256,7 +256,7 @@ static void picolcd_fb_update(struct picolcd_data *data)
 					data->fb_bitmap, data->fb_bpp, chip, tile) ||
 				data->fb_force) {
 				n += 2;
-				if (!data->fb_info->par)
+				if (data->status & PICOLCD_FAILED)
 					return; /* device lost! */
 				if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
 					usbhid_wait_io(data->hdev);
@@ -327,24 +327,17 @@ static int picolcd_fb_blank(int blank, struct fb_info *info)
 
 static void picolcd_fb_destroy(struct fb_info *info)
 {
-	struct picolcd_data *data = info->par;
-	u32 *ref_cnt = info->pseudo_palette;
-	int may_release;
+	struct picolcd_data *data;
 
+	/* make sure no work is deferred */
+	cancel_delayed_work_sync(&info->deferred_work);
+	data = info->par;
 	info->par = NULL;
 	if (data)
 		data->fb_info = NULL;
-	fb_deferred_io_cleanup(info);
 
-	ref_cnt--;
-	mutex_lock(&info->lock);
-	(*ref_cnt)--;
-	may_release = !*ref_cnt;
-	mutex_unlock(&info->lock);
-	if (may_release) {
-		vfree((u8 *)info->fix.smem_start);
-		framebuffer_release(info);
-	}
+	vfree((u8 *)info->fix.smem_start);
+	framebuffer_release(info);
 }
 
 static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
@@ -414,77 +407,10 @@ static int picolcd_set_par(struct fb_info *info)
 	return 0;
 }
 
-/* Do refcounting on our FB and cleanup per worker if FB is
- * closed after unplug of our device
- * (fb_release holds info->lock and still touches info after
- *  we return so we can't release it immediately.
- */
-struct picolcd_fb_cleanup_item {
-	struct fb_info *info;
-	struct picolcd_fb_cleanup_item *next;
-};
-static struct picolcd_fb_cleanup_item *fb_pending;
-static DEFINE_SPINLOCK(fb_pending_lock);
-
-static void picolcd_fb_do_cleanup(struct work_struct *data)
-{
-	struct picolcd_fb_cleanup_item *item;
-	unsigned long flags;
-
-	do {
-		spin_lock_irqsave(&fb_pending_lock, flags);
-		item = fb_pending;
-		fb_pending = item ? item->next : NULL;
-		spin_unlock_irqrestore(&fb_pending_lock, flags);
-
-		if (item) {
-			u8 *fb = (u8 *)item->info->fix.smem_start;
-			/* make sure we do not race against fb core when
-			 * releasing */
-			mutex_lock(&item->info->lock);
-			mutex_unlock(&item->info->lock);
-			framebuffer_release(item->info);
-			vfree(fb);
-		}
-	} while (item);
-}
-
-static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
-
-static int picolcd_fb_open(struct fb_info *info, int u)
-{
-	u32 *ref_cnt = info->pseudo_palette;
-	ref_cnt--;
-
-	(*ref_cnt)++;
-	return 0;
-}
-
-static int picolcd_fb_release(struct fb_info *info, int u)
-{
-	u32 *ref_cnt = info->pseudo_palette;
-	ref_cnt--;
-
-	(*ref_cnt)++;
-	if (!*ref_cnt) {
-		unsigned long flags;
-		struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt;
-		item--;
-		spin_lock_irqsave(&fb_pending_lock, flags);
-		item->next = fb_pending;
-		fb_pending = item;
-		spin_unlock_irqrestore(&fb_pending_lock, flags);
-		schedule_work(&picolcd_fb_cleanup);
-	}
-	return 0;
-}
-
 /* Note this can't be const because of struct fb_info definition */
 static struct fb_ops picolcdfb_ops = {
 	.owner        = THIS_MODULE,
 	.fb_destroy   = picolcd_fb_destroy,
-	.fb_open      = picolcd_fb_open,
-	.fb_release   = picolcd_fb_release,
 	.fb_read      = fb_sys_read,
 	.fb_write     = picolcd_fb_write,
 	.fb_blank     = picolcd_fb_blank,
@@ -550,7 +476,7 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev,
 		u = PICOLCDFB_UPDATE_RATE_DEFAULT;
 
 	data->fb_update_rate = u;
-	data->fb_defio.delay = HZ / data->fb_update_rate;
+	data->fb_info->fbdefio->delay = HZ / data->fb_update_rate;
 	return count;
 }
 
@@ -580,25 +506,23 @@ int picolcd_init_framebuffer(struct picolcd_data *data)
 	}
 
 	data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
-	data->fb_defio = picolcd_fb_defio;
 	/* The extra memory is:
-	 * - struct picolcd_fb_cleanup_item
-	 * - u32 for ref_count
 	 * - 256*u32 for pseudo_palette
+	 * - struct fb_deferred_io
 	 */
-	info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);
+	info = framebuffer_alloc(256 * sizeof(u32) +
+			sizeof(struct fb_deferred_io), dev);
 	if (info = NULL) {
 		dev_err(dev, "failed to allocate a framebuffer\n");
 		goto err_nomem;
 	}
 
-	palette  = info->par + sizeof(struct picolcd_fb_cleanup_item);
-	*palette = 1;
-	palette++;
+	info->fbdefio = info->par;
+	*info->fbdefio = picolcd_fb_defio;
+	palette  = info->par + sizeof(struct fb_deferred_io);
 	for (i = 0; i < 256; i++)
 		palette[i] = i > 0 && i < 16 ? 0xff : 0;
 	info->pseudo_palette = palette;
-	info->fbdefio = &data->fb_defio;
 	info->screen_base = (char __force __iomem *)fb_bitmap;
 	info->fbops = &picolcdfb_ops;
 	info->var = picolcdfb_var;
@@ -658,6 +582,10 @@ void picolcd_exit_framebuffer(struct picolcd_data *data)
 		return;
 
 	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+	mutex_lock(&info->lock);
+	fb_deferred_io_cleanup(info);
+	info->par = NULL;
+	mutex_unlock(&info->lock);
 	unregister_framebuffer(info);
 	data->fb_vbitmap = NULL;
 	data->fb_bitmap  = NULL;
@@ -665,9 +593,3 @@ void picolcd_exit_framebuffer(struct picolcd_data *data)
 	data->fb_info    = NULL;
 	kfree(fb_vbitmap);
 }
-
-void picolcd_fb_unload()
-{
-	flush_work_sync(&picolcd_fb_cleanup);
-	WARN_ON(fb_pending);
-}
-- 
1.7.8.6


^ permalink raw reply related

* [PATCH 3/7] HID: picoLCD: prevent NULL pointer dereference on unplug
From: Bruno Prémont @ 2012-07-30 19:38 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>

[  679.807480] BUG: unable to handle kernel NULL pointer dereference at 00000074
[  679.814457] IP: [<de93b5bf>] picolcd_led_set_brightness+0x1f/0xb0 [hid_picolcd]
[  679.814457] *pde = 00000000
[  679.814457] Oops: 0000 [#1]
[  679.814457] Modules linked in: hid_picolcd fb_sys_fops sysimgblt sysfillrect syscopyarea drm_kms_helper nfs lockd nfs_acl sunrpc [last unloaded: hid_picolcd]
[  679.814457]
[  679.814457] Pid: 272, comm: khubd Not tainted 3.5.0-jupiter-00006-g463a4c0 #1 NVIDIA Corporation. nFORCE-MCP/MS-6373
[  679.814457] EIP: 0060:[<de93b5bf>] EFLAGS: 00010246 CPU: 0
[  679.814457] EIP is at picolcd_led_set_brightness+0x1f/0xb0 [hid_picolcd]
[  679.814457] EAX: 00000000 EBX: d9f0c4e0 ECX: 00000000 EDX: 00000000
[  679.814457] ESI: 00000000 EDI: dd6b79c0 EBP: dd4f7d90 ESP: dd4f7d80
[  679.814457]  DS: 007b ES: 007b FS: 0000 GS: 00e0 SS: 0068
[  679.814457] CR0: 8005003b CR2: 00000074 CR3: 1d74e000 CR4: 000007d0
[  679.814457] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000
[  679.814457] DR6: ffff0ff0 DR7: 00000400
[  679.814457] Process khubd (pid: 272, tiÝ4f6000 taskÝ442470 task.tiÝ4f6000)
[  679.814457] Stack:
[  679.814457]  1d6c3300 d9f0c4e0 d9f0c4e0 dd6b79c0 dd4f7da0 c132912a 00000000 d9f0c4e0
[  679.814457]  dd4f7dac c132935d 00000000 dd4f7dc0 de93b847 dd6b79c0 00000282 c700ecc8
[  679.814457]  dd4f7ddc de93924f 00000004 c700ecc8 c700e060 c700ecbc c15ee300 dd4f7dec
[  679.814457] Call Trace:
[  679.814457]  [<c132912a>] led_brightness_set+0x2a/0x30
[  679.814457]  [<c132935d>] led_classdev_unregister+0xd/0x50
[  679.814457]  [<de93b847>] picolcd_exit_leds+0x27/0x40 [hid_picolcd]
[  679.814457]  [<de93924f>] picolcd_remove+0xbf/0x110 [hid_picolcd]
[  679.814457]  [<c132c5dd>] hid_device_remove+0x3d/0x80
[  679.814457]  [<c1294126>] __device_release_driver+0x56/0xa0
[  679.814457]  [<c1294190>] device_release_driver+0x20/0x30
[  679.814457]  [<c1293bbf>] bus_remove_device+0x9f/0xc0
[  679.814457]  [<c1291a1d>] device_del+0xdd/0x150
[  679.814457]  [<c132c205>] hid_destroy_device+0x25/0x60
[  679.814457]  [<c13368cb>] usbhid_disconnect+0x1b/0x40
[  679.814457]  [<c12f4976>] usb_unbind_interface+0x46/0x170
[  679.814457]  [<c1294126>] __device_release_driver+0x56/0xa0
[  679.814457]  [<c1294190>] device_release_driver+0x20/0x30
[  679.814457]  [<c1293bbf>] bus_remove_device+0x9f/0xc0
[  679.814457]  [<c1291a1d>] device_del+0xdd/0x150
[  679.814457]  [<c12f2975>] usb_disable_device+0x85/0x1a0
[  679.814457]  [<c1053146>] ? __cond_resched+0x16/0x30
[  679.814457]  [<c12ebdb0>] usb_disconnect+0x80/0xf0
[  679.814457]  [<c12ed61f>] hub_thread+0x3df/0x1030
[  679.814457]  [<c10484a0>] ? wake_up_bit+0x30/0x30
[  679.814457]  [<c12ed240>] ? usb_remote_wakeup+0x40/0x40
[  679.814457]  [<c1047f94>] kthread+0x74/0x80
[  679.814457]  [<c1047f20>] ? flush_kthread_worker+0x90/0x90
[  679.814457]  [<c140e33e>] kernel_thread_helper+0x6/0xd
[  679.814457] Code: e0 25 00 e0 ff ff ff 48 14 eb 99 90 55 89 e5 83 ec 10 89 5d f4 89 75 f8 89 c3 89 7d fc 8b 40 1c 89 d6 8b 00 e8 13 89 95 e2 31 c9 <39> 5c 88 74 74 13 41 83 f9 08 75 f4 8b 5d f4 8b 75 f8 8b 7d fc
[  679.814457] EIP: [<de93b5bf>] picolcd_led_set_brightness+0x1f/0xb0 [hid_picolcd] SS:ESP 0068:dd4f7d80
[  679.814457] CR2: 0000000000000074
[  680.116438] ---[ end trace 6f0d9d63bff280ff ]---

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/hid-picolcd_leds.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
index 9249c06..5573cd8 100644
--- a/drivers/hid/hid-picolcd_leds.c
+++ b/drivers/hid/hid-picolcd_leds.c
@@ -69,6 +69,8 @@ static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
 	dev  = led_cdev->dev->parent;
 	hdev = container_of(dev, struct hid_device, dev);
 	data = hid_get_drvdata(hdev);
+	if (!data)
+		return;
 	for (i = 0; i < 8; i++) {
 		if (led_cdev != data->led[i])
 			continue;
-- 
1.7.8.6


^ permalink raw reply related

* [PATCH 4/7] HID: picoLCD: satify some checkpatch warnings
From: Bruno Prémont @ 2012-07-30 19:38 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>

WARNING: static const char * array should probably be static const char * const
+static const char *error_codes[] = {

WARNING: min() should probably be min_t(size_t, 20, s)
+       raw_data[2] = min((size_t)20, s);

Note: the second min_t suggestion cannot be followed because GCC is not
smart enough to track constants through it and make
copy_from_user_overflow() check happy.

WARNING: min() should probably be min_t(u8, 20, raw_data[2])
+       if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
                return -EFAULT;

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/hid-picolcd_debugfs.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
index ff74645..f2491fa 100644
--- a/drivers/hid/hid-picolcd_debugfs.c
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -135,7 +135,7 @@ static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
 	memset(raw_data, 0, sizeof(raw_data));
 	raw_data[0] = *off & 0xff;
 	raw_data[1] = (*off >> 8) & 0xff;
-	raw_data[2] = min((size_t)20, s);
+	raw_data[2] = min_t(size_t, 20, s);
 	if (*off + raw_data[2] > 0xff)
 		raw_data[2] = 0x100 - *off;
 
@@ -370,7 +370,7 @@ static const struct file_operations picolcd_debug_flash_fops = {
 /*
  * Helper code for HID report level dumping/debugging
  */
-static const char *error_codes[] = {
+static const char * const error_codes[] = {
 	"success", "parameter missing", "data_missing", "block readonly",
 	"block not erasable", "block too big", "section overflow",
 	"invalid command length", "invalid data length",
-- 
1.7.8.6


^ permalink raw reply related

* [PATCH 5/7] HID: picoLCD: Improve unplug handling
From: Bruno Prémont @ 2012-07-30 19:38 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>

Stop earlier attempting to submit new reports/URBs (though locking and
usbhid still prevents to bail out early enough to not produce multiple
  hid-picolcd 0003:04D8:C002.0003: usb_submit_urb(out) failed: -19
messages in kernel log.

Strengthen framebuffer removal to be less racy, though quick
bind/unbind sequences triggered via sysfs still trigger weird behavior:
- either SLAB corruptions
  Seems related to fbdefio
- immediate system reboot
  Unexplained as there is no explaining output on any console, not even
  serial console.
(note: picoLCD is hosting fbcon in both cases, but manual plug/unplug
seems too slow to trigger madness)

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/hid-picolcd_backlight.c |    3 +-
 drivers/hid/hid-picolcd_core.c      |   21 ++++++++++++-----
 drivers/hid/hid-picolcd_fb.c        |   41 +++++++++++++++++++++-------------
 drivers/hid/hid-picolcd_lcd.c       |    3 +-
 drivers/hid/hid-picolcd_leds.c      |    3 +-
 5 files changed, 46 insertions(+), 25 deletions(-)

diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c
index cd1ccd7..b91f309 100644
--- a/drivers/hid/hid-picolcd_backlight.c
+++ b/drivers/hid/hid-picolcd_backlight.c
@@ -45,7 +45,8 @@ static int picolcd_set_brightness(struct backlight_device *bdev)
 	data->lcd_power      = bdev->props.power;
 	spin_lock_irqsave(&data->lock, flags);
 	hid_set_field(report->field[0], 0, data->lcd_power = FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
-	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	if (!(data->status & PICOLCD_FAILED))
+		usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
 	spin_unlock_irqrestore(&data->lock, flags);
 	return 0;
 }
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index 8d5d341..2d7ef68 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -105,12 +105,17 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
 			hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
 			k++;
 		}
-	data->pending = work;
-	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-	spin_unlock_irqrestore(&data->lock, flags);
-	wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
-	spin_lock_irqsave(&data->lock, flags);
-	data->pending = NULL;
+	if (data->status & PICOLCD_FAILED) {
+		kfree(work);
+		work = NULL;
+	} else {
+		data->pending = work;
+		usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+		spin_unlock_irqrestore(&data->lock, flags);
+		wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
+		spin_lock_irqsave(&data->lock, flags);
+		data->pending = NULL;
+	}
 	spin_unlock_irqrestore(&data->lock, flags);
 	mutex_unlock(&data->mutex);
 	return work;
@@ -235,6 +240,10 @@ int picolcd_reset(struct hid_device *hdev)
 
 	/* perform the reset */
 	hid_set_field(report->field[0], 0, 1);
+	if (data->status & PICOLCD_FAILED) {
+		spin_unlock_irqrestore(&data->lock, flags);
+		return -ENODEV;
+	}
 	usbhid_submit_report(hdev, report, USB_DIR_OUT);
 	spin_unlock_irqrestore(&data->lock, flags);
 
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
index 602786c..4d8e22c 100644
--- a/drivers/hid/hid-picolcd_fb.c
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -226,13 +226,16 @@ int picolcd_fb_reset(struct picolcd_data *data, int clear)
 }
 
 /* Update fb_vbitmap from the screen_base and send changed tiles to device */
-static void picolcd_fb_update(struct picolcd_data *data)
+static void picolcd_fb_update(struct fb_info *info)
 {
 	int chip, tile, n;
 	unsigned long flags;
+	struct picolcd_data *data;
 
+	mutex_lock(&info->lock);
+	data = info->par;
 	if (!data)
-		return;
+		goto out;
 
 	spin_lock_irqsave(&data->lock, flags);
 	if (!(data->status & PICOLCD_READY_FB)) {
@@ -256,17 +259,31 @@ static void picolcd_fb_update(struct picolcd_data *data)
 					data->fb_bitmap, data->fb_bpp, chip, tile) ||
 				data->fb_force) {
 				n += 2;
-				if (data->status & PICOLCD_FAILED)
-					return; /* device lost! */
 				if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
+					mutex_unlock(&info->lock);
 					usbhid_wait_io(data->hdev);
+					mutex_lock(&info->lock);
+					data = info->par;
+					if (!data)
+						goto out;
+					spin_lock_irqsave(&data->lock, flags);
+					if (data->status & PICOLCD_FAILED) {
+						spin_unlock_irqrestore(&data->lock, flags);
+						goto out;
+					}
+					spin_unlock_irqrestore(&data->lock, flags);
 					n = 0;
 				}
 				picolcd_fb_send_tile(data->hdev, chip, tile);
 			}
 	data->fb_force = false;
-	if (n)
+	if (n) {
+		mutex_unlock(&info->lock);
 		usbhid_wait_io(data->hdev);
+		return;
+	}
+out:
+	mutex_unlock(&info->lock);
 }
 
 /* Stub to call the system default and update the image on the picoLCD */
@@ -327,17 +344,12 @@ static int picolcd_fb_blank(int blank, struct fb_info *info)
 
 static void picolcd_fb_destroy(struct fb_info *info)
 {
-	struct picolcd_data *data;
-
 	/* make sure no work is deferred */
-	cancel_delayed_work_sync(&info->deferred_work);
-	data = info->par;
-	info->par = NULL;
-	if (data)
-		data->fb_info = NULL;
+	fb_deferred_io_cleanup(info);
 
 	vfree((u8 *)info->fix.smem_start);
 	framebuffer_release(info);
+	printk(KERN_DEBUG "picolcd_fb_destroy(%p)\n", info);
 }
 
 static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
@@ -425,7 +437,7 @@ static struct fb_ops picolcdfb_ops = {
 /* Callback from deferred IO workqueue */
 static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
 {
-	picolcd_fb_update(info->par);
+	picolcd_fb_update(info);
 }
 
 static const struct fb_deferred_io picolcd_fb_defio = {
@@ -582,10 +594,7 @@ void picolcd_exit_framebuffer(struct picolcd_data *data)
 		return;
 
 	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
-	mutex_lock(&info->lock);
-	fb_deferred_io_cleanup(info);
 	info->par = NULL;
-	mutex_unlock(&info->lock);
 	unregister_framebuffer(info);
 	data->fb_vbitmap = NULL;
 	data->fb_bitmap  = NULL;
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c
index 64a067f..2d0ddc5 100644
--- a/drivers/hid/hid-picolcd_lcd.c
+++ b/drivers/hid/hid-picolcd_lcd.c
@@ -47,7 +47,8 @@ static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
 	data->lcd_contrast = contrast & 0x0ff;
 	spin_lock_irqsave(&data->lock, flags);
 	hid_set_field(report->field[0], 0, data->lcd_contrast);
-	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	if (!(data->status & PICOLCD_FAILED))
+		usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
 	spin_unlock_irqrestore(&data->lock, flags);
 	return 0;
 }
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
index 5573cd8..28cb6a4 100644
--- a/drivers/hid/hid-picolcd_leds.c
+++ b/drivers/hid/hid-picolcd_leds.c
@@ -54,7 +54,8 @@ void picolcd_leds_set(struct picolcd_data *data)
 
 	spin_lock_irqsave(&data->lock, flags);
 	hid_set_field(report->field[0], 0, data->led_state);
-	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	if (!(data->status & PICOLCD_FAILED))
+		usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
 	spin_unlock_irqrestore(&data->lock, flags);
 }
 
-- 
1.7.8.6


^ permalink raw reply related

* [PATCH 6/7] HID: picoLCD: disable version check during probe
From: Bruno Prémont @ 2012-07-30 19:38 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>

Commit 4ea5454203d991ec85264f64f89ca8855fce69b0
[HID: Fix race condition between driver core and ll-driver] introduced
new locking around proce/remove functions that prevent any report/reply
from hardware to reach driver until it returned from probe.

As such, the ask-reply way to checking picoLCD firmware version during
probe is bound to timeout and let probe fail.

Disabling the check lets driver sucessfully probe again.

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 drivers/hid/hid-picolcd_core.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index 2d7ef68..42d0791 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -478,13 +478,13 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 {
 	int error;
 
-	error = picolcd_check_version(hdev);
+/*	error = picolcd_check_version(hdev);
 	if (error)
 		return error;
 
 	if (data->version[0] != 0 && data->version[1] != 3)
 		hid_info(hdev, "Device with untested firmware revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
-			 dev_name(&hdev->dev));
+			 dev_name(&hdev->dev)); */
 
 	/* Setup keypad input device */
 	error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
@@ -534,7 +534,7 @@ err:
 
 static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
 {
-	int error;
+/*	int error;
 
 	error = picolcd_check_version(hdev);
 	if (error)
@@ -542,7 +542,7 @@ static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data
 
 	if (data->version[0] != 1 && data->version[1] != 0)
 		hid_info(hdev, "Device with untested bootloader revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
-			 dev_name(&hdev->dev));
+			 dev_name(&hdev->dev)); */
 
 	picolcd_init_devfs(data, NULL, NULL,
 			picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
-- 
1.7.8.6


^ permalink raw reply related

* [PATCH 7/7] HID: picoLCD: add myself to MAINTAINERS
From: Bruno Prémont @ 2012-07-30 19:39 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>


Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---
 MAINTAINERS |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index fe643e7..0042553 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5283,6 +5283,12 @@ L:	linux-mtd@lists.infradead.org
 S:	Maintained
 F:	drivers/mtd/devices/phram.c
 
+PICOLCD HID DRIVER
+M:	Bruno Prémont <bonbons@linux-vserver.org>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	drivers/hid/hid-picolcd*
+
 PICOXCELL SUPPORT
 M:	Jamie Iles <jamie@jamieiles.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-- 
1.7.8.6


^ permalink raw reply related

* Re: [PATCH 1/7] HID: picoLCD: split driver code
From: Bruno Prémont @ 2012-07-30 19:59 UTC (permalink / raw)
  To: linux-input, linux-kernel, Jiri Kosina; +Cc: linux-fbdev
In-Reply-To: <20120730213828.6c78f8f3@neptune.home>

In order to make code maintenance easier, split the vairous
functions into individial files (this removes a bunch of #ifdefs).

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
---



WARNING: this is a mangled resend of this patch (removed the payload of
hunk that deletes drivers/hid/hid-picolcd.c to halve the patch's size)
as resulting mail is otherwise too big for vger (unless bless by list owner)...

If needed, please ask for off-list resending of this patch, though dropping
the first (and mangled) hunk before attempting to apply and then manually
removing the file should do.



 drivers/hid/hid-picolcd.c           | 2752 -----------------------------------
 drivers/hid/Kconfig                 |    8 +
 drivers/hid/Makefile                |   20 +
 drivers/hid/hid-picolcd.h           |  306 ++++
 drivers/hid/hid-picolcd_backlight.c |  121 ++
 drivers/hid/hid-picolcd_cir.c       |   61 +
 drivers/hid/hid-picolcd_core.c      |  704 +++++++++
 drivers/hid/hid-picolcd_debugfs.c   |  898 ++++++++++++
 drivers/hid/hid-picolcd_fb.c        |  673 +++++++++
 drivers/hid/hid-picolcd_lcd.c       |  106 ++
 drivers/hid/hid-picolcd_leds.c      |  172 +++
 11 files changed, 3069 insertions(+), 2752 deletions(-)
 delete mode 100644 drivers/hid/hid-picolcd.c
 create mode 100644 drivers/hid/hid-picolcd.h
 create mode 100644 drivers/hid/hid-picolcd_backlight.c
 create mode 100644 drivers/hid/hid-picolcd_cir.c
 create mode 100644 drivers/hid/hid-picolcd_core.c
 create mode 100644 drivers/hid/hid-picolcd_debugfs.c
 create mode 100644 drivers/hid/hid-picolcd_fb.c
 create mode 100644 drivers/hid/hid-picolcd_lcd.c
 create mode 100644 drivers/hid/hid-picolcd_leds.c

diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
deleted file mode 100644
index 45c3433..0000000
--- a/drivers/hid/hid-picolcd.c
+++ /dev/null
@@ -1,2752 +0,0 @@


<dropped as otherwise patch is too big>



diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 3fda8c8..1d806d0 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -491,6 +491,14 @@ config HID_PICOLCD_LEDS
 	---help---
 	  Provide access to PicoLCD's GPO pins via leds class.
 
+config HID_PICOLCD_CIR
+	bool "CIR via RC class" if EXPERT
+	default !EXPERT
+	depends on HID_PICOLCD
+	depends on HID_PICOLCD=RC_CORE || RC_CORE=y
+	---help---
+	  Provide access to PicoLCD's CIR interface via remote control (LIRC).
+
 config HID_PRIMAX
 	tristate "Primax non-fully HID-compliant devices"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index ca6cc9f..8b7a9b6 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -66,6 +66,26 @@ obj-$(CONFIG_HID_PRODIKEYS)	+= hid-prodikeys.o
 obj-$(CONFIG_HID_PANTHERLORD)	+= hid-pl.o
 obj-$(CONFIG_HID_PETALYNX)	+= hid-petalynx.o
 obj-$(CONFIG_HID_PICOLCD)	+= hid-picolcd.o
+hid-picolcd-y			+= hid-picolcd_core.o
+ifdef CONFIG_HID_PICOLCD_FB
+hid-picolcd-y			+= hid-picolcd_fb.o
+endif
+ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+hid-picolcd-y			+= hid-picolcd_backlight.o
+endif
+ifdef CONFIG_HID_PICOLCD_LCD
+hid-picolcd-y			+= hid-picolcd_lcd.o
+endif
+ifdef CONFIG_HID_PICOLCD_LEDS
+hid-picolcd-y			+= hid-picolcd_leds.o
+endif
+ifdef CONFIG_HID_PICOLCD_CIR
+hid-picolcd-y			+= hid-picolcd_cir.o
+endif
+ifdef CONFIG_DEBUG_FS
+hid-picolcd-y			+= hid-picolcd_debugfs.o
+endif
+
 obj-$(CONFIG_HID_PRIMAX)	+= hid-primax.o
 obj-$(CONFIG_HID_ROCCAT)	+= hid-roccat.o hid-roccat-common.o \
 	hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h
new file mode 100644
index 0000000..4ddb318
--- /dev/null
+++ b/drivers/hid/hid-picolcd.h
@@ -0,0 +1,306 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#define PICOLCD_NAME "PicoLCD (graphic)"
+
+/* Report numbers */
+#define REPORT_ERROR_CODE      0x10 /* LCD: IN[16]  */
+#define   ERR_SUCCESS            0x00
+#define   ERR_PARAMETER_MISSING  0x01
+#define   ERR_DATA_MISSING       0x02
+#define   ERR_BLOCK_READ_ONLY    0x03
+#define   ERR_BLOCK_NOT_ERASABLE 0x04
+#define   ERR_BLOCK_TOO_BIG      0x05
+#define   ERR_SECTION_OVERFLOW   0x06
+#define   ERR_INVALID_CMD_LEN    0x07
+#define   ERR_INVALID_DATA_LEN   0x08
+#define REPORT_KEY_STATE       0x11 /* LCD: IN[2]   */
+#define REPORT_IR_DATA         0x21 /* LCD: IN[63]  */
+#define REPORT_EE_DATA         0x32 /* LCD: IN[63]  */
+#define REPORT_MEMORY          0x41 /* LCD: IN[63]  */
+#define REPORT_LED_STATE       0x81 /* LCD: OUT[1]  */
+#define REPORT_BRIGHTNESS      0x91 /* LCD: OUT[1]  */
+#define REPORT_CONTRAST        0x92 /* LCD: OUT[1]  */
+#define REPORT_RESET           0x93 /* LCD: OUT[2]  */
+#define REPORT_LCD_CMD         0x94 /* LCD: OUT[63] */
+#define REPORT_LCD_DATA        0x95 /* LCD: OUT[63] */
+#define REPORT_LCD_CMD_DATA    0x96 /* LCD: OUT[63] */
+#define	REPORT_EE_READ         0xa3 /* LCD: OUT[63] */
+#define REPORT_EE_WRITE        0xa4 /* LCD: OUT[63] */
+#define REPORT_ERASE_MEMORY    0xb2 /* LCD: OUT[2]  */
+#define REPORT_READ_MEMORY     0xb3 /* LCD: OUT[3]  */
+#define REPORT_WRITE_MEMORY    0xb4 /* LCD: OUT[63] */
+#define REPORT_SPLASH_RESTART  0xc1 /* LCD: OUT[1]  */
+#define REPORT_EXIT_KEYBOARD   0xef /* LCD: OUT[2]  */
+#define REPORT_VERSION         0xf1 /* LCD: IN[2],OUT[1]    Bootloader: IN[2],OUT[1]   */
+#define REPORT_BL_ERASE_MEMORY 0xf2 /*                      Bootloader: IN[36],OUT[4]  */
+#define REPORT_BL_READ_MEMORY  0xf3 /*                      Bootloader: IN[36],OUT[4]  */
+#define REPORT_BL_WRITE_MEMORY 0xf4 /*                      Bootloader: IN[36],OUT[36] */
+#define REPORT_DEVID           0xf5 /* LCD: IN[5], OUT[1]   Bootloader: IN[5],OUT[1]   */
+#define REPORT_SPLASH_SIZE     0xf6 /* LCD: IN[4], OUT[1]   */
+#define REPORT_HOOK_VERSION    0xf7 /* LCD: IN[2], OUT[1]   */
+#define REPORT_EXIT_FLASHER    0xff /*                      Bootloader: OUT[2]         */
+
+/* Description of in-progress IO operation, used for operations
+ * that trigger response from device */
+struct picolcd_pending {
+	struct hid_report *out_report;
+	struct hid_report *in_report;
+	struct completion ready;
+	int raw_size;
+	u8 raw_data[64];
+};
+
+
+#define PICOLCD_KEYS 17
+
+/* Per device data structure */
+struct picolcd_data {
+	struct hid_device *hdev;
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debug_reset;
+	struct dentry *debug_eeprom;
+	struct dentry *debug_flash;
+	struct mutex mutex_flash;
+	int addr_sz;
+#endif
+	u8 version[2];
+	unsigned short opmode_delay;
+	/* input stuff */
+	u8 pressed_keys[2];
+	struct input_dev *input_keys;
+	struct input_dev *input_cir;
+	unsigned short keycode[PICOLCD_KEYS];
+
+#ifdef CONFIG_HID_PICOLCD_FB
+	/* Framebuffer stuff */
+	u8 fb_update_rate;
+	u8 fb_bpp;
+	u8 fb_force;
+	u8 *fb_vbitmap;		/* local copy of what was sent to PicoLCD */
+	u8 *fb_bitmap;		/* framebuffer */
+	struct fb_info *fb_info;
+	struct fb_deferred_io fb_defio;
+#endif /* CONFIG_HID_PICOLCD_FB */
+#ifdef CONFIG_HID_PICOLCD_LCD
+	struct lcd_device *lcd;
+	u8 lcd_contrast;
+#endif /* CONFIG_HID_PICOLCD_LCD */
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+	struct backlight_device *backlight;
+	u8 lcd_brightness;
+	u8 lcd_power;
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+#ifdef CONFIG_HID_PICOLCD_LEDS
+	/* LED stuff */
+	u8 led_state;
+	struct led_classdev *led[8];
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+	/* Housekeeping stuff */
+	spinlock_t lock;
+	struct mutex mutex;
+	struct picolcd_pending *pending;
+	int status;
+#define PICOLCD_BOOTLOADER 1
+#define PICOLCD_FAILED 2
+#define PICOLCD_READY_FB 4
+};
+
+
+/* Find a given report */
+#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
+#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
+
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir);
+
+#ifdef CONFIG_DEBUG_FS
+void picolcd_debug_out_report(struct picolcd_data *data,
+		struct hid_device *hdev, struct hid_report *report);
+#define usbhid_submit_report(a, b, c) \
+	do { \
+		picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
+		usbhid_submit_report(a, b, c); \
+	} while (0)
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+		struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size);
+
+void picolcd_init_devfs(struct picolcd_data *data,
+		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+		struct hid_report *flash_r, struct hid_report *flash_w,
+		struct hid_report *reset);
+
+void picolcd_exit_devfs(struct picolcd_data *data);
+#else
+static inline void picolcd_debug_raw_event(struct picolcd_data *data,
+		struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+}
+static inline void picolcd_init_devfs(struct picolcd_data *data,
+		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+		struct hid_report *flash_r, struct hid_report *flash_w,
+		struct hid_report *reset)
+{
+}
+static inline void picolcd_exit_devfs(struct picolcd_data *data)
+{
+}
+static inline void picolcd_debug_raw_event(struct picolcd_data *data,
+		struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+
+#ifdef CONFIG_HID_PICOLCD_FB
+int picolcd_fb_reset(struct picolcd_data *data, int clear);
+
+int picolcd_init_framebuffer(struct picolcd_data *data);
+
+void picolcd_exit_framebuffer(struct picolcd_data *data);
+
+void picolcd_fb_unload(void);
+
+void picolcd_fb_refresh(struct picolcd_data *data);
+#define picolcd_fbinfo(d) ((d)->fb_info)
+#else
+static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+	return 0;
+}
+static inline int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+	return 0;
+}
+static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+}
+static inline void picolcd_fb_unload(void)
+{
+}
+static inline void picolcd_fb_refresh(struct picolcd_data *data)
+{
+}
+#define picolcd_fbinfo(d) NULL
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+int picolcd_init_backlight(struct picolcd_data *data,
+		struct hid_report *report);
+
+void picolcd_exit_backlight(struct picolcd_data *data);
+
+int picolcd_resume_backlight(struct picolcd_data *data);
+
+void picolcd_suspend_backlight(struct picolcd_data *data);
+#else
+static inline int picolcd_init_backlight(struct picolcd_data *data,
+		struct hid_report *report)
+{
+	return 0;
+}
+static inline void picolcd_exit_backlight(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_backlight(struct picolcd_data *data)
+{
+	return 0;
+}
+static inline void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+}
+
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+
+
+#ifdef CONFIG_HID_PICOLCD_LCD
+int picolcd_init_lcd(struct picolcd_data *data,
+		struct hid_report *report);
+
+void picolcd_exit_lcd(struct picolcd_data *data);
+
+int picolcd_resume_lcd(struct picolcd_data *data);
+#else
+static inline int picolcd_init_lcd(struct picolcd_data *data,
+		struct hid_report *report)
+{
+	return 0;
+}
+static inline void picolcd_exit_lcd(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_lcd(struct picolcd_data *data)
+{
+	return 0;
+}
+#endif /* CONFIG_HID_PICOLCD_LCD */
+
+
+#ifdef CONFIG_HID_PICOLCD_LEDS
+int picolcd_init_leds(struct picolcd_data *data,
+		struct hid_report *report);
+
+void picolcd_exit_leds(struct picolcd_data *data);
+
+void picolcd_leds_set(struct picolcd_data *data);
+#else
+static inline int picolcd_init_leds(struct picolcd_data *data,
+		struct hid_report *report)
+{
+	return 0;
+}
+static inline void picolcd_exit_leds(struct picolcd_data *data)
+{
+}
+static inline void picolcd_leds_set(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+
+#ifdef CONFIG_HID_PICOLCD_CIR
+int picolcd_raw_cir(struct picolcd_data *data,
+		struct hid_report *report, u8 *raw_data, int size);
+
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report);
+
+void picolcd_exit_cir(struct picolcd_data *data);
+#else
+static inline int picolcd_raw_cir(struct picolcd_data *data,
+		struct hid_report *report, u8 *raw_data, int size)
+{
+	return 1;
+}
+static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+	return 0;
+}
+static inline void picolcd_exit_cir(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LIRC */
+
+int picolcd_reset(struct hid_device *hdev);
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+			int report_id, const u8 *raw_data, int size);
diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c
new file mode 100644
index 0000000..cd1ccd7
--- /dev/null
+++ b/drivers/hid/hid-picolcd_backlight.c
@@ -0,0 +1,121 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include "hid-picolcd.h"
+
+static int picolcd_get_brightness(struct backlight_device *bdev)
+{
+	struct picolcd_data *data = bl_get_data(bdev);
+	return data->lcd_brightness;
+}
+
+static int picolcd_set_brightness(struct backlight_device *bdev)
+{
+	struct picolcd_data *data = bl_get_data(bdev);
+	struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
+	unsigned long flags;
+
+	if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+		return -ENODEV;
+
+	data->lcd_brightness = bdev->props.brightness & 0x0ff;
+	data->lcd_power      = bdev->props.power;
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report->field[0], 0, data->lcd_power = FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return 0;
+}
+
+static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
+{
+	return fb && fb = picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
+}
+
+static const struct backlight_ops picolcd_blops = {
+	.update_status  = picolcd_set_brightness,
+	.get_brightness = picolcd_get_brightness,
+	.check_fb       = picolcd_check_bl_fb,
+};
+
+int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
+{
+	struct device *dev = &data->hdev->dev;
+	struct backlight_device *bdev;
+	struct backlight_properties props;
+	if (!report)
+		return -ENODEV;
+	if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+			report->field[0]->report_size != 8) {
+		dev_err(dev, "unsupported BRIGHTNESS report");
+		return -EINVAL;
+	}
+
+	memset(&props, 0, sizeof(props));
+	props.type = BACKLIGHT_RAW;
+	props.max_brightness = 0xff;
+	bdev = backlight_device_register(dev_name(dev), dev, data,
+			&picolcd_blops, &props);
+	if (IS_ERR(bdev)) {
+		dev_err(dev, "failed to register backlight\n");
+		return PTR_ERR(bdev);
+	}
+	bdev->props.brightness     = 0xff;
+	data->lcd_brightness       = 0xff;
+	data->backlight            = bdev;
+	picolcd_set_brightness(bdev);
+	return 0;
+}
+
+void picolcd_exit_backlight(struct picolcd_data *data)
+{
+	struct backlight_device *bdev = data->backlight;
+
+	data->backlight = NULL;
+	if (bdev)
+		backlight_device_unregister(bdev);
+}
+
+int picolcd_resume_backlight(struct picolcd_data *data)
+{
+	if (!data->backlight)
+		return 0;
+	return picolcd_set_brightness(data->backlight);
+}
+
+#ifdef CONFIG_PM
+void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+	int bl_power = data->lcd_power;
+	if (!data->backlight)
+		return;
+
+	data->backlight->props.power = FB_BLANK_POWERDOWN;
+	picolcd_set_brightness(data->backlight);
+	data->lcd_power = data->backlight->props.power = bl_power;
+}
+#endif /* CONFIG_PM */
+
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
new file mode 100644
index 0000000..dc632c3
--- /dev/null
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -0,0 +1,61 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+int picolcd_raw_cir(struct picolcd_data *data,
+		struct hid_report *report, u8 *raw_data, int size)
+{
+	/* Need understanding of CIR data format to implement ... */
+	return 1;
+}
+
+/* initialize CIR input device */
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+	/* support not implemented yet */
+	return 0;
+}
+
+void picolcd_exit_cir(struct picolcd_data *data)
+{
+}
+
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
new file mode 100644
index 0000000..36e7548
--- /dev/null
+++ b/drivers/hid/hid-picolcd_core.c
@@ -0,0 +1,704 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+/* Input device
+ *
+ * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
+ * and header for 4x4 key matrix. The built-in keys are part of the matrix.
+ */
+static const unsigned short def_keymap[PICOLCD_KEYS] = {
+	KEY_RESERVED,	/* none */
+	KEY_BACK,	/* col 4 + row 1 */
+	KEY_HOMEPAGE,	/* col 3 + row 1 */
+	KEY_RESERVED,	/* col 2 + row 1 */
+	KEY_RESERVED,	/* col 1 + row 1 */
+	KEY_SCROLLUP,	/* col 4 + row 2 */
+	KEY_OK,		/* col 3 + row 2 */
+	KEY_SCROLLDOWN,	/* col 2 + row 2 */
+	KEY_RESERVED,	/* col 1 + row 2 */
+	KEY_RESERVED,	/* col 4 + row 3 */
+	KEY_RESERVED,	/* col 3 + row 3 */
+	KEY_RESERVED,	/* col 2 + row 3 */
+	KEY_RESERVED,	/* col 1 + row 3 */
+	KEY_RESERVED,	/* col 4 + row 4 */
+	KEY_RESERVED,	/* col 3 + row 4 */
+	KEY_RESERVED,	/* col 2 + row 4 */
+	KEY_RESERVED,	/* col 1 + row 4 */
+};
+
+
+/* Find a given report */
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
+{
+	struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
+	struct hid_report *report = NULL;
+
+	list_for_each_entry(report, feature_report_list, list) {
+		if (report->id = id)
+			return report;
+	}
+	hid_warn(hdev, "No report with id 0x%x found\n", id);
+	return NULL;
+}
+
+/* Submit a report and wait for a reply from device - if device fades away
+ * or does not respond in time, return NULL */
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+		int report_id, const u8 *raw_data, int size)
+{
+	struct picolcd_data *data = hid_get_drvdata(hdev);
+	struct picolcd_pending *work;
+	struct hid_report *report = picolcd_out_report(report_id, hdev);
+	unsigned long flags;
+	int i, j, k;
+
+	if (!report || !data)
+		return NULL;
+	if (data->status & PICOLCD_FAILED)
+		return NULL;
+	work = kzalloc(sizeof(*work), GFP_KERNEL);
+	if (!work)
+		return NULL;
+
+	init_completion(&work->ready);
+	work->out_report = report;
+	work->in_report  = NULL;
+	work->raw_size   = 0;
+
+	mutex_lock(&data->mutex);
+	spin_lock_irqsave(&data->lock, flags);
+	for (i = k = 0; i < report->maxfield; i++)
+		for (j = 0; j < report->field[i]->report_count; j++) {
+			hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
+			k++;
+		}
+	data->pending = work;
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+	wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
+	spin_lock_irqsave(&data->lock, flags);
+	data->pending = NULL;
+	spin_unlock_irqrestore(&data->lock, flags);
+	mutex_unlock(&data->mutex);
+	return work;
+}
+
+/*
+ * input class device
+ */
+static int picolcd_raw_keypad(struct picolcd_data *data,
+		struct hid_report *report, u8 *raw_data, int size)
+{
+	/*
+	 * Keypad event
+	 * First and second data bytes list currently pressed keys,
+	 * 0x00 means no key and at most 2 keys may be pressed at same time
+	 */
+	int i, j;
+
+	/* determine newly pressed keys */
+	for (i = 0; i < size; i++) {
+		unsigned int key_code;
+		if (raw_data[i] = 0)
+			continue;
+		for (j = 0; j < sizeof(data->pressed_keys); j++)
+			if (data->pressed_keys[j] = raw_data[i])
+				goto key_already_down;
+		for (j = 0; j < sizeof(data->pressed_keys); j++)
+			if (data->pressed_keys[j] = 0) {
+				data->pressed_keys[j] = raw_data[i];
+				break;
+			}
+		input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
+		if (raw_data[i] < PICOLCD_KEYS)
+			key_code = data->keycode[raw_data[i]];
+		else
+			key_code = KEY_UNKNOWN;
+		if (key_code != KEY_UNKNOWN) {
+			dbg_hid(PICOLCD_NAME " got key press for %u:%d",
+					raw_data[i], key_code);
+			input_report_key(data->input_keys, key_code, 1);
+		}
+		input_sync(data->input_keys);
+key_already_down:
+		continue;
+	}
+
+	/* determine newly released keys */
+	for (j = 0; j < sizeof(data->pressed_keys); j++) {
+		unsigned int key_code;
+		if (data->pressed_keys[j] = 0)
+			continue;
+		for (i = 0; i < size; i++)
+			if (data->pressed_keys[j] = raw_data[i])
+				goto key_still_down;
+		input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
+		if (data->pressed_keys[j] < PICOLCD_KEYS)
+			key_code = data->keycode[data->pressed_keys[j]];
+		else
+			key_code = KEY_UNKNOWN;
+		if (key_code != KEY_UNKNOWN) {
+			dbg_hid(PICOLCD_NAME " got key release for %u:%d",
+					data->pressed_keys[j], key_code);
+			input_report_key(data->input_keys, key_code, 0);
+		}
+		input_sync(data->input_keys);
+		data->pressed_keys[j] = 0;
+key_still_down:
+		continue;
+	}
+	return 1;
+}
+
+static int picolcd_check_version(struct hid_device *hdev)
+{
+	struct picolcd_data *data = hid_get_drvdata(hdev);
+	struct picolcd_pending *verinfo;
+	int ret = 0;
+
+	if (!data)
+		return -ENODEV;
+
+	verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
+	if (!verinfo) {
+		hid_err(hdev, "no version response from PicoLCD\n");
+		return -ENODEV;
+	}
+
+	if (verinfo->raw_size = 2) {
+		data->version[0] = verinfo->raw_data[1];
+		data->version[1] = verinfo->raw_data[0];
+		if (data->status & PICOLCD_BOOTLOADER) {
+			hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
+				 verinfo->raw_data[1], verinfo->raw_data[0]);
+		} else {
+			hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
+				 verinfo->raw_data[1], verinfo->raw_data[0]);
+		}
+	} else {
+		hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
+		ret = -EINVAL;
+	}
+	kfree(verinfo);
+	return ret;
+}
+
+/*
+ * Reset our device and wait for answer to VERSION request
+ */
+int picolcd_reset(struct hid_device *hdev)
+{
+	struct picolcd_data *data = hid_get_drvdata(hdev);
+	struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
+	unsigned long flags;
+	int error;
+
+	if (!data || !report || report->maxfield != 1)
+		return -ENODEV;
+
+	spin_lock_irqsave(&data->lock, flags);
+	if (hdev->product = USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+		data->status |= PICOLCD_BOOTLOADER;
+
+	/* perform the reset */
+	hid_set_field(report->field[0], 0, 1);
+	usbhid_submit_report(hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	error = picolcd_check_version(hdev);
+	if (error)
+		return error;
+
+	picolcd_resume_lcd(data);
+	picolcd_resume_backlight(data);
+	picolcd_fb_refresh(data);
+	picolcd_leds_set(data);
+	return 0;
+}
+
+/*
+ * The "operation_mode" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct picolcd_data *data = dev_get_drvdata(dev);
+
+	if (data->status & PICOLCD_BOOTLOADER)
+		return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
+	else
+		return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
+}
+
+static ssize_t picolcd_operation_mode_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct picolcd_data *data = dev_get_drvdata(dev);
+	struct hid_report *report = NULL;
+	size_t cnt = count;
+	int timeout = data->opmode_delay;
+	unsigned long flags;
+
+	if (cnt >= 3 && strncmp("lcd", buf, 3) = 0) {
+		if (data->status & PICOLCD_BOOTLOADER)
+			report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
+		buf += 3;
+		cnt -= 3;
+	} else if (cnt >= 10 && strncmp("bootloader", buf, 10) = 0) {
+		if (!(data->status & PICOLCD_BOOTLOADER))
+			report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
+		buf += 10;
+		cnt -= 10;
+	}
+	if (!report)
+		return -EINVAL;
+
+	while (cnt > 0 && (buf[cnt-1] = '\n' || buf[cnt-1] = '\r'))
+		cnt--;
+	if (cnt != 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report->field[0], 0, timeout & 0xff);
+	hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return count;
+}
+
+static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
+		picolcd_operation_mode_store);
+
+/*
+ * The "operation_mode_delay" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct picolcd_data *data = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
+}
+
+static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct picolcd_data *data = dev_get_drvdata(dev);
+	unsigned u;
+	if (sscanf(buf, "%u", &u) != 1)
+		return -EINVAL;
+	if (u > 30000)
+		return -EINVAL;
+	else
+		data->opmode_delay = u;
+	return count;
+}
+
+static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
+		picolcd_operation_mode_delay_store);
+
+/*
+ * Handle raw report as sent by device
+ */
+static int picolcd_raw_event(struct hid_device *hdev,
+		struct hid_report *report, u8 *raw_data, int size)
+{
+	struct picolcd_data *data = hid_get_drvdata(hdev);
+	unsigned long flags;
+	int ret = 0;
+
+	if (!data)
+		return 1;
+
+	if (report->id = REPORT_KEY_STATE) {
+		if (data->input_keys)
+			ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
+	} else if (report->id = REPORT_IR_DATA) {
+		if (data->input_cir)
+			ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
+	} else {
+		spin_lock_irqsave(&data->lock, flags);
+		/*
+		 * We let the caller of picolcd_send_and_wait() check if the
+		 * report we got is one of the expected ones or not.
+		 */
+		if (data->pending) {
+			memcpy(data->pending->raw_data, raw_data+1, size-1);
+			data->pending->raw_size  = size-1;
+			data->pending->in_report = report;
+			complete(&data->pending->ready);
+		}
+		spin_unlock_irqrestore(&data->lock, flags);
+	}
+
+	picolcd_debug_raw_event(data, hdev, report, raw_data, size);
+	return 1;
+}
+
+#ifdef CONFIG_PM
+static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
+{
+	if (PMSG_IS_AUTO(message))
+		return 0;
+
+	picolcd_suspend_backlight(hid_get_drvdata(hdev));
+	dbg_hid(PICOLCD_NAME " device ready for suspend\n");
+	return 0;
+}
+
+static int picolcd_resume(struct hid_device *hdev)
+{
+	int ret;
+	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+	if (ret)
+		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+	return 0;
+}
+
+static int picolcd_reset_resume(struct hid_device *hdev)
+{
+	int ret;
+	ret = picolcd_reset(hdev);
+	if (ret)
+		dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
+	ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
+	if (ret)
+		dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
+	ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
+	if (ret)
+		dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
+	ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+	if (ret)
+		dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+	picolcd_leds_set(hid_get_drvdata(hdev));
+	return 0;
+}
+#endif
+
+/* initialize keypad input device */
+static int picolcd_init_keys(struct picolcd_data *data,
+		struct hid_report *report)
+{
+	struct hid_device *hdev = data->hdev;
+	struct input_dev *idev;
+	int error, i;
+
+	if (!report)
+		return -ENODEV;
+	if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
+			report->field[0]->report_size != 8) {
+		hid_err(hdev, "unsupported KEY_STATE report\n");
+		return -EINVAL;
+	}
+
+	idev = input_allocate_device();
+	if (idev = NULL) {
+		hid_err(hdev, "failed to allocate input device\n");
+		return -ENOMEM;
+	}
+	input_set_drvdata(idev, hdev);
+	memcpy(data->keycode, def_keymap, sizeof(def_keymap));
+	idev->name = hdev->name;
+	idev->phys = hdev->phys;
+	idev->uniq = hdev->uniq;
+	idev->id.bustype = hdev->bus;
+	idev->id.vendor  = hdev->vendor;
+	idev->id.product = hdev->product;
+	idev->id.version = hdev->version;
+	idev->dev.parent = hdev->dev.parent;
+	idev->keycode     = &data->keycode;
+	idev->keycodemax  = PICOLCD_KEYS;
+	idev->keycodesize = sizeof(data->keycode[0]);
+	input_set_capability(idev, EV_MSC, MSC_SCAN);
+	set_bit(EV_REP, idev->evbit);
+	for (i = 0; i < PICOLCD_KEYS; i++)
+		input_set_capability(idev, EV_KEY, data->keycode[i]);
+	error = input_register_device(idev);
+	if (error) {
+		hid_err(hdev, "error registering the input device\n");
+		input_free_device(idev);
+		return error;
+	}
+	data->input_keys = idev;
+	return 0;
+}
+
+static void picolcd_exit_keys(struct picolcd_data *data)
+{
+	struct input_dev *idev = data->input_keys;
+
+	data->input_keys = NULL;
+	if (idev)
+		input_unregister_device(idev);
+}
+
+static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
+{
+	int error;
+
+	error = picolcd_check_version(hdev);
+	if (error)
+		return error;
+
+	if (data->version[0] != 0 && data->version[1] != 3)
+		hid_info(hdev, "Device with untested firmware revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
+			 dev_name(&hdev->dev));
+
+	/* Setup keypad input device */
+	error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
+	if (error)
+		goto err;
+
+	/* Setup CIR input device */
+	error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
+	if (error)
+		goto err;
+
+	/* Set up the framebuffer device */
+	error = picolcd_init_framebuffer(data);
+	if (error)
+		goto err;
+
+	/* Setup lcd class device */
+	error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
+	if (error)
+		goto err;
+
+	/* Setup backlight class device */
+	error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
+	if (error)
+		goto err;
+
+	/* Setup the LED class devices */
+	error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
+	if (error)
+		goto err;
+
+	picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
+			picolcd_out_report(REPORT_EE_WRITE, hdev),
+			picolcd_out_report(REPORT_READ_MEMORY, hdev),
+			picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
+			picolcd_out_report(REPORT_RESET, hdev));
+	return 0;
+err:
+	picolcd_exit_leds(data);
+	picolcd_exit_backlight(data);
+	picolcd_exit_lcd(data);
+	picolcd_exit_framebuffer(data);
+	picolcd_exit_cir(data);
+	picolcd_exit_keys(data);
+	return error;
+}
+
+static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
+{
+	int error;
+
+	error = picolcd_check_version(hdev);
+	if (error)
+		return error;
+
+	if (data->version[0] != 1 && data->version[1] != 0)
+		hid_info(hdev, "Device with untested bootloader revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
+			 dev_name(&hdev->dev));
+
+	picolcd_init_devfs(data, NULL, NULL,
+			picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
+			picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
+	return 0;
+}
+
+static int picolcd_probe(struct hid_device *hdev,
+		     const struct hid_device_id *id)
+{
+	struct picolcd_data *data;
+	int error = -ENOMEM;
+
+	dbg_hid(PICOLCD_NAME " hardware probe...\n");
+
+	/*
+	 * Let's allocate the picolcd data structure, set some reasonable
+	 * defaults, and associate it with the device
+	 */
+	data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
+	if (data = NULL) {
+		hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
+		error = -ENOMEM;
+		goto err_no_cleanup;
+	}
+
+	spin_lock_init(&data->lock);
+	mutex_init(&data->mutex);
+	data->hdev = hdev;
+	data->opmode_delay = 5000;
+	if (hdev->product = USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+		data->status |= PICOLCD_BOOTLOADER;
+	hid_set_drvdata(hdev, data);
+
+	/* Parse the device reports and start it up */
+	error = hid_parse(hdev);
+	if (error) {
+		hid_err(hdev, "device report parse failed\n");
+		goto err_cleanup_data;
+	}
+
+	/* We don't use hidinput but hid_hw_start() fails if nothing is
+	 * claimed. So spoof claimed input. */
+	hdev->claimed = HID_CLAIMED_INPUT;
+	error = hid_hw_start(hdev, 0);
+	hdev->claimed = 0;
+	if (error) {
+		hid_err(hdev, "hardware start failed\n");
+		goto err_cleanup_data;
+	}
+
+	error = hid_hw_open(hdev);
+	if (error) {
+		hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
+		goto err_cleanup_hid_hw;
+	}
+
+	error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
+	if (error) {
+		hid_err(hdev, "failed to create sysfs attributes\n");
+		goto err_cleanup_hid_ll;
+	}
+
+	error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
+	if (error) {
+		hid_err(hdev, "failed to create sysfs attributes\n");
+		goto err_cleanup_sysfs1;
+	}
+
+	if (data->status & PICOLCD_BOOTLOADER)
+		error = picolcd_probe_bootloader(hdev, data);
+	else
+		error = picolcd_probe_lcd(hdev, data);
+	if (error)
+		goto err_cleanup_sysfs2;
+
+	dbg_hid(PICOLCD_NAME " activated and initialized\n");
+	return 0;
+
+err_cleanup_sysfs2:
+	device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+err_cleanup_sysfs1:
+	device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+err_cleanup_hid_ll:
+	hid_hw_close(hdev);
+err_cleanup_hid_hw:
+	hid_hw_stop(hdev);
+err_cleanup_data:
+	kfree(data);
+err_no_cleanup:
+	hid_set_drvdata(hdev, NULL);
+
+	return error;
+}
+
+static void picolcd_remove(struct hid_device *hdev)
+{
+	struct picolcd_data *data = hid_get_drvdata(hdev);
+	unsigned long flags;
+
+	dbg_hid(PICOLCD_NAME " hardware remove...\n");
+	spin_lock_irqsave(&data->lock, flags);
+	data->status |= PICOLCD_FAILED;
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	picolcd_exit_devfs(data);
+	device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+	device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+	hid_hw_close(hdev);
+	hid_hw_stop(hdev);
+	hid_set_drvdata(hdev, NULL);
+
+	/* Shortcut potential pending reply that will never arrive */
+	spin_lock_irqsave(&data->lock, flags);
+	if (data->pending)
+		complete(&data->pending->ready);
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	/* Cleanup LED */
+	picolcd_exit_leds(data);
+	/* Clean up the framebuffer */
+	picolcd_exit_backlight(data);
+	picolcd_exit_lcd(data);
+	picolcd_exit_framebuffer(data);
+	/* Cleanup input */
+	picolcd_exit_cir(data);
+	picolcd_exit_keys(data);
+
+	mutex_destroy(&data->mutex);
+	/* Finally, clean up the picolcd data itself */
+	kfree(data);
+}
+
+static const struct hid_device_id picolcd_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, picolcd_devices);
+
+static struct hid_driver picolcd_driver = {
+	.name =          "hid-picolcd",
+	.id_table =      picolcd_devices,
+	.probe =         picolcd_probe,
+	.remove =        picolcd_remove,
+	.raw_event =     picolcd_raw_event,
+#ifdef CONFIG_PM
+	.suspend =       picolcd_suspend,
+	.resume =        picolcd_resume,
+	.reset_resume =  picolcd_reset_resume,
+#endif
+};
+
+static int __init picolcd_init(void)
+{
+	return hid_register_driver(&picolcd_driver);
+}
+
+static void __exit picolcd_exit(void)
+{
+	hid_unregister_driver(&picolcd_driver);
+	picolcd_fb_unload();
+}
+
+module_init(picolcd_init);
+module_exit(picolcd_exit);
+MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
new file mode 100644
index 0000000..ff74645
--- /dev/null
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -0,0 +1,898 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+static int picolcd_debug_reset_show(struct seq_file *f, void *p)
+{
+	if (picolcd_fbinfo((struct picolcd_data *)f->private))
+		seq_printf(f, "all fb\n");
+	else
+		seq_printf(f, "all\n");
+	return 0;
+}
+
+static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
+{
+	return single_open(f, picolcd_debug_reset_show, inode->i_private);
+}
+
+static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
+		size_t count, loff_t *ppos)
+{
+	struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
+	char buf[32];
+	size_t cnt = min(count, sizeof(buf)-1);
+	if (copy_from_user(buf, user_buf, cnt))
+		return -EFAULT;
+
+	while (cnt > 0 && (buf[cnt-1] = ' ' || buf[cnt-1] = '\n'))
+		cnt--;
+	buf[cnt] = '\0';
+	if (strcmp(buf, "all") = 0) {
+		picolcd_reset(data->hdev);
+		picolcd_fb_reset(data, 1);
+	} else if (strcmp(buf, "fb") = 0) {
+		picolcd_fb_reset(data, 1);
+	} else {
+		return -EINVAL;
+	}
+	return count;
+}
+
+static const struct file_operations picolcd_debug_reset_fops = {
+	.owner    = THIS_MODULE,
+	.open     = picolcd_debug_reset_open,
+	.read     = seq_read,
+	.llseek   = seq_lseek,
+	.write    = picolcd_debug_reset_write,
+	.release  = single_release,
+};
+
+/*
+ * The "eeprom" file
+ */
+static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+	struct picolcd_pending *resp;
+	u8 raw_data[3];
+	ssize_t ret = -EIO;
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x0ff)
+		return 0;
+
+	/* prepare buffer with info about what we want to read (addr & len) */
+	raw_data[0] = *off & 0xff;
+	raw_data[1] = (*off >> 8) & 0xff;
+	raw_data[2] = s < 20 ? s : 20;
+	if (*off + raw_data[2] > 0xff)
+		raw_data[2] = 0x100 - *off;
+	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
+			sizeof(raw_data));
+	if (!resp)
+		return -EIO;
+
+	if (resp->in_report && resp->in_report->id = REPORT_EE_DATA) {
+		/* successful read :) */
+		ret = resp->raw_data[2];
+		if (ret > s)
+			ret = s;
+		if (copy_to_user(u, resp->raw_data+3, ret))
+			ret = -EFAULT;
+		else
+			*off += ret;
+	} /* anything else is some kind of IO error */
+
+	kfree(resp);
+	return ret;
+}
+
+static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+	struct picolcd_pending *resp;
+	ssize_t ret = -EIO;
+	u8 raw_data[23];
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x0ff)
+		return -ENOSPC;
+
+	memset(raw_data, 0, sizeof(raw_data));
+	raw_data[0] = *off & 0xff;
+	raw_data[1] = (*off >> 8) & 0xff;
+	raw_data[2] = min((size_t)20, s);
+	if (*off + raw_data[2] > 0xff)
+		raw_data[2] = 0x100 - *off;
+
+	if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
+		return -EFAULT;
+	resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
+			sizeof(raw_data));
+
+	if (!resp)
+		return -EIO;
+
+	if (resp->in_report && resp->in_report->id = REPORT_EE_DATA) {
+		/* check if written data matches */
+		if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) = 0) {
+			*off += raw_data[2];
+			ret = raw_data[2];
+		}
+	}
+	kfree(resp);
+	return ret;
+}
+
+/*
+ * Notes:
+ * - read/write happens in chunks of at most 20 bytes, it's up to userspace
+ *   to loop in order to get more data.
+ * - on write errors on otherwise correct write request the bytes
+ *   that should have been written are in undefined state.
+ */
+static const struct file_operations picolcd_debug_eeprom_fops = {
+	.owner    = THIS_MODULE,
+	.open     = simple_open,
+	.read     = picolcd_debug_eeprom_read,
+	.write    = picolcd_debug_eeprom_write,
+	.llseek   = generic_file_llseek,
+};
+
+/*
+ * The "flash" file
+ */
+/* record a flash address to buf (bounds check to be done by caller) */
+static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
+{
+	buf[0] = off & 0xff;
+	buf[1] = (off >> 8) & 0xff;
+	if (data->addr_sz = 3)
+		buf[2] = (off >> 16) & 0xff;
+	return data->addr_sz = 2 ? 2 : 3;
+}
+
+/* read a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
+		char __user *u, size_t s, loff_t *off)
+{
+	struct picolcd_pending *resp;
+	u8 raw_data[4];
+	ssize_t ret = 0;
+	int len_off, err = -EIO;
+
+	while (s > 0) {
+		err = -EIO;
+		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+		raw_data[len_off] = s > 32 ? 32 : s;
+		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
+		if (!resp || !resp->in_report)
+			goto skip;
+		if (resp->in_report->id = REPORT_MEMORY ||
+			resp->in_report->id = REPORT_BL_READ_MEMORY) {
+			if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
+				goto skip;
+			if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
+				err = -EFAULT;
+				goto skip;
+			}
+			*off += raw_data[len_off];
+			s    -= raw_data[len_off];
+			ret  += raw_data[len_off];
+			err   = 0;
+		}
+skip:
+		kfree(resp);
+		if (err)
+			return ret > 0 ? ret : err;
+	}
+	return ret;
+}
+
+static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x05fff)
+		return 0;
+	if (*off + s > 0x05fff)
+		s = 0x06000 - *off;
+
+	if (data->status & PICOLCD_BOOTLOADER)
+		return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
+	else
+		return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
+}
+
+/* erase block aligned to 64bytes boundary */
+static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
+		loff_t *off)
+{
+	struct picolcd_pending *resp;
+	u8 raw_data[3];
+	int len_off;
+	ssize_t ret = -EIO;
+
+	if (*off & 0x3f)
+		return -EINVAL;
+
+	len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+	resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
+	if (!resp || !resp->in_report)
+		goto skip;
+	if (resp->in_report->id = REPORT_MEMORY ||
+		resp->in_report->id = REPORT_BL_ERASE_MEMORY) {
+		if (memcmp(raw_data, resp->raw_data, len_off) != 0)
+			goto skip;
+		ret = 0;
+	}
+skip:
+	kfree(resp);
+	return ret;
+}
+
+/* write a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
+		const char __user *u, size_t s, loff_t *off)
+{
+	struct picolcd_pending *resp;
+	u8 raw_data[36];
+	ssize_t ret = 0;
+	int len_off, err = -EIO;
+
+	while (s > 0) {
+		err = -EIO;
+		len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+		raw_data[len_off] = s > 32 ? 32 : s;
+		if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
+			err = -EFAULT;
+			break;
+		}
+		resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
+				len_off+1+raw_data[len_off]);
+		if (!resp || !resp->in_report)
+			goto skip;
+		if (resp->in_report->id = REPORT_MEMORY ||
+			resp->in_report->id = REPORT_BL_WRITE_MEMORY) {
+			if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
+				goto skip;
+			*off += raw_data[len_off];
+			s    -= raw_data[len_off];
+			ret  += raw_data[len_off];
+			err   = 0;
+		}
+skip:
+		kfree(resp);
+		if (err)
+			break;
+	}
+	return ret > 0 ? ret : err;
+}
+
+static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
+		size_t s, loff_t *off)
+{
+	struct picolcd_data *data = f->private_data;
+	ssize_t err, ret = 0;
+	int report_erase, report_write;
+
+	if (s = 0)
+		return -EINVAL;
+	if (*off > 0x5fff)
+		return -ENOSPC;
+	if (s & 0x3f)
+		return -EINVAL;
+	if (*off & 0x3f)
+		return -EINVAL;
+
+	if (data->status & PICOLCD_BOOTLOADER) {
+		report_erase = REPORT_BL_ERASE_MEMORY;
+		report_write = REPORT_BL_WRITE_MEMORY;
+	} else {
+		report_erase = REPORT_ERASE_MEMORY;
+		report_write = REPORT_WRITE_MEMORY;
+	}
+	mutex_lock(&data->mutex_flash);
+	while (s > 0) {
+		err = _picolcd_flash_erase64(data, report_erase, off);
+		if (err)
+			break;
+		err = _picolcd_flash_write(data, report_write, u, 64, off);
+		if (err < 0)
+			break;
+		ret += err;
+		*off += err;
+		s -= err;
+		if (err != 64)
+			break;
+	}
+	mutex_unlock(&data->mutex_flash);
+	return ret > 0 ? ret : err;
+}
+
+/*
+ * Notes:
+ * - concurrent writing is prevented by mutex and all writes must be
+ *   n*64 bytes and 64-byte aligned, each write being preceded by an
+ *   ERASE which erases a 64byte block.
+ *   If less than requested was written or an error is returned for an
+ *   otherwise correct write request the next 64-byte block which should
+ *   have been written is in undefined state (mostly: original, erased,
+ *   (half-)written with write error)
+ * - reading can happen without special restriction
+ */
+static const struct file_operations picolcd_debug_flash_fops = {
+	.owner    = THIS_MODULE,
+	.open     = simple_open,
+	.read     = picolcd_debug_flash_read,
+	.write    = picolcd_debug_flash_write,
+	.llseek   = generic_file_llseek,
+};
+
+
+/*
+ * Helper code for HID report level dumping/debugging
+ */
+static const char *error_codes[] = {
+	"success", "parameter missing", "data_missing", "block readonly",
+	"block not erasable", "block too big", "section overflow",
+	"invalid command length", "invalid data length",
+};
+
+static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
+		const size_t data_len)
+{
+	int i, j;
+	for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) {
+		dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
+		dst[j++] = hex_asc[data[i] & 0x0f];
+		dst[j++] = ' ';
+	}
+	if (j < dst_sz) {
+		dst[j--] = '\0';
+		dst[j] = '\n';
+	} else
+		dst[j] = '\0';
+}
+
+void picolcd_debug_out_report(struct picolcd_data *data,
+		struct hid_device *hdev, struct hid_report *report)
+{
+	u8 raw_data[70];
+	int raw_size = (report->size >> 3) + 1;
+	char *buff;
+#define BUFF_SZ 256
+
+	/* Avoid unnecessary overhead if debugfs is disabled */
+	if (!hdev->debug_events)
+		return;
+
+	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+	if (!buff)
+		return;
+
+	snprintf(buff, BUFF_SZ, "\nout report %d (size %d) =  ",
+			report->id, raw_size);
+	hid_debug_event(hdev, buff);
+	if (raw_size + 5 > sizeof(raw_data)) {
+		kfree(buff);
+		hid_debug_event(hdev, " TOO BIG\n");
+		return;
+	} else {
+		raw_data[0] = report->id;
+		hid_output_report(report, raw_data);
+		dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
+		hid_debug_event(hdev, buff);
+	}
+
+	switch (report->id) {
+	case REPORT_LED_STATE:
+		/* 1 data byte with GPO state */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_LED_STATE", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_BRIGHTNESS:
+		/* 1 data byte with brightness */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_BRIGHTNESS", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_CONTRAST:
+		/* 1 data byte with contrast */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_CONTRAST", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_RESET:
+		/* 2 data bytes with reset duration in ms */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_RESET", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
+				raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_LCD_CMD:
+		/* 63 data bytes with LCD commands */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_LCD_CMD", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		/* TODO: format decoding */
+		break;
+	case REPORT_LCD_DATA:
+		/* 63 data bytes with LCD data */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_LCD_CMD", report->id, raw_size-1);
+		/* TODO: format decoding */
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_LCD_CMD_DATA:
+		/* 63 data bytes with LCD commands and data */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_LCD_CMD", report->id, raw_size-1);
+		/* TODO: format decoding */
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_EE_READ:
+		/* 3 data bytes with read area description */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_EE_READ", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+				raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_EE_WRITE:
+		/* 3+1..20 data bytes with write area description */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_EE_WRITE", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+				raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+		hid_debug_event(hdev, buff);
+		if (raw_data[3] = 0) {
+			snprintf(buff, BUFF_SZ, "\tNo data\n");
+		} else if (raw_data[3] + 4 <= raw_size) {
+			snprintf(buff, BUFF_SZ, "\tData: ");
+			hid_debug_event(hdev, buff);
+			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+		} else {
+			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+		}
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_ERASE_MEMORY:
+	case REPORT_BL_ERASE_MEMORY:
+		/* 3 data bytes with pointer inside erase block */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_ERASE_MEMORY", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		switch (data->addr_sz) {
+		case 2:
+			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
+					raw_data[2], raw_data[1]);
+			break;
+		case 3:
+			snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
+					raw_data[3], raw_data[2], raw_data[1]);
+			break;
+		default:
+			snprintf(buff, BUFF_SZ, "\tNot supported\n");
+		}
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_READ_MEMORY:
+	case REPORT_BL_READ_MEMORY:
+		/* 4 data bytes with read area description */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_READ_MEMORY", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		switch (data->addr_sz) {
+		case 2:
+			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+					raw_data[2], raw_data[1]);
+			hid_debug_event(hdev, buff);
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+			break;
+		case 3:
+			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+					raw_data[3], raw_data[2], raw_data[1]);
+			hid_debug_event(hdev, buff);
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+			break;
+		default:
+			snprintf(buff, BUFF_SZ, "\tNot supported\n");
+		}
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_WRITE_MEMORY:
+	case REPORT_BL_WRITE_MEMORY:
+		/* 4+1..32 data bytes with write adrea description */
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_WRITE_MEMORY", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		switch (data->addr_sz) {
+		case 2:
+			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+					raw_data[2], raw_data[1]);
+			hid_debug_event(hdev, buff);
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+			hid_debug_event(hdev, buff);
+			if (raw_data[3] = 0) {
+				snprintf(buff, BUFF_SZ, "\tNo data\n");
+			} else if (raw_data[3] + 4 <= raw_size) {
+				snprintf(buff, BUFF_SZ, "\tData: ");
+				hid_debug_event(hdev, buff);
+				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+			} else {
+				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+			}
+			break;
+		case 3:
+			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+					raw_data[3], raw_data[2], raw_data[1]);
+			hid_debug_event(hdev, buff);
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+			hid_debug_event(hdev, buff);
+			if (raw_data[4] = 0) {
+				snprintf(buff, BUFF_SZ, "\tNo data\n");
+			} else if (raw_data[4] + 5 <= raw_size) {
+				snprintf(buff, BUFF_SZ, "\tData: ");
+				hid_debug_event(hdev, buff);
+				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+			} else {
+				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+			}
+			break;
+		default:
+			snprintf(buff, BUFF_SZ, "\tNot supported\n");
+		}
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_SPLASH_RESTART:
+		/* TODO */
+		break;
+	case REPORT_EXIT_KEYBOARD:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+				raw_data[1] | (raw_data[2] << 8),
+				raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_VERSION:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_VERSION", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_DEVID:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_DEVID", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_SPLASH_SIZE:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_SPLASH_SIZE", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_HOOK_VERSION:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_HOOK_VERSION", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_EXIT_FLASHER:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"REPORT_VERSION", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+				raw_data[1] | (raw_data[2] << 8),
+				raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	default:
+		snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+			"<unknown>", report->id, raw_size-1);
+		hid_debug_event(hdev, buff);
+		break;
+	}
+	wake_up_interruptible(&hdev->debug_wait);
+	kfree(buff);
+}
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+		struct hid_device *hdev, struct hid_report *report,
+		u8 *raw_data, int size)
+{
+	char *buff;
+
+#define BUFF_SZ 256
+	/* Avoid unnecessary overhead if debugfs is disabled */
+	if (!hdev->debug_events)
+		return;
+
+	buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+	if (!buff)
+		return;
+
+	switch (report->id) {
+	case REPORT_ERROR_CODE:
+		/* 2 data bytes with affected report and error code */
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_ERROR_CODE", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		if (raw_data[2] < ARRAY_SIZE(error_codes))
+			snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
+					raw_data[2], error_codes[raw_data[2]], raw_data[1]);
+		else
+			snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
+					raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_KEY_STATE:
+		/* 2 data bytes with key state */
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_KEY_STATE", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		if (raw_data[1] = 0)
+			snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
+		else if (raw_data[2] = 0)
+			snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
+					raw_data[1], raw_data[1]);
+		else
+			snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
+					raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_IR_DATA:
+		/* Up to 20 byes of IR scancode data */
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_IR_DATA", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		if (raw_data[1] = 0) {
+			snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
+			hid_debug_event(hdev, buff);
+		} else if (raw_data[1] + 1 <= size) {
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
+					raw_data[1]-1);
+			hid_debug_event(hdev, buff);
+			dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1);
+			hid_debug_event(hdev, buff);
+		} else {
+			snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
+					raw_data[1]-1);
+			hid_debug_event(hdev, buff);
+		}
+		break;
+	case REPORT_EE_DATA:
+		/* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_EE_DATA", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+				raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+		hid_debug_event(hdev, buff);
+		if (raw_data[3] = 0) {
+			snprintf(buff, BUFF_SZ, "\tNo data\n");
+			hid_debug_event(hdev, buff);
+		} else if (raw_data[3] + 4 <= size) {
+			snprintf(buff, BUFF_SZ, "\tData: ");
+			hid_debug_event(hdev, buff);
+			dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+			hid_debug_event(hdev, buff);
+		} else {
+			snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+			hid_debug_event(hdev, buff);
+		}
+		break;
+	case REPORT_MEMORY:
+		/* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_MEMORY", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		switch (data->addr_sz) {
+		case 2:
+			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+					raw_data[2], raw_data[1]);
+			hid_debug_event(hdev, buff);
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+			hid_debug_event(hdev, buff);
+			if (raw_data[3] = 0) {
+				snprintf(buff, BUFF_SZ, "\tNo data\n");
+			} else if (raw_data[3] + 4 <= size) {
+				snprintf(buff, BUFF_SZ, "\tData: ");
+				hid_debug_event(hdev, buff);
+				dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+			} else {
+				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+			}
+			break;
+		case 3:
+			snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+					raw_data[3], raw_data[2], raw_data[1]);
+			hid_debug_event(hdev, buff);
+			snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+			hid_debug_event(hdev, buff);
+			if (raw_data[4] = 0) {
+				snprintf(buff, BUFF_SZ, "\tNo data\n");
+			} else if (raw_data[4] + 5 <= size) {
+				snprintf(buff, BUFF_SZ, "\tData: ");
+				hid_debug_event(hdev, buff);
+				dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+			} else {
+				snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+			}
+			break;
+		default:
+			snprintf(buff, BUFF_SZ, "\tNot supported\n");
+		}
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_VERSION:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_VERSION", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+				raw_data[2], raw_data[1]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_BL_ERASE_MEMORY:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_BL_ERASE_MEMORY", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		/* TODO */
+		break;
+	case REPORT_BL_READ_MEMORY:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_BL_READ_MEMORY", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		/* TODO */
+		break;
+	case REPORT_BL_WRITE_MEMORY:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_BL_WRITE_MEMORY", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		/* TODO */
+		break;
+	case REPORT_DEVID:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_DEVID", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
+				raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
+				raw_data[5]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_SPLASH_SIZE:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_SPLASH_SIZE", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
+				(raw_data[2] << 8) | raw_data[1]);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
+				(raw_data[4] << 8) | raw_data[3]);
+		hid_debug_event(hdev, buff);
+		break;
+	case REPORT_HOOK_VERSION:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"REPORT_HOOK_VERSION", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+				raw_data[1], raw_data[2]);
+		hid_debug_event(hdev, buff);
+		break;
+	default:
+		snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+			"<unknown>", report->id, size-1);
+		hid_debug_event(hdev, buff);
+		break;
+	}
+	wake_up_interruptible(&hdev->debug_wait);
+	kfree(buff);
+}
+
+void picolcd_init_devfs(struct picolcd_data *data,
+		struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+		struct hid_report *flash_r, struct hid_report *flash_w,
+		struct hid_report *reset)
+{
+	struct hid_device *hdev = data->hdev;
+
+	mutex_init(&data->mutex_flash);
+
+	/* reset */
+	if (reset)
+		data->debug_reset = debugfs_create_file("reset", 0600,
+				hdev->debug_dir, data, &picolcd_debug_reset_fops);
+
+	/* eeprom */
+	if (eeprom_r || eeprom_w)
+		data->debug_eeprom = debugfs_create_file("eeprom",
+			(eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
+			hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
+
+	/* flash */
+	if (flash_r && flash_r->maxfield = 1 && flash_r->field[0]->report_size = 8)
+		data->addr_sz = flash_r->field[0]->report_count - 1;
+	else
+		data->addr_sz = -1;
+	if (data->addr_sz = 2 || data->addr_sz = 3) {
+		data->debug_flash = debugfs_create_file("flash",
+			(flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
+			hdev->debug_dir, data, &picolcd_debug_flash_fops);
+	} else if (flash_r || flash_w)
+		hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
+}
+
+void picolcd_exit_devfs(struct picolcd_data *data)
+{
+	struct dentry *dent;
+
+	dent = data->debug_reset;
+	data->debug_reset = NULL;
+	if (dent)
+		debugfs_remove(dent);
+	dent = data->debug_eeprom;
+	data->debug_eeprom = NULL;
+	if (dent)
+		debugfs_remove(dent);
+	dent = data->debug_flash;
+	data->debug_flash = NULL;
+	if (dent)
+		debugfs_remove(dent);
+	mutex_destroy(&data->mutex_flash);
+}
+
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
new file mode 100644
index 0000000..b2124f5
--- /dev/null
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -0,0 +1,673 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+/* Framebuffer
+ *
+ * The PicoLCD use a Topway LCD module of 256x64 pixel
+ * This display area is tiled over 4 controllers with 8 tiles
+ * each. Each tile has 8x64 pixel, each data byte representing
+ * a 1-bit wide vertical line of the tile.
+ *
+ * The display can be updated at a tile granularity.
+ *
+ *       Chip 1           Chip 2           Chip 3           Chip 4
+ * +----------------+----------------+----------------+----------------+
+ * |     Tile 1     |     Tile 1     |     Tile 1     |     Tile 1     |
+ * +----------------+----------------+----------------+----------------+
+ * |     Tile 2     |     Tile 2     |     Tile 2     |     Tile 2     |
+ * +----------------+----------------+----------------+----------------+
+ *                                  ...
+ * +----------------+----------------+----------------+----------------+
+ * |     Tile 8     |     Tile 8     |     Tile 8     |     Tile 8     |
+ * +----------------+----------------+----------------+----------------+
+ */
+#define PICOLCDFB_NAME "picolcdfb"
+#define PICOLCDFB_WIDTH (256)
+#define PICOLCDFB_HEIGHT (64)
+#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
+
+#define PICOLCDFB_UPDATE_RATE_LIMIT   10
+#define PICOLCDFB_UPDATE_RATE_DEFAULT  2
+
+/* Framebuffer visual structures */
+static const struct fb_fix_screeninfo picolcdfb_fix = {
+	.id          = PICOLCDFB_NAME,
+	.type        = FB_TYPE_PACKED_PIXELS,
+	.visual      = FB_VISUAL_MONO01,
+	.xpanstep    = 0,
+	.ypanstep    = 0,
+	.ywrapstep   = 0,
+	.line_length = PICOLCDFB_WIDTH / 8,
+	.accel       = FB_ACCEL_NONE,
+};
+
+static const struct fb_var_screeninfo picolcdfb_var = {
+	.xres           = PICOLCDFB_WIDTH,
+	.yres           = PICOLCDFB_HEIGHT,
+	.xres_virtual   = PICOLCDFB_WIDTH,
+	.yres_virtual   = PICOLCDFB_HEIGHT,
+	.width          = 103,
+	.height         = 26,
+	.bits_per_pixel = 1,
+	.grayscale      = 1,
+	.red            = {
+		.offset = 0,
+		.length = 1,
+		.msb_right = 0,
+	},
+	.green          = {
+		.offset = 0,
+		.length = 1,
+		.msb_right = 0,
+	},
+	.blue           = {
+		.offset = 0,
+		.length = 1,
+		.msb_right = 0,
+	},
+	.transp         = {
+		.offset = 0,
+		.length = 0,
+		.msb_right = 0,
+	},
+};
+
+/* Send a given tile to PicoLCD */
+static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile)
+{
+	struct picolcd_data *data = hid_get_drvdata(hdev);
+	struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev);
+	struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev);
+	unsigned long flags;
+	u8 *tdata;
+	int i;
+
+	if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1)
+		return -ENODEV;
+
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report1->field[0],  0, chip << 2);
+	hid_set_field(report1->field[0],  1, 0x02);
+	hid_set_field(report1->field[0],  2, 0x00);
+	hid_set_field(report1->field[0],  3, 0x00);
+	hid_set_field(report1->field[0],  4, 0xb8 | tile);
+	hid_set_field(report1->field[0],  5, 0x00);
+	hid_set_field(report1->field[0],  6, 0x00);
+	hid_set_field(report1->field[0],  7, 0x40);
+	hid_set_field(report1->field[0],  8, 0x00);
+	hid_set_field(report1->field[0],  9, 0x00);
+	hid_set_field(report1->field[0], 10,   32);
+
+	hid_set_field(report2->field[0],  0, (chip << 2) | 0x01);
+	hid_set_field(report2->field[0],  1, 0x00);
+	hid_set_field(report2->field[0],  2, 0x00);
+	hid_set_field(report2->field[0],  3,   32);
+
+	tdata = data->fb_vbitmap + (tile * 4 + chip) * 64;
+	for (i = 0; i < 64; i++)
+		if (i < 32)
+			hid_set_field(report1->field[0], 11 + i, tdata[i]);
+		else
+			hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
+
+	usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
+	usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return 0;
+}
+
+/* Translate a single tile*/
+static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
+		int chip, int tile)
+{
+	int i, b, changed = 0;
+	u8 tdata[64];
+	u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
+
+	if (bpp = 1) {
+		for (b = 7; b >= 0; b--) {
+			const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
+			for (i = 0; i < 64; i++) {
+				tdata[i] <<= 1;
+				tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
+			}
+		}
+	} else if (bpp = 8) {
+		for (b = 7; b >= 0; b--) {
+			const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
+			for (i = 0; i < 64; i++) {
+				tdata[i] <<= 1;
+				tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
+			}
+		}
+	} else {
+		/* Oops, we should never get here! */
+		WARN_ON(1);
+		return 0;
+	}
+
+	for (i = 0; i < 64; i++)
+		if (tdata[i] != vdata[i]) {
+			changed = 1;
+			vdata[i] = tdata[i];
+		}
+	return changed;
+}
+
+void picolcd_fb_refresh(struct picolcd_data *data)
+{
+	if (data->fb_info)
+		schedule_delayed_work(&data->fb_info->deferred_work, 0);
+}
+
+/* Reconfigure LCD display */
+int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+	struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
+	int i, j;
+	unsigned long flags;
+	static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
+
+	if (!report || report->maxfield != 1)
+		return -ENODEV;
+
+	spin_lock_irqsave(&data->lock, flags);
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < report->field[0]->maxusage; j++)
+			if (j = 0)
+				hid_set_field(report->field[0], j, i << 2);
+			else if (j < sizeof(mapcmd))
+				hid_set_field(report->field[0], j, mapcmd[j]);
+			else
+				hid_set_field(report->field[0], j, 0);
+		usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	}
+
+	data->status |= PICOLCD_READY_FB;
+	spin_unlock_irqrestore(&data->lock, flags);
+
+	if (data->fb_bitmap) {
+		if (clear) {
+			memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE);
+			memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);
+		}
+		data->fb_force = 1;
+	}
+
+	/* schedule first output of framebuffer */
+	picolcd_fb_refresh(data);
+
+	return 0;
+}
+
+/* Update fb_vbitmap from the screen_base and send changed tiles to device */
+static void picolcd_fb_update(struct picolcd_data *data)
+{
+	int chip, tile, n;
+	unsigned long flags;
+
+	if (!data)
+		return;
+
+	spin_lock_irqsave(&data->lock, flags);
+	if (!(data->status & PICOLCD_READY_FB)) {
+		spin_unlock_irqrestore(&data->lock, flags);
+		picolcd_fb_reset(data, 0);
+	} else {
+		spin_unlock_irqrestore(&data->lock, flags);
+	}
+
+	/*
+	 * Translate the framebuffer into the format needed by the PicoLCD.
+	 * See display layout above.
+	 * Do this one tile after the other and push those tiles that changed.
+	 *
+	 * Wait for our IO to complete as otherwise we might flood the queue!
+	 */
+	n = 0;
+	for (chip = 0; chip < 4; chip++)
+		for (tile = 0; tile < 8; tile++)
+			if (picolcd_fb_update_tile(data->fb_vbitmap,
+					data->fb_bitmap, data->fb_bpp, chip, tile) ||
+				data->fb_force) {
+				n += 2;
+				if (!data->fb_info->par)
+					return; /* device lost! */
+				if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
+					usbhid_wait_io(data->hdev);
+					n = 0;
+				}
+				picolcd_fb_send_tile(data->hdev, chip, tile);
+			}
+	data->fb_force = false;
+	if (n)
+		usbhid_wait_io(data->hdev);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_fillrect(struct fb_info *info,
+		const struct fb_fillrect *rect)
+{
+	if (!info->par)
+		return;
+	sys_fillrect(info, rect);
+
+	schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_copyarea(struct fb_info *info,
+		const struct fb_copyarea *area)
+{
+	if (!info->par)
+		return;
+	sys_copyarea(info, area);
+
+	schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	if (!info->par)
+		return;
+	sys_imageblit(info, image);
+
+	schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/*
+ * this is the slow path from userspace. they can seek and write to
+ * the fb. it's inefficient to do anything less than a full screen draw
+ */
+static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	ssize_t ret;
+	if (!info->par)
+		return -ENODEV;
+	ret = fb_sys_write(info, buf, count, ppos);
+	if (ret >= 0)
+		schedule_delayed_work(&info->deferred_work, 0);
+	return ret;
+}
+
+static int picolcd_fb_blank(int blank, struct fb_info *info)
+{
+	if (!info->par)
+		return -ENODEV;
+	/* We let fb notification do this for us via lcd/backlight device */
+	return 0;
+}
+
+static void picolcd_fb_destroy(struct fb_info *info)
+{
+	struct picolcd_data *data = info->par;
+	u32 *ref_cnt = info->pseudo_palette;
+	int may_release;
+
+	info->par = NULL;
+	if (data)
+		data->fb_info = NULL;
+	fb_deferred_io_cleanup(info);
+
+	ref_cnt--;
+	mutex_lock(&info->lock);
+	(*ref_cnt)--;
+	may_release = !*ref_cnt;
+	mutex_unlock(&info->lock);
+	if (may_release) {
+		vfree((u8 *)info->fix.smem_start);
+		framebuffer_release(info);
+	}
+}
+
+static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	__u32 bpp      = var->bits_per_pixel;
+	__u32 activate = var->activate;
+
+	/* only allow 1/8 bit depth (8-bit is grayscale) */
+	*var = picolcdfb_var;
+	var->activate = activate;
+	if (bpp >= 8) {
+		var->bits_per_pixel = 8;
+		var->red.length     = 8;
+		var->green.length   = 8;
+		var->blue.length    = 8;
+	} else {
+		var->bits_per_pixel = 1;
+		var->red.length     = 1;
+		var->green.length   = 1;
+		var->blue.length    = 1;
+	}
+	return 0;
+}
+
+static int picolcd_set_par(struct fb_info *info)
+{
+	struct picolcd_data *data = info->par;
+	u8 *tmp_fb, *o_fb;
+	if (!data)
+		return -ENODEV;
+	if (info->var.bits_per_pixel = data->fb_bpp)
+		return 0;
+	/* switch between 1/8 bit depths */
+	if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
+		return -EINVAL;
+
+	o_fb   = data->fb_bitmap;
+	tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
+	if (!tmp_fb)
+		return -ENOMEM;
+
+	/* translate FB content to new bits-per-pixel */
+	if (info->var.bits_per_pixel = 1) {
+		int i, b;
+		for (i = 0; i < PICOLCDFB_SIZE; i++) {
+			u8 p = 0;
+			for (b = 0; b < 8; b++) {
+				p <<= 1;
+				p |= o_fb[i*8+b] ? 0x01 : 0x00;
+			}
+			tmp_fb[i] = p;
+		}
+		memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
+		info->fix.visual = FB_VISUAL_MONO01;
+		info->fix.line_length = PICOLCDFB_WIDTH / 8;
+	} else {
+		int i;
+		memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
+		for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
+			o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
+		info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+		info->fix.line_length = PICOLCDFB_WIDTH;
+	}
+
+	kfree(tmp_fb);
+	data->fb_bpp      = info->var.bits_per_pixel;
+	return 0;
+}
+
+/* Do refcounting on our FB and cleanup per worker if FB is
+ * closed after unplug of our device
+ * (fb_release holds info->lock and still touches info after
+ *  we return so we can't release it immediately.
+ */
+struct picolcd_fb_cleanup_item {
+	struct fb_info *info;
+	struct picolcd_fb_cleanup_item *next;
+};
+static struct picolcd_fb_cleanup_item *fb_pending;
+static DEFINE_SPINLOCK(fb_pending_lock);
+
+static void picolcd_fb_do_cleanup(struct work_struct *data)
+{
+	struct picolcd_fb_cleanup_item *item;
+	unsigned long flags;
+
+	do {
+		spin_lock_irqsave(&fb_pending_lock, flags);
+		item = fb_pending;
+		fb_pending = item ? item->next : NULL;
+		spin_unlock_irqrestore(&fb_pending_lock, flags);
+
+		if (item) {
+			u8 *fb = (u8 *)item->info->fix.smem_start;
+			/* make sure we do not race against fb core when
+			 * releasing */
+			mutex_lock(&item->info->lock);
+			mutex_unlock(&item->info->lock);
+			framebuffer_release(item->info);
+			vfree(fb);
+		}
+	} while (item);
+}
+
+static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
+
+static int picolcd_fb_open(struct fb_info *info, int u)
+{
+	u32 *ref_cnt = info->pseudo_palette;
+	ref_cnt--;
+
+	(*ref_cnt)++;
+	return 0;
+}
+
+static int picolcd_fb_release(struct fb_info *info, int u)
+{
+	u32 *ref_cnt = info->pseudo_palette;
+	ref_cnt--;
+
+	(*ref_cnt)++;
+	if (!*ref_cnt) {
+		unsigned long flags;
+		struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt;
+		item--;
+		spin_lock_irqsave(&fb_pending_lock, flags);
+		item->next = fb_pending;
+		fb_pending = item;
+		spin_unlock_irqrestore(&fb_pending_lock, flags);
+		schedule_work(&picolcd_fb_cleanup);
+	}
+	return 0;
+}
+
+/* Note this can't be const because of struct fb_info definition */
+static struct fb_ops picolcdfb_ops = {
+	.owner        = THIS_MODULE,
+	.fb_destroy   = picolcd_fb_destroy,
+	.fb_open      = picolcd_fb_open,
+	.fb_release   = picolcd_fb_release,
+	.fb_read      = fb_sys_read,
+	.fb_write     = picolcd_fb_write,
+	.fb_blank     = picolcd_fb_blank,
+	.fb_fillrect  = picolcd_fb_fillrect,
+	.fb_copyarea  = picolcd_fb_copyarea,
+	.fb_imageblit = picolcd_fb_imageblit,
+	.fb_check_var = picolcd_fb_check_var,
+	.fb_set_par   = picolcd_set_par,
+};
+
+
+/* Callback from deferred IO workqueue */
+static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+	picolcd_fb_update(info->par);
+}
+
+static const struct fb_deferred_io picolcd_fb_defio = {
+	.delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
+	.deferred_io = picolcd_fb_deferred_io,
+};
+
+
+/*
+ * The "fb_update_rate" sysfs attribute
+ */
+static ssize_t picolcd_fb_update_rate_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct picolcd_data *data = dev_get_drvdata(dev);
+	unsigned i, fb_update_rate = data->fb_update_rate;
+	size_t ret = 0;
+
+	for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
+		if (ret >= PAGE_SIZE)
+			break;
+		else if (i = fb_update_rate)
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
+		else
+			ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
+	if (ret > 0)
+		buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
+	return ret;
+}
+
+static ssize_t picolcd_fb_update_rate_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct picolcd_data *data = dev_get_drvdata(dev);
+	int i;
+	unsigned u;
+
+	if (count < 1 || count > 10)
+		return -EINVAL;
+
+	i = sscanf(buf, "%u", &u);
+	if (i != 1)
+		return -EINVAL;
+
+	if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
+		return -ERANGE;
+	else if (u = 0)
+		u = PICOLCDFB_UPDATE_RATE_DEFAULT;
+
+	data->fb_update_rate = u;
+	data->fb_defio.delay = HZ / data->fb_update_rate;
+	return count;
+}
+
+static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
+		picolcd_fb_update_rate_store);
+
+/* initialize Framebuffer device */
+int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+	struct device *dev = &data->hdev->dev;
+	struct fb_info *info = NULL;
+	int i, error = -ENOMEM;
+	u8 *fb_vbitmap = NULL;
+	u8 *fb_bitmap  = NULL;
+	u32 *palette;
+
+	fb_bitmap = vmalloc(PICOLCDFB_SIZE*8);
+	if (fb_bitmap = NULL) {
+		dev_err(dev, "can't get a free page for framebuffer\n");
+		goto err_nomem;
+	}
+
+	fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL);
+	if (fb_vbitmap = NULL) {
+		dev_err(dev, "can't alloc vbitmap image buffer\n");
+		goto err_nomem;
+	}
+
+	data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
+	data->fb_defio = picolcd_fb_defio;
+	/* The extra memory is:
+	 * - struct picolcd_fb_cleanup_item
+	 * - u32 for ref_count
+	 * - 256*u32 for pseudo_palette
+	 */
+	info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);
+	if (info = NULL) {
+		dev_err(dev, "failed to allocate a framebuffer\n");
+		goto err_nomem;
+	}
+
+	palette  = info->par + sizeof(struct picolcd_fb_cleanup_item);
+	*palette = 1;
+	palette++;
+	for (i = 0; i < 256; i++)
+		palette[i] = i > 0 && i < 16 ? 0xff : 0;
+	info->pseudo_palette = palette;
+	info->fbdefio = &data->fb_defio;
+	info->screen_base = (char __force __iomem *)fb_bitmap;
+	info->fbops = &picolcdfb_ops;
+	info->var = picolcdfb_var;
+	info->fix = picolcdfb_fix;
+	info->fix.smem_len   = PICOLCDFB_SIZE*8;
+	info->fix.smem_start = (unsigned long)fb_bitmap;
+	info->par = data;
+	info->flags = FBINFO_FLAG_DEFAULT;
+
+	data->fb_vbitmap = fb_vbitmap;
+	data->fb_bitmap  = fb_bitmap;
+	data->fb_bpp     = picolcdfb_var.bits_per_pixel;
+	error = picolcd_fb_reset(data, 1);
+	if (error) {
+		dev_err(dev, "failed to configure display\n");
+		goto err_cleanup;
+	}
+	error = device_create_file(dev, &dev_attr_fb_update_rate);
+	if (error) {
+		dev_err(dev, "failed to create sysfs attributes\n");
+		goto err_cleanup;
+	}
+	fb_deferred_io_init(info);
+	data->fb_info    = info;
+	error = register_framebuffer(info);
+	if (error) {
+		dev_err(dev, "failed to register framebuffer\n");
+		goto err_sysfs;
+	}
+	/* schedule first output of framebuffer */
+	data->fb_force = 1;
+	schedule_delayed_work(&info->deferred_work, 0);
+	return 0;
+
+err_sysfs:
+	fb_deferred_io_cleanup(info);
+	device_remove_file(dev, &dev_attr_fb_update_rate);
+err_cleanup:
+	data->fb_vbitmap = NULL;
+	data->fb_bitmap  = NULL;
+	data->fb_bpp     = 0;
+	data->fb_info    = NULL;
+
+err_nomem:
+	framebuffer_release(info);
+	vfree(fb_bitmap);
+	kfree(fb_vbitmap);
+	return error;
+}
+
+void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+	struct fb_info *info = data->fb_info;
+	u8 *fb_vbitmap = data->fb_vbitmap;
+
+	if (!info)
+		return;
+
+	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+	unregister_framebuffer(info);
+	data->fb_vbitmap = NULL;
+	data->fb_bitmap  = NULL;
+	data->fb_bpp     = 0;
+	data->fb_info    = NULL;
+	kfree(fb_vbitmap);
+}
+
+void picolcd_fb_unload()
+{
+	flush_work_sync(&picolcd_fb_cleanup);
+	WARN_ON(fb_pending);
+}
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c
new file mode 100644
index 0000000..64a067f
--- /dev/null
+++ b/drivers/hid/hid-picolcd_lcd.c
@@ -0,0 +1,106 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+#include "hid-picolcd.h"
+
+/*
+ * lcd class device
+ */
+static int picolcd_get_contrast(struct lcd_device *ldev)
+{
+	struct picolcd_data *data = lcd_get_data(ldev);
+	return data->lcd_contrast;
+}
+
+static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
+{
+	struct picolcd_data *data = lcd_get_data(ldev);
+	struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
+	unsigned long flags;
+
+	if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+		return -ENODEV;
+
+	data->lcd_contrast = contrast & 0x0ff;
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report->field[0], 0, data->lcd_contrast);
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+	return 0;
+}
+
+static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
+{
+	return fb && fb = picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
+}
+
+static struct lcd_ops picolcd_lcdops = {
+	.get_contrast   = picolcd_get_contrast,
+	.set_contrast   = picolcd_set_contrast,
+	.check_fb       = picolcd_check_lcd_fb,
+};
+
+int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
+{
+	struct device *dev = &data->hdev->dev;
+	struct lcd_device *ldev;
+
+	if (!report)
+		return -ENODEV;
+	if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+			report->field[0]->report_size != 8) {
+		dev_err(dev, "unsupported CONTRAST report");
+		return -EINVAL;
+	}
+
+	ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
+	if (IS_ERR(ldev)) {
+		dev_err(dev, "failed to register LCD\n");
+		return PTR_ERR(ldev);
+	}
+	ldev->props.max_contrast = 0x0ff;
+	data->lcd_contrast = 0xe5;
+	data->lcd = ldev;
+	picolcd_set_contrast(ldev, 0xe5);
+	return 0;
+}
+
+void picolcd_exit_lcd(struct picolcd_data *data)
+{
+	struct lcd_device *ldev = data->lcd;
+
+	data->lcd = NULL;
+	if (ldev)
+		lcd_device_unregister(ldev);
+}
+
+int picolcd_resume_lcd(struct picolcd_data *data)
+{
+	if (!data->lcd)
+		return 0;
+	return picolcd_set_contrast(data->lcd, data->lcd_contrast);
+}
+
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
new file mode 100644
index 0000000..9249c06
--- /dev/null
+++ b/drivers/hid/hid-picolcd_leds.c
@@ -0,0 +1,172 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   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, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+void picolcd_leds_set(struct picolcd_data *data)
+{
+	struct hid_report *report;
+	unsigned long flags;
+
+	if (!data->led[0])
+		return;
+	report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
+	if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+		return;
+
+	spin_lock_irqsave(&data->lock, flags);
+	hid_set_field(report->field[0], 0, data->led_state);
+	usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+	spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
+			enum led_brightness value)
+{
+	struct device *dev;
+	struct hid_device *hdev;
+	struct picolcd_data *data;
+	int i, state = 0;
+
+	dev  = led_cdev->dev->parent;
+	hdev = container_of(dev, struct hid_device, dev);
+	data = hid_get_drvdata(hdev);
+	for (i = 0; i < 8; i++) {
+		if (led_cdev != data->led[i])
+			continue;
+		state = (data->led_state >> i) & 1;
+		if (value = LED_OFF && state) {
+			data->led_state &= ~(1 << i);
+			picolcd_leds_set(data);
+		} else if (value != LED_OFF && !state) {
+			data->led_state |= 1 << i;
+			picolcd_leds_set(data);
+		}
+		break;
+	}
+}
+
+static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
+{
+	struct device *dev;
+	struct hid_device *hdev;
+	struct picolcd_data *data;
+	int i, value = 0;
+
+	dev  = led_cdev->dev->parent;
+	hdev = container_of(dev, struct hid_device, dev);
+	data = hid_get_drvdata(hdev);
+	for (i = 0; i < 8; i++)
+		if (led_cdev = data->led[i]) {
+			value = (data->led_state >> i) & 1;
+			break;
+		}
+	return value ? LED_FULL : LED_OFF;
+}
+
+int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
+{
+	struct device *dev = &data->hdev->dev;
+	struct led_classdev *led;
+	size_t name_sz = strlen(dev_name(dev)) + 8;
+	char *name;
+	int i, ret = 0;
+
+	if (!report)
+		return -ENODEV;
+	if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+			report->field[0]->report_size != 8) {
+		dev_err(dev, "unsupported LED_STATE report");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < 8; i++) {
+		led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
+		if (!led) {
+			dev_err(dev, "can't allocate memory for LED %d\n", i);
+			ret = -ENOMEM;
+			goto err;
+		}
+		name = (void *)(&led[1]);
+		snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
+		led->name = name;
+		led->brightness = 0;
+		led->max_brightness = 1;
+		led->brightness_get = picolcd_led_get_brightness;
+		led->brightness_set = picolcd_led_set_brightness;
+
+		data->led[i] = led;
+		ret = led_classdev_register(dev, data->led[i]);
+		if (ret) {
+			data->led[i] = NULL;
+			kfree(led);
+			dev_err(dev, "can't register LED %d\n", i);
+			goto err;
+		}
+	}
+	return 0;
+err:
+	for (i = 0; i < 8; i++)
+		if (data->led[i]) {
+			led = data->led[i];
+			data->led[i] = NULL;
+			led_classdev_unregister(led);
+			kfree(led);
+		}
+	return ret;
+}
+
+void picolcd_exit_leds(struct picolcd_data *data)
+{
+	struct led_classdev *led;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		led = data->led[i];
+		data->led[i] = NULL;
+		if (!led)
+			continue;
+		led_classdev_unregister(led);
+		kfree(led);
+	}
+}
+
+
-- 
1.7.8.6


^ permalink raw reply related

* Re: Gethering power management/policy hw drivers under drivers/power/? (Re: [RFC][PATCH v3 1/3] runt
From: Rafael J. Wysocki @ 2012-07-30 20:59 UTC (permalink / raw)
  To: Anton Vorontsov
  Cc: Alex Courbot, Jean Pihet, Greg Kroah-Hartman, David Woodhouse,
	Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Arnd Bergmann,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
	Liam Girdwood, MyungJoo Ham, linux-pm-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20120730024049.GA10442@lizard>

On Monday, July 30, 2012, Anton Vorontsov wrote:
> On Mon, Jul 30, 2012 at 10:51:42AM +0900, Alex Courbot wrote:
> [...]
> > On the other hand I have just noticed that the apparently unrelated
> > Adaptive Voltage Scaling driver just appeared in drivers/power/avs.
> > So if Anton and David are ok with this, maybe I could put the power
> > sequences code in its own subdirectory within drivers/power.
> 
> Well, currently drivers/power/ is indeed just for power supply class
> subsystem and drivers. But if the trend is to gather power management
> ("policy") stuff under one directory, i.e.
> 
> drivers/
>   power/
>     supplies/    <- former "power supply class and drivers"
>     regulators/
>     idle/
>     cpuidle/
>     cpufreq/
>     devfreq/
>     avs/
>     ...
> 
> That would probably make sense, we could easily see the big picture.
> But if we're not going to do this long-term,

Yes, we may do this eventually, but surely not right now.

Thanks,
Rafael

^ permalink raw reply

* Re: [RFC][PATCH v3 1/3] runtime interpreted power sequences
From: Stephen Warren @ 2012-07-30 22:26 UTC (permalink / raw)
  To: Rob Herring
  Cc: Alexandre Courbot, Stephen Warren, Thierry Reding, Simon Glass,
	Grant Likely, Greg Kroah-Hartman, Mark Brown, Arnd Bergmann,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <5016ABDD.5010809-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

On 07/30/2012 09:44 AM, Rob Herring wrote:
> On 07/27/2012 07:05 AM, Alexandre Courbot wrote:
>> Some device drivers (panel backlights especially) need to follow precise
>> sequences for powering on and off, involving gpios, regulators, PWMs
>> with a precise powering order and delays to respect between each steps.
>> These sequences are board-specific, and do not belong to a particular
>> driver - therefore they have been performed by board-specific hook
>> functions to far.
>>
>> With the advent of the device tree and of ARM kernels that are not
>> board-tied, we cannot rely on these board-specific hooks anymore but
>> need a way to implement these sequences in a portable manner. This patch
>> introduces a simple interpreter that can execute such power sequences
>> encoded either as platform data or within the device tree.
>>
> 
> Why not? We'll always have some amount of board code. The key is to
> limit parts that are just data. I'm not sure this is something that
> should be in devicetree.
> 
> Perhaps what is needed is a better way to hook into the driver like
> notifiers?

I would answer that by asking the reverse question - why should we have
to put some data in DT, and some data into board files still?

I'd certainly argue that the sequence of which GPIOs/regulators/PWMs to
manipulate is just data.

To be honest, if we're going to have to put some parts of a board's
configuration into board files anyway, then the entirety of DT seems
useless; I'd far rather see all the configuration in one cohesive place
than arbitrarily split into two/n different locations - that would make
everything harder to maintain.

^ permalink raw reply

* Re: [RFC][PATCH v3 1/3] runtime interpreted power sequences
From: Stephen Warren @ 2012-07-30 22:45 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Greg Kroah-Hartman, Mark Brown, Arnd Bergmann,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <1343390750-3642-2-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

On 07/27/2012 06:05 AM, Alexandre Courbot wrote:
> Some device drivers (panel backlights especially) need to follow precise
> sequences for powering on and off, involving gpios, regulators, PWMs
> with a precise powering order and delays to respect between each steps.
> These sequences are board-specific, and do not belong to a particular
> driver - therefore they have been performed by board-specific hook
> functions to far.

> diff --git a/Documentation/power/power_seq.txt b/Documentation/power/power_seq.txt

> +Sequences Format
> +----------------
> +Power sequences are a series of sequential steps during which an action is
> +performed on a resource. The supported resources so far are:
> +- GPIOs
> +- Regulators
> +- PWMs
> +
> +Each step designates a resource and the following parameters:
> +- Whether the step should enable or disable the resource,

At the highest level, i.e. what's being describe here, I wouldn't even
talk about enable/disable, but rather the action to perform on the
resource. The set of legal actions may be specific to the type of
resource in question. Admittedly enable/disable are common though.

> +- Delay to wait before performing the action,
> +- Delay to wait after performing the action.

I don't see a need to have a delay both before and after an action;
except at the start of the sequence, step n's post-delay is at the same
point in the sequence as step n+1's pre-delay. Perhaps make a "delay"
step type?

> +Both new resources and parameters can be introduced, but the goal is of course
> +to keep things as simple and compact as possible.

> +The platform data is a simple array of platform_power_seq_step instances, each

Rather than jumping straight into platform data here, I'd expect an
enumeration of legal resource types, and what actions can be performed
on each, followed by a description of a sequence (very simply, just a
list of actions and their parameters). This could be followed by a
section describing the mapping of the abstract concepts to concrete
platform data representation (and concrete device tree representation).

> +instance describing a step. The type as well as one of id or gpio members
> +(depending on the type) must be specified. The last step must be of type
> +POWER_SEQ_STOP.

I'd certainly suggest having a step count rather than a sentinel value
in the list.

> Regulator and PWM resources are identified by name. GPIO are
> +identified by number.

That's a little implementation-specific. I guess it's entirely true for
a platform data representation, but not when mapping this into device tree.

> +Usage by Drivers and Resources Management
> +-----------------------------------------
> +Power sequences make use of resources that must be properly allocated and
> +managed. The power_seq_build() function takes care of resolving the resources as
> +they are met in the sequence and to allocate them if needed:
> +
> +power_seq *power_seq_build(struct device *dev, power_seq_resources *ress,
> +			   platform_power_seq *pseq);
> +
> +You will need an instance of power_seq_resources to keep track of the resources
> +that are already allocated. On success, the function returns a devm allocated
> +resolved sequence that is ready to be passed to power_seq_run(). In case of
> +failure, and error code is returned.

If the result is devm-allocated, the function probably should be named
devm_power_seq_build().

I wonder if using the same structure/array as input and output would
simplify the API; the platform data would fill in the fields mentioned
above, and power_seq_build() would parse those, then set other fields in
the same structs to the looked-up handle values?

> +Finally, some resources that cannot be allocated through devm need to be freed
> +manually. Therefore, be sure to call power_seq_free_resources() in your device
> +remove function:
> +
> +void power_seq_free_resources(power_seq_resources *ress);

You can make a custom devm free routine for the power_seq_resources
itself, so the overall free'ing of the content can be triggered by devm,
but the free'ing function can then call whatever non-devm APIs it wants
for the non-devm-allocated members.

> +Device tree
> +-----------
> +All the same, power sequences can be encoded as device tree nodes. The following
> +properties and nodes are equivalent to the platform data defined previously:
> +
> +		power-supply = <&mydevice_reg>;
> +		enable-gpio = <&gpio 6 0>;
> +
> +		power-on-sequence {
> +			regulator@0 {

As Thierry mentioned, the step nodes should be named for the type of
object they are (a "step") not the type or name of resource they act
upon ("regulator" or "gpio").

If the nodes have a unit address (i.e. end in "@n"), which they will
have to if all named "step" and there's more than one of them, then they
will need a matching reg property. Equally, the parent node will need
#address-cells and #size-cells too. So, the last couple lines would be:

		power-on-sequence {
			#address-cells = <1>;
			#size-cells = <0>;
			step@0 {
				reg = <0>;

> +				id = "power";

"id" is usually a name or identifier. I think you mean "type" or perhaps
"action" here:

				type = "regulator";
				action = "enable";

or:

				action = "enable-regulator";

I wish we had integer constants in DT so we didn't have to do all this
with strings. Sigh.

> +				enable;
> +				post-delay = <10>;
> +			};
> +			gpio@1 {
> +				id = "enable-gpio";
> +				enable;
> +			};
> +		};
> +
> +Note that first, the phandles of the regulator and gpio used in the sequences
> +are defined as properties. Then the sequence references them through the id
> +property of every step. The name of sub-properties defines the type of the step.
> +Valid names are "regulator", "gpio" and "pwm". Steps must be numbered
> +sequentially.

Oh I see. That's a little confusing. Why not just reference the relevant
resources directly in each step; something more like:

		gpio@1 {
			action = "enable-gpio";
			gpio = <&gpio 1 0>;
		};

I guess that might make parsing/building a little harder, since you'd
have to detect when you'd already done gpio_request() on a given GPIO
and not repeat it or something like that, but to me this makes the DT a
lot easier to comprehend.

^ permalink raw reply

* RE: [PATCH 1/3] Move FIMD register headers to include/video/
From: Jingoo Han @ 2012-07-31  0:47 UTC (permalink / raw)
  To: 'Leela Krishna Amudala'
  Cc: linux-arm-kernel, linux-samsung-soc, dri-devel, linux-fbdev,
	ben-linux, inki.dae, kgene.kim, joshi, 'Marek Szyprowski',
	'Jingoo Han'
In-Reply-To: <CAL1wa8e7Joghf9S=SgbQLJFWcYOrP5uK6qYXsV_N6rX1MVEALg@mail.gmail.com>

On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
> 
> Hello Jingoo Han,
> 
> On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> >>
> >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> >> to include/video/samsung_fimd.h
> >>
> >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> >> ---
> >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403 -----------------
> >>  include/video/samsung_fimd.h                    |  533 +++++++++++++++++++++++
> >>  3 files changed, 533 insertions(+), 562 deletions(-)
> >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> >>  create mode 100644 include/video/samsung_fimd.h
> >>
> >> +*/
> >> +
> >> +/*FIMD V8 REG OFFSET */
> >> +#define FIMD_V8_VIDTCON0     (0x20010)
> >> +#define FIMD_V8_VIDTCON1     (0x20014)
> >> +#define FIMD_V8_VIDTCON2     (0x20018)
> >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> >> +#define FIMD_V8_VIDCON1              (0x20004)


How about using soc_is_exynos5250()?

+#define VIDTCON0				(soc_is_exynos5250() ? \
+						(0x20010) : (0x10))

In this case, the FIMD driver does not need to change.
Also, one binary is available.


Best regards,
Jingoo Han


> >
> >
> > CC'ed Marek.
> >
> > To Leela Krishna Amudala,
> >
> > Don't add these definitions for FIMD_V8_xxx registers, which arenot related to current "regs-fb-v4.h
> and regs-fb.h".
> > Just "move" and "merge" regs-fb-v4.h and regs-fb.h to one header file, not "add" new definitions.
> > If you want to add these definitions, please make new patch for this.
> >
> 
> Will do it in the suggested way,
> 
> 
> > Also, "#define FIMD_V8_xxx" is ugly.
> > I think that there is better way.
> > Please, find other way.
> >
> >
> 
> I used FIMD_V8_xxx instead of  EXYNOS5_FIMD_*, because in future,
> there is a possibility that version 8 FIMD can be used in other
> application processors also.
> Thanks for reviewing the patch.
> 
> Best Wishes,
> Leela Krishna.
> 
> >> --
> >> 1.7.0.4
> >
> > --
> > 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 1/3] Move FIMD register headers to include/video/
From: Marek Szyprowski @ 2012-07-31  6:28 UTC (permalink / raw)
  To: 'Jingoo Han', 'Leela Krishna Amudala'
  Cc: linux-arm-kernel, linux-samsung-soc, dri-devel, linux-fbdev,
	ben-linux, inki.dae, kgene.kim, joshi
In-Reply-To: <000a01cd6eb6$2113a050$633ae0f0$%han@samsung.com>

Hello,

On Tuesday, July 31, 2012 2:48 AM Jingoo Han wrote:

> On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
> >
> > Hello Jingoo Han,
> >
> > On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> > >>
> > >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> > >> to include/video/samsung_fimd.h
> > >>
> > >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> > >> ---
> > >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> > >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403 -----------------
> > >>  include/video/samsung_fimd.h                    |  533 +++++++++++++++++++++++
> > >>  3 files changed, 533 insertions(+), 562 deletions(-)
> > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> > >>  create mode 100644 include/video/samsung_fimd.h
> > >>
> > >> +*/
> > >> +
> > >> +/*FIMD V8 REG OFFSET */
> > >> +#define FIMD_V8_VIDTCON0     (0x20010)
> > >> +#define FIMD_V8_VIDTCON1     (0x20014)
> > >> +#define FIMD_V8_VIDTCON2     (0x20018)
> > >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> > >> +#define FIMD_V8_VIDCON1              (0x20004)
> 
> 
> How about using soc_is_exynos5250()?
> 
> +#define VIDTCON0				(soc_is_exynos5250() ? \
> +						(0x20010) : (0x10))
> 
> In this case, the FIMD driver does not need to change.
> Also, one binary is available.

Please don't mix two methods of runtime detection. FIMD driver (s3c-fb) already
has runtime hw detection based on platform device id. Adding such detection for 
exynos5 to DRM FIMD driver should not be a big issue too.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center



^ permalink raw reply

* Re: [PATCH 1/3] Move FIMD register headers to include/video/
From: Tomasz Figa @ 2012-07-31  6:37 UTC (permalink / raw)
  To: Jingoo Han
  Cc: 'Leela Krishna Amudala', linux-arm-kernel,
	linux-samsung-soc, dri-devel, linux-fbdev, ben-linux, inki.dae,
	kgene.kim, joshi, 'Marek Szyprowski'
In-Reply-To: <000a01cd6eb6$2113a050$633ae0f0$%han@samsung.com>

Hi,

On Tuesday 31 of July 2012 at 09:47:57, Jingoo Han wrote:
> On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
> > Hello Jingoo Han,
> > 
> > On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> > >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> > >> to include/video/samsung_fimd.h
> > >> 
> > >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> > >> ---
> > >> 
> > >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> > >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403
> > >>  -----------------
> > >>  include/video/samsung_fimd.h                    |  533
> > >>  +++++++++++++++++++++++ 3 files changed, 533 insertions(+), 562
> > >>  deletions(-)
> > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> > >>  create mode 100644 include/video/samsung_fimd.h
> > >> 
> > >> +*/
> > >> +
> > >> +/*FIMD V8 REG OFFSET */
> > >> +#define FIMD_V8_VIDTCON0     (0x20010)
> > >> +#define FIMD_V8_VIDTCON1     (0x20014)
> > >> +#define FIMD_V8_VIDTCON2     (0x20018)
> > >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> > >> +#define FIMD_V8_VIDCON1              (0x20004)
> 
> How about using soc_is_exynos5250()?
> 
> +#define VIDTCON0				(soc_is_exynos5250() ? \
> +						(0x20010) : (0x10))
> 
> In this case, the FIMD driver does not need to change.
> Also, one binary is available.
> 

This would look good indeed, but there are some drawbacks:
- it would not scale nicely for future SoCs using the new FIMD
- it would add the overhead of checking SoC ID for every access to affected 
registers (at least 1 load, 1 AND, 1 compare, 1 move and 1 conditional OR, so 
5 instructions in total, possibly even more, as opposed to a single load from 
a variant struct).

I would stay with the way used in s3c-fb driver, using variant structs 
describing FIMD revisions.

Best regards,
Tomasz Figa

> 
> Best regards,
> Jingoo Han
> 
> > > CC'ed Marek.
> > > 
> > > To Leela Krishna Amudala,
> > > 
> > > Don't add these definitions for FIMD_V8_xxx registers, which arenot
> > > related to current "regs-fb-v4.h> 
> > and regs-fb.h".
> > 
> > > Just "move" and "merge" regs-fb-v4.h and regs-fb.h to one header file,
> > > not "add" new definitions. If you want to add these definitions, please
> > > make new patch for this.> 
> > Will do it in the suggested way,
> > 
> > > Also, "#define FIMD_V8_xxx" is ugly.
> > > I think that there is better way.
> > > Please, find other way.
> > 
> > I used FIMD_V8_xxx instead of  EXYNOS5_FIMD_*, because in future,
> > there is a possibility that version 8 FIMD can be used in other
> > application processors also.
> > Thanks for reviewing the patch.
> > 
> > Best Wishes,
> > Leela Krishna.
> > 
> > >> --
> > >> 1.7.0.4
> > > 
> > > --
> > > 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
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
> 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 1/3] Move FIMD register headers to include/video/
From: Leela Krishna Amudala @ 2012-07-31  7:24 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Jingoo Han, linux-arm-kernel, linux-samsung-soc, dri-devel,
	linux-fbdev, ben-linux, inki.dae, kgene.kim, joshi
In-Reply-To: <032201cd6ee5$ad4a0d50$07de27f0$%szyprowski@samsung.com>

Hello Marek,

On Tue, Jul 31, 2012 at 11:58 AM, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> Hello,
>
> On Tuesday, July 31, 2012 2:48 AM Jingoo Han wrote:
>
>> On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
>> >
>> > Hello Jingoo Han,
>> >
>> > On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
>> > > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
>> > >>
>> > >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
>> > >> to include/video/samsung_fimd.h
>> > >>
>> > >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
>> > >> ---
>> > >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
>> > >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403 -----------------
>> > >>  include/video/samsung_fimd.h                    |  533 +++++++++++++++++++++++
>> > >>  3 files changed, 533 insertions(+), 562 deletions(-)
>> > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
>> > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
>> > >>  create mode 100644 include/video/samsung_fimd.h
>> > >>
>> > >> +*/
>> > >> +
>> > >> +/*FIMD V8 REG OFFSET */
>> > >> +#define FIMD_V8_VIDTCON0     (0x20010)
>> > >> +#define FIMD_V8_VIDTCON1     (0x20014)
>> > >> +#define FIMD_V8_VIDTCON2     (0x20018)
>> > >> +#define FIMD_V8_VIDTCON3     (0x2001C)
>> > >> +#define FIMD_V8_VIDCON1              (0x20004)
>>
>>
>> How about using soc_is_exynos5250()?
>>
>> +#define VIDTCON0                             (soc_is_exynos5250() ? \
>> +                                             (0x20010) : (0x10))
>>
>> In this case, the FIMD driver does not need to change.
>> Also, one binary is available.
>
> Please don't mix two methods of runtime detection. FIMD driver (s3c-fb) already
> has runtime hw detection based on platform device id. Adding such detection for
> exynos5 to DRM FIMD driver should not be a big issue too.
>

I have code ready for DRM-FIMD driver with the above approach (getting
FIMD version by runtime detection)
will post the patches soon along with arch side patches once the arch
changes are ready.
and I'll continue the same name FIMD_V8_xxx for macro names.

> Best regards
> --
> Marek Szyprowski
> Samsung Poland R&D Center
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" 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/7] HID: picoLCD updates
From: David Herrmann @ 2012-07-31  7:26 UTC (permalink / raw)
  To: Bruno Prémont
  Cc: linux-input, linux-kernel, Jiri Kosina, Jaya Kumar, linux-fbdev
In-Reply-To: <20120730213656.0a9f6d30@neptune.home>

Hi Bruno

On Mon, Jul 30, 2012 at 9:36 PM, Bruno Prémont
<bonbons@linux-vserver.org> wrote:
> Hi,
>
> This series updates picoLCD driver:
> - split the driver functions into separate files which get included
>   depending on Kconfig selection
>   (implementation for CIR using RC_CORE will follow later)
> - drop private framebuffer refcounting in favor of refcounting added
>   to fb_info some time ago
> - fix various bugs issues
> - disabled firmware version checking in probe() as it does not work
>   anymore since commit 4ea5454203d991ec85264f64f89ca8855fce69b0
>   [HID: Fix race condition between driver core and ll-driver]
>
> Note: I still get weird behavior on quick unbind/bind sequences
> issued via sysfs (CONFIG_SMP=n system) that are triggered by framebuffer
> support and apparently more specifically fb_defio part of it.
>
> Unfortunately I'm out of ideas as to how to track down the problem which
> shows either as SLAB corruption (detected with SLUB debugging, e.g.
>
> [ 6383.521833] ======================================> [ 6383.530020] BUG kmalloc-64 (Not tainted): Object already free
> [ 6383.530020] -----------------------------------------------------------------------------
> [ 6383.530020]
> [ 6383.530020] INFO: Slab 0xdde0ea20 objectsQ used@ fp=0xcef516e0 flags=0x40000080
> [ 6383.530020] INFO: Object 0xcef51190 @offset@0 fp=0xcef51f50
> [ 6383.530020]
> [ 6383.530020] Bytes b4 cef51180: cc cc cc cc d0 12 f5 ce 5a 5a 5a 5a 5a 5a 5a 5a  ........ZZZZZZZZ
> [ 6383.530020] Object cef51190: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
> [ 6383.530020] Object cef511a0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
> [ 6383.530020] Object cef511b0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b  kkkkkkkkkkkkkkkk
> [ 6383.530020] Object cef511c0: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5  kkkkkkkkkkkkkkk.
> [ 6383.530020] Redzone cef511d0: bb bb bb bb                                      ....
> [ 6383.530020] Padding cef511d8: 5a 5a 5a 5a 5a 5a 5a 5a                          ZZZZZZZZ
> [ 6383.530020] Pid: 1922, comm: bash Not tainted 3.5.0-jupiter-00003-g8d858b1-dirty #2
> [ 6383.530020] Call Trace:
> [ 6383.530020]  [<c10bd3cc>] print_trailer+0x11c/0x130
> [ 6383.530020]  [<c10bd415>] object_err+0x35/0x40
> [ 6383.530020]  [<c10be809>] free_debug_processing+0x99/0x200
> [ 6383.530020]  [<c10bf77e>] __slab_free+0x2e/0x280
> [ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
> [ 6383.530020]  [<c1322870>] ? __usbhid_submit_report+0xc0/0x3c0
> [ 6383.530020]  [<c10bfbda>] ? kfree+0xfa/0x110
> [ 6383.530020]  [<de932aa4>] ? picolcd_debug_out_report+0x8c4/0x8e0 [hid_picolcd]
> [ 6383.530020]  [<c10bfbda>] kfree+0xfa/0x110
> [ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
> [ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
> [ 6383.530020]  [<c1322284>] ? hid_submit_out+0xa4/0x120
> [ 6383.530020]  [<c1322284>] hid_submit_out+0xa4/0x120
> [ 6383.530020]  [<c1322908>] __usbhid_submit_report+0x158/0x3c0
> [ 6383.530020]  [<c1322c2b>] usbhid_submit_report+0x1b/0x30
> [ 6383.530020]  [<de930789>] picolcd_fb_reset+0xb9/0x180 [hid_picolcd]
> [ 6383.530020]  [<de930f1d>] picolcd_init_framebuffer+0x20d/0x2e0 [hid_picolcd]
> [ 6383.530020]  [<de92fb9c>] picolcd_probe+0x3cc/0x580 [hid_picolcd]
> [ 6383.530020]  [<c1319147>] hid_device_probe+0x67/0xf0
> [ 6383.530020]  [<c1282f97>] ? driver_sysfs_add+0x57/0x80
> [ 6383.530020]  [<c128329d>] driver_probe_device+0xbd/0x1c0
> [ 6383.530020]  [<c1318a1b>] ? hid_match_device+0x7b/0x90
> [ 6383.530020]  [<c12821e5>] driver_bind+0x75/0xd0
> [ 6383.530020]  [<c1282170>] ? driver_unbind+0x90/0x90
> [ 6383.530020]  [<c12818b7>] drv_attr_store+0x27/0x30
> [ 6383.530020]  [<c1114aec>] sysfs_write_file+0xac/0xf0
> [ 6383.530020]  [<c10c794c>] vfs_write+0x9c/0x130
> [ 6383.530020]  [<c10d4a1f>] ? sys_dup3+0x11f/0x160
> [ 6383.530020]  [<c1114a40>] ? sysfs_poll+0x90/0x90
> [ 6383.530020]  [<c10c7bbd>] sys_write+0x3d/0x70
> [ 6383.530020]  [<c13f2557>] sysenter_do_call+0x12/0x26
> [ 6383.530020] FIX kmalloc-64: Object at 0xcef51190 not freed
>
> or worse spontaneous reboot of the system without any trace on netconsole or
> serial console.
>
> echo $devid > bind; echo $devid > unbind
> or
> echo $devid > bind; echo $devid > unbind; sleep 0.2; echo $devid > bind; echo $devid > unbind
>
> is sufficient to trigger the above issue while waiting a few seconds between bind and unbind
> shows no sign of trouble.
>
> Suggestions as to how to debug this and fix it are welcome!

This is not directly related to this patchset, but did you fix the
locking issue with hid-core? It is still on my todo-list but I haven't
gotten around fixing it, yet. However, I plan on fixing it this
summer, but if picolcd does not require it, it's probably not worth
the work.

Regards
David

^ permalink raw reply

* Re: [PATCH 0/7] HID: picoLCD updates
From: Bruno Prémont @ 2012-07-31  7:59 UTC (permalink / raw)
  To: David Herrmann
  Cc: linux-input, linux-kernel, Jiri Kosina, Jaya Kumar, linux-fbdev
In-Reply-To: <CANq1E4Tb1Er+O54rN3OteMXqeXAXDr4Z-dvY+RQxZW_NdAfifw@mail.gmail.com>

Hi David,

On Tue, 31 Jul 2012 09:26:07 David Herrmann wrote:
> This is not directly related to this patchset, but did you fix the
> locking issue with hid-core? It is still on my todo-list but I haven't
> gotten around fixing it, yet. However, I plan on fixing it this
> summer, but if picolcd does not require it, it's probably not worth
> the work.

I worked around it with patch 6/7 as in disabling the version check.

The other probing steps don't expect a response from the hardware, thus
are not affected.

But it would definitely be helpful to be able to revert that one.
Understanding and fixing the FB-related misbehavior on quick bind/unbind
seems more important though.


Not related with this, getting a return value from
usbhid_submit_report() as in -EAGAIN or -ENODEV would help in order
to avoid the ugly 
  hid-picolcd 0003:04D8:C002.0003: usb_submit_urb(out) failed: -19
lines in kernel log and stopping interaction with hardware when it's
gone already or giving it time to consume its queue when there is lots
of data being sent.


Regards,
Bruno

^ permalink raw reply

* Re: [PATCH 1/3] Move FIMD register headers to include/video/
From: Jingoo Han @ 2012-07-31  8:09 UTC (permalink / raw)
  To: 'Tomasz Figa'
  Cc: 'Leela Krishna Amudala', linux-arm-kernel,
	linux-samsung-soc, dri-devel, linux-fbdev, ben-linux, inki.dae,
	kgene.kim, joshi, 'Marek Szyprowski'
In-Reply-To: <2073177.5bSgcKjVcU@easynote>


On Tuesday, July 31, 2012 3:37 PM, Tomasz Figa wrote:
> 
> Hi,
> 
> On Tuesday 31 of July 2012 at 09:47:57, Jingoo Han wrote:
> > On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
> > > Hello Jingoo Han,
> > >
> > > On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > > > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> > > >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> > > >> to include/video/samsung_fimd.h
> > > >>
> > > >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> > > >> ---
> > > >>
> > > >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> > > >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403
> > > >>  -----------------
> > > >>  include/video/samsung_fimd.h                    |  533
> > > >>  +++++++++++++++++++++++ 3 files changed, 533 insertions(+), 562
> > > >>  deletions(-)
> > > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> > > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> > > >>  create mode 100644 include/video/samsung_fimd.h
> > > >>
> > > >> +*/
> > > >> +
> > > >> +/*FIMD V8 REG OFFSET */
> > > >> +#define FIMD_V8_VIDTCON0     (0x20010)
> > > >> +#define FIMD_V8_VIDTCON1     (0x20014)
> > > >> +#define FIMD_V8_VIDTCON2     (0x20018)
> > > >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> > > >> +#define FIMD_V8_VIDCON1              (0x20004)
> >
> > How about using soc_is_exynos5250()?
> >
> > +#define VIDTCON0				(soc_is_exynos5250() ? \
> > +						(0x20010) : (0x10))
> >
> > In this case, the FIMD driver does not need to change.
> > Also, one binary is available.
> >
> 
> This would look good indeed, but there are some drawbacks:
> - it would not scale nicely for future SoCs using the new FIMD

I don't think so.
Currently, one clear thing is that only Exynos5250 FIMD has VIDTCON0 offset
with 0x20000.
We cannot know whether future SoCs have VIDTCON0 offset with 0x20000.

Also, VIDTCON offset is not relevant to FIMD version.
There was the case that FIMD version 7 has VIDTCON0 offset with 0x20000.


> - it would add the overhead of checking SoC ID for every access to affected
> registers (at least 1 load, 1 AND, 1 compare, 1 move and 1 conditional OR, so
> 5 instructions in total, possibly even more, as opposed to a single load from
> a variant struct).

I don't think that it's critical.
VIDTCON registers are used for controlling LCD timing values.
These registers are just used for probing and resuming.
It is not used at running time.

Anyway, your point would be considered.
Thank you.

> 
> I would stay with the way used in s3c-fb driver, using variant structs
> describing FIMD revisions.
> 
> Best regards,
> Tomasz Figa
> 
> >
> > Best regards,
> > Jingoo Han
> >
> > > > CC'ed Marek.
> > > >
> > > > To Leela Krishna Amudala,
> > > >
> > > > Don't add these definitions for FIMD_V8_xxx registers, which arenot
> > > > related to current "regs-fb-v4.h>
> > > and regs-fb.h".
> > >
> > > > Just "move" and "merge" regs-fb-v4.h and regs-fb.h to one header file,
> > > > not "add" new definitions. If you want to add these definitions, please
> > > > make new patch for this.>
> > > Will do it in the suggested way,
> > >
> > > > Also, "#define FIMD_V8_xxx" is ugly.
> > > > I think that there is better way.
> > > > Please, find other way.
> > >
> > > I used FIMD_V8_xxx instead of  EXYNOS5_FIMD_*, because in future,
> > > there is a possibility that version 8 FIMD can be used in other
> > > application processors also.
> > > Thanks for reviewing the patch.
> > >
> > > Best Wishes,
> > > Leela Krishna.
> > >
> > > >> --
> > > >> 1.7.0.4
> > > >
> > > > --
> > > > 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
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
> > 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 1/3] Move FIMD register headers to include/video/
From: Jingoo Han @ 2012-07-31  8:19 UTC (permalink / raw)
  To: 'Marek Szyprowski', 'Leela Krishna Amudala'
  Cc: linux-arm-kernel, linux-samsung-soc, dri-devel, linux-fbdev,
	ben-linux, inki.dae, kgene.kim, joshi, 'Jingoo Han'
In-Reply-To: <032201cd6ee5$ad4a0d50$07de27f0$%szyprowski@samsung.com>

On Tuesday, July 31, 2012 3:28 PM Marek Szyprowski wrote:
> 
> Hello,
> 
> On Tuesday, July 31, 2012 2:48 AM Jingoo Han wrote:
> 
> > On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
> > >
> > > Hello Jingoo Han,
> > >
> > > On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > > > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> > > >>
> > > >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> > > >> to include/video/samsung_fimd.h
> > > >>
> > > >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> > > >> ---
> > > >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> > > >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403 -----------------
> > > >>  include/video/samsung_fimd.h                    |  533 +++++++++++++++++++++++
> > > >>  3 files changed, 533 insertions(+), 562 deletions(-)
> > > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> > > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> > > >>  create mode 100644 include/video/samsung_fimd.h
> > > >>
> > > >> +*/
> > > >> +
> > > >> +/*FIMD V8 REG OFFSET */
> > > >> +#define FIMD_V8_VIDTCON0     (0x20010)
> > > >> +#define FIMD_V8_VIDTCON1     (0x20014)
> > > >> +#define FIMD_V8_VIDTCON2     (0x20018)
> > > >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> > > >> +#define FIMD_V8_VIDCON1              (0x20004)
> >
> >
> > How about using soc_is_exynos5250()?
> >
> > +#define VIDTCON0				(soc_is_exynos5250() ? \
> > +						(0x20010) : (0x10))
> >
> > In this case, the FIMD driver does not need to change.
> > Also, one binary is available.
> 
> Please don't mix two methods of runtime detection. FIMD driver (s3c-fb) already
> has runtime hw detection based on platform device id. Adding such detection for
> exynos5 to DRM FIMD driver should not be a big issue too.

Marek,
Then, do you want use like this?

#define VIDTCON0				(0x10)
+#define FIMD_V8_VIDTCON0			(0x20010)

--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -1924,7 +1924,7 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
 static struct s3c_fb_driverdata s3c_fb_data_exynos5 = {
        .variant = {
                .nr_windows     = 5,
-               .vidtcon        = VIDTCON0,
+               .vidtcon        = FIMD_V8_VIDTCON0,


> 
> Best regards
> --
> Marek Szyprowski
> Samsung Poland R&D Center



^ permalink raw reply

* RE: [PATCH 1/3] Move FIMD register headers to include/video/
From: Marek Szyprowski @ 2012-07-31  8:32 UTC (permalink / raw)
  To: 'Jingoo Han', 'Leela Krishna Amudala'
  Cc: linux-arm-kernel, linux-samsung-soc, dri-devel, linux-fbdev,
	ben-linux, inki.dae, kgene.kim, joshi
In-Reply-To: <000701cd6ef5$24315c70$6c941550$%han@samsung.com>

Hello,

On Tuesday, July 31, 2012 10:19 AM Jingoo Han wrote:

> On Tuesday, July 31, 2012 3:28 PM Marek Szyprowski wrote:
> >
> > Hello,
> >
> > On Tuesday, July 31, 2012 2:48 AM Jingoo Han wrote:
> >
> > > On Monday, July 30, 2012 8:16 PM, Leela Krishna Amudala wrote:
> > > >
> > > > Hello Jingoo Han,
> > > >
> > > > On Mon, Jul 30, 2012 at 2:23 PM, Jingoo Han <jg1.han@samsung.com> wrote:
> > > > > On Monday, July 30, 2012 5:45 PM, Leela Krishna Amudala wrote:
> > > > >>
> > > > >> Moved the contents of regs-fb-v4.h and regs-fb.h from arch side
> > > > >> to include/video/samsung_fimd.h
> > > > >>
> > > > >> Signed-off-by: Leela Krishna Amudala <l.krishna@samsung.com>
> > > > >> ---
> > > > >>  arch/arm/plat-samsung/include/plat/regs-fb-v4.h |  159 -------
> > > > >>  arch/arm/plat-samsung/include/plat/regs-fb.h    |  403 -----------------
> > > > >>  include/video/samsung_fimd.h                    |  533 +++++++++++++++++++++++
> > > > >>  3 files changed, 533 insertions(+), 562 deletions(-)
> > > > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb-v4.h
> > > > >>  delete mode 100644 arch/arm/plat-samsung/include/plat/regs-fb.h
> > > > >>  create mode 100644 include/video/samsung_fimd.h
> > > > >>
> > > > >> +*/
> > > > >> +
> > > > >> +/*FIMD V8 REG OFFSET */
> > > > >> +#define FIMD_V8_VIDTCON0     (0x20010)
> > > > >> +#define FIMD_V8_VIDTCON1     (0x20014)
> > > > >> +#define FIMD_V8_VIDTCON2     (0x20018)
> > > > >> +#define FIMD_V8_VIDTCON3     (0x2001C)
> > > > >> +#define FIMD_V8_VIDCON1              (0x20004)
> > >
> > >
> > > How about using soc_is_exynos5250()?
> > >
> > > +#define VIDTCON0				(soc_is_exynos5250() ? \
> > > +						(0x20010) : (0x10))
> > >
> > > In this case, the FIMD driver does not need to change.
> > > Also, one binary is available.
> >
> > Please don't mix two methods of runtime detection. FIMD driver (s3c-fb) already
> > has runtime hw detection based on platform device id. Adding such detection for
> > exynos5 to DRM FIMD driver should not be a big issue too.
> 
> Marek,
> Then, do you want use like this?
> 
> #define VIDTCON0				(0x10)
> +#define FIMD_V8_VIDTCON0			(0x20010)
> 
> --- a/drivers/video/s3c-fb.c
> +++ b/drivers/video/s3c-fb.c
> @@ -1924,7 +1924,7 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
>  static struct s3c_fb_driverdata s3c_fb_data_exynos5 = {
>         .variant = {
>                 .nr_windows     = 5,
> -               .vidtcon        = VIDTCON0,
> +               .vidtcon        = FIMD_V8_VIDTCON0,

Yes, this method looks good imo. Maybe even having something like vidtcon_base in 
variant structure will be enough to cover all VIDTCON0-3 registers.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox